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

Commit 0681a98

Browse files
committed
add ability to define a callback for a JS Function
1 parent d1dd01d commit 0681a98

11 files changed

Lines changed: 292 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: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
// -*- mode: c++ -*-
2+
#ifndef RR_FUNCTION_CALLBACK_H
3+
#define RR_FUNCTION_CALLBACK_H
4+
5+
namespace rr {
6+
7+
typedef Wrapper<v8::ReturnValue<v8::Value>> ReturnValueWrapper;
8+
9+
class ReturnValue : public ReturnValueWrapper {
10+
public:
11+
ReturnValue(v8::ReturnValue<v8::Value> value) : ReturnValueWrapper(value) {}
12+
ReturnValue(VALUE self) : ReturnValueWrapper(self) {}
13+
14+
static VALUE Set(VALUE self, VALUE handle) {
15+
ReturnValue ret(self);
16+
Locker lock(ret->GetIsolate());
17+
v8::Local<v8::Value> value((Value(handle)));
18+
ret->Set(value);
19+
return Qnil;
20+
}
21+
22+
static VALUE Set_bool(VALUE self, VALUE value) {
23+
ReturnValue ret(self);
24+
Locker lock(ret->GetIsolate());
25+
ret->Set((bool)Bool(value));
26+
return Qnil;
27+
}
28+
29+
static VALUE Set_double(VALUE self, VALUE value) {
30+
ReturnValue ret(self);
31+
Locker lock(ret->GetIsolate());
32+
ret->Set(NUM2DBL(value));
33+
return Qnil;
34+
}
35+
36+
static VALUE Set_int32_t(VALUE self, VALUE i) {
37+
ReturnValue ret(self);
38+
Locker lock(ret->GetIsolate());
39+
ret->Set(NUM2INT(i));
40+
return Qnil;
41+
}
42+
43+
static VALUE Set_uint32_t(VALUE self, VALUE i) {
44+
ReturnValue ret(self);
45+
Locker lock(ret->GetIsolate());
46+
ret->Set(NUM2UINT(i));
47+
return Qnil;
48+
}
49+
50+
static VALUE SetNull(VALUE self) {
51+
ReturnValue ret(self);
52+
Locker lock(ret->GetIsolate());
53+
ret->SetNull();
54+
return Qnil;
55+
}
56+
57+
static VALUE SetUndefined(VALUE self) {
58+
ReturnValue ret(self);
59+
Locker lock(ret->GetIsolate());
60+
ret->SetUndefined();
61+
return Qnil;
62+
}
63+
64+
static VALUE SetEmptyString(VALUE self) {
65+
ReturnValue ret(self);
66+
Locker lock(ret->GetIsolate());
67+
ret->SetEmptyString();
68+
return Qnil;
69+
}
70+
71+
static VALUE GetIsolate(VALUE self) {
72+
ReturnValue ret(self);
73+
return Isolate(ret->GetIsolate());
74+
}
75+
76+
static inline void Init() {
77+
ClassBuilder("ReturnValue").
78+
defineMethod("Set", &Set).
79+
defineMethod("Set_bool", &Set_bool).
80+
defineMethod("Set_double", &Set_double).
81+
defineMethod("Set_int32_t", &Set_int32_t).
82+
defineMethod("Set_uint32_t", &Set_uint32_t).
83+
defineMethod("SetNull", &SetNull).
84+
defineMethod("SetUndefined", &SetUndefined).
85+
defineMethod("SetEmptyString", &SetEmptyString).
86+
defineMethod("GetIsolate", &GetIsolate).
87+
store(&Class);
88+
}
89+
90+
Container* container;
91+
};
92+
93+
typedef Wrapper<v8::FunctionCallbackInfo<v8::Value>> FunctionCallbackInfoWrapper;
94+
95+
class FunctionCallbackInfo : public FunctionCallbackInfoWrapper {
96+
public:
97+
98+
inline FunctionCallbackInfo(v8::FunctionCallbackInfo<v8::Value> info, v8::Local<v8::Value> data_) :
99+
FunctionCallbackInfoWrapper(info), data(data_) {}
100+
101+
inline FunctionCallbackInfo(VALUE self) : FunctionCallbackInfoWrapper(self) {}
102+
103+
inline v8::Local<v8::Value> operator [](int i) {
104+
return this->container->content[i];
105+
}
106+
107+
static v8::Local<v8::Value> wrapData(v8::Isolate* isolate, VALUE r_callback, VALUE r_data) {
108+
v8::Local<v8::Object> holder = v8::Object::New(isolate);
109+
v8::Local<v8::String> callback_key = v8::String::NewFromUtf8(isolate, "rr::callback");
110+
v8::Local<v8::String> data_key = v8::String::NewFromUtf8(isolate, "rr::data");
111+
holder->SetHiddenValue(callback_key, External::wrap(isolate, r_callback));
112+
holder->SetHiddenValue(data_key, Value(r_data));
113+
return holder;
114+
}
115+
116+
static void invoke(const v8::FunctionCallbackInfo<v8::Value>& info) {
117+
v8::Isolate* isolate = info.GetIsolate();
118+
v8::Local<v8::Object> holder = v8::Local<v8::Object>::Cast<v8::Value>(info.Data());
119+
v8::Local<v8::String> data_key = v8::String::NewFromUtf8(isolate, "rr::data");
120+
v8::Local<v8::String> callback_key = v8::String::NewFromUtf8(isolate, "rr::callback");
121+
v8::Local<v8::Value> data(holder->GetHiddenValue(data_key));
122+
123+
VALUE code(External::unwrap(holder->GetHiddenValue(callback_key)));
124+
Unlocker unlock(info.GetIsolate());
125+
rb_funcall(code, rb_intern("call"), 1, (VALUE)FunctionCallbackInfo(info, data));
126+
}
127+
128+
static VALUE Length(VALUE self) {
129+
FunctionCallbackInfo info(self);
130+
Locker lock(info->GetIsolate());
131+
return INT2FIX(info->Length());
132+
}
133+
134+
static VALUE at(VALUE self, VALUE i) {
135+
FunctionCallbackInfo info(self);
136+
Locker lock(info->GetIsolate());
137+
return Value::handleToRubyObject(info->GetIsolate(), info[NUM2INT(i)]);
138+
}
139+
140+
static VALUE Callee(VALUE self) {
141+
FunctionCallbackInfo info(self);
142+
Locker lock(info->GetIsolate());
143+
return Function(info->GetIsolate(), info->Callee());
144+
}
145+
146+
static VALUE This(VALUE self) {
147+
FunctionCallbackInfo info(self);
148+
Locker lock(info->GetIsolate());
149+
return Object(info->GetIsolate(), info->This());
150+
}
151+
152+
static VALUE IsConstructCall(VALUE self) {
153+
FunctionCallbackInfo info(self);
154+
Locker lock(info->GetIsolate());
155+
return Bool(info->IsConstructCall());
156+
}
157+
158+
static VALUE Data(VALUE self) {
159+
FunctionCallbackInfo info(self);
160+
Locker lock(info->GetIsolate());
161+
return Value::handleToRubyObject(info->GetIsolate(), info.data);
162+
}
163+
164+
static VALUE GetIsolate(VALUE self) {
165+
FunctionCallbackInfo info(self);
166+
return Isolate(info->GetIsolate());
167+
}
168+
169+
static VALUE GetReturnValue(VALUE self) {
170+
FunctionCallbackInfo info(self);
171+
Locker lock(info->GetIsolate());
172+
return ReturnValue(info->GetReturnValue());
173+
}
174+
175+
static inline void Init() {
176+
ClassBuilder("FunctionCallbackInfo").
177+
defineMethod("Length", &Length).
178+
defineMethod("[]", &at).
179+
defineMethod("Callee", &Callee).
180+
defineMethod("This", &This).
181+
defineMethod("IsConstructCall", &IsConstructCall).
182+
defineMethod("Data", &Data).
183+
defineMethod("GetIsolate", &GetIsolate).
184+
defineMethod("GetReturnValue", &GetReturnValue).
185+
store(&Class);
186+
}
187+
188+
v8::Local<v8::Value> data;
189+
};
190+
}
191+
192+
#endif /* RR_FUNCTION_CALLBACK_H */

