h8 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 38c8f3b25b694cb718a7be51eb9ad7cdb3796d7a
4
- data.tar.gz: 548f468f6a4dec9f63bd5de2fe6eff950a51b5b2
3
+ metadata.gz: 62539324e412e959064c165f17099c2f688eca44
4
+ data.tar.gz: bfcbcf2ed6018b7a6704f5dcf70a57bcb9482b66
5
5
  SHA512:
6
- metadata.gz: 63ab563ae77b67b5b2a89e545e350736a6fc34a0ff22b7a74d796ce102c8bf98486c71201712e42275b0dbe1901f7473367cc772cb504da1fe0b99cb793dbaa6
7
- data.tar.gz: d6a50a1bb8f933027f920fcca11be972922d6744ea95174409e99431a3255d1af34519d2d751b2cc51ecfadd0804cc6e153e1635c15b9eaa32ad4f56ed3f69c0
6
+ metadata.gz: bee24241453a7b41afda2cef2ae636abf2fb4290697d5c9bb1e11f8092e07a0da59387ca6f41a3f8c48df4b349c64bd27a768348714c0b0191c1cd75a6c93ffa
7
+ data.tar.gz: 0f7ced45565b634b5e7f3a67819ec390fa6960f14d5004c9a298891be7d6d53f91cda8831d47f5902bddf61ee5205ca05a30709156bf89ef229d2236bfdc1ea2
@@ -47,6 +47,39 @@ void h8::JsTimeoutError::raise() const {
47
47
  rb_raise(js_timeout_exception, "timeout expired");
48
48
  }
49
49
 
