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

Commit f5b5644

Browse files
committed
supply a function callback to function template
1 parent e704d73 commit f5b5644

11 files changed

Lines changed: 137 additions & 70 deletions

ext/v8/function-callback.h

Lines changed: 62 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -9,55 +9,15 @@ namespace rr {
99
class FunctionCallbackInfo : public FunctionCallbackInfoWrapper {
1010
public:
1111

12-
inline FunctionCallbackInfo(v8::FunctionCallbackInfo<v8::Value> info, v8::Local<v8::Value> data_) :
13-
FunctionCallbackInfoWrapper(info), data(data_) {}
12+
inline FunctionCallbackInfo(v8::FunctionCallbackInfo<v8::Value> info) :
13+
FunctionCallbackInfoWrapper(info) {}
1414

1515
inline FunctionCallbackInfo(VALUE self) : FunctionCallbackInfoWrapper(self) {}
1616

1717
inline v8::Local<v8::Value> operator [](int i) {
1818
return this->container->content[i];
1919
}
2020

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-
6121
static VALUE Length(VALUE self) {
6222
FunctionCallbackInfo info(self);
6323
Locker lock(info->GetIsolate());
@@ -90,8 +50,13 @@ namespace rr {
9050

9151
static VALUE Data(VALUE self) {
9252
FunctionCallbackInfo info(self);
53+
Isolate isolate(info->GetIsolate());
9354
Locker lock(info->GetIsolate());
94-
return Value::handleToRubyObject(info->GetIsolate(), info.data);
55+
56+
v8::Local<v8::Object> holder = v8::Local<v8::Object>::Cast<v8::Value>(info->Data());
57+
v8::Local<v8::String> data_key = v8::String::NewFromUtf8(isolate, "rr::data");
58+
v8::Local<v8::Value> data(holder->GetHiddenValue(data_key));
59+
return Value::handleToRubyObject(isolate, data);
9560
}
9661

9762
static VALUE GetIsolate(VALUE self) {
@@ -117,8 +82,61 @@ namespace rr {
11782
defineMethod("GetReturnValue", &GetReturnValue).
11883
store(&Class);
11984
}
85+
};
86+
87+
class FunctionCallback {
88+
public:
89+
FunctionCallback(v8::Isolate* i, VALUE c, VALUE d) : isolate(i), code(c), data(d) {}
90+
91+
/**
92+
* Package up the callback data for this function so that it can
93+
* invoke a Ruby callable.
94+
*
95+
* Each `v8::Function` can have one `v8::Value` associated with it
96+
* that is passed to its `v8::FunctionCallback`. To support this
97+
* same API from ruby, we take the `Value` passed into the
98+
* Function constructor *and* the callback and store them *both*
99+
* in a single `v8::Object` which we use for the C++ level
100+
* callback data.
101+
*/
102+
inline operator v8::Local<v8::Value>() {
103+
v8::Local<v8::Object> holder = v8::Object::New(isolate);
104+
v8::Local<v8::String> callback_key = v8::String::NewFromUtf8(isolate, "rr::callback");
105+
v8::Local<v8::String> data_key = v8::String::NewFromUtf8(isolate, "rr::data");
106+
holder->SetHiddenValue(callback_key, External::wrap(isolate, code));
107+
holder->SetHiddenValue(data_key, Value(data));
108+
return holder;
109+
}
110+
111+
/**
112+
* Call the Ruby code associated with this callback.
113+
*
114+
* Unpack the Ruby code, and the callback data from the C++
115+
* callback data, and then invoke that code.
116+
*
117+
* Note: This function implements the `v8::FunctionCallback` API.
118+
*/
119+
static void invoke(const v8::FunctionCallbackInfo<v8::Value>& info) {
120+
v8::Isolate* isolate = info.GetIsolate();
121+
v8::Local<v8::Object> holder = v8::Local<v8::Object>::Cast<v8::Value>(info.Data());
122+
v8::Local<v8::String> callback_key = v8::String::NewFromUtf8(isolate, "rr::callback");
123+
124+
VALUE code(External::unwrap(holder->GetHiddenValue(callback_key)));
125+
Unlocker unlock(info.GetIsolate());
126+
rb_funcall(code, rb_intern("call"), 1, (VALUE)FunctionCallbackInfo(info));
127+
}
128+
129+
inline operator v8::FunctionCallback() {
130+
if (RTEST(code)) {
131+
return &invoke;
132+
} else {
133+
return 0;
134+
}
135+
}
120136

121-
v8::Local<v8::Value> data;
137+
v8::Isolate* isolate;
138+
VALUE code;
139+
VALUE data;
122140
};
123141
}
124142

ext/v8/function-template.h

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
// -*- mode: c++ -*-
22

3+
#ifndef RR_FUNCTION_TEMPLATE_H
4+
#define RR_FUNCTION_TEMPLATE_H
5+
36
namespace rr {
7+
8+
49
class FunctionTemplate : public Ref<v8::FunctionTemplate> {
510
public:
611
FunctionTemplate(VALUE self) : Ref<v8::FunctionTemplate>(self) {}
@@ -14,12 +19,16 @@ namespace rr {
1419
}
1520

1621
static VALUE New(int argc, VALUE argv[], VALUE self) {
17-
VALUE r_isolate, callback, data, signature, length;
18-
rb_scan_args(argc, argv, "14", &r_isolate, &callback, &data, &signature, &length);
22+
VALUE r_isolate, r_callback, r_data, r_signature, r_length;
23+
rb_scan_args(argc, argv, "14", &r_isolate, &r_callback, &r_data, &r_signature, &r_length);
1924
Isolate isolate(r_isolate);
2025
Locker lock(isolate);
2126

22-
return FunctionTemplate(isolate, v8::FunctionTemplate::New(isolate));
27+
FunctionCallback callback(isolate, r_callback, r_data);
28+
Signature signature(r_signature);
29+
int length(RTEST(r_length) ? NUM2INT(r_length) : 0);
30+
31+
return FunctionTemplate(isolate, v8::FunctionTemplate::New(isolate, callback, callback, v8::Local<v8::Signature>(), length));
2332
}
2433

2534
static VALUE GetFunction(VALUE self, VALUE context) {
@@ -31,3 +40,5 @@ namespace rr {
3140
}
3241
};
3342
}
43+
44+
#endif /* RR_FUNCTION_TEMPLATE_H */

ext/v8/function.cc

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,13 @@ namespace rr {
2626
Isolate isolate(r_isolate);
2727
Locker lock(isolate);
2828

29-
// package up the function's callback data to have bot the code and the data
29+
// package up the function's callback data to have both the code and the data
3030
// parameter.
31-
v8::Local<v8::Value> data(FunctionCallbackInfo::wrapData(isolate, r_callback, r_data));
31+
FunctionCallback callback(isolate, r_callback, r_data);
3232

3333
int length = RTEST(r_length) ? NUM2INT(r_length) : 0;
3434

35-
v8::FunctionCallback callback = &FunctionCallbackInfo::invoke;
36-
37-
return Function(isolate, v8::Function::New(isolate, callback, data, length));
35+
return Function(isolate, v8::Function::New(isolate, callback, callback, length));
3836
}
3937

4038
VALUE Function::NewInstance(int argc, VALUE argv[], VALUE self) {

ext/v8/init.cc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ extern "C" {
3434
Template::Init();
3535
ObjectTemplate::Init();
3636
FunctionTemplate::Init();
37+
Signature::Init();
3738

3839
// Accessor::Init();
3940
// Invocation::Init();

ext/v8/object-template.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ namespace rr {
3131
v8::MaybeLocal<v8::Object> object(t->NewInstance());
3232
return Object::Maybe(isolate, ObjectTemplate(self)->NewInstance(context));
3333
}
34-
3534
};
3635
}
3736

ext/v8/rr.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ inline VALUE not_implemented(const char* message) {
5555
#include "function-callback.h"
5656

5757
#include "template.h"
58+
#include "signature.h"
5859
#include "function-template.h"
5960
#include "object-template.h"
6061

ext/v8/signature.cc

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#include "rr.h"
2+
3+
namespace rr {
4+
VALUE Signature::New(int argc, VALUE argv[], VALUE self) {
5+
VALUE r_isolate;
6+
VALUE r_receiver;
7+
rb_scan_args(argc, argv, "11", &r_isolate, &r_receiver);
8+
9+
Isolate isolate(r_isolate);
10+
Locker lock(isolate);
11+
return Signature(isolate, v8::Signature::New(isolate, FunctionTemplate(r_receiver)));
12+
}
13+
}

ext/v8/signature.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// -*- mode: c++ -*-
2+
#ifndef RR_SIGNATURE_H
3+
#define RR_SIGNATURE_H
4+
5+
namespace rr {
6+
class Signature : public Ref<v8::Signature> {
7+
public:
8+
Signature(VALUE self) : Ref<v8::Signature>(self) {}
9+
Signature(v8::Isolate* i, v8::Local<v8::Signature> s) :
10+
Ref<v8::Signature>(i, s) {}
11+
12+
static void Init() {
13+
ClassBuilder("Signature").
14+
defineSingletonMethod("New", &New).
15+
store(&Class);
16+
}
17+
18+
static VALUE New(int argc, VALUE argv[], VALUE self);
19+
};
20+
}
21+
22+
#endif /* RR_SIGNATURE_H */

spec/c/function_spec.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,9 @@ def call(info)
6262
end
6363
end
6464

65+
let(:data) { V8::C::Object::New(@isolate) }
6566
let(:callback) { FunctionCallback.new @isolate}
66-
let(:fn) { V8::C::Function::New(@isolate, callback)}
67+
let(:fn) { V8::C::Function::New(@isolate, callback, data)}
6768

6869
before do
6970
expect(fn.Call(@ctx.Global(), ["world"]).Utf8Value()).to eql "ohai world"
@@ -75,6 +76,8 @@ def call(info)
7576
expect(callback.callee.GetIdentityHash()).to eql fn.GetIdentityHash()
7677
expect(callback.this.GetIdentityHash()).to eql @ctx.Global().GetIdentityHash()
7778
expect(callback.is_construct_call).to be false
79+
expect(callback.data).not_to be_nil
80+
expect(callback.data.StrictEquals(data)).to be true
7881
end
7982
end
8083

spec/c/function_template_spec.rb

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22

33
describe V8::C::FunctionTemplate do
44
requires_v8_context
5+
let(:function) { template.GetFunction(@ctx).FromJust() }
56

67
describe "New()" do
78
describe "with no arguments" do
8-
let(:function) { template.GetFunction(@ctx).FromJust() }
99
let(:template) { V8::C::FunctionTemplate::New(@isolate) }
1010
it "creates an empty template" do
1111
expect(template).to be
@@ -15,5 +15,21 @@
1515
expect(function.Call(@ctx.Global(), []).StrictEquals(@ctx.Global())).to be true
1616
end
1717
end
18+
describe "with a function callback" do
19+
let(:data) { V8::C::Integer::New(@isolate, 42) }
20+
let(:signature) { V8::C::Signature::New(@isolate) }
21+
let(:template) { V8::C::FunctionTemplate::New(@isolate, @callback, data, @signature, 0) }
22+
before do
23+
@callback = ->(info) do
24+
@data = info.Data()
25+
end
26+
function.Call(@ctx.Global(), [])
27+
end
28+
29+
it "does something" do
30+
expect(@data).not_to be_nil
31+
expect(@data.Value()).to be 42
32+
end
33+
end
1834
end
1935
end

0 commit comments

Comments
 (0)