h8 0.4.0 → 0.4.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/ext/h8/h8.cpp +34 -8
- data/ext/h8/h8.h +15 -3
- data/ext/h8/ruby_gate.cpp +35 -30
- data/ext/h8/ruby_gate.h +1 -0
- data/lib/h8/context.rb +18 -4
- data/lib/h8/version.rb +1 -1
- data/spec/js_gate_spec.rb +2 -1
- data/spec/ruby_gate_spec.rb +57 -33
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: da7dc92fe9f18d03e202dbd8566c5469f3b122b2
|
4
|
+
data.tar.gz: 4c570745ab23ac22f705405742ff10b7fb9310df
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3eade24933e29d3d5b124b4665a3eef82e4e6dee3cde4a7e62c1a5f68acf08ee5d060bdaa1f7a61eeedd53aea417b751618d847ffd67dd8f75dddbdd32f5cc56
|
7
|
+
data.tar.gz: 90c14a2a879418134c41ce3958ce8b943cc5d86285811657e280af079e3802d275e1156860f6af4f3f664515ec37f19e9a51d07d8914a8e5eaa5c03218d20026
|
data/ext/h8/h8.cpp
CHANGED
@@ -71,12 +71,18 @@ h8::H8::H8()
|
|
71
71
|
templ->SetNamedPropertyHandler(RubyGate::mapGet, RubyGate::mapSet);
|
72
72
|
templ->SetIndexedPropertyHandler(RubyGate::indexGet, RubyGate::indexSet);
|
73
73
|
|
74
|
-
global->Set(isolate, "RubyGate", ft);
|
75
|
-
|
76
74
|
v8::Handle<v8::Context> context = v8::Context::New(isolate, NULL,
|
77
75
|
global);
|
76
|
+
|
77
|
+
v8::Context::Scope cs(context);
|
78
|
+
|
79
|
+
Local<Function> fn = ft->GetFunction();
|
80
|
+
|
81
|
+
context->Global()->Set(js("RubyGate"), fn);
|
82
|
+
|
78
83
|
persistent_context.Reset(isolate, context);
|
79
|
-
|
84
|
+
gate_function_template.Reset(isolate,ft);
|
85
|
+
gate_function.Reset(isolate,fn);
|
80
86
|
}
|
81
87
|
|
82
88
|
|
@@ -91,13 +97,15 @@ Local<Value> h8::H8::gateObject(VALUE ruby_value) {
|
|
91
97
|
}
|
92
98
|
if( ruby_value == Rundefined )
|
93
99
|
return v8::Undefined(isolate);
|
94
|
-
// Generic Ruby object
|
95
|
-
// RubyGate *gate = new RubyGate(this, ruby_value);
|
96
|
-
// return gate->handle(isolate);
|
97
100
|
// Generic ruby object - new logic
|
98
101
|
assert( sizeof(VALUE) <= sizeof(void*) );
|
102
|
+
|
103
|
+
RubyGate *gate = find_gate(ruby_value);
|
104
|
+
if( gate )
|
105
|
+
return gate->handle();
|
106
|
+
|
99
107
|
Local<Value> wrapped_ruby_value = External::New(isolate, (void*)ruby_value);
|
100
|
-
return getGateFunction()->
|
108
|
+
return getGateFunction()->CallAsConstructor(1, &wrapped_ruby_value);
|
101
109
|
}
|
102
110
|
|
103
111
|
void h8::H8::gate_class(VALUE name,VALUE callable) {
|
@@ -111,7 +119,7 @@ void h8::H8::gate_class(VALUE name,VALUE callable) {
|
|
111
119
|
);
|
112
120
|
Local<String> class_name = js(name);
|
113
121
|
ft->SetClassName(class_name);
|
114
|
-
ft->Inherit(
|
122
|
+
ft->Inherit(getGateFunctionTemplate());
|
115
123
|
|
116
124
|
Local<ObjectTemplate> templ = ft->InstanceTemplate();
|
117
125
|
|
@@ -165,6 +173,23 @@ void h8::H8::invoke(v8::Handle<v8::Script> script, Local<Value>& result) {
|
|
165
173
|
#endif
|
166
174
|
}
|
167
175
|
|
176
|
+
void h8::H8::register_ruby_gate(RubyGate* gate) {
|
177
|
+
add_resource(gate);
|
178
|
+
id_map.insert(std::pair<VALUE,RubyGate*>(gate->ruby_object, gate));
|
179
|
+
}
|
180
|
+
|
181
|
+
void h8::H8::unregister_ruby_gate(RubyGate* gate) {
|
182
|
+
add_resource(gate);
|
183
|
+
id_map.erase(gate->ruby_object);
|
184
|
+
}
|
185
|
+
|
186
|
+
h8::RubyGate* h8::H8::find_gate(VALUE rb_object) {
|
187
|
+
auto it = id_map.find(rb_object);
|
188
|
+
if( it == id_map.end() )
|
189
|
+
return 0;
|
190
|
+
return it->second;
|
191
|
+
}
|
192
|
+
|
168
193
|
v8::Handle<v8::Value> h8::H8::eval(const char* script_utf, unsigned max_ms,const char* source_name) {
|
169
194
|
v8::EscapableHandleScope escape(isolate);
|
170
195
|
Local<Value> result;
|
@@ -213,6 +238,7 @@ h8::H8::~H8() {
|
|
213
238
|
}
|
214
239
|
persistent_context.Reset();
|
215
240
|
gate_function.Reset();
|
241
|
+
gate_function_template.Reset();
|
216
242
|
}
|
217
243
|
isolate->Dispose();
|
218
244
|
}
|
data/ext/h8/h8.h
CHANGED
@@ -5,6 +5,8 @@
|
|
5
5
|
#include <ruby.h>
|
6
6
|
#include <iostream>
|
7
7
|
#include <exception>
|
8
|
+
#include <map>
|
9
|
+
|
8
10
|
#include "allocated_resource.h"
|
9
11
|
#include "js_catcher.h"
|
10
12
|
|
@@ -126,8 +128,12 @@ public:
|
|
126
128
|
return Local<Context>::New(isolate, persistent_context);
|
127
129
|
}
|
128
130
|
|
129
|
-
Handle<FunctionTemplate>
|
130
|
-
return Local<FunctionTemplate>::New(isolate,
|
131
|
+
Handle<FunctionTemplate> getGateFunctionTemplate() {
|
132
|
+
return Local<FunctionTemplate>::New(isolate, gate_function_template);
|
133
|
+
}
|
134
|
+
|
135
|
+
Handle<Function> getGateFunction() {
|
136
|
+
return Local<Function>::New(isolate, gate_function);
|
131
137
|
}
|
132
138
|
|
133
139
|
bool isError() const {
|
@@ -219,6 +225,10 @@ public:
|
|
219
225
|
return rb_interrupted;
|
220
226
|
}
|
221
227
|
|
228
|
+
void register_ruby_gate(RubyGate* gate);
|
229
|
+
void unregister_ruby_gate(RubyGate* gate);
|
230
|
+
RubyGate *find_gate(VALUE rb_object);
|
231
|
+
|
222
232
|
virtual ~H8();
|
223
233
|
|
224
234
|
private:
|
@@ -229,13 +239,15 @@ private:
|
|
229
239
|
VALUE self;
|
230
240
|
|
231
241
|
Persistent<Context> persistent_context;
|
232
|
-
Persistent<FunctionTemplate>
|
242
|
+
Persistent<FunctionTemplate> gate_function_template;
|
243
|
+
Persistent<Function> gate_function;
|
233
244
|
|
234
245
|
bool is_error = false;
|
235
246
|
bool gvl_released = false;
|
236
247
|
bool rb_interrupted = false;
|
237
248
|
|
238
249
|
chain resources;
|
250
|
+
std::map<VALUE,RubyGate*> id_map;
|
239
251
|
};
|
240
252
|
// Context
|
241
253
|
}
|
data/ext/h8/ruby_gate.cpp
CHANGED
@@ -36,16 +36,17 @@ void h8::RubyGate::ClassGateConstructor(
|
|
36
36
|
assert(lambda != 0);
|
37
37
|
|
38
38
|
with_gvl(h8, [&] {
|
39
|
-
VALUE rb_args = ruby_args(h8, args,
|
39
|
+
VALUE rb_args = ruby_args(h8, args, 2);
|
40
|
+
rb_ary_push(rb_args, h8->ruby_context());
|
40
41
|
rb_ary_push(rb_args, lambda->ruby_object);
|
41
42
|
// Object creating ruby code can raise exceptions:
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
43
|
+
lambda->rescued_call(
|
44
|
+
rb_args,
|
45
|
+
call,
|
46
|
+
[&] (VALUE res) {
|
47
|
+
new RubyGate(h8, args.This(), res);
|
48
|
+
});
|
49
|
+
});
|
49
50
|
args.GetReturnValue().Set(args.This());
|
50
51
|
}
|
51
52
|
|
@@ -57,19 +58,8 @@ void h8::RubyGate::GateConstructor(
|
|
57
58
|
Local<Value> val = args[0];
|
58
59
|
VALUE ruby_object = Qnil;
|
59
60
|
|
60
|
-
|
61
|
-
|
62
|
-
else {
|
63
|
-
assert(val->IsObject());
|
64
|
-
puts("val is object");
|
65
|
-
RubyGate *rg = RubyGate::unwrap(val.As<Object>());
|
66
|
-
puts("Hurray - unwrap");
|
67
|
-
assert(rg != 0);
|
68
|
-
puts("Hurray - not 0");
|
69
|
-
ruby_object = rg->ruby_object;
|
70
|
-
char* ss = StringValueCStr(ruby_object);
|
71
|
-
rb_warn("Object passed %s\n", ss);
|
72
|
-
}
|
61
|
+
assert(val->IsExternal());
|
62
|
+
ruby_object = (VALUE) val.As<External>()->Value(); // External::Cast(*val)->Value();
|
73
63
|
|
74
64
|
new RubyGate(h8, args.This(), ruby_object);
|
75
65
|
args.GetReturnValue().Set(args.This());
|
@@ -78,16 +68,21 @@ void h8::RubyGate::GateConstructor(
|
|
78
68
|
h8::RubyGate::RubyGate(H8* _context, Handle<Object> instance, VALUE object) :
|
79
69
|
context(_context), ruby_object(object), next(0), prev(0) {
|
80
70
|
v8::HandleScope scope(context->getIsolate());
|
81
|
-
context->
|
71
|
+
context->register_ruby_gate(this);
|
82
72
|
instance->SetAlignedPointerInInternalField(1, RUBYGATE_ID);
|
83
73
|
Wrap(instance);
|
84
74
|
}
|
85
75
|
|
86
76
|
void h8::RubyGate::mapGet(Local<String> name,
|
87
77
|
const PropertyCallbackInfo<Value> &info) {
|
88
|
-
|
89
|
-
|
90
|
-
|
78
|
+
Local<Value> loc = info.This()->GetRealNamedPropertyInPrototypeChain(name);
|
79
|
+
if (!loc.IsEmpty())
|
80
|
+
info.GetReturnValue().Set(loc);
|
81
|
+
else {
|
82
|
+
RubyGate *rg = RubyGate::unwrap(info.This());
|
83
|
+
assert(rg != 0);
|
84
|
+
rg->getProperty(name, info);
|
85
|
+
}
|
91
86
|
}
|
92
87
|
|
93
88
|
void h8::RubyGate::mapSet(Local<String> name, Local<Value> value,
|
@@ -128,7 +123,9 @@ VALUE h8::RubyGate::rescue_callback(VALUE me, VALUE exception_object) {
|
|
128
123
|
|
129
124
|
VALUE RubyGate::call(VALUE args) {
|
130
125
|
VALUE callable = rb_ary_pop(args);
|
131
|
-
|
126
|
+
VALUE context = rb_ary_pop(args);
|
127
|
+
VALUE res = rb_funcall(context, rb_intern("safe_proc_call"), 2, callable, args);
|
128
|
+
return res;
|
132
129
|
}
|
133
130
|
|
134
131
|
VALUE RubyGate::secure_call(VALUE args) {
|
@@ -171,19 +168,24 @@ void h8::RubyGate::rescued_call(VALUE rb_args, VALUE (*call)(VALUE),
|
|
171
168
|
v8::Object>();
|
172
169
|
context->getIsolate()->ThrowException(error);
|
173
170
|
}
|
174
|
-
} else
|
171
|
+
} else {
|
175
172
|
throw_js();
|
173
|
+
}
|
176
174
|
}
|
177
175
|
|
178
176
|
void h8::RubyGate::doObjectCallback(
|
179
177
|
const v8::FunctionCallbackInfo<v8::Value>& args) {
|
180
178
|
with_gvl(this, [&] {
|
181
179
|
VALUE rb_args = ruby_args(context, args, 1);
|
180
|
+
rb_ary_push(rb_args, context->ruby_context());
|
182
181
|
rb_ary_push(rb_args, ruby_object);
|
183
182
|
rescued_call(rb_args, call, [&] (VALUE res) {
|
184
|
-
|
185
|
-
|
186
|
-
|
183
|
+
if( res == ruby_object )
|
184
|
+
args.GetReturnValue().Set(args.This());
|
185
|
+
else
|
186
|
+
args.GetReturnValue().Set(context->to_js(res));
|
187
|
+
});
|
188
|
+
});
|
187
189
|
}
|
188
190
|
|
189
191
|
void h8::RubyGate::getProperty(Local<String> name,
|
@@ -193,6 +195,9 @@ void h8::RubyGate::getProperty(Local<String> name,
|
|
193
195
|
rb_ary_push(rb_args, ruby_object);
|
194
196
|
rb_ary_push(rb_args, context->to_ruby(name));
|
195
197
|
rescued_call(rb_args, secure_call, [&] (VALUE res) {
|
198
|
+
if( res == ruby_object )
|
199
|
+
info.GetReturnValue().Set(info.This());
|
200
|
+
else
|
196
201
|
info.GetReturnValue().Set(context->to_js(res));
|
197
202
|
});
|
198
203
|
});
|
data/ext/h8/ruby_gate.h
CHANGED
data/lib/h8/context.rb
CHANGED
@@ -69,12 +69,12 @@ module H8
|
|
69
69
|
begin
|
70
70
|
m = instance.public_method(method)
|
71
71
|
owner = m.owner
|
72
|
-
if owner
|
72
|
+
if can_access?(owner)
|
73
73
|
return m.call(*args) if method[0] == '[' || method[-1] == '='
|
74
74
|
if m.arity != 0
|
75
75
|
return -> (*args) { m.call *args }
|
76
76
|
else
|
77
|
-
return m.call
|
77
|
+
return m.call
|
78
78
|
end
|
79
79
|
end
|
80
80
|
rescue NameError
|
@@ -86,8 +86,12 @@ module H8
|
|
86
86
|
end
|
87
87
|
begin
|
88
88
|
m = instance.public_method(method)
|
89
|
-
if
|
90
|
-
|
89
|
+
if can_access?(owner)
|
90
|
+
if method == :[]
|
91
|
+
return m.call(*args) || m.call(args[0].to_sym)
|
92
|
+
else
|
93
|
+
return m.call(*args)
|
94
|
+
end
|
91
95
|
end
|
92
96
|
rescue NameError
|
93
97
|
# It means there is no [] or []=, e.g. undefined
|
@@ -96,6 +100,16 @@ module H8
|
|
96
100
|
H8::Undefined
|
97
101
|
end
|
98
102
|
|
103
|
+
# This is workaround for buggy rb_proc_call which produces segfaults
|
104
|
+
# if proc is not exactly a proc, so we call it like this:
|
105
|
+
def safe_proc_call proc, args
|
106
|
+
proc.call *args
|
107
|
+
end
|
108
|
+
|
109
|
+
def self.can_access?(owner)
|
110
|
+
owner != Object.class && owner != Kernel && owner != BasicObject.class
|
111
|
+
end
|
112
|
+
|
99
113
|
protected
|
100
114
|
|
101
115
|
# Set var that could be either a callable, class instance, simple value or a Class class
|
data/lib/h8/version.rb
CHANGED
data/spec/js_gate_spec.rb
CHANGED
@@ -40,10 +40,11 @@ describe 'js_gate' do
|
|
40
40
|
H8::Context.eval('null').should == nil
|
41
41
|
end
|
42
42
|
|
43
|
-
it 'should retreive JS fieds as indexes' do
|
43
|
+
it 'should retreive JS fieds as indexes indiffirently' do
|
44
44
|
res = H8::Context.eval("({ 'foo': 'bar', 'bar': 122 });")
|
45
45
|
res['foo'].to_s.should == 'bar'
|
46
46
|
res['bar'].to_i.should == 122
|
47
|
+
res[:foo].should == 'bar'
|
47
48
|
end
|
48
49
|
|
49
50
|
it 'should retreive JS fields as properties' do
|
data/spec/ruby_gate_spec.rb
CHANGED
@@ -19,26 +19,6 @@ describe 'ruby gate' do
|
|
19
19
|
count.should == 1
|
20
20
|
end
|
21
21
|
|
22
|
-
# it 'should gate callables in therubyrace mode' do
|
23
|
-
# cxt = H8::Context.new
|
24
|
-
# cxt[:fn] = -> (this, a, b) {
|
25
|
-
# p this.to_s
|
26
|
-
# p this.offset
|
27
|
-
# this.offset + a + b
|
28
|
-
# }
|
29
|
-
#
|
30
|
-
# res = cxt.eval <<-End
|
31
|
-
# function Test() {
|
32
|
-
# this.offset = 110;
|
33
|
-
# this.method = function(a,b) {
|
34
|
-
# return fn(this,a,b);
|
35
|
-
# }
|
36
|
-
# }
|
37
|
-
# new Test().method(11, 22);
|
38
|
-
# End
|
39
|
-
# res.to_i.should == 143
|
40
|
-
# end
|
41
|
-
|
42
22
|
it 'should allow edit context on yield' do
|
43
23
|
cxt = H8::Context.new
|
44
24
|
cxt[:fn] = -> (a, b) {
|
@@ -237,6 +217,7 @@ describe 'ruby gate' do
|
|
237
217
|
|
238
218
|
it 'should access object properties and methods' do
|
239
219
|
cxt = H8::Context.new
|
220
|
+
cxt.eval('RubyGate.prototype.test2 = function() { return "ttt"; }; null;');
|
240
221
|
cxt[:foo] = Test.new
|
241
222
|
cxt.eval('foo.ro').should == 'readonly'
|
242
223
|
cxt.eval('foo.rw').should == 'not initialized'
|
@@ -336,9 +317,10 @@ describe 'ruby gate' do
|
|
336
317
|
|
337
318
|
it 'should access plain hashes' do
|
338
319
|
cxt = H8::Context.new
|
339
|
-
h = { 'one' => 2 }
|
320
|
+
h = { 'one' => 2, :two => 21 }
|
340
321
|
cxt[:h] = h
|
341
322
|
cxt.eval("h['one']").should == 2
|
323
|
+
cxt.eval("h['two']").should == 21
|
342
324
|
eval("h['one']=1;")
|
343
325
|
h['one'].should == 1
|
344
326
|
end
|
@@ -371,23 +353,31 @@ describe 'ruby gate' do
|
|
371
353
|
c.eval('res instanceof RubyGate').should == true
|
372
354
|
end
|
373
355
|
|
374
|
-
|
375
|
-
|
376
|
-
attr :init_args
|
356
|
+
class Gated
|
357
|
+
attr :init_args
|
377
358
|
|
378
|
-
|
379
|
-
|
380
|
-
|
359
|
+
def initialize *args
|
360
|
+
@init_args = args
|
361
|
+
end
|
381
362
|
|
382
|
-
|
383
|
-
|
384
|
-
|
363
|
+
def inspect
|
364
|
+
"Gated<#{@init_args.inspect}>"
|
365
|
+
end
|
385
366
|
|
386
|
-
|
387
|
-
|
388
|
-
|
367
|
+
def checkself
|
368
|
+
self
|
369
|
+
end
|
370
|
+
|
371
|
+
def checkself2 *args
|
372
|
+
self
|
389
373
|
end
|
390
374
|
|
375
|
+
def to_str
|
376
|
+
inspect
|
377
|
+
end
|
378
|
+
end
|
379
|
+
|
380
|
+
it 'should gate classes' do
|
391
381
|
cxt = H8::Context.new RClass: Gated
|
392
382
|
c = cxt.eval 'new RClass()'
|
393
383
|
c.should be_a(Gated)
|
@@ -400,6 +390,40 @@ describe 'ruby gate' do
|
|
400
390
|
c.init_args.should == ['hello', 'world']
|
401
391
|
cxt.eval('rc.init_args').should == ['hello', 'world']
|
402
392
|
end
|
393
|
+
|
394
|
+
it 'should not die on calling wrong arity' do
|
395
|
+
cxt = H8::Context.new RClass: Gated
|
396
|
+
g1 = cxt.eval 'var g1 = new RClass(1,2.3); g1'
|
397
|
+
|
398
|
+
# We call gated ruby object with wrong number of args
|
399
|
+
# which in turn causes attempt to call not callable result:
|
400
|
+
expect(-> {cxt.eval('g1.checkself(12)')}).to raise_error(NoMethodError)
|
401
|
+
end
|
402
|
+
|
403
|
+
it 'should return self from gated class' do
|
404
|
+
cxt = H8::Context.new RClass: Gated
|
405
|
+
g1 = cxt.eval 'var g1 = new RClass(1,2.3); g1'
|
406
|
+
g1.should be_a(Gated)
|
407
|
+
g2 = cxt.eval 'g1.checkself'
|
408
|
+
g2.should be_a(Gated)
|
409
|
+
g1.equal?(g2).should == true
|
410
|
+
cxt.eval('g1 instanceof RClass').should == true
|
411
|
+
# p cxt.eval('g1.checkself.toString()')
|
412
|
+
cxt.eval('g1.checkself instanceof RClass').should == true
|
413
|
+
cxt.eval('g1.checkself === g1').should == true
|
414
|
+
|
415
|
+
# This checks how id map works (lite check)
|
416
|
+
cxt.eval('g1.checkself2(1) instanceof RClass').should == true
|
417
|
+
cxt.eval('g1.checkself2(2) === g1').should == true
|
418
|
+
end
|
419
|
+
|
420
|
+
it 'should expendable gate classes' do
|
421
|
+
cxt = H8::Context.new RClass: Gated
|
422
|
+
expect(-> { cxt.eval 'new RClass().mm(1)' }).to raise_error(H8::JsError)
|
423
|
+
cxt.eval('RClass.prototype.mm = function(a) { return "bar" + a; };')
|
424
|
+
cxt.eval('var x = new RClass(11); x.mm("hi!");').should == 'barhi!';
|
425
|
+
end
|
426
|
+
|
403
427
|
end
|
404
428
|
|
405
429
|
it 'should survive recursive constructions' do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: h8
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- sergeych
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-01-
|
11
|
+
date: 2015-01-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|