50
+ h8::H8::H8()
51
+ : self(Qnil)
52
+ {
53
+ isolate = Isolate::New();
54
+ Locker l(isolate);
55
+ Isolate::Scope isolate_scope(isolate);
56
+ HandleScope handle_scope(isolate);
57
+ isolate->SetCaptureStackTraceForUncaughtExceptions(true);
58
+
59
+ isolate->SetData(0, this);
60
+
61
+ v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New(
62
+ isolate);
63
+
64
+ // Set up RubyGate in JS context
65
+ Local<FunctionTemplate> ft = v8::FunctionTemplate::New(isolate,RubyGate::GateConstructor);
66
+ ft->SetClassName(js("RubyGate"));
67
+ Local<ObjectTemplate> templ = ft->InstanceTemplate();
68
+
69
+ templ->SetInternalFieldCount(2);
70
+ templ->SetCallAsFunctionHandler(&RubyGate::ObjectCallback);
71
+ templ->SetNamedPropertyHandler(RubyGate::mapGet, RubyGate::mapSet);
72
+ templ->SetIndexedPropertyHandler(RubyGate::indexGet, RubyGate::indexSet);
73
+
74
+ global->Set(isolate, "RubyGate", ft);
75
+
76
+ v8::Handle<v8::Context> context = v8::Context::New(isolate, NULL,
77
+ global);
78
+ persistent_context.Reset(isolate, context);
79
+ gate_function.Reset(isolate,ft);
80
+ }
81
+
82
+
50
83
  Local<Value> h8::H8::gateObject(VALUE ruby_value) {
51
84
  if ( Qtrue == rb_funcall(ruby_value, id_is_a, 1, value_class)) {
52
85
  JsGate *gate;
@@ -59,8 +92,35 @@ Local<Value> h8::H8::gateObject(VALUE ruby_value) {
59
92
  if( ruby_value == Rundefined )
60
93
  return v8::Undefined(isolate);
61
94
  // Generic Ruby object
62
- RubyGate *gate = new RubyGate(this, ruby_value);
63
- return gate->handle(isolate);
95
+ // RubyGate *gate = new RubyGate(this, ruby_value);
96
+ // return gate->handle(isolate);
97
+ // Generic ruby object - new logic
98
+ assert( sizeof(VALUE) <= sizeof(void*) );
99
+ Local<Value> wrapped_ruby_value = External::New(isolate, (void*)ruby_value);
100
+ return getGateFunction()->GetFunction()->CallAsConstructor(1, &wrapped_ruby_value);
101
+ }
102
+
103
+ void h8::H8::gate_class(VALUE name,VALUE callable) {
104
+ Scope scope(this);
105
+
106
+ Local<Object> global = getContext()->Global();
107
+
108
+ Local<FunctionTemplate> ft = v8::FunctionTemplate::New(isolate,
109
+ RubyGate::ClassGateConstructor,
110
+ to_js(callable)
111
+ );
112
+ Local<String> class_name = js(name);
113
+ ft->SetClassName(class_name);
114
+ ft->Inherit(getGateFunction());
115
+
116
+ Local<ObjectTemplate> templ = ft->InstanceTemplate();
117
+
118
+ templ->SetInternalFieldCount(2);
119
+ templ->SetCallAsFunctionHandler(&RubyGate::ObjectCallback);
120
+ templ->SetNamedPropertyHandler(RubyGate::mapGet, RubyGate::mapSet);
121
+ templ->SetIndexedPropertyHandler(RubyGate::indexGet, RubyGate::indexSet);
122
+
123
+ global->Set(class_name, ft->GetFunction());
64
124
  }
65
125
 
66
126
  void h8::H8::ruby_mark_gc() const {
@@ -152,6 +212,7 @@ h8::H8::~H8() {
152
212
  resources.peek_first<AllocatedResource>()->free();
153
213
  }
154
214
  persistent_context.Reset();
215
+ gate_function.Reset();
155
216
  }
156
217
  isolate->Dispose();
157
218
  }
@@ -103,20 +103,7 @@ public:
103
103
 
104
104
  static void init();
105
105
 
106
- H8() :
107
- self(Qnil) {
108
- isolate = Isolate::New();
109
- Locker l(isolate);
110
- Isolate::Scope isolate_scope(isolate);
111
- HandleScope handle_scope(isolate);
112
- isolate->SetCaptureStackTraceForUncaughtExceptions(true);
113
-
114
- v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New(
115
- isolate);
116
- v8::Handle<v8::Context> context = v8::Context::New(isolate, NULL,
117
- global);
118
- persistent_context.Reset(isolate, context);
119
- }
106
+ H8();
120
107
 
121
108
  /**
122
109
  * Evaluate javascript.
@@ -139,6 +126,10 @@ public:
139
126
  return Local<Context>::New(isolate, persistent_context);
140
127
  }
141
128
 
129
+ Handle<FunctionTemplate> getGateFunction() {
130
+ return Local<FunctionTemplate>::New(isolate, gate_function);
131
+ }
132
+
142
133
  bool isError() const {
143
134
  return is_error;
144
135
  }
@@ -147,6 +138,8 @@ public:
147
138
  return isolate;
148
139
  }
149
140
 
141
+ void gate_class(VALUE name, VALUE proc);
142
+
150
143
  VALUE to_ruby(Handle<Value> value);
151
144
 
152
145
  v8::Local<v8::String> js(VALUE val) const {
@@ -236,6 +229,7 @@ private:
236
229
  VALUE self;
237
230
 
238
231
  Persistent<Context> persistent_context;
232
+ Persistent<FunctionTemplate> gate_function;
239
233
 
240
234
  bool is_error = false;
241
235
  bool gvl_released = false;
@@ -148,6 +148,13 @@ static VALUE context_set_var(VALUE self, VALUE name, VALUE value) {
148
148
  });
149
149
  }
150
150
 
151
+ static VALUE context_gate_class(VALUE self, VALUE name, VALUE lambda) {
152
+ return protect_ruby([&] {
153
+ rc(self)->gate_class(name, lambda);
154
+ return Qnil;
155
+ });
156
+ }
157
+
151
158
  static VALUE context_force_gc(VALUE self) {
152
159
  rc(self)->gc();
153
160
  return Qnil;
@@ -191,6 +198,8 @@ void Init_h8(void) {
191
198
  rb_define_method(context_class, "_eval", (ruby_method) context_eval, 3);
192
199
  rb_define_method(context_class, "_set_var", (ruby_method) context_set_var,
193
200
  2);
201
+ rb_define_method(context_class, "_gate_class", (ruby_method) context_gate_class,
202
+ 2);
194
203
  rb_define_method(context_class, "javascript_gc",
195
204
  (ruby_method) context_force_gc, 0);
196
205
 
@@ -13,8 +13,7 @@ static void* unblock_caller(void *param) {
13
13
  return NULL;
14
14
  }
15
15
 
16
- static void with_gvl(RubyGate *gate, const std::function<void(void)> &block) {
17
- H8 *h8 = gate->getH8();
16
+ static void with_gvl(H8 *h8, const std::function<void(void)> &block) {
18
17
  if (h8->isGvlReleased()) {
19
18
  h8->setGvlReleased(false);
20
19
  rb_thread_call_with_gvl(unblock_caller, (void*) &block);
@@ -23,22 +22,65 @@ static void with_gvl(RubyGate *gate, const std::function<void(void)> &block) {
23
22
  block();
24
23
  }
25
24
 
26
- h8::RubyGate::RubyGate(H8* _context, VALUE object) :
25
+ static void with_gvl(RubyGate *gate, const std::function<void(void)> &block) {
26
+ with_gvl(gate->getH8(), block);
27
+ }
28
+
29
+ void h8::RubyGate::ClassGateConstructor(
30
+ const v8::FunctionCallbackInfo<Value>& args) {
31
+ Isolate *isolate = args.GetIsolate();
32
+ H8* h8 = (H8*) isolate->GetData(0);
33
+ assert(!args.Data().IsEmpty());
34
+
35
+ RubyGate *lambda = RubyGate::unwrap(args.Data().As<Object>());
36
+ assert(lambda != 0);
37
+
38
+ with_gvl(h8, [&] {
39
+ VALUE rb_args = ruby_args(h8, args, 1);
40
+ rb_ary_push(rb_args, lambda->ruby_object);
41
+ // Object creating ruby code can raise exceptions:
42
+ lambda->rescued_call(
43
+ rb_args,
44
+ call,
45
+ [&] (VALUE res) {
46
+ new RubyGate(h8, args.This(), res);
47
+ });
48
+ });
49
+ args.GetReturnValue().Set(args.This());
50
+ }
51
+
52
+ void h8::RubyGate::GateConstructor(
53
+ const v8::FunctionCallbackInfo<Value>& args) {
54
+ Isolate *isolate = args.GetIsolate();
55
+ H8* h8 = (H8*) isolate->GetData(0);
56
+ assert(args.Length() == 1);
57
+ Local<Value> val = args[0];
58
+ VALUE ruby_object = Qnil;
59
+
60
+ if (val->IsExternal())
61
+ ruby_object = (VALUE) val.As<External>()->Value(); // External::Cast(*val)->Value();
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
+ }
73
+
74
+ new RubyGate(h8, args.This(), ruby_object);
75
+ args.GetReturnValue().Set(args.This());
76
+ }
77
+
78
+ h8::RubyGate::RubyGate(H8* _context, Handle<Object> instance, VALUE object) :
27
79
  context(_context), ruby_object(object), next(0), prev(0) {
28
80
  v8::HandleScope scope(context->getIsolate());
29
- // printf("Ruby object gate constructor\n");
30
81
  context->add_resource(this);
31
-
32
- v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New();
33
- templ->SetInternalFieldCount(2);
34
- templ->SetCallAsFunctionHandler(&ObjectCallback);
35
-
36
- templ->SetNamedPropertyHandler(RubyGate::mapGet, RubyGate::mapSet);
37
- templ->SetIndexedPropertyHandler(RubyGate::indexGet, RubyGate::indexSet);
38
-
39
- v8::Handle<v8::Object> handle = templ->NewInstance();
40
- handle->SetAlignedPointerInInternalField(1, RUBYGATE_ID);
41
- Wrap(handle);
82
+ instance->SetAlignedPointerInInternalField(1, RUBYGATE_ID);
83
+ Wrap(instance);
42
84
  }
43
85
 
44
86
  void h8::RubyGate::mapGet(Local<String> name,
@@ -118,26 +160,25 @@ void h8::RubyGate::rescued_call(VALUE rb_args, VALUE (*call)(VALUE),
118
160
  // exceptions...
119
161
  try {
120
162
  block(res);
121
- }
122
- catch(JsError& e) {
163
+ } catch (JsError& e) {
123
164
  Local<v8::Object> error = v8::Exception::Error(
124
165
  context->js(e.what())).As<v8::Object>();
125
166
  context->getIsolate()->ThrowException(error);
126
- }
127
- catch(...) {
128
- Local<v8::Object> error = v8::Exception::Error(
129
- context->js("unknown exception (inner bug)")).As<v8::Object>();
167
+ } catch (...) {
168
+ Local<v8::Object> error =
169
+ v8::Exception::Error(
170
+ context->js("unknown exception (inner bug)")).As<
171
+ v8::Object>();
130
172
  context->getIsolate()->ThrowException(error);
131
173
  }
132
- }
133
- else
174
+ } else
134
175
  throw_js();
135
176
  }
136
177
 
137
178
  void h8::RubyGate::doObjectCallback(
138
179
  const v8::FunctionCallbackInfo<v8::Value>& args) {
139
180
  with_gvl(this, [&] {
140
- VALUE rb_args = ruby_args(args, 1);
181
+ VALUE rb_args = ruby_args(context, args, 1);
141
182
  rb_ary_push(rb_args, ruby_object);
142
183
  rescued_call(rb_args, call, [&] (VALUE res) {
143
184
  args.GetReturnValue().Set(context->to_js(res));
@@ -18,7 +18,7 @@ namespace h8 {
18
18
  */
19
19
  class RubyGate: public ObjectWrap, public AllocatedResource {
20
20
  public:
21
- RubyGate(H8* _context, VALUE object);
21
+ RubyGate(H8* _context, Handle<Object> instance, VALUE object);
22
22
 
23
23
  /**
24
24
  * Check the handle and unwrap the RubyGate if it is wrapped
@@ -77,7 +77,7 @@ protected:
77
77
  * extra slots in array if need
78
78
  */
79
79
  template<class T>
80
- VALUE ruby_args(const T& args, unsigned extras = 0) {
80
+ static VALUE ruby_args(H8* context,const T& args, unsigned extras = 0) {
81
81
  unsigned n = args.Length();
82
82
  VALUE rb_args = rb_ary_new2(n + extras);
83
83
  for (unsigned i = 0; i < n; i++)
@@ -110,6 +110,9 @@ protected:
110
110
  void getIndex(uint32_t index, const PropertyCallbackInfo<Value> &info);
111
111
  void setIndex(uint32_t index, Local<Value> value,
112
112
  const PropertyCallbackInfo<Value> &info);
113
+
114
+ static void GateConstructor(const v8::FunctionCallbackInfo<Value>& args);
115
+ static void ClassGateConstructor(const v8::FunctionCallbackInfo<Value>& args);
113
116
  private:
114
117
 
115
118
  void doObjectCallback(const v8::FunctionCallbackInfo<v8::Value>& args);
@@ -7,7 +7,7 @@ module H8
7
7
  @idcount = 0
8
8
  set_all **kwargs
9
9
  _set_var '___create_ruby_class', -> (cls, args) {
10
- _do_cretate_ruby_class cls, args
10
+ _do_create_ruby_class cls, args
11
11
  }
12
12
  end
13
13
 
@@ -102,13 +102,7 @@ module H8
102
102
  # in which case constructor function will be created
103
103
  def set_var name, value
104
104
  if value.is_a?(Class)
105
- clsid = "__ruby_class_#{@idcount += 1}"
106
- _set_var clsid, value
107
- eval <<-End
108
- function #{name.to_s}() {
109
- return ___create_ruby_class(#{clsid}, arguments);
110
- }
111
- End
105
+ _gate_class name.to_s, -> (*args) { value.new *args }
112
106
  else
113
107
  _set_var name, value
114
108
  end
@@ -116,7 +110,7 @@ module H8
116
110
 
117
111
  # create class instance passing it arguments stored in javascript 'arguments' object
118
112
  # (so it repacks them first)
119
- def _do_cretate_ruby_class(klass, arguments)
113
+ def _do_create_ruby_class(klass, arguments)
120
114
  klass.new *H8::arguments_to_a(arguments.to_ruby.values)
121
115
  end
122
116
  end
@@ -1,3 +1,3 @@
1
1
  module H8
2
- VERSION = "0.3.0"
2
+ VERSION = "0.4.0"
3
3
  end
@@ -5,7 +5,7 @@ describe 'ruby gate' do
5
5
 
6
6
  it 'should gate callables' do
7
7
  cxt = H8::Context.new
8
- count = 0
8
+ count = 0
9
9
  cxt[:fn] = -> (a, b) {
10
10
  count += 1
11
11
  a + b
@@ -44,8 +44,8 @@ describe 'ruby gate' do
44
44
  cxt[:fn] = -> (a, b) {
45
45
  a + b
46
46
  }
47
- res = cxt.eval("fn(11, 22);") { |cxt|
48
- cxt[:fn] = -> (a,b) { a - b }
47
+ res = cxt.eval("fn(11, 22);") { |cxt|
48
+ cxt[:fn] = -> (a, b) { a - b }
49
49
  }
50
50
  res.to_i.should == -11
51
51
  end
@@ -62,7 +62,7 @@ describe 'ruby gate' do
62
62
 
63
63
  it 'should convert nil, true, false and undefined' do
64
64
  cxt = H8::Context.new
65
- value = false
65
+ value = false
66
66
  cxt[:fn] = -> {
67
67
  [nil, true, false, value, H8::Undefined]
68
68
  }
@@ -82,9 +82,9 @@ describe 'ruby gate' do
82
82
  end
83
83
 
84
84
  it 'should convert strings to native string' do
85
- cxt = H8::Context.new
85
+ cxt = H8::Context.new
86
86
  cxt[:str] = src = "Пример строки"
87
- res = 'ПРИМЕР СТРОКИ'
87
+ res = 'ПРИМЕР СТРОКИ'
88
88
  cxt.eval('str.toLocaleUpperCase()').should == res
89
89
  up = cxt.eval('fn = function(t) { return t.toLocaleUpperCase(); }')
90
90
  up.call(src).should == res
@@ -151,6 +151,7 @@ describe 'ruby gate' do
151
151
  expect(-> {
152
152
  res = cxt.eval <<-End
153
153
  result = fn(11, 22);
154
+ throw Error("It should not happen");
154
155
  End
155
156
  }).to raise_error(MyException) { |e|
156
157
  e.message.should == 'Shit happens'
@@ -174,7 +175,7 @@ describe 'ruby gate' do
174
175
  End
175
176
  fail 'did not raise error'
176
177
  rescue H8::JsError => e
177
- x = e.javascript_error
178
+ x = e.javascript_error
178
179
  e.name.should == 'Error'
179
180
  e.message.should =~ /test/
180
181
  e.javascript_backtrace.should =~ /at bad \(eval\:4\:17\)/
@@ -251,6 +252,7 @@ describe 'ruby gate' do
251
252
  cxt.eval('foo.priv_method').should == H8::Undefined
252
253
  cxt.eval('foo.test_args').should be_kind_of(Proc)
253
254
  cxt.eval('foo.test_args("hi", "you")').should == 'hi-you'
255
+ cxt.eval('foo instanceof RubyGate').should == true
254
256
  end
255
257
 
256
258
  it 'should set ruby properties' do
@@ -342,12 +344,12 @@ describe 'ruby gate' do
342
344
  end
343
345
 
344
346
  it 'should pass varargs' do
345
- cxt = H8::Context.new
347
+ cxt = H8::Context.new
346
348
  cxt[:test] = -> (args) {
347
349
  # Sample how to deal with javascript 'arguments' vararg object:
348
350
  H8::arguments_to_a(args).join(',')
349
351
  }
350
- res = cxt.eval <<-End
352
+ res = cxt.eval <<-End
351
353
  function test2() {
352
354
  return test(arguments);
353
355
  }
@@ -356,6 +358,19 @@ describe 'ruby gate' do
356
358
  res.should == '1,2,ho'
357
359
  end
358
360
 
361
+ it 'should gate classes through API' do
362
+ c = H8::Context.new
363
+ la = -> (*args) {
364
+ { 'hello' => 'world'}
365
+ }
366
+ c._gate_class 'Tec', la
367
+ c.eval("var res = new Tec()")
368
+ c.eval('res').should == { 'hello' => 'world' }
369
+ c.eval("res['hello']").should == 'world'
370
+ c.eval('res instanceof Tec').should == true
371
+ c.eval('res instanceof RubyGate').should == true
372
+ end
373
+
359
374
  it 'should gate classes' do
360
375
  class Gated
361
376
  attr :init_args
@@ -363,15 +378,25 @@ describe 'ruby gate' do
363
378
  def initialize *args
364
379
  @init_args = args
365
380
  end
381
+
382
+ def inspect
383
+ "Gated<#{@init_args.inspect}>"
384
+ end
385
+
386
+ def to_str
387
+ inspect
388
+ end
366
389
  end
367
390
 
368
391
  cxt = H8::Context.new RClass: Gated
369
- c = cxt.eval 'new RClass()'
392
+ c = cxt.eval 'new RClass()'
370
393
  c.should be_a(Gated)
371
394
  c.init_args.should == []
372
395
 
373
396
  c = cxt.eval 'rc = new RClass("hello", "world")'
374
397
  c.should be_a(Gated)
398
+ cxt.eval('rc instanceof RubyGate').should == true
399
+ cxt.eval('new RClass() instanceof RClass').should == true
375
400
  c.init_args.should == ['hello', 'world']
376
401
  cxt.eval('rc.init_args').should == ['hello', 'world']
377
402
  end
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.3.0
4
+ version: 0.4.0
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-19 00:00:00.000000000 Z
11
+ date: 2015-01-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler