h8 0.4.8 → 0.4.10
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 +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
|