h8 0.0.1 → 0.0.2

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: 342d8b09078eb9d8601e80c49e49550eb3d179d8
4
- data.tar.gz: bf0f41dc3286c9f67a04266083cc385142dcc2ce
3
+ metadata.gz: 7353dcd3c307b93e4f7fd68ccbc7af260e51d09c
4
+ data.tar.gz: 8603a9cfb342305c5dfcf156bf92d595bde23a34
5
5
  SHA512:
6
- metadata.gz: 66763287432f0694d92ec467f0b96c986d528ba58321b16674200e7c1815d4f39a1ca6bdb2cab986f7e2de822359d42391458a27c8295d46ac35943383915b67
7
- data.tar.gz: 3ec3557325aece0f43ccedc11cf1475ef867fe6b15a508f256328fcc6ecdb0e1837565c4129b78439a69a7a19f41d8f22e4b1746d6ab4652cfc58e5f8750bcaa
6
+ metadata.gz: 94056bd7b5378d50ceefd3322479e38b3eb122ee019a0c4ce4a9531bb447fa062e26393c4a5738d97138eaf973a6627b55002126850133fe59c6a9b652028c96
7
+ data.tar.gz: b1a98f23583bc8cb77ffd35bae2cb1f3d71ea6b9e6f6553149ee2162b468ff9474654d6069d5aa68bb8c0cd97713b26caa24b08604f4b52bbd8b8cca92cc5eb5
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/README.md CHANGED
@@ -1,11 +1,30 @@
1
- # Hybrid8
1
+ # Hybrid8, aka H8
2
2
 
3
- _Warning_ this gem is not yet functional. Please do not try to use until versions >= 0.1.0!
3
+ _Warning_ this gem has yet limited functionality and is under main development. Versions suitable
4
+ for an open beta test will start from 0.1.*.
4
5
 
5
- Therubyracer gem alternative to work with ruby 2.1+ in multithreaded environment.
6
+ Therubyracer gem alternative to effectively work with ruby 2.1+ in multithreaded environment in an
7
+ effective and GC-safe way. Should be out of the box replacement for most scenarios.
8
+
9
+ Special features:
10
+
11
+ - care about wrapped Ruby objects lifespan (are kept until either ruby or javascript context
12
+ reference wrapped object). In no way GC will not reclaim ruby object that is in use by the
13
+ javascript context
14
+
15
+ - care about wrapped Javascript objects lifetime the same. Referenced JS items will not be recycled
16
+ while there are ruby objects referencing it. It also means that once created H8::Context will not
17
+ be reclaimed as long as there is at least one active wrapped object returned from the script.
6
18
 
7
19
  ## Installation
8
20
 
21
+ ### Prerequisites
22
+
23
+ You should have installed libv8, use latest version with v8::Isolate and v8::Locker. This version
24
+ may not find you installation, contact me if you have problems, I'll tune it up.
25
+
26
+ ### Setting up
27
+
9
28
  Add this line to your application's Gemfile:
10
29
 
11
30
  gem 'hybrid8'
@@ -20,10 +39,55 @@ Or install it yourself as:
20
39
 
21
40
  ## Usage
22
41
 
23
- TODO: Write usage instructions here
42
+ Is generally like therubyracer gem. Create context, set variables, run scripts.
43
+
44
+ require 'h8'
45
+
46
+ res = H8::Context.eval "({foo: 'hello', bar: 'world'});"
47
+ puts "#{res.foo} #{res.bar}"
48
+
49
+ another way to access attributes:
50
+
51
+ puts res['foo']
52
+
53
+ The same works with arrays:
54
+
55
+ res = H8::Context.eval "['foo', bar'];"
56
+ puts res[1]
57
+
58
+ To set context variables:
59
+
60
+ cxt = H8::Context.new some: 'value'
61
+ cxt[:pi] = 3.1415
62
+
63
+ You can return function and call it from ruby:
64
+
65
+ fun = cxt.eval "(function pi_n(n) { return pi * n; })"
66
+ p fun(2)
67
+
68
+ The same you can return objects and call its member functions - if a member is a function,
69
+ it will be called with given arguments:
70
+
71
+ res = H8::Context.eval <<-End
72
+ function cls(base) {
73
+ this.base = base;
74
+ this.someVal = 'hello!';
75
+ this.noArgs = function() { return 'world!'};
76
+ this.doAdd = function(a, b) {
77
+ return a + b + base;
78
+ }
79
+ }
80
+ new cls(100);
81
+ End
82
+ res.someVal.should == 'hello!'
83
+ res.noArgs.should == 'world!'
84
+ res.doAdd(10, 1).should == 111
24
85
 
