h8 0.3.0 → 0.4.0

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 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