h8 0.0.2 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -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'