h8 0.0.2 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +8 -0
- data/README.md +55 -2
- data/ext/h8/JsCatcher.cpp +24 -0
- data/ext/h8/JsCatcher.h +29 -0
- data/ext/h8/allocated_resource.h +37 -0
- data/ext/h8/chain.h +191 -0
- data/ext/h8/extconf.rb +48 -30
- data/ext/h8/h8.cpp +100 -8
- data/ext/h8/h8.h +109 -42
- data/ext/h8/js_gate.cpp +18 -0
- data/ext/h8/js_gate.h +70 -31
- data/ext/h8/main.cpp +69 -25
- data/ext/h8/object_wrap.h +1 -1
- data/ext/h8/ruby_gate.cpp +117 -0
- data/ext/h8/ruby_gate.h +118 -0
- data/hybrid8.gemspec +3 -1
- data/lib/h8.rb +61 -2
- data/lib/h8/context.rb +38 -5
- data/lib/h8/value.rb +104 -19
- data/lib/h8/version.rb +1 -1
- data/spec/context_spec.rb +42 -4
- data/spec/js_gate_spec.rb +95 -16
- data/spec/ruby_gate_spec.rb +159 -0
- data/spec/spec_helper.rb +31 -1
- metadata +15 -4
- data/ext/h8/ruby_wrap.h +0 -32
data/ext/h8/h8.cpp
CHANGED
@@ -1,16 +1,108 @@
|
|
1
|
+
#include <thread>
|
2
|
+
#include <mutex>
|
3
|
+
#include <condition_variable>
|
4
|
+
#include <chrono>
|
5
|
+
|
1
6
|
#include "h8.h"
|
7
|
+
#include "ruby_gate.h"
|
8
|
+
|
9
|
+
void h8::JsError::raise() {
|
10
|
+
if (has_js_exception) {
|
11
|
+
VALUE ruby_exception;
|
12
|
+
{
|
13
|
+
// raising exception does longjump so we should keep all memory
|
14
|
+
// allocation done before:
|
15
|
+
H8::Scope scope(h8);
|
16
|
+
|
17
|
+
Local<Object> jsx = exception().As<Object>();
|
18
|
+
Local<Value> source = jsx->Get(h8->js("source"));
|
19
|
+
RubyGate *rg = RubyGate::unwrap(source.As<Object>());
|
20
|
+
if (rg) {
|
21
|
+
// Passing thru the Ruby exception
|
22
|
+
ruby_exception = rg->rubyObject();
|
23
|
+
} else {
|
24
|
+
Local<String> s = message()->Get();
|
25
|
+
String::Utf8Value res(s->ToString());
|
26
|
+
ruby_exception = ruby_exception = rb_exc_new2(js_exception,
|
27
|
+
*res ? *res : "test");
|
28
|
+
rb_iv_set(ruby_exception, "@message", h8->to_ruby(s));
|
29
|
+
// TODO: Pass also all information from Message instance
|
30
|
+
rb_iv_set(ruby_exception, "@source", h8->to_ruby(source));
|
31
|
+
}
|
32
|
+
}
|
33
|
+
rb_exc_raise(ruby_exception);
|
34
|
+
// }
|
35
|
+
} else {
|
36
|
+
rb_raise(h8_exception, "%s", reason);
|
37
|
+
}
|
38
|
+
}
|
2
39
|
|
3
|
-
|
40
|
+
void h8::JsTimeoutError::raise() {
|
41
|
+
rb_raise(js_timeout_exception, "timeout expired");
|
42
|
+
}
|
43
|
+
|
44
|
+
Local<Value> h8::H8::gateObject(VALUE ruby_value) {
|
4
45
|
if ( Qtrue == rb_funcall(ruby_value, id_is_a, 1, value_class)) {
|
5
46
|
JsGate *gate;
|
6
47
|
Data_Get_Struct(ruby_value, JsGate, gate);
|
7
|
-
if(
|
8
|
-
|
9
|
-
|
10
|
-
}
|
11
|
-
else
|
48
|
+
if (gate->h8 != this) {
|
49
|
+
throw JsError(this, "H8::Value is bound to other H8::Context");
|
50
|
+
} else
|
12
51
|
return gate->value();
|
13
52
|
}
|
14
|
-
|
15
|
-
|
53
|
+
// Generic Ruby object
|
54
|
+
RubyGate *gate = new RubyGate(this, ruby_value);
|
55
|
+
return gate->handle(isolate);
|
56
|
+
}
|
57
|
+
|
58
|
+
void h8::H8::ruby_mark_gc() const {
|
59
|
+
for (chain::link *x : resources)
|
60
|
+
((AllocatedResource*) x)->rb_mark_gc();
|
61
|
+
}
|
62
|
+
|
63
|
+
v8::Handle<v8::Value> h8::H8::eval(const char* script_utf,unsigned max_ms) {
|
64
|
+
v8::EscapableHandleScope escape(isolate);
|
65
|
+
Local<Value> result;
|
66
|
+
|
67
|
+
Handle<v8::String> script_source = String::NewFromUtf8(isolate, script_utf);
|
68
|
+
v8::Handle<v8::Script> script;
|
69
|
+
JsCatcher try_catch(this);
|
70
|
+
v8::ScriptOrigin origin(String::NewFromUtf8(isolate, "eval"));
|
71
|
+
|
72
|
+
script = v8::Script::Compile(script_source, &origin);
|
73
|
+
|
74
|
+
if (script.IsEmpty()) {
|
75
|
+
try_catch.throwIfCaught();
|
76
|
+
result = Undefined(isolate);
|
77
|
+
} else {
|
78
|
+
result = Undefined(isolate);
|
79
|
+
if( max_ms > 0 ) {
|
80
|
+
std::mutex m;
|
81
|
+
std::condition_variable cv;
|
82
|
+
std::thread thr( [&] {
|
83
|
+
std::unique_lock<std::mutex> lock(m);
|
84
|
+
if( std::cv_status::timeout == cv.wait_for(lock, std::chrono::milliseconds(max_ms) ) ) {
|
85
|
+
isolate->TerminateExecution();
|
86
|
+
}
|
87
|
+
});
|
88
|
+
script->Run();
|
89
|
+
cv.notify_all();
|
90
|
+
thr.join();
|
91
|
+
}
|
92
|
+
else {
|
93
|
+
result = script->Run();
|
94
|
+
}
|
95
|
+
try_catch.throwIfCaught();
|
96
|
+
}
|
97
|
+
return escape.Escape(result);
|
98
|
+
}
|
99
|
+
|
100
|
+
h8::H8::~H8() {
|
101
|
+
while (!resources.is_empty()) {
|
102
|
+
// this should also remove it from the list:
|
103
|
+
resources.peek_first<AllocatedResource>()->free();
|
104
|
+
}
|
105
|
+
|
106
|
+
persistent_context.Reset();
|
107
|
+
isolate->Dispose();
|
16
108
|
}
|
data/ext/h8/h8.h
CHANGED
@@ -4,24 +4,76 @@
|
|
4
4
|
#include <include/v8.h>
|
5
5
|
#include <ruby.h>
|
6
6
|
#include <iostream>
|
7
|
+
#include <exception>
|
8
|
+
#include "allocated_resource.h"
|
9
|
+
#include "JsCatcher.h"
|
7
10
|
|
8
11
|
using namespace v8;
|
9
12
|
using namespace std;
|
10
13
|
|
11
|
-
extern VALUE h8_exception;
|
12
|
-
extern VALUE
|
14
|
+
extern VALUE h8_exception, js_exception, js_timeout_exception;
|
15
|
+
extern VALUE context_class;
|
13
16
|
extern VALUE value_class;
|
17
|
+
extern VALUE ruby_gate_class;
|
18
|
+
extern VALUE Rundefined;
|
14
19
|
|
15
20
|
extern ID id_is_a;
|
16
|
-
|
17
|
-
//#include <ruby/thread.h>
|
21
|
+
extern ID id_safe_call;
|
18
22
|
|
19
23
|
namespace h8 {
|
20
24
|
|
25
|
+
/// Allocate ruby H8::Context class (wrapper for h8::H8), ruby utility function
|
26
|
+
/// shold be declared as friend
|
27
|
+
VALUE context_alloc(VALUE klass);
|
28
|
+
|
29
|
+
class RubyGate;
|
30
|
+
class H8;
|
31
|
+
|
21
32
|
template<class T> inline void t(const T& x) {
|
22
33
|
cout << x << endl << flush;
|
23
34
|
}
|
24
35
|
|
36
|
+
/**
|
37
|
+
* The exception that is raised toward ruby code, e.g. when JS code throws uncaught exception,
|
38
|
+
* interpreter fails to cope with syntax, parameters are used in a wrong way, etc. Instead of calling
|
39
|
+
* rb_raise() which will longjump() over all your C++ code, throw instance of JsError.
|
40
|
+
*/
|
41
|
+
class JsError: public std::exception {
|
42
|
+
public:
|
43
|
+
JsError(H8* h8, const char* str_reason) :
|
44
|
+
h8(h8), has_js_exception(false) {
|
45
|
+
reason = str_reason;
|
46
|
+
}
|
47
|
+
|
48
|
+
JsError(H8* h8, v8::Local<v8::Message> message, v8::Local<v8::Value> exception);
|
49
|
+
|
50
|
+
/**
|
51
|
+
* Call it with a proper exception class and be careful - after this call no code will be executed!
|
52
|
+
*/
|
53
|
+
virtual void raise();
|
54
|
+
|
55
|
+
Local<Message> message() const;
|
56
|
+
|
57
|
+
Local<Value> exception() const;
|
58
|
+
|
59
|
+
virtual ~JsError() noexcept {
|
60
|
+
_message.Reset();
|
61
|
+
_exception.Reset();
|
62
|
+
}
|
63
|
+
protected:
|
64
|
+
const char* reason;
|
65
|
+
bool has_js_exception;
|
66
|
+
H8 *h8;
|
67
|
+
v8::Persistent<v8::Message, v8::CopyablePersistentTraits<v8::Message>> _message;
|
68
|
+
v8::Persistent<v8::Value, v8::CopyablePersistentTraits<v8::Value>> _exception;
|
69
|
+
};
|
70
|
+
|
71
|
+
class JsTimeoutError : public JsError {
|
72
|
+
public:
|
73
|
+
JsTimeoutError(H8* h8) : JsError(h8, NULL) {}
|
74
|
+
virtual void raise();
|
75
|
+
};
|
76
|
+
|
25
77
|
class H8 {
|
26
78
|
public:
|
27
79
|
|
@@ -29,6 +81,7 @@ public:
|
|
29
81
|
v8::Isolate::Scope isolate_scope;
|
30
82
|
v8::Context::Scope context_scope;
|
31
83
|
H8* rcontext;
|
84
|
+
|
32
85
|
public:
|
33
86
|
Scope(H8* cxt) :
|
34
87
|
HandleScope(cxt->getIsolate()), isolate_scope(
|
@@ -39,7 +92,8 @@ public:
|
|
39
92
|
|
40
93
|
static void init();
|
41
94
|
|
42
|
-
H8()
|
95
|
+
H8() :
|
96
|
+
self(Qnil) {
|
43
97
|
isolate = Isolate::New();
|
44
98
|
Isolate::Scope isolate_scope(isolate);
|
45
99
|
HandleScope handle_scope(isolate);
|
@@ -51,33 +105,19 @@ public:
|
|
51
105
|
persistent_context.Reset(isolate, context);
|
52
106
|
}
|
53
107
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
if (script.IsEmpty()) {
|
67
|
-
report_exception(try_catch);
|
68
|
-
result = Undefined(isolate);
|
69
|
-
} else {
|
70
|
-
result = script->Run();
|
71
|
-
if (try_catch.HasCaught()) {
|
72
|
-
report_exception(try_catch);
|
73
|
-
}
|
74
|
-
}
|
75
|
-
return escape.Escape(result);
|
76
|
-
}
|
77
|
-
|
78
|
-
VALUE eval_to_ruby(const char* script_utf) {
|
108
|
+
/**
|
109
|
+
* Evaluate javascript.
|
110
|
+
*
|
111
|
+
* \param script_utf the null-terminated script string in utf8 endcoding
|
112
|
+
* \param max_ms if set, then script maximum execution time will be limited
|
113
|
+
* to this value, JsTimeoutError will be thrown if exceeded
|
114
|
+
* \return the value returned by the script.
|
115
|
+
*/
|
116
|
+
Handle<Value> eval(const char* script_utf,unsigned max_ms=0);
|
117
|
+
|
118
|
+
VALUE eval_to_ruby(const char* script_utf,int timeout=0) {
|
79
119
|
// TODO: throw ruby exception on error
|
80
|
-
return to_ruby(eval(script_utf));
|
120
|
+
return to_ruby(eval(script_utf,timeout));
|
81
121
|
}
|
82
122
|
|
83
123
|
Handle<Context> getContext() {
|
@@ -107,7 +147,7 @@ public:
|
|
107
147
|
getContext()->Global()->Set(js(name), to_js(value));
|
108
148
|
}
|
109
149
|
|
110
|
-
Local<Value> to_js(VALUE ruby_value)
|
150
|
+
Local<Value> to_js(VALUE ruby_value) {
|
111
151
|
switch (TYPE(ruby_value)) {
|
112
152
|
case T_STRING:
|
113
153
|
return js(ruby_value);
|
@@ -115,35 +155,46 @@ public:
|
|
115
155
|
return v8::Int32::New(isolate, FIX2INT(ruby_value));
|
116
156
|
case T_FLOAT:
|
117
157
|
return v8::Number::New(isolate, NUM2DBL(ruby_value));
|
158
|
+
case T_UNDEF:
|
159
|
+
return v8::Undefined(isolate);
|
160
|
+
case T_NIL:
|
161
|
+
return v8::Null(isolate);
|
118
162
|
case T_DATA:
|
119
163
|
case T_OBJECT:
|
120
164
|
return gateObject(ruby_value);
|
121
165
|
default:
|
122
|
-
|
166
|
+
VALUE msg = rb_str_new2("can't gate to js (unknown): ");
|
167
|
+
rb_str_append(msg, rb_any_to_s(ruby_value));
|
168
|
+
throw JsError(this, StringValueCStr(msg));
|
123
169
|
}
|
124
170
|
return Undefined(isolate);
|
125
171
|
}
|
126
172
|
|
127
|
-
Local<Value> gateObject(VALUE object)
|
173
|
+
Local<Value> gateObject(VALUE object);
|
128
174
|
|
129
|
-
|
130
|
-
|
175
|
+
VALUE ruby_context() const {
|
176
|
+
return self;
|
131
177
|
}
|
132
178
|
|
179
|
+
void add_resource(AllocatedResource *resource) {
|
180
|
+
resources.push(resource);
|
181
|
+
}
|
182
|
+
|
183
|
+
void ruby_mark_gc() const;
|
184
|
+
|
185
|
+
virtual ~H8();
|
186
|
+
|
133
187
|
private:
|
134
|
-
friend VALUE context_alloc(VALUE klass);
|
135
|
-
friend void rvalue_mark(void* ptr);
|
188
|
+
friend VALUE h8::context_alloc(VALUE klass);
|
136
189
|
|
137
190
|
Isolate *isolate;
|
138
191
|
VALUE self;
|
139
192
|
|
140
|
-
void report_exception(v8::TryCatch& tc) {
|
141
|
-
rb_raise(h8_exception, "Failed to compile/execute script");
|
142
|
-
}
|
143
|
-
|
144
193
|
Persistent<Context> persistent_context;
|
145
194
|
|
146
195
|
bool is_error = false;
|
196
|
+
|
197
|
+
chain resources;
|
147
198
|
};
|
148
199
|
// Context
|
149
200
|
}
|
@@ -156,4 +207,20 @@ inline VALUE h8::H8::to_ruby(Handle<Value> value) {
|
|
156
207
|
return JsGate::to_ruby(this, value);
|
157
208
|
}
|
158
209
|
|
210
|
+
inline h8::JsError::JsError(H8* h8, v8::Local<v8::Message> message,
|
211
|
+
v8::Local<v8::Value> exception) :
|
212
|
+
h8(h8), _message(h8->getIsolate(), message), _exception(h8->getIsolate(),
|
213
|
+
exception), has_js_exception(true), reason(NULL) {
|
214
|
+
}
|
215
|
+
|
216
|
+
inline Local<Message> h8::JsError::message() const {
|
217
|
+
return Local<Message>::New(h8->getIsolate(), _message);
|
218
|
+
}
|
219
|
+
|
220
|
+
inline Local<Value> h8::JsError::exception() const {
|
221
|
+
return Local<Value>::New(h8->getIsolate(), _exception);
|
222
|
+
}
|
223
|
+
|
224
|
+
|
225
|
+
|
159
226
|
#endif
|
data/ext/h8/js_gate.cpp
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
#include "h8.h"
|
2
|
+
#include "JsCatcher.h"
|
3
|
+
|
4
|
+
using namespace h8;
|
5
|
+
|
6
|
+
VALUE JsGate::apply(Local<Value> self, VALUE args) const {
|
7
|
+
H8::Scope scope(h8);
|
8
|
+
long count = RARRAY_LEN(args);
|
9
|
+
Local<Value> *js_args = new Local<Value> [count];
|
10
|
+
for (int i = 0; i < count; i++) {
|
11
|
+
js_args[i] = h8->to_js(rb_ary_entry(args, i));
|
12
|
+
}
|
13
|
+
h8::JsCatcher catcher(h8);
|
14
|
+
Local<Value> result = object()->CallAsFunction(self, count, js_args);
|
15
|
+
catcher.throwIfCaught();
|
16
|
+
delete [] js_args;
|
17
|
+
return h8->to_ruby(result);
|
18
|
+
}
|
data/ext/h8/js_gate.h
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
#define __js_gate_h
|
3
3
|
|
4
4
|
#include "h8.h"
|
5
|
+
#include "allocated_resource.h"
|
5
6
|
|
6
7
|
using namespace v8;
|
7
8
|
|
@@ -10,7 +11,7 @@ namespace h8 {
|
|
10
11
|
/**
|
11
12
|
* Interface to anything that could be converted to a Javascipt object. Provides common helpers.
|
12
13
|
*/
|
13
|
-
class JsValue {
|
14
|
+
class JsValue: public AllocatedResource {
|
14
15
|
public:
|
15
16
|
virtual Local<Value> value() const = 0;
|
16
17
|
|
@@ -19,9 +20,10 @@ public:
|
|
19
20
|
}
|
20
21
|
|
21
22
|
virtual Isolate* isolate() = 0;
|
23
|
+
virtual ~JsValue() {
|
24
|
+
}
|
22
25
|
};
|
23
26
|
|
24
|
-
|
25
27
|
/**
|
26
28
|
* Gates JS object to ruby environment. Holds persistent reference to the source js object until
|
27
29
|
* ruby object is recycled (then frees it). Note that this ruby object is not meant to be kept alive
|
@@ -29,7 +31,7 @@ public:
|
|
29
31
|
*
|
30
32
|
* Methods of this class do not need the H8::Scope, they create one internally.
|
31
33
|
*/
|
32
|
-
class JsGate
|
34
|
+
class JsGate: public JsValue {
|
33
35
|
public:
|
34
36
|
/**
|
35
37
|
* Used in the ruby allocator. Do not call unless you know what you do.
|
@@ -41,14 +43,8 @@ public:
|
|
41
43
|
* Return Ruby object that gates specified Handled javascript object. Ruby object
|
42
44
|
* locks permanently value until get recycled.
|
43
45
|
*/
|
44
|
-
template
|
45
|
-
static VALUE to_ruby(H8* h8, const Handle<T>& value)
|
46
|
-
JsGate *gate;
|
47
|
-
VALUE ruby_gate = rb_class_new_instance(0, NULL, value_class);
|
48
|
-
Data_Get_Struct(ruby_gate, JsGate, gate);
|
49
|
-
gate->set(h8, value);
|
50
|
-
return ruby_gate;
|
51
|
-
}
|
46
|
+
template<class T>
|
47
|
+
static VALUE to_ruby(H8* h8, const Handle<T>& value);
|
52
48
|
|
53
49
|
/**
|
54
50
|
* Reset gate to the specified handle.
|
@@ -57,6 +53,7 @@ public:
|
|
57
53
|
void set(H8 *h8, const Handle<T>& val) {
|
58
54
|
this->h8 = h8;
|
59
55
|
persistent_value.Reset(h8->getIsolate(), val);
|
56
|
+
h8->add_resource(this);
|
60
57
|
}
|
61
58
|
|
62
59
|
/**
|
@@ -73,7 +70,7 @@ public:
|
|
73
70
|
*/
|
74
71
|
VALUE to_i() {
|
75
72
|
H8::Scope scope(h8);
|
76
|
-
|
73
|
+
return INT2FIX(value()->IntegerValue());
|
77
74
|
}
|
78
75
|
|
79
76
|
/**
|
@@ -81,7 +78,7 @@ public:
|
|
81
78
|
*/
|
82
79
|
VALUE to_f() {
|
83
80
|
H8::Scope scope(h8);
|
84
|
-
|
81
|
+
return DBL2NUM(value()->NumberValue());
|
85
82
|
}
|
86
83
|
|
87
84
|
/**
|
@@ -89,7 +86,7 @@ public:
|
|
89
86
|
*/
|
90
87
|
VALUE is_int() {
|
91
88
|
H8::Scope scope(h8);
|
92
|
-
|
89
|
+
return value()->IsInt32() ? Qtrue : Qfalse;
|
93
90
|
}
|
94
91
|
|
95
92
|
/**
|
@@ -97,7 +94,7 @@ public:
|
|
97
94
|
*/
|
98
95
|
VALUE is_float() {
|
99
96
|
H8::Scope scope(h8);
|
100
|
-
|
97
|
+
return value()->IsNumber() ? Qtrue : Qfalse;
|
101
98
|
}
|
102
99
|
|
103
100
|
/**
|
@@ -105,7 +102,7 @@ public:
|
|
105
102
|
*/
|
106
103
|
VALUE is_array() {
|
107
104
|
H8::Scope scope(h8);
|
108
|
-
|
105
|
+
return value()->IsArray() ? Qtrue : Qfalse;
|
109
106
|
}
|
110
107
|
|
111
108
|
/**
|
@@ -113,7 +110,7 @@ public:
|
|
113
110
|
*/
|
114
111
|
VALUE is_object() {
|
115
112
|
H8::Scope scope(h8);
|
116
|
-
|
113
|
+
return value()->IsObject() ? Qtrue : Qfalse;
|
117
114
|
}
|
118
115
|
|
119
116
|
/**
|
@@ -122,7 +119,8 @@ public:
|
|
122
119
|
*/
|
123
120
|
VALUE get_attribute(VALUE name) {
|
124
121
|
H8::Scope scope(h8);
|
125
|
-
Local<Value> v8_name = v8::String::NewFromUtf8(isolate(),
|
122
|
+
Local<Value> v8_name = v8::String::NewFromUtf8(isolate(),
|
123
|
+
StringValueCStr(name));
|
126
124
|
return h8->to_ruby(object()->Get(v8_name));
|
127
125
|
}
|
128
126
|
|
@@ -136,7 +134,7 @@ public:
|
|
136
134
|
*/
|
137
135
|
VALUE is_string() {
|
138
136
|
H8::Scope scope(h8);
|
139
|
-
|
137
|
+
return value()->IsString() ? Qtrue : Qfalse;
|
140
138
|
}
|
141
139
|
|
142
140
|
VALUE is_function() {
|
@@ -149,6 +147,9 @@ public:
|
|
149
147
|
return value()->IsUndefined() ? Qtrue : Qfalse;
|
150
148
|
}
|
151
149
|
|
150
|
+
/**
|
151
|
+
* Call this as function over global context
|
152
|
+
*/
|
152
153
|
VALUE call(VALUE args) const {
|
153
154
|
v8::HandleScope scope(h8->getIsolate());
|
154
155
|
return apply(h8->getContext()->Global(), args);
|
@@ -159,16 +160,19 @@ public:
|
|
159
160
|
return apply(h8->gateObject(self), args);
|
160
161
|
}
|
161
162
|
|
162
|
-
VALUE
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
163
|
+
VALUE ruby_context() const {
|
164
|
+
return h8->ruby_context();
|
165
|
+
}
|
166
|
+
|
167
|
+
/**
|
168
|
+
* Call this object as function applying it to the object self
|
169
|
+
* (which will be this during javascript call)
|
170
|
+
*/
|
171
|
+
VALUE apply(Local<Value> self, VALUE args) const;
|
172
|
+
|
173
|
+
virtual void free() {
|
174
|
+
persistent_value.Reset();
|
175
|
+
AllocatedResource::free();
|
172
176
|
}
|
173
177
|
|
174
178
|
virtual Local<Value> value() const {
|
@@ -179,7 +183,7 @@ public:
|
|
179
183
|
return h8->getIsolate();
|
180
184
|
}
|
181
185
|
|
182
|
-
~JsGate() {
|
186
|
+
virtual ~JsGate() {
|
183
187
|
persistent_value.Reset();
|
184
188
|
}
|
185
189
|
|
@@ -187,10 +191,45 @@ private:
|
|
187
191
|
friend void rvalue_mark(void* ptr);
|
188
192
|
friend class H8;
|
189
193
|
|
190
|
-
H8 *h8=0;
|
194
|
+
H8 *h8 = 0;
|
191
195
|
Persistent<Value> persistent_value;
|
192
196
|
};
|
193
197
|
|
194
198
|
}
|
195
199
|
|
200
|
+
#include "ruby_gate.h"
|
201
|
+
|
202
|
+
template<class T>
|
203
|
+
VALUE h8::JsGate::to_ruby(H8* h8, const Handle<T>& value) {
|
204
|
+
// Convert primitives
|
205
|
+
Local<Value> v = value;
|
206
|
+
if (v->IsString()) {
|
207
|
+
H8::Scope scope(h8);
|
208
|
+
String::Utf8Value res(v);
|
209
|
+
return *res ? rb_str_new2(*res) : Qnil;
|
210
|
+
}
|
211
|
+
if (v->IsInt32()) {
|
212
|
+
return INT2FIX(v->Int32Value());
|
213
|
+
}
|
214
|
+
if (v->IsNumber()) {
|
215
|
+
return DBL2NUM(v->NumberValue());
|
216
|
+
}
|
217
|
+
if( v->IsUndefined()) {
|
218
|
+
return Rundefined;
|
219
|
+
}
|
220
|
+
if( v->IsNull() ) {
|
221
|
+
return Qnil;
|
222
|
+
}
|
223
|
+
RubyGate *rg = RubyGate::unwrap(v.As<v8::Object>());
|
224
|
+
if( rg ) {
|
225
|
+
return rg->rubyObject();
|
226
|
+
}
|
227
|
+
JsGate *gate;
|
228
|
+
VALUE ruby_gate = rb_class_new_instance(0, NULL, value_class);
|
229
|
+
Data_Get_Struct(ruby_gate, JsGate, gate);
|
230
|
+
gate->set(h8, value);
|
231
|
+
return ruby_gate;
|
232
|
+
}
|
233
|
+
|
234
|
+
|
196
235
|
#endif
|