h8 0.4.0 → 0.4.5
Sign up to get free protection for your applications and to get access to all the features.
- 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
|