h8 0.4.8 → 0.4.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +46 -50
- data/Rakefile +22 -0
- data/benchmark/README.md +8 -0
- data/benchmark/km.rb +56 -0
- data/benchmark/knightsmove.rb +10 -45
- data/benchmark/tools.rb +1 -12
- data/ext/h8/h8.cpp +9 -11
- data/ext/h8/h8.h +2 -1
- data/ext/h8/js_catcher.h +2 -0
- data/ext/h8/main.cpp +2 -1
- data/ext/h8/ruby_gate.cpp +38 -12
- data/ext/h8/ruby_gate.h +15 -4
- data/hybrid8.gemspec +13 -12
- data/lib/h8/context.rb +13 -0
- data/lib/h8/value.rb +1 -1
- data/lib/h8/version.rb +1 -1
- data/spec/context_spec.rb +7 -0
- data/spec/ruby_gate_spec.rb +26 -1
- metadata +24 -23
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1f5c5adc5319ca033ede79775cb4c46f72afff6c
|
4
|
+
data.tar.gz: fddfbb454e2e80d137012fe3c1b62a58712cded2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: aa01ac0f07a92865c81034f4d2d983b85c7a2de368f146556568dbf2d775ed15e44be34bd83f84f1e1a5bf904728a004833858f9171355859a2a6ba9e72d2d11
|
7
|
+
data.tar.gz: 88e68a141d22a7a0f4eeab6e3d84af9d13e8d38408ae630a2e0d1b8dbd15b4f05697eec0fd30e3edfc4486a4a2bab28e02134a0194589338cf8239b92d989688
|
data/README.md
CHANGED
@@ -1,59 +1,57 @@
|
|
1
1
|
# Hybrid8, aka H8
|
2
2
|
|
3
|
-
_Warning_ this gem is
|
4
|
-
yet production
|
3
|
+
_Warning_ this gem is in public beta at the moment - beta testers are welcome! It means it's not
|
4
|
+
yet stable enough for running in production - we haven't tried it yet ourselves.
|
5
5
|
|
6
|
-
_Current implementation is somewhat slower than it could
|
7
|
-
and javascript code in different H8::Context instances run in parallel
|
8
|
-
|
9
|
-
|
6
|
+
_Current implementation is somewhat slower than it could be, with the price of letting ruby threads
|
7
|
+
and javascript code in different H8::Context instances run in parallel on multicore hardware_.
|
8
|
+
Please let me know if it significantly degrades performance on ruby-to-js calls and back. In other words,
|
9
|
+
with 8-core cpu you can create 8 H8::Contexts and run them truly parallel.
|
10
10
|
|
11
11
|
This gem was intended to replace therubyracer for many reasons:
|
12
12
|
|
13
|
-
* therubyracer had critical bugs
|
14
|
-
under load it produces numerous frequent random crashes.
|
13
|
+
* therubyracer has/had critical bugs that are not being fixed for a long time. As the result it produces numerous and frequent random crashes under load.
|
15
14
|
|
16
|
-
* therubyracer still uses antique version of V8
|
15
|
+
* therubyracer still uses antique version of V8. H8 uses the latest 3.31 branch, which has many
|
17
16
|
improvements, harmony support and so on.
|
18
17
|
|
19
|
-
* H8 is designed to provide tight
|
20
|
-
passing the same objects
|
21
|
-
and changing
|
18
|
+
* H8 is designed to provide tight integration between two allocation systems and object models,
|
19
|
+
passing the same objects within different systems, wrapping and unwrapping them rather than copying
|
20
|
+
and changing.
|
22
21
|
|
23
|
-
* We hope
|
24
|
-
|
22
|
+
* We hope to provide faster execution with a tradeoff in non-significant changes. And let the modern branch of V8
|
23
|
+
help us with it ;)
|
25
24
|
|
26
25
|
Special features:
|
27
26
|
|
28
|
-
- care
|
29
|
-
|
30
|
-
javascript context
|
27
|
+
- H8 takes care of wrapped Ruby objects lifespan. Objects live until either ruby or javascript context
|
28
|
+
references them. GC will not reclaim memory occupied by ruby object while it is used by javascript context.
|
31
29
|
|
32
|
-
- care
|
33
|
-
while there are ruby objects referencing
|
34
|
-
be reclaimed as long as there is at least one active wrapped object returned from
|
30
|
+
- H8 takes care of wrapped Javascript objects lifetime in the same way. Referenced JS items will not be recycled
|
31
|
+
while there are ruby objects referencing them. It also means that once created H8::Context will not
|
32
|
+
be reclaimed as long as there is at least one active wrapped object returned from a script.
|
35
33
|
|
36
|
-
-
|
34
|
+
- You can pass ruby objects from ruby code called from javascript back to the ruby code intact.
|
37
35
|
Ruby objects are automatically wrapped in js code and unwrapped in ruby code (you might need to
|
38
|
-
call #to_ruby)
|
36
|
+
call #to_ruby).
|
39
37
|
|
40
|
-
- Uncaught ruby exceptions are thrown as javascript exceptions in javascript code.
|
41
|
-
uncaught javascript exceptions raise ruby
|
38
|
+
- Uncaught ruby exceptions are thrown as javascript exceptions in javascript code. Likewise,
|
39
|
+
uncaught javascript exceptions raise ruby errors in ruby code.
|
42
40
|
|
43
|
-
- Integrated CoffeeScript support
|
41
|
+
- Integrated CoffeeScript support.
|
44
42
|
|
45
|
-
- H8 is thread safe. It releases gvl lock while executing ruby code and unlocks
|
46
|
-
calling ruby code thus
|
43
|
+
- H8 is thread safe. It releases gvl lock while executing ruby code and unlocks V8 isolate when
|
44
|
+
calling ruby code thus maximally allows the possibility of parallel execution of ruby and javascript code
|
47
45
|
in separate threads.
|
48
46
|
|
49
|
-
Due to
|
50
|
-
execute javascript code in it)
|
51
|
-
Still, having multiple H8::Context in different ruby threads
|
47
|
+
Due to V8 and ruby MRI limitations, only one ruby thread can access any given H8::Context (e.g.
|
48
|
+
execute javascript code in it) and, as usual, all ruby threads are locked by a single mutex (gvl).
|
49
|
+
Still, having multiple H8::Context in different ruby threads lets you run many java/coffee scripts in
|
52
50
|
parallel with a single ruby thread - what you can not have with pure MRI ruby.
|
53
51
|
|
54
|
-
## Main
|
52
|
+
## Main differences from therubyracer and future features
|
55
53
|
|
56
|
-
- lambda/proc passed as var to the context **does not
|
54
|
+
- lambda/proc passed as var to the context **does not receive first (this) argument
|
57
55
|
automatically!**
|
58
56
|
|
59
57
|
E.g. rubyracer code
|
@@ -64,20 +62,19 @@ Becomes
|
|
64
62
|
|
65
63
|
cxt[:fn] = -> (a, b) { a + b }
|
66
64
|
|
67
|
-
|
65
|
+
Looks nicer, doesn't it? And if you really need `this` in the callable, just mention it in
|
68
66
|
the call:
|
69
67
|
|
70
68
|
cxt[:fn] = -> (this, a, b) { a + b + this.offset }
|
71
69
|
cxt.eval 'fn(this, 10, 20)'
|
72
70
|
|
73
|
-
This, again, is done
|
74
|
-
procedure which is always performed in rubyracer - no matter
|
75
|
-
spend your resources only when it worth extra processing. From my experience, it is a rare situation
|
76
|
-
when such a lambda needs javascript's this - but
|
77
|
-
you need ;)
|
71
|
+
This, again, is done to improve execution speed. Always wrapping `this` to pass it to ruby is a costly
|
72
|
+
procedure which is always performed in rubyracer - no matter if it is really needed or not. In H8 you can
|
73
|
+
spend your resources only when it is worth extra processing. From my experience, it is a rare situation
|
74
|
+
when such a lambda needs javascript's this - but you still have the possibility if you need it ;)
|
78
75
|
|
79
|
-
- there is no 'Context object initialization'
|
80
|
-
likely
|
76
|
+
- there is no 'Context object initialization'. It does not work well in rubyracer, so it's not
|
77
|
+
likely that it's widely used. We can add it later, though - if you need it, add an issue.
|
81
78
|
|
82
79
|
|
83
80
|
|
@@ -85,14 +82,13 @@ likely used widely. We can add it later, though - if you need it, add an issue.
|
|
85
82
|
|
86
83
|
### Prerequisites
|
87
84
|
|
88
|
-
You should have installed
|
89
|
-
may not find you installation, contact me if you have problems, I'll tune it up.
|
85
|
+
You should have libv8 installed, use the latest version with v8::Isolate and v8::Locker. It might not find your installation, contact me if you have problems - I'll tune it up.
|
90
86
|
|
91
87
|
#### Macos (10.9 maybe+)
|
92
88
|
|
93
89
|
The working process:
|
94
90
|
|
95
|
-
install
|
91
|
+
install V8 from sources 3.31.77 (or try newer), then execute:
|
96
92
|
|
97
93
|
gclient update
|
98
94
|
export CXXFLAGS='-std=c++11 -stdlib=libc++ -mmacosx-version-min=10.9'
|
@@ -100,16 +96,16 @@ install v8 from sources 3.31.77 (or try newer), then execute:
|
|
100
96
|
make native
|
101
97
|
export V8_3_31_ROOT=`pwd` # or somehow else set it
|
102
98
|
|
103
|
-
Note that exporting symbols is a hack that may not be
|
99
|
+
Note that exporting symbols is a hack that may not be needed anymore. After that the gem should
|
104
100
|
install normally.
|
105
101
|
|
106
102
|
#### Debian and like
|
107
103
|
|
108
|
-
|
104
|
+
First install a valid v8 version. We provide a ready-to-use package!
|
109
105
|
|
110
106
|
sudo apt-get install libv8-3.31-dev
|
111
107
|
|
112
|
-
Usually it
|
108
|
+
Usually it's all you need. In rare cases you might also need to install GMP.
|
113
109
|
|
114
110
|
### Setting up
|
115
111
|
|
@@ -127,14 +123,14 @@ Or install it yourself as:
|
|
127
123
|
|
128
124
|
## Usage
|
129
125
|
|
130
|
-
Is generally like therubyracer gem. Create context, set variables, run scripts.
|
126
|
+
Is's generally like therubyracer gem. Create context, set variables, run scripts.
|
131
127
|
|
132
128
|
require 'h8'
|
133
129
|
|
134
130
|
res = H8::Context.eval "({foo: 'hello', bar: 'world'});"
|
135
131
|
puts "#{res.foo} #{res.bar}"
|
136
132
|
|
137
|
-
|
133
|
+
Another way to access attributes:
|
138
134
|
|
139
135
|
puts res['foo']
|
140
136
|
|
@@ -153,7 +149,7 @@ You can return function and call it from ruby:
|
|
153
149
|
fun = cxt.eval "(function pi_n(n) { return pi * n; })"
|
154
150
|
p fun(2)
|
155
151
|
|
156
|
-
|
152
|
+
Likewise, you can return objects and call it's member functions. If a member is a function,
|
157
153
|
it will be called with given arguments:
|
158
154
|
|
159
155
|
res = H8::Context.eval <<-End
|
@@ -173,7 +169,7 @@ it will be called with given arguments:
|
|
173
169
|
|
174
170
|
## Contributing
|
175
171
|
|
176
|
-
Note that at this early point of development
|
172
|
+
Note that at this early point of development it's better that you talk to me first to not to reinvent the
|
177
173
|
wheel.
|
178
174
|
|
179
175
|
1. Fork it ( https://github.com/[my-github-username]/hybrid8/fork )
|
data/Rakefile
CHANGED
@@ -1,2 +1,24 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
|
+
require "rake/extensiontask"
|
3
|
+
require 'rubygems/package_task'
|
4
|
+
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
|
13
|
+
$gemspec = Bundler::GemHelper.gemspec
|
14
|
+
|
15
|
+
Gem::PackageTask.new($gemspec) do |pkg|
|
16
|
+
end
|
17
|
+
|
18
|
+
Rake::ExtensionTask.new "h8", $gemspec do |ext|
|
19
|
+
ext.lib_dir = "lib/h8"
|
20
|
+
ext.source_pattern = "*.{c,cpp,js}"
|
21
|
+
ext.gem_spec = $gemspec
|
22
|
+
end
|
23
|
+
|
2
24
|
|
data/benchmark/README.md
CHANGED
@@ -17,3 +17,11 @@ Moreover, if you run optimized C++ version, you'll have:
|
|
17
17
|
which is, in turn, *1.76 times slower than coffeescript!*
|
18
18
|
|
19
19
|
The results are very inspiring, as for me.
|
20
|
+
|
21
|
+
## Some ruby platform comarisons
|
22
|
+
|
23
|
+
ruby 2.1.5 : 13.767739 scaled: 68.838695
|
24
|
+
ruby 2.2.0 : 12.859456 scaled: 64.29728
|
25
|
+
rbx 2.1.0 : 9.35995 scaled: 46.799749999999996
|
26
|
+
|
27
|
+
Rbx, as expected, is way ahead of MRI but is still too bad to be compared to coffeescript/v8(h8)
|
data/benchmark/km.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require './tools'
|
2
|
+
|
3
|
+
class Solver
|
4
|
+
|
5
|
+
def initialize rank
|
6
|
+
@n, @nn = rank, rank*rank
|
7
|
+
@n2 = @n+@n
|
8
|
+
@desk = []
|
9
|
+
@n.times { @desk << [0] * @n }
|
10
|
+
@depth = 0
|
11
|
+
solve 0, 0
|
12
|
+
end
|
13
|
+
|
14
|
+
def solve r, c
|
15
|
+
@desk[r][c] = (@depth+=1)
|
16
|
+
return true if @depth >= @nn
|
17
|
+
moves(r, c) { |r1, c1|
|
18
|
+
return true if solve(r1, c1)
|
19
|
+
}
|
20
|
+
@desk[r][c] = 0
|
21
|
+
@depth -= 1
|
22
|
+
false
|
23
|
+
end
|
24
|
+
|
25
|
+
@@shifts = [[-2, +1], [-1, +2], [+1, +2], [+2, +1], [+2, -1], [+1, -2], [-1, -2], [-2, -1]]
|
26
|
+
|
27
|
+
def moves r, c
|
28
|
+
@@shifts.each { |sr, sc|
|
29
|
+
r1 = r + sr
|
30
|
+
if r1 >= 0 && r1 < @n
|
31
|
+
c1 = c + sc
|
32
|
+
if c1 >= 0 && c1 < @n
|
33
|
+
yield r1, c1 if @desk[r1][c1] == 0
|
34
|
+
end
|
35
|
+
end
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_s
|
40
|
+
res = []
|
41
|
+
@n.times do |row|
|
42
|
+
res << @n.times.map { |col|
|
43
|
+
d = @desk[row][col]
|
44
|
+
d == 0 ? ' .' : ("%3d" % d)
|
45
|
+
}.join('')
|
46
|
+
end
|
47
|
+
res.join "\n"
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
if __FILE__ == $0
|
53
|
+
timing "#{RUBY_ENGINE} #{RUBY_VERSION}", 1, 5 do
|
54
|
+
Solver.new 7
|
55
|
+
end
|
56
|
+
end
|
data/benchmark/knightsmove.rb
CHANGED
@@ -1,52 +1,17 @@
|
|
1
1
|
require './tools'
|
2
|
+
require './km'
|
2
3
|
|
3
|
-
class Solver
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
solve 0, 0
|
12
|
-
end
|
13
|
-
|
14
|
-
def solve r, c
|
15
|
-
@desk[r][c] = (@depth+=1)
|
16
|
-
return true if @depth >= @nn
|
17
|
-
moves(r, c) { |r1, c1|
|
18
|
-
return true if solve(r1, c1)
|
19
|
-
}
|
20
|
-
@desk[r][c] = 0
|
21
|
-
@depth -= 1
|
22
|
-
false
|
23
|
-
end
|
24
|
-
|
25
|
-
@@shifts = [[-2, +1], [-1, +2], [+1, +2], [+2, +1], [+2, -1], [+1, -2], [-1, -2], [-2, -1]]
|
26
|
-
|
27
|
-
def moves r, c
|
28
|
-
@@shifts.each { |sr, sc|
|
29
|
-
r1 = r + sr
|
30
|
-
if r1 >= 0 && r1 < @n
|
31
|
-
c1 = c + sc
|
32
|
-
if c1 >= 0 && c1 < @n
|
33
|
-
yield r1, c1 if @desk[r1][c1] == 0
|
34
|
-
end
|
35
|
-
end
|
36
|
-
}
|
37
|
-
end
|
38
|
-
|
39
|
-
def to_s
|
40
|
-
res = []
|
41
|
-
@n.times do |row|
|
42
|
-
res << @n.times.map { |col|
|
43
|
-
d = @desk[row][col]
|
44
|
-
d == 0 ? ' .' : ("%3d" % d)
|
45
|
-
}.join('')
|
46
|
-
end
|
47
|
-
res.join "\n"
|
48
|
-
end
|
5
|
+
def js_context
|
6
|
+
cxt = H8::Context.new
|
7
|
+
cxt[:print] = -> (*args) { puts args.join(' ') }
|
8
|
+
cxt[:console] = Console
|
9
|
+
cxt
|
10
|
+
end
|
49
11
|
|
12
|
+
def coffee script_file_name
|
13
|
+
@base ||= File.dirname(File.expand_path(__FILE__))
|
14
|
+
H8::Coffee.compile open("#{@base}/#{script_file_name}.coffee").read
|
50
15
|
end
|
51
16
|
|
52
17
|
cs = js_context.eval coffee(:knightsmove)
|
data/benchmark/tools.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'h8'
|
1
|
+
# require 'h8'
|
2
2
|
|
3
3
|
def timing name, repetitions = 1, scale = 1
|
4
4
|
s = Time.now
|
@@ -20,14 +20,3 @@ class Console
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
-
def js_context
|
24
|
-
cxt = H8::Context.new
|
25
|
-
cxt[:print] = -> (*args) { puts args.join(' ') }
|
26
|
-
cxt[:console] = Console
|
27
|
-
cxt
|
28
|
-
end
|
29
|
-
|
30
|
-
def coffee script_file_name
|
31
|
-
@base ||= File.dirname(File.expand_path(__FILE__))
|
32
|
-
H8::Coffee.compile open("#{@base}/#{script_file_name}.coffee").read
|
33
|
-
end
|
data/ext/h8/h8.cpp
CHANGED
@@ -47,6 +47,13 @@ void h8::JsTimeoutError::raise() const {
|
|
47
47
|
rb_raise(js_timeout_exception, "timeout expired");
|
48
48
|
}
|
49
49
|
|
50
|
+
void h8::H8::SetupGateTemplate(const Local<ObjectTemplate>& templ) {
|
51
|
+
templ->SetInternalFieldCount(2);
|
52
|
+
templ->SetCallAsFunctionHandler(&RubyGate::ObjectCallback);
|
53
|
+
templ->SetNamedPropertyHandler(RubyGate::mapGet, RubyGate::mapSet, 0, RubyGate::mapDelete);
|
54
|
+
templ->SetIndexedPropertyHandler(RubyGate::indexGet, RubyGate::indexSet);
|
55
|
+
}
|
56
|
+
|
50
57
|
h8::H8::H8()
|
51
58
|
: self(Qnil)
|
52
59
|
{
|
@@ -66,11 +73,7 @@ h8::H8::H8()
|
|
66
73
|
ft->SetClassName(js("RubyGate"));
|
67
74
|
Local<ObjectTemplate> templ = ft->InstanceTemplate();
|
68
75
|
|
69
|
-
templ
|
70
|
-
templ->SetCallAsFunctionHandler(&RubyGate::ObjectCallback);
|
71
|
-
templ->SetNamedPropertyHandler(RubyGate::mapGet, RubyGate::mapSet);
|
72
|
-
templ->SetIndexedPropertyHandler(RubyGate::indexGet, RubyGate::indexSet);
|
73
|
-
|
76
|
+
SetupGateTemplate(templ);
|
74
77
|
v8::Handle<v8::Context> context = v8::Context::New(isolate, NULL,
|
75
78
|
global);
|
76
79
|
|
@@ -122,12 +125,7 @@ void h8::H8::gate_class(VALUE name,VALUE callable) {
|
|
122
125
|
ft->Inherit(getGateFunctionTemplate());
|
123
126
|
|
124
127
|
Local<ObjectTemplate> templ = ft->InstanceTemplate();
|
125
|
-
|
126
|
-
templ->SetInternalFieldCount(2);
|
127
|
-
templ->SetCallAsFunctionHandler(&RubyGate::ObjectCallback);
|
128
|
-
templ->SetNamedPropertyHandler(RubyGate::mapGet, RubyGate::mapSet);
|
129
|
-
templ->SetIndexedPropertyHandler(RubyGate::indexGet, RubyGate::indexSet);
|
130
|
-
|
128
|
+
SetupGateTemplate(templ);
|
131
129
|
global->Set(class_name, ft->GetFunction());
|
132
130
|
}
|
133
131
|
|
data/ext/h8/h8.h
CHANGED
@@ -19,7 +19,7 @@ extern VALUE value_class;
|
|
19
19
|
extern VALUE ruby_gate_class;
|
20
20
|
extern VALUE Rundefined;
|
21
21
|
|
22
|
-
extern ID id_is_a, id_safe_call, id_safe_proc_call;
|
22
|
+
extern ID id_is_a, id_safe_call, id_safe_proc_call, id_delete_handler;
|
23
23
|
|
24
24
|
VALUE protect_ruby(const std::function<VALUE()> &block);
|
25
25
|
|
@@ -233,6 +233,7 @@ public:
|
|
233
233
|
private:
|
234
234
|
friend VALUE h8::context_alloc(VALUE klass);
|
235
235
|
void invoke(v8::Handle<v8::Script> script, Local<Value>& result);
|
236
|
+
static void SetupGateTemplate(const Local<ObjectTemplate>& templ);
|
236
237
|
|
237
238
|
Isolate *isolate;
|
238
239
|
VALUE self;
|
data/ext/h8/js_catcher.h
CHANGED
data/ext/h8/main.cpp
CHANGED
@@ -14,7 +14,7 @@ VALUE ruby_gate_class;
|
|
14
14
|
VALUE value_class;
|
15
15
|
VALUE Rundefined;
|
16
16
|
|
17
|
-
ID id_is_a, id_safe_call, id_safe_proc_call;
|
17
|
+
ID id_is_a, id_safe_call, id_safe_proc_call, id_delete_handler;
|
18
18
|
|
19
19
|
VALUE protect_ruby(const std::function<VALUE()> &block) {
|
20
20
|
try {
|
@@ -189,6 +189,7 @@ void Init_h8(void) {
|
|
189
189
|
id_is_a = rb_intern("is_a?");
|
190
190
|
id_safe_call = rb_intern("secure_call");
|
191
191
|
id_safe_proc_call = rb_intern("safe_proc_call");
|
192
|
+
id_delete_handler = rb_intern("delete_handler");
|
192
193
|
|
193
194
|
VALUE h8 = rb_define_module("H8");
|
194
195
|
|
data/ext/h8/ruby_gate.cpp
CHANGED
@@ -92,6 +92,13 @@ void h8::RubyGate::mapSet(Local<String> name, Local<Value> value,
|
|
92
92
|
rg->setProperty(name, value, info);
|
93
93
|
}
|
94
94
|
|
95
|
+
void h8::RubyGate::mapDelete(Local<String> name,
|
96
|
+
const PropertyCallbackInfo<Boolean> &info) {
|
97
|
+
RubyGate *rg = RubyGate::unwrap(info.This());
|
98
|
+
assert(rg != 0);
|
99
|
+
rg->deleteProperty(name, info);
|
100
|
+
}
|
101
|
+
|
95
102
|
void h8::RubyGate::indexGet(uint32_t index,
|
96
103
|
const PropertyCallbackInfo<Value> &info) {
|
97
104
|
RubyGate *rg = RubyGate::unwrap(info.This());
|
@@ -128,6 +135,11 @@ VALUE RubyGate::call(VALUE args) {
|
|
128
135
|
return res;
|
129
136
|
}
|
130
137
|
|
138
|
+
VALUE RubyGate::ruby_delete_handler(VALUE args) {
|
139
|
+
VALUE method = rb_ary_pop(args);
|
140
|
+
return rb_funcall(context_class, id_delete_handler, 2, method, args);
|
141
|
+
}
|
142
|
+
|
131
143
|
VALUE RubyGate::secure_call(VALUE args) {
|
132
144
|
VALUE method = rb_ary_pop(args);
|
133
145
|
VALUE receiver = rb_ary_pop(args);
|
@@ -180,12 +192,12 @@ void h8::RubyGate::doObjectCallback(
|
|
180
192
|
rb_ary_push(rb_args, context->ruby_context());
|
181
193
|
rb_ary_push(rb_args, ruby_object);
|
182
194
|
rescued_call(rb_args, call, [&] (VALUE res) {
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
});
|
195
|
+
if( res == ruby_object )
|
196
|
+
args.GetReturnValue().Set(args.This());
|
197
|
+
else
|
198
|
+
args.GetReturnValue().Set(context->to_js(res));
|
199
|
+
});
|
200
|
+
});
|
189
201
|
}
|
190
202
|
|
191
203
|
void h8::RubyGate::getProperty(Local<String> name,
|
@@ -195,9 +207,9 @@ void h8::RubyGate::getProperty(Local<String> name,
|
|
195
207
|
rb_ary_push(rb_args, ruby_object);
|
196
208
|
rb_ary_push(rb_args, context->to_ruby(name));
|
197
209
|
rescued_call(rb_args, secure_call, [&] (VALUE res) {
|
198
|
-
|
210
|
+
if( res == ruby_object )
|
199
211
|
info.GetReturnValue().Set(info.This());
|
200
|
-
|
212
|
+
else
|
201
213
|
info.GetReturnValue().Set(context->to_js(res));
|
202
214
|
});
|
203
215
|
});
|
@@ -207,17 +219,31 @@ void h8::RubyGate::setProperty(Local<String> name, Local<Value> value,
|
|
207
219
|
const PropertyCallbackInfo<Value> &info) {
|
208
220
|
with_gvl(this, [&] {
|
209
221
|
VALUE rb_args = rb_ary_new2(3);
|
210
|
-
rb_ary_push(rb_args, context->to_ruby(value));
|
211
|
-
rb_ary_push(rb_args, ruby_object);
|
222
|
+
rb_ary_push(rb_args, context->to_ruby(value)); // value
|
223
|
+
rb_ary_push(rb_args, ruby_object); // object
|
212
224
|
VALUE method = context->to_ruby(name);
|
213
225
|
method = rb_str_cat2(method, "=");
|
214
|
-
rb_ary_push(rb_args, method);
|
226
|
+
rb_ary_push(rb_args, method); // name=
|
215
227
|
rescued_call(rb_args, secure_call, [&] (VALUE res) {
|
216
228
|
info.GetReturnValue().Set(context->to_js(res));
|
217
|
-
|
229
|
+
});
|
218
230
|
});
|
219
231
|
}
|
220
232
|
|
233
|
+
void h8::RubyGate::deleteProperty(Local<String> name,
|
234
|
+
const PropertyCallbackInfo<Boolean> &info) {
|
235
|
+
with_gvl(this, [&] {
|
236
|
+
VALUE rb_args = rb_ary_new2(2);
|
237
|
+
rb_ary_push(rb_args, context->to_ruby(name)); // name
|
238
|
+
rb_ary_push(rb_args, ruby_object); // object
|
239
|
+
|
240
|
+
rescued_call(rb_args, ruby_delete_handler, [&] (VALUE res) {
|
241
|
+
auto success = Boolean::New(isolate(), res == Qnil ? false : true );
|
242
|
+
info.GetReturnValue().Set(success);
|
243
|
+
});
|
244
|
+
});
|
245
|
+
}
|
246
|
+
|
221
247
|
void h8::RubyGate::getIndex(uint32_t index,
|
222
248
|
const PropertyCallbackInfo<Value> &info) {
|
223
249
|
with_gvl(this, [&] {
|
data/ext/h8/ruby_gate.h
CHANGED
@@ -25,8 +25,8 @@ public:
|
|
25
25
|
* @return wrapped RubyGate* or 0
|
26
26
|
*/
|
27
27
|
static RubyGate* unwrap(v8::Handle<v8::Object> handle) {
|
28
|
-
if (handle->InternalFieldCount()
|
29
|
-
&& handle->GetAlignedPointerFromInternalField(1) == RUBYGATE_ID) {
|
28
|
+
if (handle->InternalFieldCount()
|
29
|
+
== 2 && handle->GetAlignedPointerFromInternalField(1) == RUBYGATE_ID) {
|
30
30
|
return ObjectWrap::Unwrap<RubyGate>(handle);
|
31
31
|
}
|
32
32
|
return 0;
|
@@ -78,7 +78,7 @@ protected:
|
|
78
78
|
* extra slots in array if need
|
79
79
|
*/
|
80
80
|
template<class T>
|
81
|
-
static VALUE ruby_args(H8* context,const T& args, unsigned extras = 0) {
|
81
|
+
static VALUE ruby_args(H8* context, const T& args, unsigned extras = 0) {
|
82
82
|
unsigned n = args.Length();
|
83
83
|
VALUE rb_args = rb_ary_new2(n + extras);
|
84
84
|
for (unsigned i = 0; i < n; i++)
|
@@ -93,6 +93,11 @@ protected:
|
|
93
93
|
*/
|
94
94
|
static VALUE call(VALUE args);
|
95
95
|
|
96
|
+
/**
|
97
|
+
* Call delete handler for ruby gate.
|
98
|
+
*/
|
99
|
+
static VALUE ruby_delete_handler(VALUE args);
|
100
|
+
|
96
101
|
/**
|
97
102
|
* Call ruby method via H8::Context#secure_call
|
98
103
|
*/
|
@@ -108,12 +113,16 @@ protected:
|
|
108
113
|
void setProperty(Local<String> name, Local<Value> value,
|
109
114
|
const PropertyCallbackInfo<Value> &info);
|
110
115
|
|
116
|
+
void deleteProperty(Local<String> name,
|
117
|
+
const PropertyCallbackInfo<Boolean> &info);
|
118
|
+
|
111
119
|
void getIndex(uint32_t index, const PropertyCallbackInfo<Value> &info);
|
112
120
|
void setIndex(uint32_t index, Local<Value> value,
|
113
121
|
const PropertyCallbackInfo<Value> &info);
|
114
122
|
|
115
123
|
static void GateConstructor(const v8::FunctionCallbackInfo<Value>& args);
|
116
|
-
static void ClassGateConstructor(
|
124
|
+
static void ClassGateConstructor(
|
125
|
+
const v8::FunctionCallbackInfo<Value>& args);
|
117
126
|
private:
|
118
127
|
|
119
128
|
void doObjectCallback(const v8::FunctionCallbackInfo<v8::Value>& args);
|
@@ -124,6 +133,8 @@ private:
|
|
124
133
|
const PropertyCallbackInfo<Value> &info);
|
125
134
|
static void mapSet(Local<String> name, Local<Value> value,
|
126
135
|
const PropertyCallbackInfo<Value> &info);
|
136
|
+
static void mapDelete(Local<String> name,
|
137
|
+
const PropertyCallbackInfo<Boolean> &info);
|
127
138
|
|
128
139
|
static void indexGet(uint32_t index,
|
129
140
|
const PropertyCallbackInfo<Value> &info);
|
data/hybrid8.gemspec
CHANGED
@@ -3,8 +3,7 @@ lib = File.expand_path('../lib', __FILE__)
|
|
3
3
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
4
|
|
5
5
|
require 'h8/version'
|
6
|
-
require
|
7
|
-
require 'rubygems/package_task'
|
6
|
+
require 'rake'
|
8
7
|
|
9
8
|
spec = Gem::Specification.new do |spec|
|
10
9
|
spec.name = "h8"
|
@@ -30,22 +29,24 @@ spec = Gem::Specification.new do |spec|
|
|
30
29
|
spec.platform = Gem::Platform::RUBY
|
31
30
|
|
32
31
|
spec.add_development_dependency "bundler", "~> 1.6"
|
33
|
-
spec.add_development_dependency "rake"
|
34
|
-
spec.add_development_dependency "rake-compiler"
|
32
|
+
# spec.add_development_dependency "rake"
|
33
|
+
# spec.add_development_dependency "rake-compiler"
|
35
34
|
spec.add_development_dependency "rspec", '~> 3.1'
|
36
35
|
spec.add_development_dependency 'hashie', '>= 0.1.2'
|
37
36
|
|
38
37
|
spec.add_dependency 'pargser', '>= 0.1.2'
|
38
|
+
spec.add_dependency 'rake'
|
39
|
+
spec.add_dependency 'rake-compiler', '>= 0.9.4'
|
39
40
|
end
|
40
41
|
|
41
|
-
Gem::PackageTask.new(spec) do |pkg|
|
42
|
-
end
|
43
|
-
|
44
|
-
Rake::ExtensionTask.new "h8", spec do |ext|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
end
|
42
|
+
# Gem::PackageTask.new(spec) do |pkg|
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# Rake::ExtensionTask.new "h8", spec do |ext|
|
46
|
+
# ext.lib_dir = "lib/h8"
|
47
|
+
# ext.source_pattern = "*.{c,cpp,js}"
|
48
|
+
# ext.gem_spec = spec
|
49
|
+
# end
|
49
50
|
|
50
51
|
spec
|
51
52
|
|
data/lib/h8/context.rb
CHANGED
@@ -113,6 +113,19 @@ module H8
|
|
113
113
|
proc.call *args
|
114
114
|
end
|
115
115
|
|
116
|
+
# :nodoc:
|
117
|
+
# Internal handler to properly delete fields/keys from ruby Hashes or
|
118
|
+
# OpenStruct
|
119
|
+
#
|
120
|
+
def self.delete_handler object, args
|
121
|
+
name = args[0]
|
122
|
+
if object.is_a?(OpenStruct)
|
123
|
+
object.delete_field name
|
124
|
+
else
|
125
|
+
object.delete name
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
116
129
|
def self.can_access?(owner)
|
117
130
|
owner != Object.class && owner != Kernel && owner != BasicObject.class
|
118
131
|
end
|
data/lib/h8/value.rb
CHANGED
data/lib/h8/version.rb
CHANGED
data/spec/context_spec.rb
CHANGED
data/spec/ruby_gate_spec.rb
CHANGED
@@ -313,14 +313,39 @@ describe 'ruby gate' do
|
|
313
313
|
c.coffee 'data.foo = "baz"'
|
314
314
|
s.foo.should == 'baz'
|
315
315
|
c.coffee 'data.h = { foo: "bar", bar: { baz: 1 } }'
|
316
|
+
c.coffee 'data.bad = "nonono"'
|
316
317
|
s.h.foo.should == 'bar'
|
317
|
-
|
318
|
+
s.bad.should == 'nonono'
|
319
|
+
c.coffee 'assert data.h.bar.baz == 1'
|
320
|
+
c.coffee 'delete data.bad'
|
318
321
|
s.h.bar.baz.should == 1
|
319
322
|
c.coffee 'data.h.bar.arr = ["hello", { one: 2 }]'
|
320
323
|
s.to_ruby.should == { 'test' => "bar", 'foo' => "baz", 'h' => { "foo" => "bar", "bar" => { "baz" => 1, "arr" => ["hello", { "one" => 2 }] } } }
|
321
324
|
}
|
322
325
|
end
|
323
326
|
|
327
|
+
it 'should delete gated prperties' do
|
328
|
+
s = OpenStruct.new 'foo' => 'bar', 'bar' => 'baz'
|
329
|
+
s.foo.should == 'bar'
|
330
|
+
c = H8::Context.new s: s
|
331
|
+
c.eval('s.foo').should == 'bar'
|
332
|
+
c.eval 'delete s.foo'
|
333
|
+
s.foo.should == nil
|
334
|
+
|
335
|
+
s = Hashie::Mash.new 'foo' => 'bar', 'bar' => 'baz'
|
336
|
+
s.foo.should == 'bar'
|
337
|
+
c = H8::Context.new s: s
|
338
|
+
c.eval('s.foo').should == 'bar'
|
339
|
+
c.eval 'delete s.foo'
|
340
|
+
s.foo.should == nil
|
341
|
+
|
342
|
+
s = { 'foo' => 'bar', 'bar' => 'baz' }
|
343
|
+
c = H8::Context.new s: s
|
344
|
+
c.eval('s.foo').should == 'bar'
|
345
|
+
c.eval 'delete s.foo'
|
346
|
+
s['foo'].should == nil
|
347
|
+
end
|
348
|
+
|
324
349
|
it 'should access plain arrays (provide numeric indexes)' do
|
325
350
|
cxt = H8::Context.new
|
326
351
|
array = [10, 20, 30]
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: h8
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- sergeych
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-01-
|
11
|
+
date: 2015-01-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -25,75 +25,75 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '1.6'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: rspec
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '3.1'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '3.1'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: hashie
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
47
|
+
version: 0.1.2
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
54
|
+
version: 0.1.2
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: pargser
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- - "
|
59
|
+
- - ">="
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
62
|
-
type: :
|
61
|
+
version: 0.1.2
|
62
|
+
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- - "
|
66
|
+
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
68
|
+
version: 0.1.2
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
70
|
+
name: rake
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - ">="
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: 0
|
76
|
-
type: :
|
75
|
+
version: '0'
|
76
|
+
type: :runtime
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - ">="
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: 0
|
82
|
+
version: '0'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
84
|
+
name: rake-compiler
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
87
|
- - ">="
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: 0.
|
89
|
+
version: 0.9.4
|
90
90
|
type: :runtime
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
94
|
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version: 0.
|
96
|
+
version: 0.9.4
|
97
97
|
description: |2
|
98
98
|
Synergy of ruby and javascript code, almost completely integrates both languages, their
|
99
99
|
garbage collection models, object models and so on. Effective for tight ruby-javascript
|
@@ -115,6 +115,7 @@ files:
|
|
115
115
|
- benchmark/README.md
|
116
116
|
- benchmark/big.txt
|
117
117
|
- benchmark/km.cpp
|
118
|
+
- benchmark/km.rb
|
118
119
|
- benchmark/knightsmove.coffee
|
119
120
|
- benchmark/knightsmove.rb
|
120
121
|
- benchmark/process_text.coffee
|