h8 0.0.2 → 0.0.4

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.
@@ -1,3 +1,4 @@
1
+ #include <functional>
1
2
  #include <h8.h>
2
3
  #include <include/libplatform/libplatform.h>
3
4
 
@@ -7,23 +8,37 @@ extern "C" {
7
8
  void Init_h8(void);
8
9
  }
9
10
 
10
- VALUE h8_exception;
11
+ VALUE h8_exception, js_exception, js_timeout_exception;
11
12
  VALUE context_class;
13
+ VALUE ruby_gate_class;
12
14
  VALUE value_class;
15
+ VALUE Rundefined;
13
16
 
14
17
  ID id_is_a;
18
+ ID id_safe_call;
19
+
20
+ VALUE protect_ruby(const std::function<VALUE()> &block) {
21
+ try {
22
+ return block();
23
+ } catch (JsError& e) {
24
+ e.raise();
25
+ } catch (...) {
26
+ rb_raise(rb_eStandardError, "unknown error in JS");
27
+ }
28
+ return Qnil;
29
+ }
15
30
 
16
31
  static void rvalue_free(void* ptr) {
17
32
  delete (JsGate*) ptr;
18
33
  }
19
34
 
20
- void h8::rvalue_mark(void* ptr) {
21
- JsGate *gate = (JsGate*)ptr;
22
- rb_gc_mark(gate->h8->self);
35
+ static void rvalue_mark(void* ptr) {
36
+ JsGate *gate = (JsGate*) ptr;
37
+ rb_gc_mark(gate->ruby_context());
23
38
  }
24
39
 
25
40
  VALUE rvalue_alloc(VALUE klass) {
26
- return Data_Wrap_Struct(klass, h8::rvalue_mark, rvalue_free, new JsGate);
41
+ return Data_Wrap_Struct(klass, rvalue_mark, rvalue_free, new JsGate);
27
42
  }
28
43
 
29
44
  inline JsGate* rv(VALUE self) {
@@ -56,11 +71,11 @@ static VALUE rvalue_is_undefined(VALUE self) {
56
71
  return rv(self)->is_undefined();
57
72
  }
58
73
 
59
- static VALUE rvalue_get_attr(VALUE self,VALUE name) {
74
+ static VALUE rvalue_get_attr(VALUE self, VALUE name) {
60
75
  return rv(self)->get_attribute(name);
61
76
  }
62
77
 
63
- static VALUE rvalue_get_index(VALUE self,VALUE index) {
78
+ static VALUE rvalue_get_index(VALUE self, VALUE index) {
64
79
  return rv(self)->get_index(index);
65
80
  }
66
81
 
@@ -81,11 +96,19 @@ static VALUE rvalue_is_function(VALUE self) {
81
96
  }
82
97
 
83
98
  static VALUE rvalue_call(VALUE self, VALUE args) {
84
- return rv(self)->call(args);
99
+ return protect_ruby([&] {
100
+ return rv(self)->call(args);
101
+ });
85
102
  }
86
103
 
87
- static VALUE rvalue_apply(VALUE self, VALUE to,VALUE args) {
88
- return rv(self)->apply(to,args);
104
+ static VALUE rvalue_apply(VALUE self, VALUE to, VALUE args) {
105
+ return protect_ruby([&] {
106
+ return rv(self)->apply(to, args);
107
+ });
108
+ }
109
+
110
+ static VALUE rvalue_get_ruby_context(VALUE self) {
111
+ return rv(self)->ruby_context();
89
112
  }
90
113
 
91
114
  //------------ context ----------------------------------------------------------------
@@ -96,26 +119,37 @@ inline H8* rc(VALUE self) {
96
119
  return prcxt;
97
120
  }
98
121
 
99
- static VALUE context_eval(VALUE self, VALUE script) {
100
- H8* cxt = rc(self);
101
- H8::Scope s(cxt);
102
- return cxt->eval_to_ruby(StringValueCStr(script));
122
+ static VALUE context_eval(VALUE self, VALUE script,VALUE timeout) {
123
+ return protect_ruby([&] {
124
+ H8* cxt = rc(self);// v8::Locker l(cxt->getIsolate());
125
+ H8::Scope s(cxt);
126
+ return cxt->eval_to_ruby(StringValueCStr(script), FIX2INT(timeout));
127
+ });
103
128
  }
104
129
 
105
- static VALUE context_set_var(VALUE self, VALUE name,VALUE value) {
106
- rc(self)->set_var(name, value);
107
- return Qnil;
130
+ static VALUE context_set_var(VALUE self, VALUE name, VALUE value) {
131
+ return protect_ruby([=] {
132
+ rc(self)->set_var(name, value);
133
+ return Qnil;
134
+ });
108
135
  }
109
136
 
110
137
  static void context_free(void* ptr) {
111
138
  delete (H8*) ptr;
112
139
  }
113
140
 
114
- VALUE h8::context_alloc(VALUE klass) {
141
+ static void context_mark(void* ptr) {
142
+ H8* h8 = (H8*) ptr;
143
+ h8->ruby_mark_gc();
144
+ }
145
+
146
+ namespace h8 {
147
+ VALUE context_alloc(VALUE klass) {
115
148
  H8 *h8 = new H8;
116
- h8->self = Data_Wrap_Struct(klass, 0, context_free, h8);
149
+ h8->self = Data_Wrap_Struct(klass, context_mark, context_free, h8);
117
150
  return h8->self;
118
151
  }
152
+ }
119
153
 
120
154
  void init_v8() {
121
155
  v8::V8::InitializeICU();
@@ -128,13 +162,16 @@ void Init_h8(void) {
128
162
  init_v8();
129
163
 
130
164
  id_is_a = rb_intern("is_a?");
165
+ id_safe_call = rb_intern("secure_call");
131
166
 
132
167
  VALUE h8 = rb_define_module("H8");
133
168
 
134
169
  context_class = rb_define_class_under(h8, "Context", rb_cObject);
170
+ ruby_gate_class = rb_define_class_under(h8, "RubyGate", rb_cObject);
135
171
  rb_define_alloc_func(context_class, context_alloc);
136
- rb_define_method(context_class, "eval", (ruby_method) context_eval, 1);
137
- rb_define_method(context_class, "set_var", (ruby_method) context_set_var, 2);
172
+ rb_define_method(context_class, "_eval", (ruby_method) context_eval, 2);
173
+ rb_define_method(context_class, "set_var", (ruby_method) context_set_var,
174
+ 2);
138
175
 
139
176
  value_class = rb_define_class_under(h8, "Value", rb_cObject);
140
177
  rb_define_alloc_func(value_class, rvalue_alloc);
@@ -143,20 +180,27 @@ void Init_h8(void) {
143
180
  rb_define_method(value_class, "to_f", (ruby_method) rvalue_to_f, 0);
144
181
  rb_define_method(value_class, "integer?", (ruby_method) rvalue_is_int, 0);
145
182
  rb_define_method(value_class, "float?", (ruby_method) rvalue_is_float, 0);
146
- rb_define_method(value_class, "string?", (ruby_method) rvalue_is_string,
147
- 0);
183
+ rb_define_method(value_class, "string?", (ruby_method) rvalue_is_string, 0);
148
184
  rb_define_method(value_class, "array?", (ruby_method) rvalue_is_array, 0);
149
185
  rb_define_method(value_class, "object?", (ruby_method) rvalue_is_object, 0);
150
- rb_define_method(value_class, "function?", (ruby_method) rvalue_is_function, 0);
151
- rb_define_method(value_class, "undefined?", (ruby_method) rvalue_is_undefined,
186
+ rb_define_method(value_class, "function?", (ruby_method) rvalue_is_function,
152
187
  0);
188
+ rb_define_method(value_class, "undefined?",
189
+ (ruby_method) rvalue_is_undefined, 0);
153
190
  rb_define_method(value_class, "_get_attr", (ruby_method) rvalue_get_attr,
154
191
  1);
155
192
  rb_define_method(value_class, "_get_index", (ruby_method) rvalue_get_index,
156
193
  1);
157
194
  rb_define_method(value_class, "_call", (ruby_method) rvalue_call, 1);
158
195
  rb_define_method(value_class, "_apply", (ruby_method) rvalue_apply, 2);
196
+ rb_define_method(value_class, "context",
197
+ (ruby_method) rvalue_get_ruby_context, 0);
159
198
 
160
199
  h8_exception = rb_define_class_under(h8, "Error", rb_eStandardError);
200
+ js_exception = rb_define_class_under(h8, "JsError", h8_exception);
201
+ js_timeout_exception = rb_define_class_under(h8, "TimeoutError", js_exception);
161
202
 
203
+ VALUE u_class = rb_define_class_under(h8, "UndefinedClass", rb_cObject);
204
+ Rundefined = rb_funcall(u_class, rb_intern("instance"), 0);
162
205
  }
206
+
@@ -23,7 +23,7 @@
23
23
  #ifndef SRC_OBJECT_WRAP_H_
24
24
  #define SRC_OBJECT_WRAP_H_
25
25
 
26
- #include "v8.h"
26
+ #include <include/v8.h>
27
27
  #include <assert.h>
28
28
 
29
29
 
@@ -0,0 +1,117 @@
1
+ #include "h8.h"
2
+ #include "ruby_gate.h"
3
+ #include <ruby.h>
4
+
5
+ using namespace h8;
6
+
7
+ h8::RubyGate::RubyGate(H8* _context, VALUE object) :
8
+ context(_context), ruby_object(object), next(0), prev(0) {
9
+ v8::HandleScope scope(context->getIsolate());
10
+ // printf("Ruby object gate constructor\n");
11
+ context->add_resource(this);
12
+
13
+ v8::Local<v8::ObjectTemplate> templ = ObjectTemplate::New();
14
+ templ->SetInternalFieldCount(2);
15
+ templ->SetCallAsFunctionHandler(&ObjectCallback);
16
+
17
+ templ->SetNamedPropertyHandler(h8::RubyGate::mapGet, h8::RubyGate::mapSet);
18
+
19
+ v8::Handle<v8::Object> handle = templ->NewInstance();
20
+ handle->SetAlignedPointerInInternalField(1, RUBYGATE_ID);
21
+ Wrap(handle);
22
+ }
23
+
24
+ void h8::RubyGate::mapGet(Local<String> name,
25
+ const PropertyCallbackInfo<Value> &info) {
26
+ RubyGate *rg = RubyGate::unwrap(info.This());
27
+ assert(rg != 0);
28
+ rg->getProperty(name, info);
29
+ }
30
+
31
+ void h8::RubyGate::mapSet(Local<String> name,
32
+ Local<Value> value,
33
+ const PropertyCallbackInfo<Value> &info) {
34
+ RubyGate *rg = RubyGate::unwrap(info.This());
35
+ assert(rg != 0);
36
+ rg->setProperty(name, value, info);
37
+ }
38
+
39
+ void h8::RubyGate::ObjectCallback(
40
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
41
+ v8::HandleScope scope(args.GetIsolate());
42
+ v8::Handle<v8::Object> obj = args.This();
43
+ RubyGate* rg = h8::ObjectWrap::Unwrap<RubyGate>(args.This());
44
+ rg->doObjectCallback(args);
45
+ }
46
+
47
+ VALUE h8::RubyGate::rescue_callback(VALUE me, VALUE exception_object) {
48
+ RubyGate* gate;
49
+ Data_Get_Struct(me, RubyGate, gate);
50
+ gate->last_ruby_error = exception_object;
51
+ return Qnil;
52
+ }
53
+
54
+ VALUE RubyGate::call(VALUE args) {
55
+ VALUE callable = rb_ary_pop(args);
56
+ return rb_proc_call(callable, args);
57
+ }
58
+
59
+ VALUE RubyGate::secure_call(VALUE args) {
60
+ VALUE method = rb_ary_pop(args);
61
+ VALUE receiver = rb_ary_pop(args);
62
+ return rb_funcall(context_class, id_safe_call, 3, receiver, method, args);
63
+ }
64
+
65
+ void h8::RubyGate::throw_js() {
66
+ Local<v8::Object> error = v8::Exception::Error(
67
+ context->js("ruby exception")).As<v8::Object>();
68
+ error->Set(context->js("source"), context->to_js(last_ruby_error));
69
+ context->getIsolate()->ThrowException(error);
70
+ }
71
+
72
+ void h8::RubyGate::rescued_call(VALUE rb_args, VALUE (*call)(VALUE),
73
+ const std::function<void(VALUE)> &block) {
74
+ last_ruby_error = Qnil;
75
+ VALUE me = Data_Wrap_Struct(ruby_gate_class, 0, 0, this);
76
+ VALUE res = rb_rescue((ruby_method) (call), rb_args,
77
+ (ruby_method) (rescue_callback), me);
78
+ if (last_ruby_error == Qnil)
79
+ block(res);
80
+ else
81
+ throw_js();
82
+ }
83
+
84
+ void h8::RubyGate::doObjectCallback(
85
+ const v8::FunctionCallbackInfo<v8::Value>& args) {
86
+
87
+ VALUE rb_args = ruby_args(args, 1);
88
+ rb_ary_push(rb_args, ruby_object);
89
+ return rescued_call(rb_args, call, [&] (VALUE res) {
90
+ args.GetReturnValue().Set(context->to_js(res));
91
+ });
92
+ }
93
+
94
+ void h8::RubyGate::getProperty(Local<String> name,
95
+ const PropertyCallbackInfo<Value> &info) {
96
+ VALUE rb_args = rb_ary_new2(2);
97
+ rb_ary_push(rb_args, ruby_object);
98
+ rb_ary_push(rb_args, context->to_ruby(name));
99
+ return rescued_call(rb_args, secure_call, [&] (VALUE res) {
100
+ info.GetReturnValue().Set(context->to_js(res));
101
+ });
102
+ }
103
+
104
+ void h8::RubyGate::setProperty(Local<String> name,
105
+ Local<Value> value,
106
+ const PropertyCallbackInfo<Value> &info) {
107
+ VALUE rb_args = rb_ary_new2(3);
108
+ rb_ary_push(rb_args, context->to_ruby(value));
109
+ rb_ary_push(rb_args, ruby_object);
110
+ VALUE method = context->to_ruby(name);
111
+ method = rb_str_cat2(method, "=");
112
+ rb_ary_push(rb_args, method);
113
+ return rescued_call(rb_args, secure_call, [&] (VALUE res) {
114
+ info.GetReturnValue().Set(context->to_js(res));
115
+ });
116
+ }
117
+
@@ -0,0 +1,118 @@
1
+ #ifndef __ruby_gate_h
2
+ #define __ruby_gate_h
3
+
4
+ #include <exception>
5
+ #include <functional>
6
+
7
+ #include "h8.h"
8
+ #include "object_wrap.h"
9
+ #include "allocated_resource.h"
10
+
11
+ namespace h8 {
12
+
13
+ #define RUBYGATE_ID ((void*)0xF0200)
14
+
15
+ /**
16
+ * Gate a generic ruby object to Javascript context and retain it for
17
+ * the lifetime of the javascript object
18
+ */
19
+ class RubyGate: public ObjectWrap, public AllocatedResource {
20
+ public:
21
+ RubyGate(H8* _context, VALUE object);
22
+
23
+ /**
24
+ * Check the handle and unwrap the RubyGate if it is wrapped
25
+ * @return wrapped RubyGate* or 0
26
+ */
27
+ static RubyGate* unwrap(v8::Handle<v8::Object> handle) {
28
+ if (handle->InternalFieldCount() == 2
29
+ && handle->GetAlignedPointerFromInternalField(1) == RUBYGATE_ID) {
30
+ return ObjectWrap::Unwrap<RubyGate>(handle);
31
+ }
32
+ return 0;
33
+ }
34
+
35
+ void setRubyInstance(VALUE instance) {
36
+ this->ruby_object = instance;
37
+ }
38
+
39
+ virtual void rb_mark_gc() {
40
+ rb_gc_mark(ruby_object);
41
+ }
42
+
43
+ virtual void free() {
44
+ AllocatedResource::free();
45
+ persistent().ClearWeak();
46
+ persistent().Reset();
47
+ delete this;
48
+ }
49
+
50
+ VALUE rubyObject() const {
51
+ return ruby_object;
52
+ }
53
+
54
+ virtual ~RubyGate() {
55
+ }
56
+
57
+ protected:
58
+ /**
59
+ * Perform rb_rescue call to 'call' callback, and invoke block with value returned by callback
60
+ * unless a ruby exception is caught, in which a correct JsError is thrown.
61
+ */
62
+ void rescued_call(VALUE rb_args, VALUE (*call)(VALUE),const std::function<void(VALUE)> &block);
63
+
64
+ /**
65
+ * Convert some v8 parameters-like object to ruby arguments array, allocating
66
+ * extra slots in array if need
67
+ */
68
+ template <class T>
69
+ VALUE ruby_args(const T& args, unsigned extras = 0) {
70
+ unsigned n = args.Length();
71
+ VALUE rb_args = rb_ary_new2(n+extras);
72
+ for (unsigned i = 0; i < n; i++)
73
+ rb_ary_push(rb_args, context->to_ruby(args[i]));
74
+ return rb_args;
75
+ }
76
+
77
+
78
+ /**
79
+ * Ruby callable callback for rb_rescue and like. Args [0..-2] are call arguments,
80
+ * last arg[-1] should be a callable to perform call with (for performance
81
+ * reasons it should be the last).
82
+ */
83
+ static VALUE call(VALUE args);
84
+
85
+ /**
86
+ * Call ruby method via H8::Context#secure_call
87
+ */
88
+ static VALUE secure_call(VALUE args);
89
+
90
+ /**
91
+ * callback for rb_rescue. Sets last_ruby_error.
92
+ */
93
+ static VALUE rescue_callback(VALUE me,VALUE exception_object);
94
+
95
+ void getProperty(Local<String> name, const PropertyCallbackInfo<Value> &info);
96
+ void setProperty(Local<String> name, Local<Value> value,const PropertyCallbackInfo<Value> &info);
97
+ private:
98
+
99
+ void doObjectCallback(const v8::FunctionCallbackInfo<v8::Value>& args);
100
+
101
+ static void ObjectCallback(const v8::FunctionCallbackInfo<v8::Value>& args);
102
+ static void mapGet(Local<String> name, const PropertyCallbackInfo<Value> &info);
103
+ static void mapSet(Local<String> name, Local<Value> value,const PropertyCallbackInfo<Value> &info);
104
+
105
+ void throw_js();
106
+
107
+
108
+ friend class H8;
109
+
110
+ H8 *context;
111
+ VALUE ruby_object = Qnil;
112
+ VALUE last_ruby_error = Qnil;
113
+
114
+ RubyGate *next, *prev;
115
+ };
116
+ }
117
+
118
+ #endif
@@ -1,6 +1,7 @@
1
1
  # coding: utf-8
2
2
  lib = File.expand_path('../lib', __FILE__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
4
5
  require 'h8/version'
5
6
  require "rake/extensiontask"
6
7
  require 'rubygems/package_task'
@@ -11,7 +12,7 @@ spec = Gem::Specification.new do |spec|
11
12
  spec.authors = ["sergeych"]
12
13
  spec.email = ["real.sergeych@gmail.com"]
13
14
  spec.summary = %q{Minimalistic and sane v8 bindings}
14
- spec.description = %q{Should be more or less replacement for broken therubyracer gem and riny 2.1+ }
15
+ spec.description = %q{Should be more or less replacement for broken therubyracer gem and ruby 2.1+ }
15
16
  spec.homepage = ""
16
17
  spec.license = "MIT"
17
18
 
@@ -42,3 +43,4 @@ Rake::ExtensionTask.new "h8", spec do |ext|
42
43
  end
43
44
 
44
45
  spec
46
+
data/lib/h8.rb CHANGED
@@ -1,9 +1,68 @@
1
1
  require 'h8/version'
2
2
  require 'h8/context'
3
- require 'h8/h8'
4
3
  require 'h8/value'
4
+ require 'singleton'
5
5
 
6
6
  module H8
7
+ # The exception that H8 raises on errors that are not caused by executing
8
+ # javascript (e.g. bad parameters, illegal conversion and so on)
9
+ class Error < StandardError
10
+ end
11
+
12
+ # The general error caused by the script execution, e.g. uncaught javascript exceptinos and like.
13
+ # Check #message to see the cause.
14
+ class JsError < Error
15
+ attr :message
16
+ attr :source
17
+
18
+ def to_s
19
+ message
20
+ end
21
+ end
22
+
23
+ # Script execution is timed out (see H8::Context#eval timeout parameter)
24
+ class TimeoutError < JsError
25
+ def initialize message
26
+ super
27
+ @message = message
28
+ @source = nil
29
+ end
30
+ end
31
+
32
+ # The class representing undefined in javascript. Singleton
33
+ # Nota that H8::Undefined == false but is not FalseClass
34
+ class UndefinedClass
35
+ include Singleton
36
+
37
+ def blank?
38
+ true
39
+ end
40
+
41
+ def undefined?
42
+ true
43
+ end
7
44
 
8
- # Your code goes here...
45
+ def empty?
46
+ true
47
+ end
48
+
49
+ def present?
50
+ false
51
+ end
52
+
53
+ def !
54
+ true
55
+ end
56
+
57
+ def == x
58
+ x.is_a?(H8::UndefinedClass) || x == false
59
+ end
60
+
61
+ end
62
+
63
+ # The constant representing 'undefined' value in Javascript
64
+ # The proper use is to compare returned value res == H8::Undefined
65
+ Undefined = UndefinedClass.instance
9
66
  end
67
+
68
+ require 'h8/h8'