h8 0.0.1 → 0.0.2

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