h8 0.0.2 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +8 -0
- data/README.md +55 -2
- data/ext/h8/JsCatcher.cpp +24 -0
- data/ext/h8/JsCatcher.h +29 -0
- data/ext/h8/allocated_resource.h +37 -0
- data/ext/h8/chain.h +191 -0
- data/ext/h8/extconf.rb +48 -30
- data/ext/h8/h8.cpp +100 -8
- data/ext/h8/h8.h +109 -42
- data/ext/h8/js_gate.cpp +18 -0
- data/ext/h8/js_gate.h +70 -31
- data/ext/h8/main.cpp +69 -25
- data/ext/h8/object_wrap.h +1 -1
- data/ext/h8/ruby_gate.cpp +117 -0
- data/ext/h8/ruby_gate.h +118 -0
- data/hybrid8.gemspec +3 -1
- data/lib/h8.rb +61 -2
- data/lib/h8/context.rb +38 -5
- data/lib/h8/value.rb +104 -19
- data/lib/h8/version.rb +1 -1
- data/spec/context_spec.rb +42 -4
- data/spec/js_gate_spec.rb +95 -16
- data/spec/ruby_gate_spec.rb +159 -0
- data/spec/spec_helper.rb +31 -1
- metadata +15 -4
- data/ext/h8/ruby_wrap.h +0 -32
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a72940e0db998af017aa93553d91b90eefe2662c
|
4
|
+
data.tar.gz: b2d87d6c19c23c983cdc00b60bf874d921133f29
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 713eedbe21d86dcaeac35d3d3d052af41bb36fc9ff434c096fa1c15f95299194c1fce45945150ac8f8ad77fb8732fa2acbbe7e6c9a4d37aa6fca82e3cf075387
|
7
|
+
data.tar.gz: d0ff9c201ae06798b57d1c80bcb471f72a5bc29b8e4e3404b33df1ec689422472efc2e547836df50846f955d6aca441e2ab81488853d3e26af25f63ab85dec15
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
# Hybrid8, aka H8
|
2
2
|
|
3
|
-
_Warning_ this gem
|
4
|
-
|
3
|
+
_Warning_ this gem functionality is almost complete but it lacks of testing and some features
|
4
|
+
(see below). This is a working (or better say, test passing) alpha. Beta suitable versions
|
5
|
+
will start from 0.1.*.
|
5
6
|
|
6
7
|
Therubyracer gem alternative to effectively work with ruby 2.1+ in multithreaded environment in an
|
7
8
|
effective and GC-safe way. Should be out of the box replacement for most scenarios.
|
@@ -16,6 +17,29 @@ javascript context
|
|
16
17
|
while there are ruby objects referencing it. It also means that once created H8::Context will not
|
17
18
|
be reclaimed as long as there is at least one active wrapped object returned from the script.
|
18
19
|
|
20
|
+
- you can pass ruby objects from ruby code called from javascript back to the ruby code intact.
|
21
|
+
Ruby objects are automatically wrapped in js code and unwrapped in ruby code (you might need to
|
22
|
+
call #to_ruby)
|
23
|
+
|
24
|
+
- Uncaught ruby exceptions are thrown as javascript exceptions in javascript code. The same,
|
25
|
+
uncaught javascript exceptions raise ruby error in ruby code.
|
26
|
+
|
27
|
+
## Main difference from therubyracer/features not ready
|
28
|
+
|
29
|
+
*This version is not (yet?) thread safe*. For the sake of effectiveness, do not access same
|
30
|
+
H8::Context and its returned values from concurrent threads. Use Mutexes!
|
31
|
+
|
32
|
+
- correct and accurate object tracking in both JS and Ruby VMs, GC aware.
|
33
|
+
|
34
|
+
- passed thru objects and exceptions in js -> ruby -> js -> ruby chains are usually kept unchanged,
|
35
|
+
e.g. wrapped in one language then unwrapped when passed to the original language
|
36
|
+
|
37
|
+
- Not Yet: source information in uncaught exception in js
|
38
|
+
|
39
|
+
- Script is executed in the calling ruby thread without unblocking it (for the sake of
|
40
|
+
effectiveness). If we would release GIL and reacquire it, it would take more time. And there is no
|
41
|
+
multithreading support yet (this one might be added soon).
|
42
|
+
|
19
43
|
## Installation
|
20
44
|
|
21
45
|
### Prerequisites
|
@@ -23,6 +47,33 @@ be reclaimed as long as there is at least one active wrapped object returned fro
|
|
23
47
|
You should have installed libv8, use latest version with v8::Isolate and v8::Locker. This version
|
24
48
|
may not find you installation, contact me if you have problems, I'll tune it up.
|
25
49
|
|
50
|
+
#### Macos (10.9 maybe+)
|
51
|
+
|
52
|
+
The working process:
|
53
|
+
|
54
|
+
install v8 from sources 3.31.77 (or try newer), then execute:
|
55
|
+
|
56
|
+
gclient update
|
57
|
+
export CXXFLAGS='-std=c++11 -stdlib=libc++ -mmacosx-version-min=10.9'
|
58
|
+
export LDFLAGS=-lc++
|
59
|
+
make native
|
60
|
+
exportexport V8_3_31_ROOT=`pwd` # or somehow else set it
|
61
|
+
|
62
|
+
Note that exporting symbols is a hack that may not be in need anymore. After that the gem should
|
63
|
+
install normally.
|
64
|
+
|
65
|
+
#### Debian and like
|
66
|
+
|
67
|
+
Install first a valid v8 version. We provide a ready package!
|
68
|
+
|
69
|
+
sudo apt-get install libv8-3.31-dev
|
70
|
+
|
71
|
+
It should install prerequisites, if not, manually install
|
72
|
+
|
73
|
+
sudo apt-get install libicu-dev
|
74
|
+
|
75
|
+
You might also need to install GMP.
|
76
|
+
|
26
77
|
### Setting up
|
27
78
|
|
28
79
|
Add this line to your application's Gemfile:
|
@@ -93,3 +144,5 @@ wheel.
|
|
93
144
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
94
145
|
4. Push to the branch (`git push origin my-new-feature`)
|
95
146
|
5. Create a new Pull Request
|
147
|
+
|
148
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
/*
|
2
|
+
* JsCatcher.cpp
|
3
|
+
*
|
4
|
+
* Created on: Dec 23, 2014
|
5
|
+
* Author: sergeych
|
6
|
+
*/
|
7
|
+
|
8
|
+
#include "JsCatcher.h"
|
9
|
+
|
10
|
+
namespace h8 {
|
11
|
+
|
12
|
+
|
13
|
+
JsCatcher::JsCatcher(H8* h8) : h8(h8), v8::TryCatch(h8->getIsolate()) {}
|
14
|
+
|
15
|
+
void JsCatcher::throwIfCaught() {
|
16
|
+
if( HasCaught() ) {
|
17
|
+
if( !CanContinue() && HasTerminated() ) {
|
18
|
+
throw JsTimeoutError(h8);
|
19
|
+
}
|
20
|
+
throw JsError(h8, Message(), Exception());
|
21
|
+
}
|
22
|
+
}
|
23
|
+
|
24
|
+
} /* namespace h8 */
|
data/ext/h8/JsCatcher.h
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
/*
|
2
|
+
* JsCatcher.h
|
3
|
+
*
|
4
|
+
* Created on: Dec 23, 2014
|
5
|
+
* Author: sergeych
|
6
|
+
*/
|
7
|
+
|
8
|
+
#ifndef JSCATCHER_H_
|
9
|
+
#define JSCATCHER_H_
|
10
|
+
|
11
|
+
#include <include/v8.h>
|
12
|
+
#include "h8.h"
|
13
|
+
|
14
|
+
namespace h8 {
|
15
|
+
|
16
|
+
class H8;
|
17
|
+
|
18
|
+
class JsCatcher : public v8::TryCatch {
|
19
|
+
public:
|
20
|
+
JsCatcher(H8* h8);
|
21
|
+
|
22
|
+
void throwIfCaught();
|
23
|
+
private:
|
24
|
+
H8* h8;
|
25
|
+
};
|
26
|
+
|
27
|
+
} /* namespace h8 */
|
28
|
+
|
29
|
+
#endif /* JSCATCHER_H_ */
|
@@ -0,0 +1,37 @@
|
|
1
|
+
#ifndef __allocated_resource
|
2
|
+
#define __allocated_resource
|
3
|
+
|
4
|
+
#include "chain.h"
|
5
|
+
|
6
|
+
namespace h8 {
|
7
|
+
|
8
|
+
/**
|
9
|
+
* Resource that allocates anything that is tied to Isolate and should be
|
10
|
+
* freed _before_ isolate is destroyed.
|
11
|
+
*
|
12
|
+
* (1) derive any of such your resources from it
|
13
|
+
* (2) register them in the parent H8 (h8->add_resource())
|
14
|
+
* (3) free() resource in free(), then call unlink()/super
|
15
|
+
* (4) free all resources in the destructor if any.
|
16
|
+
*/
|
17
|
+
class AllocatedResource : public chain::link {
|
18
|
+
public:
|
19
|
+
/**
|
20
|
+
* If your resource references any ruby object, be sure to mark them
|
21
|
+
* all overriding this method!
|
22
|
+
*/
|
23
|
+
virtual void rb_mark_gc() {};
|
24
|
+
|
25
|
+
virtual void free() {
|
26
|
+
unlink();
|
27
|
+
};
|
28
|
+
|
29
|
+
/* Default implementation does nothing but call unlink throgh parent
|
30
|
+
* destructor
|
31
|
+
*/
|
32
|
+
virtual ~AllocatedResource() {};
|
33
|
+
};
|
34
|
+
|
35
|
+
}
|
36
|
+
|
37
|
+
#endif
|
data/ext/h8/chain.h
ADDED
@@ -0,0 +1,191 @@
|
|
1
|
+
#ifndef __chain_h
|
2
|
+
#define __chain_h
|
3
|
+
|
4
|
+
#include <assert.h>
|
5
|
+
|
6
|
+
/**
|
7
|
+
* Copyright (C) by Sergey S. Chernov, iCodici S.n.C
|
8
|
+
*
|
9
|
+
* Free software under MIT license.
|
10
|
+
*
|
11
|
+
* Extremely fast minimalistic double linked list.
|
12
|
+
*
|
13
|
+
* List item derives from chain::link. List item can not be inserted in
|
14
|
+
* more than one list. Linking chain::link into other list removes it
|
15
|
+
* from any other list. Unlinked chain::link links to self.
|
16
|
+
*
|
17
|
+
* Not thread safe! Inserting and removing in any position of the list
|
18
|
+
* is O(1) operation, iterating is O(N) operation.
|
19
|
+
*
|
20
|
+
* Typical usages:
|
21
|
+
*
|
22
|
+
* minimal:
|
23
|
+
* Use any chain::link instance as a root of you list. You can do
|
24
|
+
* everything with it without creating chain instance
|
25
|
+
*
|
26
|
+
* typical:
|
27
|
+
* create chain instance and feed it with chain::instance derivatives.
|
28
|
+
* does the same as above but provide more commonly used unterface
|
29
|
+
* (actually it is a wrapped chain::link item)
|
30
|
+
*/
|
31
|
+
class chain {
|
32
|
+
public:
|
33
|
+
class link;
|
34
|
+
|
35
|
+
/**
|
36
|
+
* Iterator that can iterate over either chain or even chain::link
|
37
|
+
* item (in which case it iterates from a given link and traverses
|
38
|
+
* all the list
|
39
|
+
*/
|
40
|
+
class iterator {
|
41
|
+
public:
|
42
|
+
iterator(link *start) :
|
43
|
+
current(start) {
|
44
|
+
}
|
45
|
+
|
46
|
+
bool operator !=(const iterator& other) const {
|
47
|
+
return current != other.current;
|
48
|
+
}
|
49
|
+
|
50
|
+
const iterator& operator++() {
|
51
|
+
current = current->next;
|
52
|
+
return *this;
|
53
|
+
}
|
54
|
+
|
55
|
+
link* operator*() const {
|
56
|
+
return current;
|
57
|
+
}
|
58
|
+
|
59
|
+
private:
|
60
|
+
link *current;
|
61
|
+
};
|
62
|
+
|
63
|
+
/**
|
64
|
+
* chain list item. Can be used as a list itself: can be iterated, can insert items,
|
65
|
+
* can provide head and tail items and any of its linked items can be easily removed
|
66
|
+
*/
|
67
|
+
class link {
|
68
|
+
public:
|
69
|
+
/// Create empty list item (which is locked to itself)
|
70
|
+
link() {
|
71
|
+
next = prev = this;
|
72
|
+
}
|
73
|
+
|
74
|
+
/// Leave whatever chain it is in (or do nothing)
|
75
|
+
void unlink() {
|
76
|
+
next->prev = prev;
|
77
|
+
prev->next = next;
|
78
|
+
next = prev = this;
|
79
|
+
}
|
80
|
+
|
81
|
+
// Insert in the chain after the specified item
|
82
|
+
void link_after(link *left) {
|
83
|
+
unlink();
|
84
|
+
next = left->next;
|
85
|
+
left->next = this;
|
86
|
+
prev = left;
|
87
|
+
next->prev = this;
|
88
|
+
}
|
89
|
+
|
90
|
+
/**
|
91
|
+
* @return true if this is the only item in the list (this is not connected
|
92
|
+
* to any other item)
|
93
|
+
*/
|
94
|
+
bool is_disconnected() const {
|
95
|
+
return next == prev && next == this;
|
96
|
+
}
|
97
|
+
|
98
|
+
/**
|
99
|
+
* Get the next item in the list.
|
100
|
+
*
|
101
|
+
* @return this if the list is empty
|
102
|
+
*/
|
103
|
+
template<class T>
|
104
|
+
T* get_next() {
|
105
|
+
return (T*) (next);
|
106
|
+
}
|
107
|
+
|
108
|
+
/**
|
109
|
+
* Get the previous item in the list.
|
110
|
+
*
|
111
|
+
* @return this if the list is empty
|
112
|
+
*/
|
113
|
+
template<class T>
|
114
|
+
T* get_prev() {
|
115
|
+
return (T*) (prev);
|
116
|
+
}
|
117
|
+
|
118
|
+
~link() {
|
119
|
+
unlink();
|
120
|
+
}
|
121
|
+
|
122
|
+
chain::iterator begin() const {
|
123
|
+
return iterator(next);
|
124
|
+
}
|
125
|
+
|
126
|
+
chain::iterator end() const {
|
127
|
+
return iterator(const_cast<link*>(this));
|
128
|
+
}
|
129
|
+
|
130
|
+
private:
|
131
|
+
link *next, *prev;
|
132
|
+
friend class chain;
|
133
|
+
};
|
134
|
+
|
135
|
+
iterator begin() const {
|
136
|
+
return root.begin();
|
137
|
+
}
|
138
|
+
|
139
|
+
iterator end() const {
|
140
|
+
return root.end();
|
141
|
+
}
|
142
|
+
|
143
|
+
/**
|
144
|
+
* Add item to the tail of the list
|
145
|
+
*/
|
146
|
+
void push(link* item) {
|
147
|
+
item->link_after(root.prev);
|
148
|
+
}
|
149
|
+
|
150
|
+
void push_first(link* item) {
|
151
|
+
item->link_after(&root);
|
152
|
+
}
|
153
|
+
|
154
|
+
template<class T>
|
155
|
+
T* peek_first() {
|
156
|
+
return (T*) root.next;
|
157
|
+
}
|
158
|
+
|
159
|
+
template<class T>
|
160
|
+
T* peek_last() {
|
161
|
+
return (T*) root.prev;
|
162
|
+
}
|
163
|
+
|
164
|
+
/**
|
165
|
+
* Convenience method: removes head item (if any) and returns it.
|
166
|
+
* Actually, you can get any item from the list (using peek_head(), peek_tail() or iterating
|
167
|
+
* the list) and then remove it from the list (@see chain::link#unlink()) - it is effective and
|
168
|
+
* simple way.
|
169
|
+
*
|
170
|
+
* @return detached head item or NULL
|
171
|
+
*/
|
172
|
+
template<class T>
|
173
|
+
T* pop() {
|
174
|
+
if (root.is_disconnected())
|
175
|
+
return 0;
|
176
|
+
link *first = root.next;
|
177
|
+
first->unlink();
|
178
|
+
return (T*) first;
|
179
|
+
}
|
180
|
+
|
181
|
+
bool is_empty() const {
|
182
|
+
return root.is_disconnected();
|
183
|
+
}
|
184
|
+
|
185
|
+
private:
|
186
|
+
// Root item default constructor is enough
|
187
|
+
link root;
|
188
|
+
|
189
|
+
};
|
190
|
+
|
191
|
+
#endif
|
data/ext/h8/extconf.rb
CHANGED
@@ -16,44 +16,63 @@
|
|
16
16
|
|
17
17
|
require "mkmf"
|
18
18
|
|
19
|
+
cxx11flag = " --std=c++11"
|
20
|
+
|
21
|
+
$CXXFLAGS = CONFIG["CXXFLAGS"] unless defined?($CXXFLAGS)
|
22
|
+
$CXXFLAGS += cxx11flag unless $CXXFLAGS.include?(cxx11flag)
|
19
23
|
|
20
24
|
abort 'missing malloc()' unless have_func 'malloc'
|
21
25
|
abort 'missing free()' unless have_func 'free'
|
22
26
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
27
|
+
begin
|
28
|
+
|
29
|
+
# Give it a name
|
30
|
+
extension_name = 'h8'
|
31
|
+
|
32
|
+
chk_headers = ['include/v8.h']
|
33
|
+
chk_libs = %w(v8_base v8_libbase v8_libplatform v8_snapshot icudata icui18n icuuc)
|
34
|
+
|
35
|
+
case RbConfig::CONFIG['target_os']
|
36
|
+
when /darwin/
|
37
|
+
v8_path = ENV['V8_3_31_ROOT'] or raise "Please give me export V8_3_31_ROOT=..."
|
38
|
+
# dir_config('v8', '/Users/sergeych/dev/v8', '/Users/sergeych/dev/v8/out/native')
|
39
|
+
dir_config('v8', v8_path, v8_path+'/out/native')
|
40
|
+
CONFIG['CXXFLAGS'] += ' --std=c++11'
|
41
|
+
else
|
42
|
+
# example linux package https://github.com/porzione/v8-git-debian
|
43
|
+
dir_config('v8', '/usr/include/libv8-3.31', '/usr/lib/libv8-3.31')
|
44
|
+
# force static, but system icu
|
45
|
+
$LOCAL_LIBS = chk_libs.reject { |l| l.match /^icu/ }.map { |l| "-l#{l}" }.join(" ")
|
46
|
+
end
|
47
|
+
|
48
|
+
dir_config(extension_name)
|
49
|
+
|
50
|
+
chk_headers.each do |h|
|
51
|
+
unless have_header(h)
|
52
|
+
raise "can't find v8 header '#{h}', install libv8 3.31+ first"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
chk_libs.each do |lib|
|
57
|
+
unless have_library(lib)
|
58
|
+
raise "can't find v8 lib '#{lib}'"
|
59
|
+
end
|
60
|
+
end
|
42
61
|
|
43
62
|
# This test is actually due to a Clang 3.3 shortcoming, included in OS X 10.9,
|
44
63
|
# fixed in Clang 3.4:
|
45
64
|
# http://llvm.org/releases/3.4/tools/clang/docs/ReleaseNotes.html#new-compiler-flags
|
46
|
-
if try_compile('', '-O6')
|
47
|
-
|
48
|
-
else
|
49
|
-
|
50
|
-
end
|
51
|
-
|
52
|
-
CONFIG['CXXFLAGS'] += " --std=c++11"
|
65
|
+
# if try_compile('', '-O6')
|
66
|
+
# $CFLAGS += ' -Wall -W -O6 -g'
|
67
|
+
# else
|
68
|
+
# $CFLAGS += ' -Wall -W -O3 -g'
|
69
|
+
# end
|
53
70
|
|
54
|
-
|
71
|
+
# create_makefile('h8/h8')
|
55
72
|
create_makefile(extension_name)
|
56
|
-
|
73
|
+
rescue
|
74
|
+
$stderr.puts "*********************************************************************"
|
75
|
+
$stderr.puts "\n#{$!}\n\n"
|
57
76
|
$stderr.puts "*********************************************************************"
|
58
77
|
$stderr.puts "Your compiler was unable to link to all necessary libraries"
|
59
78
|
$stderr.puts "Please install all prerequisites first"
|
@@ -62,7 +81,7 @@ else
|
|
62
81
|
raise "Unable to build, correct above errors and rerun"
|
63
82
|
end
|
64
83
|
|
65
|
-
# LIBV8_COMPATIBILITY = '~> 3.
|
84
|
+
# LIBV8_COMPATIBILITY = '~> 3.31'
|
66
85
|
#
|
67
86
|
# begin
|
68
87
|
# require 'rubygems'
|
@@ -77,5 +96,4 @@ end
|
|
77
96
|
#
|
78
97
|
# Libv8.configure_makefile
|
79
98
|
|
80
|
-
create_makefile('h8/h8')
|
81
99
|
|