diff --git a/ext/v8/accessor-signature.cc b/ext/v8/accessor-signature.cc new file mode 100644 index 00000000..2d8cd149 --- /dev/null +++ b/ext/v8/accessor-signature.cc @@ -0,0 +1,13 @@ +#include "rr.h" + +namespace rr { + VALUE AccessorSignature::New(int argc, VALUE argv[], VALUE self) { + VALUE r_isolate; + VALUE r_receiver; + rb_scan_args(argc, argv, "11", &r_isolate, &r_receiver); + + Isolate isolate(r_isolate); + Locker lock(isolate); + return AccessorSignature(isolate, v8::AccessorSignature::New(isolate, FunctionTemplate(r_receiver))); + } +} diff --git a/ext/v8/accessor-signature.h b/ext/v8/accessor-signature.h new file mode 100644 index 00000000..c0412138 --- /dev/null +++ b/ext/v8/accessor-signature.h @@ -0,0 +1,22 @@ +// -*- mode: c++ -*- +#ifndef RR_ACCESSOR_SIGNATURE_H +#define RR_ACCESSOR_SIGNATURE_H + +namespace rr { + class AccessorSignature : public Ref { + public: + AccessorSignature(VALUE self) : Ref(self) {} + AccessorSignature(v8::Isolate* i, v8::Local s) : + Ref(i, s) {} + + static void Init() { + ClassBuilder("AccessorSignature"). + defineSingletonMethod("New", &New). + store(&Class); + } + + static VALUE New(int argc, VALUE argv[], VALUE self); + }; +} + +#endif /* RR_ACCESSOR_SIGNATURE_H */ diff --git a/ext/v8/function-template.h b/ext/v8/function-template.h index 9069fae4..d0a3d5f4 100644 --- a/ext/v8/function-template.h +++ b/ext/v8/function-template.h @@ -11,10 +11,21 @@ namespace rr { FunctionTemplate(VALUE self) : Ref(self) {} FunctionTemplate(v8::Isolate* isolate, v8::Local t) : Ref(isolate, t) {} + static inline void Init() { ClassBuilder("FunctionTemplate", Template::Class). defineSingletonMethod("New", &New). defineMethod("GetFunction", &GetFunction). + defineMethod("SetCallHandler", &SetCallHandler). + defineMethod("SetLength", &SetLength). + defineMethod("InstanceTemplate", &InstanceTemplate). + defineMethod("PrototypeTemplate", &PrototypeTemplate). + defineMethod("SetClassName", &SetClassName). + defineMethod("SetAcceptAnyReceiver", &SetAcceptAnyReceiver). + defineMethod("SetHiddenPrototype", &SetHiddenPrototype). + defineMethod("ReadOnlyPrototype", &ReadOnlyPrototype). + defineMethod("RemovePrototype", &RemovePrototype). + defineMethod("HasInstance", &HasInstance). store(&Class); } @@ -26,9 +37,8 @@ namespace rr { FunctionCallback callback(isolate, r_callback, r_data); Signature signature(r_signature); - int length(RTEST(r_length) ? NUM2INT(r_length) : 0); - return FunctionTemplate(isolate, v8::FunctionTemplate::New(isolate, callback, callback, v8::Local(), length)); + return FunctionTemplate(isolate, v8::FunctionTemplate::New(isolate, callback, callback, v8::Local(), Int(r_length))); } static VALUE GetFunction(VALUE self, VALUE context) { @@ -38,6 +48,101 @@ namespace rr { return Function::Maybe(isolate, t->GetFunction(Context(context))); } + + static VALUE SetCallHandler(VALUE self, VALUE r_callback, VALUE r_data) { + FunctionTemplate t(self); + Isolate isolate(t.getIsolate()); + Locker lock(isolate); + FunctionCallback callback(isolate, r_callback, r_data); + + t->SetCallHandler(callback, callback); + + return Qnil; + } + + static VALUE SetLength(VALUE self, VALUE r_length) { + FunctionTemplate t(self); + Isolate isolate(t.getIsolate()); + Locker lock(isolate); + + t->SetLength(Int(r_length)); + + return Qnil; + } + + static VALUE InstanceTemplate(VALUE self) { + FunctionTemplate t(self); + Isolate isolate(t.getIsolate()); + Locker lock(isolate); + + return ObjectTemplate(isolate, t->InstanceTemplate()); + } + + static VALUE PrototypeTemplate(VALUE self) { + FunctionTemplate t(self); + Isolate isolate(t.getIsolate()); + Locker lock(isolate); + + return ObjectTemplate(isolate, t->PrototypeTemplate()); + } + + static VALUE SetClassName(VALUE self, VALUE name) { + FunctionTemplate t(self); + Isolate isolate(t.getIsolate()); + Locker lock(isolate); + + t->SetClassName(String(name)); + + return Qnil; + } + + static VALUE SetAcceptAnyReceiver(VALUE self, VALUE value) { + FunctionTemplate t(self); + Isolate isolate(t.getIsolate()); + Locker lock(isolate); + + t->SetAcceptAnyReceiver(Bool(value)); + + return Qnil; + } + + static VALUE SetHiddenPrototype(VALUE self, VALUE value) { + FunctionTemplate t(self); + Isolate isolate(t.getIsolate()); + Locker lock(isolate); + + t->SetHiddenPrototype(Bool(value)); + + return Qnil; + } + + static VALUE ReadOnlyPrototype(VALUE self) { + FunctionTemplate t(self); + Isolate isolate(t.getIsolate()); + Locker lock(isolate); + + t->ReadOnlyPrototype(); + + return Qnil; + } + + static VALUE RemovePrototype(VALUE self) { + FunctionTemplate t(self); + Isolate isolate(t.getIsolate()); + Locker lock(isolate); + + t->ReadOnlyPrototype(); + + return Qnil; + } + + static VALUE HasInstance(VALUE self, VALUE object) { + FunctionTemplate t(self); + Isolate isolate(t.getIsolate()); + Locker lock(isolate); + + return Bool(t->HasInstance(Value(object))); + } }; } diff --git a/ext/v8/init.cc b/ext/v8/init.cc index 0a0a9a29..ca127b44 100644 --- a/ext/v8/init.cc +++ b/ext/v8/init.cc @@ -39,6 +39,7 @@ extern "C" { ScriptOrigin::Init(); Array::Init(); External::Init(); + AccessorSignature::Init(); Template::Init(); ObjectTemplate::Init(); FunctionTemplate::Init(); diff --git a/ext/v8/object-template.h b/ext/v8/object-template.h index 98f1342f..50a3f1c8 100644 --- a/ext/v8/object-template.h +++ b/ext/v8/object-template.h @@ -3,7 +3,7 @@ #define RR_OBJECT_TEMPLATE_H namespace rr { - class ObjectTemplate : Ref { + class ObjectTemplate : public Ref { public: ObjectTemplate(VALUE self) : Ref(self) {} ObjectTemplate(v8::Isolate* isolate, v8::Handle tmpl) : @@ -13,6 +13,11 @@ namespace rr { ClassBuilder("ObjectTemplate", Template::Class). defineSingletonMethod("New", &New). defineMethod("NewInstance", &NewInstance). + defineMethod("SetAccessor", &SetAccessor). + defineMethod("SetNamedPropertyHandler", &SetNamedPropertyHandler). + defineMethod("SetIndexedPropertyHandler", &SetIndexedPropertyHandler). + defineMethod("SetCallAsFunctionHandler", &SetCallAsFunctionHandler). + defineMethod("InternalFieldCount", &InternalFieldCount). defineMethod("SetInternalFieldCount", &SetInternalFieldCount). store(&Class); } @@ -35,6 +40,13 @@ namespace rr { return Object::Maybe(isolate, t->NewInstance(context)); } + static VALUE InternalFieldCount(VALUE self) { + ObjectTemplate t(self); + Locker lock(t); + + return INT2NUM(t->InternalFieldCount()); + } + static VALUE SetInternalFieldCount(VALUE self, VALUE value) { ObjectTemplate t(self); Locker lock(t); @@ -43,6 +55,94 @@ namespace rr { return Qnil; } + + static VALUE SetAccessor(int argc, VALUE argv[], VALUE self) { + VALUE r_name, r_getter, r_setter, r_data, r_settings, r_attribute, r_signature; + rb_scan_args( + argc, argv, "25", + &r_name, &r_getter, &r_setter, &r_data, &r_settings, &r_attribute, &r_signature + ); + + ObjectTemplate t(self); + Isolate isolate(t.getIsolate()); + Locker lock(isolate); + + PropertyCallback callback(isolate, r_getter, r_setter, r_data); + + t->SetAccessor( + *Name(r_name), + callback.getter(), + callback.setter(), + callback, + Enum(r_settings, v8::DEFAULT), + Enum(r_attribute, v8::None), + AccessorSignature(r_signature) + ); + + return Qnil; + } + + static VALUE SetNamedPropertyHandler(int argc, VALUE argv[], VALUE self) { + VALUE r_getter, r_setter, r_query, r_deleter, r_enumerator, r_data; + rb_scan_args( + argc, argv, "15", + &r_getter, &r_setter, &r_query, &r_deleter, &r_enumerator, &r_data + ); + + ObjectTemplate t(self); + Isolate isolate(t.getIsolate()); + Locker lock(isolate); + + PropertyCallback callback(isolate, r_getter, r_setter, r_query, r_deleter, r_enumerator, r_data); + + t->SetNamedPropertyHandler( + callback.propertyGetter(), + callback.propertySetter(), + callback.propertyQuery(), + callback.propertyDeleter(), + callback.enumerator(), + callback + ); + + return Qnil; + } + + static VALUE SetIndexedPropertyHandler(int argc, VALUE argv[], VALUE self) { + VALUE r_getter, r_setter, r_query, r_deleter, r_enumerator, r_data; + rb_scan_args( + argc, argv, "15", + &r_getter, &r_setter, &r_query, &r_deleter, &r_enumerator, &r_data + ); + + ObjectTemplate t(self); + Isolate isolate(t.getIsolate()); + Locker lock(isolate); + + PropertyCallback callback(isolate, r_getter, r_setter, r_query, r_deleter, r_enumerator, r_data); + + t->SetIndexedPropertyHandler( + callback.indexedGetter(), + callback.indexedSetter(), + callback.indexedQuery(), + callback.indexedDeleter(), + callback.enumerator(), + callback + ); + + return Qnil; + } + + static VALUE SetCallAsFunctionHandler(VALUE self, VALUE r_callback, VALUE r_data) { + ObjectTemplate t(self); + Isolate isolate(t.getIsolate()); + Locker lock(isolate); + + FunctionCallback callback(isolate, r_callback, r_data); + + t->SetCallAsFunctionHandler(callback, callback); + + return Qnil; + } }; } diff --git a/ext/v8/object.cc b/ext/v8/object.cc index 53015aca..d87585e5 100644 --- a/ext/v8/object.cc +++ b/ext/v8/object.cc @@ -119,12 +119,14 @@ namespace rr { Isolate isolate(object.getIsolate()); Locker lock(isolate); + PropertyCallback callback(isolate, getter, setter, data); + return Bool::Maybe(object->SetAccessor( context, Name(name), - &PropertyCallback::invokeGetter, - RTEST(setter) ? &PropertyCallback::invokeSetter : 0, - v8::MaybeLocal(PropertyCallback::wrapData(isolate, getter, setter, data)), + callback.getter(), + callback.setter(), + callback, Enum(settings, v8::DEFAULT), Enum(attribute, v8::None) )); diff --git a/ext/v8/property-callback-info.h b/ext/v8/property-callback-info.h index 726a7d25..6863ece1 100644 --- a/ext/v8/property-callback-info.h +++ b/ext/v8/property-callback-info.h @@ -62,6 +62,81 @@ namespace rr { }; + class Integer : public Base { + public: + + inline Integer(v8::PropertyCallbackInfo info) : + Base(info) {} + + inline Integer(VALUE self) : Base(self) {} + + static VALUE GetReturnValue(VALUE self) { + Integer info(self); + Locker lock(info->GetIsolate()); + return ReturnValue::Integer(info->GetReturnValue()); + } + + static inline void Init() { + ClassBuilder("Integer", PropertyCallbackInfo::Class, PropertyCallbackInfo::Class). + defineMethod("This", &This). + defineMethod("Data", &Data). + defineMethod("GetIsolate", &GetIsolate). + defineMethod("GetReturnValue", &GetReturnValue). + store(&Class); + } + + }; + + class Array : public Base { + public: + + inline Array(v8::PropertyCallbackInfo info) : + Base(info) {} + + inline Array(VALUE self) : Base(self) {} + + static VALUE GetReturnValue(VALUE self) { + Array info(self); + Locker lock(info->GetIsolate()); + return ReturnValue::Array(info->GetReturnValue()); + } + + static inline void Init() { + ClassBuilder("Array", PropertyCallbackInfo::Class, PropertyCallbackInfo::Class). + defineMethod("This", &This). + defineMethod("Data", &Data). + defineMethod("GetIsolate", &GetIsolate). + defineMethod("GetReturnValue", &GetReturnValue). + store(&Class); + } + + }; + + class Boolean : public Base { + public: + + inline Boolean(v8::PropertyCallbackInfo info) : + Base(info) {} + + inline Boolean(VALUE self) : Base(self) {} + + static VALUE GetReturnValue(VALUE self) { + Boolean info(self); + Locker lock(info->GetIsolate()); + return ReturnValue::Boolean(info->GetReturnValue()); + } + + static inline void Init() { + ClassBuilder("Boolean", PropertyCallbackInfo::Class, PropertyCallbackInfo::Class). + defineMethod("This", &This). + defineMethod("Data", &Data). + defineMethod("GetIsolate", &GetIsolate). + defineMethod("GetReturnValue", &GetReturnValue). + store(&Class); + } + + }; + class Void : public Base { public: @@ -94,6 +169,9 @@ namespace rr { store(&Class); Value::Init(); + Integer::Init(); + Array::Init(); + Boolean::Init(); Void::Init(); } diff --git a/ext/v8/property-callback.cc b/ext/v8/property-callback.cc index 59f1ccfe..b43348d2 100644 --- a/ext/v8/property-callback.cc +++ b/ext/v8/property-callback.cc @@ -2,17 +2,33 @@ namespace rr { - v8::Local PropertyCallback::wrapData(v8::Isolate* isolate, VALUE r_getter, VALUE r_setter, VALUE r_data) { + PropertyCallback::operator v8::Local() { v8::Local holder = v8::Object::New(isolate); v8::Local getter_key = v8::String::NewFromUtf8(isolate, "rr::getter"); - v8::Local setter_key = v8::String::NewFromUtf8(isolate, "rr::setter"); - v8::Local data_key = v8::String::NewFromUtf8(isolate, "rr::data"); - holder->SetHiddenValue(getter_key, External::wrap(isolate, r_getter)); + + v8::Local setter_key = v8::String::NewFromUtf8(isolate, "rr::setter"); holder->SetHiddenValue(setter_key, External::wrap(isolate, r_setter)); + + v8::Local data_key = v8::String::NewFromUtf8(isolate, "rr::data"); holder->SetHiddenValue(data_key, rr::Value(r_data)); + if (RTEST(r_query)) { + v8::Local query_key = v8::String::NewFromUtf8(isolate, "rr::query"); + holder->SetHiddenValue(query_key, External::wrap(isolate, r_query)); + } + + if (RTEST(r_deleter)) { + v8::Local deleter_key = v8::String::NewFromUtf8(isolate, "rr::deleter"); + holder->SetHiddenValue(deleter_key, External::wrap(isolate, r_deleter)); + } + + if (RTEST(r_enumerator)) { + v8::Local enumerator_key = v8::String::NewFromUtf8(isolate, "rr::enumerator"); + holder->SetHiddenValue(enumerator_key, External::wrap(isolate, r_enumerator)); + } + return holder; } @@ -37,6 +53,27 @@ namespace rr { return External::unwrap(holder->GetHiddenValue(callback_key)); } + VALUE PropertyCallback::unwrapQuery(v8::Isolate* isolate, v8::Local value) { + v8::Local holder = v8::Local::Cast(value); + v8::Local callback_key = v8::String::NewFromUtf8(isolate, "rr::query"); + + return External::unwrap(holder->GetHiddenValue(callback_key)); + } + + VALUE PropertyCallback::unwrapDeleter(v8::Isolate* isolate, v8::Local value) { + v8::Local holder = v8::Local::Cast(value); + v8::Local callback_key = v8::String::NewFromUtf8(isolate, "rr::deleter"); + + return External::unwrap(holder->GetHiddenValue(callback_key)); + } + + VALUE PropertyCallback::unwrapEnumerator(v8::Isolate* isolate, v8::Local value) { + v8::Local holder = v8::Local::Cast(value); + v8::Local callback_key = v8::String::NewFromUtf8(isolate, "rr::enumerator"); + + return External::unwrap(holder->GetHiddenValue(callback_key)); + } + void PropertyCallback::invokeGetter(v8::Local property, const v8::PropertyCallbackInfo& info) { v8::Isolate* isolate = info.GetIsolate(); @@ -74,4 +111,103 @@ namespace rr { ); } + void PropertyCallback::invokeNamedPropertyGetter(v8::Local property, const v8::PropertyCallbackInfo& info) { + v8::Isolate* isolate = info.GetIsolate(); + + VALUE code(unwrapGetter(isolate, info.Data())); + + VALUE rb_property = String(isolate, property->ToString()); + + Unlocker unlock(info.GetIsolate()); + rb_funcall(code, rb_intern("call"), 2, rb_property, (VALUE)PropertyCallbackInfo::Value(info)); + } + + void PropertyCallback::invokeNamedPropertySetter(v8::Local property, v8::Local value, const v8::PropertyCallbackInfo& info) { + v8::Isolate* isolate = info.GetIsolate(); + + VALUE code(unwrapSetter(isolate, info.Data())); + + VALUE rb_property = String(isolate, property->ToString()); + + Unlocker unlock(info.GetIsolate()); + rb_funcall( + code, rb_intern("call"), 3, + rb_property, + (VALUE)Value(isolate, value), + (VALUE)PropertyCallbackInfo::Value(info) + ); + } + + void PropertyCallback::invokeNamedPropertyQuery(v8::Local property, const v8::PropertyCallbackInfo& info) { + v8::Isolate* isolate = info.GetIsolate(); + + VALUE code(unwrapQuery(isolate, info.Data())); + + VALUE rb_property = String(isolate, property->ToString()); + + Unlocker unlock(info.GetIsolate()); + rb_funcall(code, rb_intern("call"), 2, rb_property, (VALUE)PropertyCallbackInfo::Integer(info)); + } + + void PropertyCallback::invokeNamedPropertyDeleter(v8::Local property, const v8::PropertyCallbackInfo& info) { + v8::Isolate* isolate = info.GetIsolate(); + + VALUE code(unwrapDeleter(isolate, info.Data())); + + VALUE rb_property = String(isolate, property->ToString()); + + Unlocker unlock(info.GetIsolate()); + rb_funcall(code, rb_intern("call"), 2, rb_property, (VALUE)PropertyCallbackInfo::Boolean(info)); + } + + void PropertyCallback::invokePropertyEnumerator(const v8::PropertyCallbackInfo& info) { + v8::Isolate* isolate = info.GetIsolate(); + + VALUE code(unwrapEnumerator(isolate, info.Data())); + + Unlocker unlock(info.GetIsolate()); + rb_funcall(code, rb_intern("call"), 1, (VALUE)PropertyCallbackInfo::Array(info)); + } + + void PropertyCallback::invokeIndexedPropertyGetter(uint32_t index, const v8::PropertyCallbackInfo& info) { + v8::Isolate* isolate = info.GetIsolate(); + + VALUE code(unwrapGetter(isolate, info.Data())); + + Unlocker unlock(info.GetIsolate()); + rb_funcall(code, rb_intern("call"), 2, UINT2NUM(index), (VALUE)PropertyCallbackInfo::Value(info)); + } + + void PropertyCallback::invokeIndexedPropertySetter(uint32_t index, v8::Local value, const v8::PropertyCallbackInfo& info) { + v8::Isolate* isolate = info.GetIsolate(); + + VALUE code(unwrapSetter(isolate, info.Data())); + + Unlocker unlock(info.GetIsolate()); + rb_funcall( + code, rb_intern("call"), 3, + UINT2NUM(index), + (VALUE)Value(isolate, value), + (VALUE)PropertyCallbackInfo::Value(info) + ); + } + + void PropertyCallback::invokeIndexedPropertyQuery(uint32_t index, const v8::PropertyCallbackInfo& info) { + v8::Isolate* isolate = info.GetIsolate(); + + VALUE code(unwrapQuery(isolate, info.Data())); + + Unlocker unlock(info.GetIsolate()); + rb_funcall(code, rb_intern("call"), 2, UINT2NUM(index), (VALUE)PropertyCallbackInfo::Integer(info)); + } + + void PropertyCallback::invokeIndexedPropertyDeleter(uint32_t index, const v8::PropertyCallbackInfo& info) { + v8::Isolate* isolate = info.GetIsolate(); + + VALUE code(unwrapDeleter(isolate, info.Data())); + + Unlocker unlock(info.GetIsolate()); + rb_funcall(code, rb_intern("call"), 2, UINT2NUM(index), (VALUE)PropertyCallbackInfo::Boolean(info)); + } + } diff --git a/ext/v8/property-callback.h b/ext/v8/property-callback.h index 92d42497..b4995755 100644 --- a/ext/v8/property-callback.h +++ b/ext/v8/property-callback.h @@ -5,7 +5,20 @@ namespace rr { class PropertyCallback { + v8::Isolate* isolate; + VALUE r_getter, r_setter, r_query, r_deleter, r_enumerator, r_data; + public: + PropertyCallback(v8::Isolate* isolate_, VALUE r_getter_, VALUE r_setter_, VALUE r_data_) + : isolate(isolate_), r_getter(r_getter_), r_setter(r_setter_), r_query(Qnil), r_deleter(Qnil), + r_enumerator(Qnil), r_data(r_data_) { + } + + PropertyCallback(v8::Isolate* isolate_, VALUE r_getter_, VALUE r_setter_, + VALUE r_query_, VALUE r_deleter_, VALUE r_enumerator_, VALUE r_data_) + : isolate(isolate_), r_getter(r_getter_), r_setter(r_setter_), r_query(r_query_), r_deleter(r_deleter_), + r_enumerator(r_enumerator_), r_data(r_data_) { + } /** * Package up the callback data for this object so that it can @@ -17,7 +30,11 @@ namespace rr { * the callbacks and store them *both* in a single `v8::Object` * which we use for the C++ level callback data. */ - static v8::Local wrapData(v8::Isolate* isolate, VALUE r_getter, VALUE r_setter, VALUE r_data); + operator v8::Local(); + + inline operator v8::MaybeLocal() { + return v8::MaybeLocal((v8::Local)*this); + } /** * Get the data packaged in the object. @@ -34,6 +51,21 @@ namespace rr { */ static VALUE unwrapSetter(v8::Isolate* isolate, v8::Local holder); + /** + * Get the query callback packaged in the object. + */ + static VALUE unwrapQuery(v8::Isolate* isolate, v8::Local holder); + + /** + * Get the deleter packaged in the object. + */ + static VALUE unwrapDeleter(v8::Isolate* isolate, v8::Local holder); + + /** + * Get the enumerator packaged in the object. + */ + static VALUE unwrapEnumerator(v8::Isolate* isolate, v8::Local holder); + /** * Call the Ruby code associated with this callback. * @@ -44,6 +76,14 @@ namespace rr { */ static void invokeGetter(v8::Local property, const v8::PropertyCallbackInfo& info); + inline v8::AccessorNameGetterCallback getter() { + if (RTEST(r_getter)) { + return &PropertyCallback::invokeGetter; + } else { + return 0; + } + } + /** * Call the Ruby code associated with this callback. * @@ -54,6 +94,180 @@ namespace rr { */ static void invokeSetter(v8::Local property, v8::Local value, const v8::PropertyCallbackInfo& info); + inline v8::AccessorNameSetterCallback setter() { + if (RTEST(r_setter)) { + return &PropertyCallback::invokeSetter; + } else { + return 0; + } + } + + /** + * Call the Ruby code associated with this callback. + * + * Unpack the Ruby code, and the callback data from the C++ + * callback data, and then invoke that code. + * + * Note: This function implements the `v8::NamedPropertyGetterCallback` API. + */ + static void invokeNamedPropertyGetter(v8::Local property, const v8::PropertyCallbackInfo& info); + + inline v8::NamedPropertyGetterCallback propertyGetter() { + if (RTEST(r_getter)) { + return &PropertyCallback::invokeNamedPropertyGetter; + } else { + return 0; + } + } + + /** + * Call the Ruby code associated with this callback. + * + * Unpack the Ruby code, and the callback data from the C++ + * callback data, and then invoke that code. + * + * Note: This function implements the `v8::NamedPropertySetterCallback` API. + */ + static void invokeNamedPropertySetter(v8::Local property, v8::Local value, const v8::PropertyCallbackInfo& info); + + inline v8::NamedPropertySetterCallback propertySetter() { + if (RTEST(r_setter)) { + return &PropertyCallback::invokeNamedPropertySetter; + } else { + return 0; + } + } + + /** + * Call the Ruby code associated with this callback. + * + * Unpack the Ruby code, and the callback data from the C++ + * callback data, and then invoke that code. + * + * Note: This function implements the `v8::NamedPropertyQueryCallback` API. + */ + static void invokeNamedPropertyQuery(v8::Local property, const v8::PropertyCallbackInfo& info); + + inline v8::NamedPropertyQueryCallback propertyQuery() { + if (RTEST(r_query)) { + return &PropertyCallback::invokeNamedPropertyQuery; + } else { + return 0; + } + } + + /** + * Call the Ruby code associated with this callback. + * + * Unpack the Ruby code, and the callback data from the C++ + * callback data, and then invoke that code. + * + * Note: This function implements the `v8::NamedPropertyDeleterCallback` API. + */ + static void invokeNamedPropertyDeleter(v8::Local property, const v8::PropertyCallbackInfo& info); + + inline v8::NamedPropertyDeleterCallback propertyDeleter() { + if (RTEST(r_deleter)) { + return &PropertyCallback::invokeNamedPropertyDeleter; + } else { + return 0; + } + } + + /** + * Call the Ruby code associated with this callback. + * + * Unpack the Ruby code, and the callback data from the C++ + * callback data, and then invoke that code. + * + * Note: This function implements the `v8::NamedPropertyEnumeratorCallback` + * and `v8::IndexedPropertyEnumeratorCallback` API. + */ + static void invokePropertyEnumerator(const v8::PropertyCallbackInfo& info); + + /** + * This handles both `v8::NamedPropertyEnumeratorCallback` and `v8::IndexedPropertyEnumeratorCallback` + * since they are the same type. + */ + inline v8::NamedPropertyEnumeratorCallback enumerator() { + if (RTEST(r_enumerator)) { + return &PropertyCallback::invokePropertyEnumerator; + } else { + return 0; + } + } + + /** + * Call the Ruby code associated with this callback. + * + * Unpack the Ruby code, and the callback data from the C++ + * callback data, and then invoke that code. + * + * Note: This function implements the `v8::IndexedPropertyGetterCallback` API. + */ + static void invokeIndexedPropertyGetter(uint32_t index, const v8::PropertyCallbackInfo& info); + + inline v8::IndexedPropertyGetterCallback indexedGetter() { + if (RTEST(r_getter)) { + return &PropertyCallback::invokeIndexedPropertyGetter; + } else { + return 0; + } + } + + /** + * Call the Ruby code associated with this callback. + * + * Unpack the Ruby code, and the callback data from the C++ + * callback data, and then invoke that code. + * + * Note: This function implements the `v8::IndexedPropertySetterCallback` API. + */ + static void invokeIndexedPropertySetter(uint32_t index, v8::Local value, const v8::PropertyCallbackInfo& info); + + inline v8::IndexedPropertySetterCallback indexedSetter() { + if (RTEST(r_setter)) { + return &PropertyCallback::invokeIndexedPropertySetter; + } else { + return 0; + } + } + + /** + * Call the Ruby code associated with this callback. + * + * Unpack the Ruby code, and the callback data from the C++ + * callback data, and then invoke that code. + * + * Note: This function implements the `v8::IndexedPropertyQueryCallback` API. + */ + static void invokeIndexedPropertyQuery(uint32_t index, const v8::PropertyCallbackInfo& info); + + inline v8::IndexedPropertyQueryCallback indexedQuery() { + if (RTEST(r_query)) { + return &PropertyCallback::invokeIndexedPropertyQuery; + } else { + return 0; + } + } + + /** + * Call the Ruby code associated with this callback. + * + * Unpack the Ruby code, and the callback data from the C++ + * callback data, and then invoke that code. + * + * Note: This function implements the `v8::IndexedPropertyDeleterCallback` API. + */ + static void invokeIndexedPropertyDeleter(uint32_t index, const v8::PropertyCallbackInfo& info); + + inline v8::IndexedPropertyDeleterCallback indexedDeleter() { + if (RTEST(r_deleter)) { + return &PropertyCallback::invokeIndexedPropertyDeleter; + } else { + return 0; + } + } }; } diff --git a/ext/v8/return-value.h b/ext/v8/return-value.h index f2b9f843..5b1e52c9 100644 --- a/ext/v8/return-value.h +++ b/ext/v8/return-value.h @@ -97,6 +97,48 @@ namespace rr { } }; + class Integer : public Base { + public: + Integer(v8::ReturnValue value) : Base(value) {} + Integer(VALUE self) : Base(self) {} + + static inline void Init() { + ClassBuilder("Integer", ReturnValue::Class, ReturnValue::Class). + defineMethod("Set", &Set). + defineMethod("Set_int32_t", &Set_int32_t). + defineMethod("Set_uint32_t", &Set_uint32_t). + defineMethod("GetIsolate", &GetIsolate). + store(&Class); + } + }; + + class Array : public Base { + public: + Array(v8::ReturnValue value) : Base(value) {} + Array(VALUE self) : Base(self) {} + + static inline void Init() { + ClassBuilder("Array", ReturnValue::Class, ReturnValue::Class). + defineMethod("Set", &Set). + defineMethod("GetIsolate", &GetIsolate). + store(&Class); + } + }; + + class Boolean : public Base { + public: + Boolean(v8::ReturnValue value) : Base(value) {} + Boolean(VALUE self) : Base(self) {} + + static inline void Init() { + ClassBuilder("Boolean", ReturnValue::Class, ReturnValue::Class). + defineMethod("Set", &Set). + defineMethod("Set_bool", &Set_bool). + defineMethod("GetIsolate", &GetIsolate). + store(&Class); + } + }; + class Void : public Base { public: @@ -117,6 +159,9 @@ namespace rr { store(&Class); Value::Init(); + Integer::Init(); + Array::Init(); + Boolean::Init(); Void::Init(); } }; diff --git a/ext/v8/rr.h b/ext/v8/rr.h index 37a5e4d0..afcd0702 100644 --- a/ext/v8/rr.h +++ b/ext/v8/rr.h @@ -55,19 +55,20 @@ inline VALUE not_implemented(const char* message) { #include "object.h" #include "date.h" +#include "array.h" #include "return-value.h" #include "property-callback.h" #include "property-callback-info.h" -#include "array.h" #include "script.h" #include "script-origin.h" #include "function-callback.h" +#include "accessor-signature.h" #include "template.h" #include "signature.h" -#include "function-template.h" #include "object-template.h" +#include "function-template.h" #include "stack-frame.h" #include "stack-trace.h" diff --git a/ext/v8/template.h b/ext/v8/template.h index 600b4f25..792bb8ac 100644 --- a/ext/v8/template.h +++ b/ext/v8/template.h @@ -11,6 +11,7 @@ namespace rr { ClassBuilder("Template"). defineMethod("Set", &Set). defineMethod("SetAccessorProperty", &SetAccessorProperty). + // TODO: SetNativeDataProperty store(&Class); } diff --git a/spec/c/function_template_spec.rb b/spec/c/function_template_spec.rb index 2f39ef4e..e7ca8431 100644 --- a/spec/c/function_template_spec.rb +++ b/spec/c/function_template_spec.rb @@ -7,18 +7,22 @@ describe "New()" do describe "with no arguments" do let(:template) { V8::C::FunctionTemplate::New(@isolate) } + it "creates an empty template" do expect(template).to be end + it "has an empty function" do expect(function).to be expect(function.Call(@ctx.Global(), []).StrictEquals(@ctx.Global())).to be true end end + describe "with a function callback" do let(:data) { V8::C::Integer::New(@isolate, 42) } let(:signature) { V8::C::Signature::New(@isolate) } let(:template) { V8::C::FunctionTemplate::New(@isolate, @callback, data, @signature, 0) } + before do @callback = ->(info) do @data = info.Data() @@ -32,4 +36,39 @@ end end end + + describe "SetCallHandler" do + let(:data) { V8::C::Integer::New(@isolate, 42) } + let(:template) { V8::C::FunctionTemplate::New(@isolate) } + + before do + @callback = ->(info) do + @data = info.Data() + end + end + + it "can set a callback" do + template.SetCallHandler(@callback, data) + function.Call(@ctx.Global(), []) + + expect(@data).to_not be_nil + expect(@data.Value()).to be 42 + end + end + + describe "InstanceTemplate" do + let(:template) { V8::C::FunctionTemplate::New(@isolate) } + + it "returns an object template" do + expect(template.InstanceTemplate).to be_a V8::C::ObjectTemplate + end + end + + describe "PrototypeTemplate" do + let(:template) { V8::C::FunctionTemplate::New(@isolate) } + + it "returns an object template" do + expect(template.PrototypeTemplate).to be_a V8::C::ObjectTemplate + end + end end diff --git a/spec/c/object_template_spec.rb b/spec/c/object_template_spec.rb index a62e7801..b326d604 100644 --- a/spec/c/object_template_spec.rb +++ b/spec/c/object_template_spec.rb @@ -7,4 +7,282 @@ it "can be used to create object instances" do expect(template.NewInstance(@ctx).FromJust()).to be_instance_of V8::C::Object end + + describe 'InternalFieldCount' do + it 'can get and set internal field count' do + template.SetInternalFieldCount(10); + expect(template.InternalFieldCount).to eq 10 + end + end + + describe 'SetAccessor' do + it 'can set getters' do + key = V8::C::String.NewFromUtf8(@isolate, 'foo') + data = V8::C::String.NewFromUtf8(@isolate, 'data') + get_value = V8::C::String.NewFromUtf8(@isolate, 'bar') + + get_name = nil + get_data = nil + + getter = proc do |name, info| + get_name = name + get_data = info.Data + + info.GetReturnValue.Set(get_value) + end + template.SetAccessor(key, getter, nil, data) + + o = template.NewInstance(@ctx).FromJust + expect(o.Get(@ctx, key)).to strict_eq get_value + + expect(get_name).to v8_eq key + expect(get_data).to strict_eq data + end + + it 'can set setters' do + key = V8::C::String.NewFromUtf8(@isolate, 'foo') + data = V8::C::String.NewFromUtf8(@isolate, 'data') + + set_value = nil + set_data = nil + + setter = proc do |name, value, info| + set_data = info.Data + set_value = value + end + + template.SetAccessor(key, proc { }, setter, data) + + o = template.NewInstance(@ctx).FromJust + expect(o.Set(@ctx, key, data)).to be_successful + + expect(set_data).to strict_eq data + expect(set_value).to strict_eq data + end + end + + describe 'SetNamedPropertyHandler' do + let(:key) { V8::C::String.NewFromUtf8(@isolate, 'foo') } + let(:data) { V8::C::String.NewFromUtf8(@isolate, 'data') } + + it 'can set getters' do + get_value = V8::C::String.NewFromUtf8(@isolate, 'bar') + + get_name = nil + get_data = nil + + getter = proc do |name, info| + get_name = name + get_data = info.Data + + info.GetReturnValue.Set(get_value) + end + template.SetNamedPropertyHandler(getter, nil, nil, nil, nil, data) + + o = template.NewInstance(@ctx).FromJust + expect(o.Get(@ctx, key)).to strict_eq get_value + + expect(get_name).to v8_eq key + expect(get_data).to strict_eq data + end + + it 'can set setters' do + set_value = nil + set_data = nil + + setter = proc do |name, value, info| + set_data = info.Data + set_value = value + end + + template.SetNamedPropertyHandler(proc { }, setter, nil, nil, nil, data) + + o = template.NewInstance(@ctx).FromJust + expect(o.Set(@ctx, key, data)).to be_successful + + expect(set_data).to strict_eq data + expect(set_value).to strict_eq data + end + + it 'can set query callbacks' do + value = V8::C::PropertyAttribute::ReadOnly + + query_name = nil + query_data = nil + + query = proc do |name, info| + query_name = name + query_data = info.Data + + info.GetReturnValue.Set_int32_t(value) + end + template.SetNamedPropertyHandler(proc { }, nil, query, nil, nil, data) + + o = template.NewInstance(@ctx).FromJust + expect(o.GetPropertyAttributes(@ctx, key).FromJust).to eq value + + expect(query_name).to v8_eq key + expect(query_data).to strict_eq data + end + + it 'can set deleters' do + deleter_name = nil + deleter_data = nil + + deleter = proc do |name, info| + deleter_name = name + deleter_data = info.Data + + info.GetReturnValue.Set_bool(false) + end + template.SetNamedPropertyHandler(proc { }, nil, nil, deleter, nil, data) + + o = template.NewInstance(@ctx).FromJust + expect(o.Delete(@ctx, key)).to_not be_successful + + expect(deleter_name).to v8_eq key + expect(deleter_data).to strict_eq data + end + + it 'can set enumerators' do + array = V8::C::Array::New(@isolate) + array.Set(@ctx, 0, key) + + enumerator_data = nil + + enumerator = proc do |info| + enumerator_data = info.Data + + info.GetReturnValue.Set(array) + end + template.SetNamedPropertyHandler(proc { }, nil, nil, nil, enumerator, data) + + o = template.NewInstance(@ctx).FromJust + expect(o.GetPropertyNames(@ctx).FromJust.Get(@ctx, 0)).to v8_eq key + + expect(enumerator_data).to strict_eq data + end + end + + describe 'SetIndexedPropertyHandler' do + let(:key) { V8::C::Integer.New(@isolate, 1) } + let(:data) { V8::C::String.NewFromUtf8(@isolate, 'data') } + + it 'can set getters' do + get_value = V8::C::String.NewFromUtf8(@isolate, 'bar') + + get_index = nil + get_data = nil + + getter = proc do |index, info| + get_index = index + get_data = info.Data + + info.GetReturnValue.Set(get_value) + end + template.SetIndexedPropertyHandler(getter, nil, nil, nil, nil, data) + + o = template.NewInstance(@ctx).FromJust + expect(o.Get(@ctx, key)).to strict_eq get_value + + expect(get_index).to eq 1 + expect(get_data).to strict_eq data + end + + it 'can set setters' do + set_value = nil + set_data = nil + + setter = proc do |index, value, info| + set_data = info.Data + set_value = value + end + + template.SetIndexedPropertyHandler(proc { }, setter, nil, nil, nil, data) + + o = template.NewInstance(@ctx).FromJust + expect(o.Set(@ctx, key, data)).to be_successful + + expect(set_data).to strict_eq data + expect(set_value).to strict_eq data + end + + it 'can set query callbacks' do + value = V8::C::PropertyAttribute::ReadOnly + + query_index = nil + query_data = nil + + query = proc do |index, info| + query_index = index + query_data = info.Data + + info.GetReturnValue.Set_int32_t(value) + end + template.SetIndexedPropertyHandler(proc { }, nil, query, nil, nil, data) + + o = template.NewInstance(@ctx).FromJust + expect(o.GetPropertyAttributes(@ctx, key).FromJust).to eq value + + expect(query_index).to eq 1 + expect(query_data).to strict_eq data + end + + it 'can set deleters' do + deleter_index = nil + deleter_data = nil + + deleter = proc do |index, info| + deleter_index = index + deleter_data = info.Data + + info.GetReturnValue.Set_bool(false) + end + template.SetIndexedPropertyHandler(proc { }, nil, nil, deleter, nil, data) + + o = template.NewInstance(@ctx).FromJust + expect(o.Delete(@ctx, key)).to_not be_successful + + expect(deleter_index).to eq 1 + expect(deleter_data).to strict_eq data + end + + it 'can set enumerators' do + array = V8::C::Array::New(@isolate) + array.Set(@ctx, 0, key) + + enumerator_data = nil + + enumerator = proc do |info| + enumerator_data = info.Data + + info.GetReturnValue.Set(array) + end + template.SetIndexedPropertyHandler(proc { }, nil, nil, nil, enumerator, data) + + o = template.NewInstance(@ctx).FromJust + expect(o.GetPropertyNames(@ctx).FromJust.Get(@ctx, 0)).to v8_eq key + + expect(enumerator_data).to strict_eq data + end + end + + describe 'SetCallAsFunctionHandler' do + let(:data) { V8::C::String.NewFromUtf8(@isolate, 'data') } + + it 'can set a CallAsFunction handler' do + callback_data = nil + + callback = proc do |info| + callback_data = info.Data + + info.GetReturnValue.Set(data) + end + + template.SetCallAsFunctionHandler(callback, data) + + o = template.NewInstance(@ctx).FromJust + expect(o.CallAsFunction(@ctx, o, [])).to strict_eq data + end + end end