ext/v8/function.cc

Lines changed: 16 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,21 @@ 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+
v8::Local<v8::Value> data(FunctionCallbackInfo::wrapData(isolate, r_callback, r_data));
30+
31+
int length = RTEST(r_length) ? NUM2INT(r_length) : 0;
32+
33+
v8::FunctionCallback callback = &FunctionCallbackInfo::invoke;
34+
35+
return Function(isolate, v8::Function::New(isolate, callback, data, length));
36+
}
37+
2338
VALUE Function::NewInstance(int argc, VALUE argv[], VALUE self) {
2439
VALUE args;
2540
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
@@ -20,6 +20,8 @@ extern "C" {
2020
String::Init();
2121
Symbol::Init();
2222
Function::Init();
23+
FunctionCallbackInfo::Init();
24+
ReturnValue::Init();
2325
Script::Init();
2426
ScriptOrigin::Init();
2527
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
}
@@ -42,6 +43,13 @@ namespace rr {
4243
}
4344
}
4445

46+
VALUE Object::GetIdentityHash(VALUE self) {
47+
Object object(self);
48+
Locker lock(object.getIsolate());
49+
50+
return INT2FIX(object->GetIdentityHash());
51+
}
52+
4553
Object::operator VALUE() {
4654
Locker lock(getIsolate());
4755

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/rr.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ inline VALUE not_implemented(const char* message) {
2121
#include "equiv.h"
2222
#include "bool.h"
2323
#include "pointer.h"
24+
#include "wrapper.h"
2425
#include "isolate.h"
2526

2627
#include "ref.h"
@@ -46,5 +47,6 @@ inline VALUE not_implemented(const char* message) {
4647
#include "script.h"
4748
#include "script-origin.h"
4849
#include "function.h"
50+
#include "function-callback.h"
4951

5052
#endif

0 commit comments

Comments
 (0)