25
86
  ## Contributing
26
87
 
88
+ Note that at this early point of development you better first talk to me to not to reinvent the
89
+ wheel.
90
+
27
91
  1. Fork it ( https://github.com/[my-github-username]/hybrid8/fork )
28
92
  2. Create your feature branch (`git checkout -b my-new-feature`)
29
93
  3. Commit your changes (`git commit -am 'Add some feature'`)
@@ -21,21 +21,23 @@ abort 'missing malloc()' unless have_func 'malloc'
21
21
  abort 'missing free()' unless have_func 'free'
22
22
 
23
23
  # Give it a name
24
- extension_name = 'hybrid8'
24
+ extension_name = 'h8'
25
25
 
26
+ dir_config('v8', '/Users/sergeych/dev/v8', '/Users/sergeych/dev/v8/lib')
26
27
 
27
28
  dir_config(extension_name)
28
29
  ok = true
29
30
 
30
- # unless have_header('gmp.h')
31
- # $stderr.puts "can't find gmp.h, try --with-gmp-include=<path>"
32
- # ok = false
33
- # end
34
- #
35
- # unless have_library('gmp', '__gmpz_init')
36
- # $stderr.puts "can't find -lgmp, try --with-gmp-lib=<path>"
37
- # ok = false
38
- # end
31
+ unless have_header('include/v8.h')
32
+ $stderr.puts "can't find v8.h, install libv8 3.25.30+ first"
33
+ ok = false
34
+ end
35
+
36
+ unless have_library('v8_base') && have_library('v8_snapshot') && have_library('v8_libplatform') \
37
+ && have_library('v8_libbase') && have_library('icuuc') && have_library('icudata')
38
+ $stderr.puts "can't find libv8"
39
+ ok = false
40
+ end
39
41
 
40
42
 
41
43
  # This test is actually due to a Clang 3.3 shortcoming, included in OS X 10.9,
@@ -60,3 +62,20 @@ else
60
62
  raise "Unable to build, correct above errors and rerun"
61
63
  end
62
64
 
65
+ # LIBV8_COMPATIBILITY = '~> 3.30'
66
+ #
67
+ # begin
68
+ # require 'rubygems'
69
+ # gem 'libv8', LIBV8_COMPATIBILITY
70
+ # rescue Gem::LoadError
71
+ # warn "Warning! Unable to load libv8 #{LIBV8_COMPATIBILITY}."
72
+ # rescue LoadError
73
+ # warn "Warning! Could not load rubygems. Please make sure you have libv8 #{LIBV8_COMPATIBILITY} installed."
74
+ # ensure
75
+ # require 'libv8'
76
+ # end
77
+ #
78
+ # Libv8.configure_makefile
79
+
80
+ create_makefile('h8/h8')
81
+
@@ -0,0 +1,16 @@
1
+ #include "h8.h"
2
+
3
+ Local<Value> h8::H8::gateObject(VALUE ruby_value) const {
4
+ if ( Qtrue == rb_funcall(ruby_value, id_is_a, 1, value_class)) {
5
+ JsGate *gate;
6
+ Data_Get_Struct(ruby_value, JsGate, gate);
7
+ if( gate->h8 != this ) {
8
+ rb_raise(h8_exception, "H8::Value is bound to other H8::Context");
9
+ return Undefined(isolate);
10
+ }
11
+ else
12
+ return gate->value();
13
+ }
14
+ rb_raise(h8_exception, "Object gate is not implemented");
15
+ return Undefined(isolate);
16
+ }
@@ -0,0 +1,159 @@
1
+ #ifndef _h8_h
2
+ #define _h8_h
3
+
4
+ #include <include/v8.h>
5
+ #include <ruby.h>
6
+ #include <iostream>
7
+
8
+ using namespace v8;
9
+ using namespace std;
10
+
11
+ extern VALUE h8_exception;
12
+ extern VALUE h8_class;
13
+ extern VALUE value_class;
14
+
15
+ extern ID id_is_a;
16
+
17
+ //#include <ruby/thread.h>
18
+
19
+ namespace h8 {
20
+
21
+ template<class T> inline void t(const T& x) {
22
+ cout << x << endl << flush;
23
+ }
24
+
25
+ class H8 {
26
+ public:
27
+
28
+ class Scope: public HandleScope {
29
+ v8::Isolate::Scope isolate_scope;
30
+ v8::Context::Scope context_scope;
31
+ H8* rcontext;
32
+ public:
33
+ Scope(H8* cxt) :
34
+ HandleScope(cxt->getIsolate()), isolate_scope(
35
+ cxt->getIsolate()), context_scope(cxt->getContext()), rcontext(
36
+ cxt) {
37
+ }
38
+ };
39
+
40
+ static void init();
41
+
42
+ H8() {
43
+ isolate = Isolate::New();
44
+ Isolate::Scope isolate_scope(isolate);
45
+ HandleScope handle_scope(isolate);
46
+
47
+ v8::Handle<v8::ObjectTemplate> global = v8::ObjectTemplate::New(
48
+ isolate);
49
+ v8::Handle<v8::Context> context = v8::Context::New(isolate, NULL,
50
+ global);
51
+ persistent_context.Reset(isolate, context);
52
+ }
53
+
54
+ Handle<Value> eval(const char* script_utf) {
55
+ v8::EscapableHandleScope escape(isolate);
56
+ Local<Value> result;
57
+
58
+ Handle<v8::String> script_source = String::NewFromUtf8(isolate,
59
+ script_utf);
60
+ v8::Handle<v8::Script> script;
61
+ v8::TryCatch try_catch;
62
+ v8::ScriptOrigin origin(String::NewFromUtf8(isolate, "eval"));
63
+
64
+ script = v8::Script::Compile(script_source, &origin);
65
+
66
+ if (script.IsEmpty()) {
67
+ report_exception(try_catch);
68
+ result = Undefined(isolate);
69
+ } else {
70
+ result = script->Run();
71
+ if (try_catch.HasCaught()) {
72
+ report_exception(try_catch);
73
+ }
74
+ }
75
+ return escape.Escape(result);
76
+ }
77
+
78
+ VALUE eval_to_ruby(const char* script_utf) {
79
+ // TODO: throw ruby exception on error
80
+ return to_ruby(eval(script_utf));
81
+ }
82
+
83
+ Handle<Context> getContext() {
84
+ return Local<Context>::New(isolate, persistent_context);
85
+ }
86
+
87
+ bool isError() const {
88
+ return is_error;
89
+ }
90
+
91
+ Isolate* getIsolate() const {
92
+ return isolate;
93
+ }
94
+
95
+ VALUE to_ruby(Handle<Value> value);
96
+
97
+ v8::Local<v8::String> js(VALUE val) const {
98
+ return js(StringValueCStr(val));
99
+ }
100
+
101
+ v8::Local<v8::String> js(const char* str) const {
102
+ return v8::String::NewFromUtf8(isolate, str);
103
+ }
104
+
105
+ void set_var(VALUE name, VALUE value) {
106
+ Scope scope(this);
107
+ getContext()->Global()->Set(js(name), to_js(value));
108
+ }
109
+
110
+ Local<Value> to_js(VALUE ruby_value) const {
111
+ switch (TYPE(ruby_value)) {
112
+ case T_STRING:
113
+ return js(ruby_value);
114
+ case T_FIXNUM:
115
+ return v8::Int32::New(isolate, FIX2INT(ruby_value));
116
+ case T_FLOAT:
117
+ return v8::Number::New(isolate, NUM2DBL(ruby_value));
118
+ case T_DATA:
119
+ case T_OBJECT:
120
+ return gateObject(ruby_value);
121
+ default:
122
+ rb_raise(h8_exception, "can't gate to js: unknown type");
123
+ }
124
+ return Undefined(isolate);
125
+ }
126
+
127
+ Local<Value> gateObject(VALUE object) const;
128
+
129
+ virtual ~H8() {
130
+ persistent_context.Reset();
131
+ }
132
+
133
+ private:
134
+ friend VALUE context_alloc(VALUE klass);
135
+ friend void rvalue_mark(void* ptr);
136
+
137
+ Isolate *isolate;
138
+ VALUE self;
139
+
140
+ void report_exception(v8::TryCatch& tc) {
141
+ rb_raise(h8_exception, "Failed to compile/execute script");
142
+ }
143
+
144
+ Persistent<Context> persistent_context;
145
+
146
+ bool is_error = false;
147
+ };
148
+ // Context
149
+ }
150
+
151
+ typedef VALUE (*ruby_method)(...);
152
+
153
+ #include "js_gate.h"
154
+
155
+ inline VALUE h8::H8::to_ruby(Handle<Value> value) {
156
+ return JsGate::to_ruby(this, value);
157
+ }
158
+
159
+ #endif
@@ -0,0 +1,196 @@
1
+ #ifndef __js_gate_h
2
+ #define __js_gate_h
3
+
4
+ #include "h8.h"
5
+
6
+ using namespace v8;
7
+
8
+ namespace h8 {
9
+
10
+ /**
11
+ * Interface to anything that could be converted to a Javascipt object. Provides common helpers.
12
+ */
13
+ class JsValue {
14
+ public:
15
+ virtual Local<Value> value() const = 0;
16
+
17
+ Local<Object> object() const {
18
+ return value()->ToObject();
19
+ }
20
+
21
+ virtual Isolate* isolate() = 0;
22
+ };
23
+
24
+
25
+ /**
26
+ * Gates JS object to ruby environment. Holds persistent reference to the source js object until
27
+ * ruby object is recycled (then frees it). Note that this ruby object is not meant to be kept alive
28
+ * by the H8 instance, instead, its owner should.
29
+ *
30
+ * Methods of this class do not need the H8::Scope, they create one internally.
31
+ */
32
+ class JsGate : public JsValue {
33
+ public:
34
+ /**
35
+ * Used in the ruby allocator. Do not call unless you know what you do.
36
+ */
37
+ JsGate() {
38
+ }
39
+
40
+ /**
41
+ * Return Ruby object that gates specified Handled javascript object. Ruby object
42
+ * locks permanently value until get recycled.
43
+ */
44
+ template <class T>
45
+ static VALUE to_ruby(H8* h8, const Handle<T>& value) {
46
+ JsGate *gate;
47
+ VALUE ruby_gate = rb_class_new_instance(0, NULL, value_class);
48
+ Data_Get_Struct(ruby_gate, JsGate, gate);
49
+ gate->set(h8, value);
50
+ return ruby_gate;
51
+ }
52
+
53
+ /**
54
+ * Reset gate to the specified handle.
55
+ */
56
+ template<class T>
57
+ void set(H8 *h8, const Handle<T>& val) {
58
+ this->h8 = h8;
59
+ persistent_value.Reset(h8->getIsolate(), val);
60
+ }
61
+
62
+ /**
63
+ * Get ruby string representation
64
+ */
65
+ VALUE to_s() {
66
+ H8::Scope scope(h8);
67
+ String::Utf8Value res(value());
68
+ return *res ? rb_str_new2(*res) : Qnil;
69
+ }
70
+
71
+ /**
72
+ * Get ruby integer representation (FIXNUM)
73
+ */
74
+ VALUE to_i() {
75
+ H8::Scope scope(h8);
76
+ return INT2FIX(value()->IntegerValue());
77
+ }
78
+
79
+ /**
80
+ * Get ruby Float representation (FIXNUM)
81
+ */
82
+ VALUE to_f() {
83
+ H8::Scope scope(h8);
84
+ return DBL2NUM(value()->NumberValue());
85
+ }
86
+
87
+ /**
88
+ * @return true if the object is a primitive integer
89
+ */
90
+ VALUE is_int() {
91
+ H8::Scope scope(h8);
92
+ return value()->IsInt32() ? Qtrue : Qfalse;
93
+ }
94
+
95
+ /**
96
+ * @return true if the object is a primitive float
97
+ */
98
+ VALUE is_float() {
99
+ H8::Scope scope(h8);
100
+ return value()->IsNumber() ? Qtrue : Qfalse;
101
+ }
102
+
103
+ /**
104
+ * @return true if the object is an array
105
+ */
106
+ VALUE is_array() {
107
+ H8::Scope scope(h8);
108
+ return value()->IsArray() ? Qtrue : Qfalse;
109
+ }
110
+
111
+ /**
112
+ * @return true if the object is an object
113
+ */
114
+ VALUE is_object() {
115
+ H8::Scope scope(h8);
116
+ return value()->IsObject() ? Qtrue : Qfalse;
117
+ }
118
+
119
+ /**
120
+ * Retreive JS object attribute and convert it to the ruby wrapper
121
+ * of the new JsGate instace.
122
+ */
123
+ VALUE get_attribute(VALUE name) {
124
+ H8::Scope scope(h8);
125
+ Local<Value> v8_name = v8::String::NewFromUtf8(isolate(), StringValueCStr(name));
126
+ return h8->to_ruby(object()->Get(v8_name));
127
+ }
128
+
129
+ VALUE get_index(VALUE index) {
130
+ H8::Scope scope(h8);
131
+ return h8->to_ruby(object()->Get(NUM2INT(index)));
132
+ }
133
+
134
+ /**
135
+ * @return true if the object is a primitive string
136
+ */
137
+ VALUE is_string() {
138
+ H8::Scope scope(h8);
139
+ return value()->IsString() ? Qtrue : Qfalse;
140
+ }
141
+
142
+ VALUE is_function() {
143
+ H8::Scope scope(h8);
144
+ return value()->IsFunction();
145
+ }
146
+
147
+ VALUE is_undefined() {
148
+ H8::Scope scope(h8);
149
+ return value()->IsUndefined() ? Qtrue : Qfalse;
150
+ }
151
+
152
+ VALUE call(VALUE args) const {
153
+ v8::HandleScope scope(h8->getIsolate());
154
+ return apply(h8->getContext()->Global(), args);
155
+ }
156
+
157
+ VALUE apply(VALUE self, VALUE args) const {
158
+ v8::HandleScope scope(h8->getIsolate());
159
+ return apply(h8->gateObject(self), args);
160
+ }
161
+
162
+ VALUE apply(Local<Value> self, VALUE args) const {
163
+ H8::Scope scope(h8);
164
+ long count = RARRAY_LEN(args);
165
+ Local<Value> *js_args = new Local<Value>[count];
166
+ for (int i = 0; i < count; i++) {
167
+ js_args[i] = h8->to_js(rb_ary_entry(args, i));
168
+ }
169
+ Local<Value> result = object()->CallAsFunction(self, count, js_args);
170
+ delete[] js_args;
171
+ return h8->to_ruby(result);
172
+ }
173
+
174
+ virtual Local<Value> value() const {
175
+ return Local<Value>::New(h8->getIsolate(), persistent_value);
176
+ }
177
+
178
+ virtual Isolate* isolate() {
179
+ return h8->getIsolate();
180
+ }
181
+
182
+ ~JsGate() {
183
+ persistent_value.Reset();
184
+ }
185
+
186
+ private:
187
+ friend void rvalue_mark(void* ptr);
188
+ friend class H8;
189
+
190
+ H8 *h8=0;
191
+ Persistent<Value> persistent_value;
192
+ };
193
+
194
+ }
195
+
196
+ #endif