Skip to content
This repository was archived by the owner on Dec 4, 2023. It is now read-only.

Commit 9ab2d8b

Browse files
committed
Merge pull request #362 from cowboyd/ruby-defined-function-callbacks
Ruby defined function callbacks
2 parents 0235696 + 638f6ea commit 9ab2d8b

13 files changed

Lines changed: 431 additions & 23 deletions

File tree

ext/v8/external.cc

Lines changed: 2 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,31 +13,14 @@ namespace rr {
1313

1414
VALUE External::New(VALUE self, VALUE r_isolate, VALUE object) {
1515
Isolate isolate(r_isolate);
16-
17-
// as long as this external is alive within JavaScript, it should not be
18-
// garbage collected by Ruby.
19-
isolate.retainObject(object);
20-
2116
Locker lock(isolate);
2217

23-
// create the external.
24-
Container* container = new Container(object);
25-
v8::Local<v8::External> external(v8::External::New(isolate, (void*)container));
26-
27-
// next, we create a weak reference to this external so that we can be
28-
// notified when V8 is done with it. At that point, we can let Ruby know
29-
// that this external is done with it.
30-
v8::Global<v8::External>* global(new v8::Global<v8::External>(isolate, external));
31-
global->SetWeak<Container>(container, &release, v8::WeakCallbackType::kParameter);
32-
container->global = global;
33-
34-
return External(isolate, external);
18+
return External(isolate, wrap(isolate, object));
3519
}
3620

3721
VALUE External::Value(VALUE self) {
3822
External external(self);
3923
Locker lock(external);
40-
Container* container((Container*)external->Value());
41-
return container->object;
24+
return unwrap(external);
4225
}
4326
}

