From c66e43e4385de7bf27cb65f9933be49b3d626eea Mon Sep 17 00:00:00 2001 From: Charles Lowell Date: Mon, 21 May 2012 12:22:07 -0500 Subject: [PATCH] support for function callbacks --- ext/v8/init.cc | 2 + ext/v8/invocation.cc | 86 +++++++++++++++++++++++++++++ ext/v8/rr.h | 48 ++++++++++++++++ ext/v8/template.cc | 118 ++++++++++++++++++---------------------- spec/c/template_spec.rb | 41 ++++++++++++++ 5 files changed, 230 insertions(+), 65 deletions(-) create mode 100644 ext/v8/invocation.cc create mode 100644 spec/c/template_spec.rb diff --git a/ext/v8/init.cc b/ext/v8/init.cc index 141477f..02f6124 100644 --- a/ext/v8/init.cc +++ b/ext/v8/init.cc @@ -13,6 +13,8 @@ extern "C" { Handles::Init(); Accessor::Init(); Context::Init(); + Invocation::Init(); + Signature::Init(); Value::Init(); String::Init(); Object::Init(); diff --git a/ext/v8/invocation.cc b/ext/v8/invocation.cc new file mode 100644 index 0000000..497e530 --- /dev/null +++ b/ext/v8/invocation.cc @@ -0,0 +1,86 @@ +#include "rr.h" + +namespace rr { + + VALUE Invocation::Arguments::Class; + + void Invocation::Init() { + Arguments::Init(); + } + + void Invocation::Arguments::Init() { + ClassBuilder("Arguments"). + defineMethod("Length", &Length). + defineMethod("[]", &Get). + defineMethod("Callee", &Callee). + defineMethod("This", &This). + defineMethod("Holder", &Holder). + defineMethod("IsConstructCall", &IsConstructCall). + defineMethod("Data", &Data). + store(&Invocation::Arguments::Class); + } + + Invocation::Invocation(VALUE code, VALUE data) { + this->code = code; + this->data = data; + } + Invocation::Invocation(v8::Handle value) { + v8::Local wrapper = value->ToObject(); + this->code = External::unwrap((v8::Handle)v8::External::Cast(*wrapper->Get(0))); + this->data = Value(wrapper->Get(1)); + } + Invocation::operator v8::InvocationCallback() { + return &Callback; + } + Invocation::operator v8::Handle() { + v8::Local wrapper = v8::Object::New(); + wrapper->Set(0, External::wrap(this->code)); + wrapper->Set(1, Value(this->data)); + return wrapper; + } + + v8::Handle Invocation::Callback(const v8::Arguments& args) { + return Arguments(args).Call(); + } + + Invocation::Arguments::Arguments(const v8::Arguments& args) { + this->args = &args; + } + + Invocation::Arguments::Arguments(VALUE value) { + Data_Get_Struct(value, class v8::Arguments, args); + } + + v8::Handle Invocation::Arguments::Call() { + Invocation invocation(args->Data()); + return Value(rb_funcall(invocation.code, rb_intern("call"), 1, Data_Wrap_Struct(Class, 0, 0, (void*)this->args))); + } + + VALUE Invocation::Arguments::Length(VALUE self) { + return INT2FIX(Arguments(self)->Length()); + } + + VALUE Invocation::Arguments::Get(VALUE self, VALUE index) { + return Value((*Arguments(self))[NUM2INT(index)]); + } + + VALUE Invocation::Arguments::Callee(VALUE self) { + return Function(Arguments(self)->Callee()); + } + + VALUE Invocation::Arguments::This(VALUE self) { + return Object(Arguments(self)->This()); + } + + VALUE Invocation::Arguments::Holder(VALUE self) { + return Object(Arguments(self)->Holder()); + } + + VALUE Invocation::Arguments::IsConstructCall(VALUE self) { + return Bool(Arguments(self)->IsConstructCall()); + } + + VALUE Invocation::Arguments::Data(VALUE self) { + return Invocation(Arguments(self)->Data()).data; + } +} \ No newline at end of file diff --git a/ext/v8/rr.h b/ext/v8/rr.h index c766511..9efd4c9 100644 --- a/ext/v8/rr.h +++ b/ext/v8/rr.h @@ -260,6 +260,41 @@ private: static VALUE Class; }; +class Invocation { +public: + static void Init(); + Invocation(VALUE code, VALUE data); + Invocation(v8::Handle wrapper); + operator v8::InvocationCallback(); + operator v8::Handle(); + static v8::Handle Callback(const v8::Arguments& args); + + class Arguments { + public: + static void Init(); + Arguments(const v8::Arguments& args); + Arguments(VALUE value); + inline const v8::Arguments* operator->() {return this->args;} + inline const v8::Arguments operator*() {return *this->args;} + v8::Handle Call(); + + static VALUE Length(VALUE self); + static VALUE Get(VALUE self, VALUE index); + static VALUE Callee(VALUE self); + static VALUE This(VALUE self); + static VALUE Holder(VALUE self); + static VALUE IsConstructCall(VALUE self); + static VALUE Data(VALUE self); + private: + const v8::Arguments* args; + static VALUE Class; + }; +private: + VALUE code; + VALUE data; + friend class Arguments; +}; + class Object : public Ref { public: static void Init(); @@ -345,6 +380,7 @@ public: }; class Signature : public Ref { +public: static void Init(); static VALUE New(int argc, VALUE argv[], VALUE self); @@ -360,12 +396,24 @@ public: class ObjectTemplate : public Ref { public: static void Init(); + + inline ObjectTemplate(VALUE value) : Ref(value) {} + inline ObjectTemplate(v8::Handle t) : Ref(t) {} }; class FunctionTemplate : public Ref { public: static void Init(); static VALUE New(int argc, VALUE argv[], VALUE self); + static VALUE GetFunction(VALUE self); + static VALUE SetCallHandler(int argc, VALUE argv[], VALUE self); + static VALUE InstanceTemplate(VALUE self); + static VALUE Inherit(VALUE self, VALUE parent); + static VALUE PrototypeTemplate(VALUE self); + static VALUE SetClassName(VALUE self, VALUE name); + static VALUE SetHiddenPrototype(VALUE self, VALUE value); + static VALUE ReadOnlyPrototype(VALUE self); + static VALUE HasInstance(VALUE self, VALUE object); inline FunctionTemplate(VALUE value) : Ref(value) {} inline FunctionTemplate(v8::Handle t) : Ref(t) {} diff --git a/ext/v8/template.cc b/ext/v8/template.cc index de6369d..5e9a3ad 100644 --- a/ext/v8/template.cc +++ b/ext/v8/template.cc @@ -14,78 +14,66 @@ namespace rr { void FunctionTemplate::Init() { ClassBuilder("FunctionTemplate", "Template"). - defineMethod("New", &New). + defineSingletonMethod("New", &New). + defineMethod("GetFunction", &GetFunction). + defineMethod("SetCallHandler", &SetCallHandler). + defineMethod("InstanceTemplate", &InstanceTemplate). + defineMethod("Inherit", &Inherit). + defineMethod("PrototypeTemplate", &PrototypeTemplate). + defineMethod("SetClassName", &SetClassName). + defineMethod("SetHiddenPrototype", &SetHiddenPrototype). + defineMethod("ReadOnlyPrototype", &ReadOnlyPrototype). + defineMethod("HasInstance", &HasInstance). store(&Class); } VALUE FunctionTemplate::New(int argc, VALUE argv[], VALUE self) { VALUE code; VALUE data; VALUE signature; rb_scan_args(argc, argv, "03", &code, &data, &signature); - return FunctionTemplate(v8::FunctionTemplate::New()); - // InvocationCallback callback(code, data); - // return FunctionTemplate(v8::FunctionTemplate::New(callback, callback.data, Signature(signature))); + if (RTEST(code)) { + Invocation invocation(code, data); + return FunctionTemplate(v8::FunctionTemplate::New(invocation, invocation, Signature(signature))); + } else { + return FunctionTemplate(v8::FunctionTemplate::New()); + } } - // /** Creates a function template.*/ - // static Local New( - // InvocationCallback callback = 0, - // Handle data = Handle(), - // Handle signature = Handle()); - // /** Returns the unique function instance in the current execution context.*/ - // Local GetFunction(); - // - // /** - // * Set the call-handler callback for a FunctionTemplate. This - // * callback is called whenever the function created from this - // * FunctionTemplate is called. - // */ - // void SetCallHandler(InvocationCallback callback, - // Handle data = Handle()); - // - // /** Get the InstanceTemplate. */ - // Local InstanceTemplate(); - // - // /** Causes the function template to inherit from a parent function template.*/ - // void Inherit(Handle parent); - // - // /** - // * A PrototypeTemplate is the template used to create the prototype object - // * of the function created by this template. - // */ - // Local PrototypeTemplate(); - // - // - // /** - // * Set the class name of the FunctionTemplate. This is used for - // * printing objects created with the function created from the - // * FunctionTemplate as its constructor. - // */ - // void SetClassName(Handle name); - // - // /** - // * Determines whether the __proto__ accessor ignores instances of - // * the function template. If instances of the function template are - // * ignored, __proto__ skips all instances and instead returns the - // * next object in the prototype chain. - // * - // * Call with a value of true to make the __proto__ accessor ignore - // * instances of the function template. Call with a value of false - // * to make the __proto__ accessor not ignore instances of the - // * function template. By default, instances of a function template - // * are not ignored. - // */ - // void SetHiddenPrototype(bool value); - // - // /** - // * Sets the ReadOnly flag in the attributes of the 'prototype' property - // * of functions created from this FunctionTemplate to true. - // */ - // void ReadOnlyPrototype(); - // - // /** - // * Returns true if the given object is an instance of this function - // * template. - // */ - // bool HasInstance(Handle object); + VALUE FunctionTemplate::GetFunction(VALUE self) { + return Function(FunctionTemplate(self)->GetFunction()); + } + VALUE FunctionTemplate::SetCallHandler(int argc, VALUE argv[], VALUE self) { + VALUE code; VALUE data; + rb_scan_args(argc, argv, "11", &code, &data); + Invocation invocation(code, data); + Void(FunctionTemplate(self)->SetCallHandler(invocation, invocation)); + } + + VALUE FunctionTemplate::InstanceTemplate(VALUE self) { + return ObjectTemplate(FunctionTemplate(self)->InstanceTemplate()); + } + + VALUE FunctionTemplate::Inherit(VALUE self, VALUE parent) { + Void(FunctionTemplate(self)->Inherit(FunctionTemplate(parent))); + } + + VALUE FunctionTemplate::PrototypeTemplate(VALUE self) { + return ObjectTemplate(FunctionTemplate(self)->PrototypeTemplate()); + } + + VALUE FunctionTemplate::SetClassName(VALUE self, VALUE name) { + Void(FunctionTemplate(self)->SetClassName(String(name))); + } + + VALUE FunctionTemplate::SetHiddenPrototype(VALUE self, VALUE value) { + Void(FunctionTemplate(self)->SetHiddenPrototype(Bool(value))); + } + + VALUE FunctionTemplate::ReadOnlyPrototype(VALUE self) { + Void(FunctionTemplate(self)->ReadOnlyPrototype()); + } + + VALUE FunctionTemplate::HasInstance(VALUE self, VALUE object) { + return Bool(FunctionTemplate(self)->HasInstance(Value(object))); + } } \ No newline at end of file diff --git a/spec/c/template_spec.rb b/spec/c/template_spec.rb new file mode 100644 index 0000000..13b18f5 --- /dev/null +++ b/spec/c/template_spec.rb @@ -0,0 +1,41 @@ +require 'spec_helper' + +describe V8::C::Template do + before do + @cxt = V8::C::Context::New() + @cxt.Enter() + end + after do + @cxt.Exit() + end + + describe V8::C::FunctionTemplate do + it "can be created with no arguments" do + V8::C::HandleScope() do + t = V8::C::FunctionTemplate::New() + t.GetFunction().Call(@cxt.Global(),0, []).StrictEquals(@cxt.Global()).should be_true + end + end + + it "can be created with a callback" do + V8::C::HandleScope() do + receiver = V8::C::Object::New() + f = nil + callback = lambda do |arguments| + arguments.Length().should be(2) + arguments[0].Utf8Value().should eql 'one' + arguments[1].Utf8Value().should eql 'two' + arguments.Callee().StrictEquals(f).should be_true + arguments.This().StrictEquals(receiver).should be_true + arguments.Holder().StrictEquals(receiver).should be_true + arguments.IsConstructCall().should be_false + arguments.Data().Value().should be(42) + nil + end + t = V8::C::FunctionTemplate::New(callback, V8::C::External::New(42)) + f = t.GetFunction() + f.Call(receiver, 2, [V8::C::String::New('one'), V8::C::String::New('two')]) + end + end + end +end \ No newline at end of file