ext/v8/external.h

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,33 @@ namespace rr {
1616
inline External(v8::Isolate* isolate, v8::Handle<v8::Value> value) :
1717
External(isolate, v8::Handle<v8::External>::Cast<v8::Value>(value)) {}
1818

19+
static inline v8::Local<v8::External> wrap(Isolate isolate, VALUE object) {
20+
// as long as this external is alive within JavaScript, it should not be
21+
// garbage collected by Ruby.
22+
isolate.retainObject(object);
23+
24+
// create the external.
25+
Container* container = new Container(object);
26+
v8::Local<v8::External> external(v8::External::New(isolate, (void*)container));
27+
28+
// next, we create a weak reference to this external so that we can be
29+
// notified when V8 is done with it. At that point, we can let Ruby know
30+
// that this external is done with it.
31+
v8::Global<v8::External>* global(new v8::Global<v8::External>(isolate, external));
32+
global->SetWeak<Container>(container, &release, v8::WeakCallbackType::kParameter);
33+
container->global = global;
34+
35+
return External(isolate, external);
36+
}
37+
static inline VALUE unwrap(v8::Local<v8::External> external) {
38+
Container* container = (Container*)external->Value();
39+
return container->object;
40+
};
41+
42+
static inline VALUE unwrap(v8::Local<v8::Value> external) {
43+
return unwrap(v8::Local<v8::External>::Cast<v8::Value>(external));
44+
}
45+
1946
struct Container {
2047
Container(VALUE v) : object(v) {}
2148

ext/v8/function-callback.h

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
// -*- mode: c++ -*-
2+
#ifndef RR_FUNCTION_CALLBACK_H
3+
#define RR_FUNCTION_CALLBACK_H
4+
5+
namespace rr {
6+
7+
typedef Wrapper<v8::FunctionCallbackInfo<v8::Value>> FunctionCallbackInfoWrapper;
8+
9+
class FunctionCallbackInfo : public FunctionCallbackInfoWrapper {
10+
public:
11+
12+
inline FunctionCallbackInfo(v8::FunctionCallbackInfo<v8::Value> info, v8::Local<v8::Value> data_) :
13+
FunctionCallbackInfoWrapper(info), data(data_) {}
14+
15+
inline FunctionCallbackInfo(VALUE self) : FunctionCallbackInfoWrapper(self) {}
16+
17+
inline v8::Local<v8::Value> operator [](int i) {
18+
return this->container->content[i];
19+
}
20+
21+
/**
22+
* Package up the callback data for this function so that it can
23+
* invoke a Ruby callable.
24+
*
25+
* Each `v8::Function` can have one `v8::Value` associated with it
26+
* that is passed to its `v8::FunctionCallback`. To support this
27+
* same API from ruby, we take the `Value` passed into the
28+
* Function constructor *and* the callback and store them *both*
29+
* in a single `v8::Object` which we use for the C++ level
30+
* callback data.
31+
*/
32+
static v8::Local<v8::Value> wrapData(v8::Isolate* isolate, VALUE r_callback, VALUE r_data) {
33+
v8::Local<v8::Object> holder = v8::Object::New(isolate);
34+
v8::Local<v8::String> callback_key = v8::String::NewFromUtf8(isolate, "rr::callback");
35+
v8::Local<v8::String> data_key = v8::String::NewFromUtf8(isolate, "rr::data");
36+
holder->SetHiddenValue(callback_key, External::wrap(isolate, r_callback));
37+
holder->SetHiddenValue(data_key, Value(r_data));
38+
return holder;
39+
}
40+
41+
/**
42+
* Call the Ruby code associated with this callback.
43+
*
44+
* Unpack the Ruby code, and the callback data from the C++
45+
* callback data, and then invoke that code.
46+
*
47+
* Note: This function implements the `v8::FunctionCallback` API.
48+
*/
49+
static void invoke(const v8::FunctionCallbackInfo<v8::Value>& info) {
50+
v8::Isolate* isolate = info.GetIsolate();
51+
v8::Local<v8::Object> holder = v8::Local<v8::Object>::Cast<v8::Value>(info.Data());
52+
v8::Local<v8::String> data_key = v8::String::NewFromUtf8(isolate, "rr::data");
53+
v8::Local<v8::String> callback_key = v8::String::NewFromUtf8(isolate, "rr::callback");
54+
v8::Local<v8::Value> data(holder->GetHiddenValue(data_key));
55+
56+
VALUE code(External::unwrap(holder->GetHiddenValue(callback_key)));
57+
Unlocker unlock(info.GetIsolate());
58+
rb_funcall(code, rb_intern("call"), 1, (VALUE)FunctionCallbackInfo(info, data));
59+
}
60+
61+
static VALUE Length(VALUE self) {
62+
FunctionCallbackInfo info(self);
63+
Locker lock(info->GetIsolate());
64+
return INT2FIX(info->Length());
65+
}
66+
67+
static VALUE at(VALUE self, VALUE i) {
68+
FunctionCallbackInfo info(self);
69+
Locker lock(info->GetIsolate());
70+
return Value::handleToRubyObject(info->GetIsolate(), info[NUM2INT(i)]);
71+
}
72+
73+
static VALUE Callee(VALUE self) {
74+
FunctionCallbackInfo info(self);
75+
Locker lock(info->GetIsolate());
76+
return Function(info->GetIsolate(), info->Callee());
77+
}
78+
79+
static VALUE This(VALUE self) {
80+
FunctionCallbackInfo info(self);
81+
Locker lock(info->GetIsolate());
82+
return Object(info->GetIsolate(), info->This());
83+
}
84+
85+
static VALUE IsConstructCall(VALUE self) {
86+
FunctionCallbackInfo info(self);
87+
Locker lock(info->GetIsolate());
88+
return Bool(info->IsConstructCall());
89+
}
90+
91+
static VALUE Data(VALUE self) {
92+
FunctionCallbackInfo info(self);
93+
Locker lock(info->GetIsolate());
94+
return Value::handleToRubyObject(info->GetIsolate(), info.data);
95+
}
96+
97+
static VALUE GetIsolate(VALUE self) {
98+
FunctionCallbackInfo info(self);
99+
return Isolate(info->GetIsolate());
100+
}
101+
102+
static VALUE GetReturnValue(VALUE self) {
103+
FunctionCallbackInfo info(self);
104+
Locker lock(info->GetIsolate());
105+
return ReturnValue(info->GetReturnValue());
106+
}
107+
108+
static inline void Init() {
109+
ClassBuilder("FunctionCallbackInfo").
110+
defineMethod("Length", &Length).
111+
defineMethod("[]", &at).
112+
defineMethod("Callee", &Callee).
113+
defineMethod("This", &This).
114+
defineMethod("IsConstructCall", &IsConstructCall).
115+
defineMethod("Data", &Data).
116+
defineMethod("GetIsolate", &GetIsolate).
117+
defineMethod("GetReturnValue", &GetReturnValue).
118+
store(&Class);
119+
}
120+
121+
v8::Local<v8::Value> data;
122+
};
123+
}
124+
125+
#endif /* RR_FUNCTION_CALLBACK_H */

ext/v8/function.cc

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ namespace rr {
44

55
void Function::Init() {
66
ClassBuilder("Function", Object::Class).
7-
7+
defineSingletonMethod("New", &New).
88
defineMethod("NewInstance", &NewInstance).
99
defineMethod("Call", &Call).
1010
defineMethod("SetName", &SetName).
@@ -20,6 +20,23 @@ namespace rr {
2020
store(&Class);
2121
}
2222

23+
VALUE Function::New(int argc, VALUE argv[], VALUE self) {
24+
VALUE r_isolate, r_callback, r_data, r_length;
25+
rb_scan_args(argc, argv, "22", &r_isolate, &r_callback, &r_data, &r_length);
26+
Isolate isolate(r_isolate);
27+
Locker lock(isolate);
28+
29+
// package up the function's callback data to have bot the code and the data
30+
// parameter.
31+
v8::Local<v8::Value> data(FunctionCallbackInfo::wrapData(isolate, r_callback, r_data));
32+
33+
int length = RTEST(r_length) ? NUM2INT(r_length) : 0;
34+
35+
v8::FunctionCallback callback = &FunctionCallbackInfo::invoke;
36+
37+
return Function(isolate, v8::Function::New(isolate, callback, data, length));
38+
}
39+
2340
VALUE Function::NewInstance(int argc, VALUE argv[], VALUE self) {
2441
VALUE args;
2542
rb_scan_args(argc, argv, "01", &args);

ext/v8/function.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// -*- mode: c++ -*-
12
#ifndef RR_FUNCTION
23
#define RR_FUNCTION
34

@@ -7,8 +8,9 @@ namespace rr {
78
public:
89
static void Init();
910

11+
static VALUE New(int argc, VALUE argv[], VALUE self);
1012
static VALUE NewInstance(int argc, VALUE argv[], VALUE self);
11-
static VALUE Call(VALUE self, VALUE receiver, VALUE argv);
13+
static VALUE Call(VALUE self, VALUE receiver, VALUE arguments);
1214
static VALUE SetName(VALUE self, VALUE name);
1315
static VALUE GetName(VALUE self);
1416
static VALUE GetInferredName(VALUE self);
@@ -23,7 +25,6 @@ namespace rr {
2325
inline Function(VALUE value) : Ref<v8::Function>(value) {}
2426
inline Function(v8::Isolate* isolate, v8::Handle<v8::Function> function) : Ref<v8::Function>(isolate, function) {}
2527
};
26-
2728
}
2829

2930
#endif

ext/v8/init.cc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ extern "C" {
2323
String::Init();
2424
Symbol::Init();
2525
Function::Init();
26+
FunctionCallbackInfo::Init();
27+
ReturnValue::Init();
2628
Script::Init();
2729
ScriptOrigin::Init();
2830
Array::Init();

ext/v8/locks.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// -*- mode: c++ -*-
12
#ifndef RR_LOCKER
23
#define RR_LOCKER
34

ext/v8/object.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ namespace rr {
88

99
defineMethod("Set", &Set).
1010
defineMethod("Get", &Get).
11+
defineMethod("GetIdentityHash", &GetIdentityHash).
1112

1213
store(&Class);
1314
}
@@ -46,6 +47,13 @@ namespace rr {
4647
}
4748
}
4849

50+
VALUE Object::GetIdentityHash(VALUE self) {
51+
Object object(self);
52+
Locker lock(object.getIsolate());
53+
54+
return INT2FIX(object->GetIdentityHash());
55+
}
56+
4957
Object::operator VALUE() {
5058
Locker lock(getIsolate());
5159

ext/v8/object.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ namespace rr {
3434
// static VALUE HasNamedLookupInterceptor(VALUE self);
3535
// static VALUE HasIndexedLookupInterceptor(VALUE self);
3636
// static VALUE TurnOnAccessCheck(VALUE self);
37-
// static VALUE GetIdentityHash(VALUE self);
37+
static VALUE GetIdentityHash(VALUE self);
3838
// static VALUE SetHiddenValue(VALUE self, VALUE key, VALUE value);
3939
// static VALUE GetHiddenValue(VALUE self, VALUE key);
4040
// static VALUE DeleteHiddenValue(VALUE self, VALUE key);

ext/v8/return-value.h

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// -*- mode: c++ -*-
2+
#ifndef RR_RETURN_VALUE_H
3+
#define RR_RETURN_VALUE_H
4+
5+
namespace rr {
6+
typedef Wrapper<v8::ReturnValue<v8::Value>> ReturnValueWrapper;
7+
8+
class ReturnValue : public ReturnValueWrapper {
9+
public:
10+
ReturnValue(v8::ReturnValue<v8::Value> value) : ReturnValueWrapper(value) {}
11+
ReturnValue(VALUE self) : ReturnValueWrapper(self) {}
12+
13+
static VALUE Set(VALUE self, VALUE handle) {
14+
ReturnValue ret(self);
15+
Locker lock(ret->GetIsolate());
16+
v8::Local<v8::Value> value((Value(handle)));
17+
ret->Set(value);
18+
return Qnil;
19+
}
20+
21+
static VALUE Set_bool(VALUE self, VALUE value) {
22+
ReturnValue ret(self);
23+
Locker lock(ret->GetIsolate());
24+
ret->Set((bool)Bool(value));
25+
return Qnil;
26+
}
27+
28+
static VALUE Set_double(VALUE self, VALUE value) {
29+
ReturnValue ret(self);
30+
Locker lock(ret->GetIsolate());
31+
ret->Set(NUM2DBL(value));
32+
return Qnil;
33+
}
34+
35+
static VALUE Set_int32_t(VALUE self, VALUE i) {
36+
ReturnValue ret(self);
37+
Locker lock(ret->GetIsolate());
38+
ret->Set(NUM2INT(i));
39+
return Qnil;
40+
}
41+
42+
static VALUE Set_uint32_t(VALUE self, VALUE i) {
43+
ReturnValue ret(self);
44+
Locker lock(ret->GetIsolate());
45+
ret->Set(NUM2UINT(i));
46+
return Qnil;
47+
}
48+
49+
static VALUE SetNull(VALUE self) {
50+
ReturnValue ret(self);
51+
Locker lock(ret->GetIsolate());
52+
ret->SetNull();
53+
return Qnil;
54+
}
55+
56+
static VALUE SetUndefined(VALUE self) {
57+
ReturnValue ret(self);
58+
Locker lock(ret->GetIsolate());
59+
ret->SetUndefined();
60+
return Qnil;
61+
}
62+
63+
static VALUE SetEmptyString(VALUE self) {
64+
ReturnValue ret(self);
65+
Locker lock(ret->GetIsolate());
66+
ret->SetEmptyString();
67+
return Qnil;
68+
}
69+
70+
static VALUE GetIsolate(VALUE self) {
71+
ReturnValue ret(self);
72+
return Isolate(ret->GetIsolate());
73+
}
74+
75+
static inline void Init() {
76+
ClassBuilder("ReturnValue").
77+
defineMethod("Set", &Set).
78+
defineMethod("Set_bool", &Set_bool).
79+
defineMethod("Set_double", &Set_double).
80+
defineMethod("Set_int32_t", &Set_int32_t).
81+
defineMethod("Set_uint32_t", &Set_uint32_t).
82+
defineMethod("SetNull", &SetNull).
83+
defineMethod("SetUndefined", &SetUndefined).
84+
defineMethod("SetEmptyString", &SetEmptyString).
85+
defineMethod("GetIsolate", &GetIsolate).
86+
store(&Class);
87+
}
88+
};
89+
}
90+
#endif /* RR_RETURN_VALUE_H */

0 commit comments

Comments
 (0)