h8 0.2.2 → 0.2.3
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 +3 -2
- data/benchmark/big.txt +121706 -0
- data/benchmark/knightsmove.coffee +48 -0
- data/benchmark/knigthsmove.rb +70 -0
- data/benchmark/process_text.coffee +12 -0
- data/benchmark/text_process.rb +51 -0
- data/benchmark/tools.rb +21 -0
- data/bin/h8 +12 -0
- data/ext/h8/h8.cpp +4 -2
- data/ext/h8/h8.h +8 -3
- data/ext/h8/js_gate.h +25 -1
- data/ext/h8/main.cpp +12 -5
- data/ext/h8/object_wrap.h +96 -102
- data/ext/h8/ruby_gate.cpp +0 -1
- data/ext/h8/ruby_gate.h +5 -3
- data/lib/h8/coffee.rb +19 -3
- data/lib/h8/command.rb +128 -0
- data/lib/h8/context.rb +14 -21
- data/lib/h8/errors.rb +9 -8
- data/lib/h8/pargser.rb +133 -0
- data/lib/h8/version.rb +1 -1
- data/lib/{h8 → scripts}/coffee-script.js +0 -0
- data/lib/scripts/globals.coffee +4 -0
- data/spec/coffee/cli_tests.coffee +50 -0
- data/spec/coffee_spec.rb +50 -0
- data/spec/command_spec.rb +72 -0
- data/spec/context_spec.rb +21 -1
- data/spec/js_gate_spec.rb +53 -1
- data/spec/pargser_spec.rb +67 -0
- data/spec/ruby_gate_spec.rb +17 -0
- metadata +21 -4
@@ -0,0 +1,48 @@
|
|
1
|
+
pad = (n, len) ->
|
2
|
+
len ?= 3
|
3
|
+
res = n.toString()
|
4
|
+
res = ' '+res while res.length < len
|
5
|
+
res
|
6
|
+
|
7
|
+
shifts = [[-2, +1], [-1, +2], [+1, +2], [+2, +1], [+2, -1], [-2, -1]]
|
8
|
+
|
9
|
+
class Solver
|
10
|
+
|
11
|
+
constructor: (@n, @left_free) ->
|
12
|
+
@nn = @n * @n
|
13
|
+
@n2 = @n + @n
|
14
|
+
@desk = []
|
15
|
+
@depth = 0
|
16
|
+
for r in [0...@n]
|
17
|
+
@desk.push (0 for col in [0...@n])
|
18
|
+
@solve 0, 0
|
19
|
+
|
20
|
+
solve: (r, c) ->
|
21
|
+
@desk[r][c] = ++@depth
|
22
|
+
return true if @depth + @left_free >= @nn
|
23
|
+
for [r1, c1] in @moves(r, c)
|
24
|
+
return true if @solve(r1, c1)
|
25
|
+
@desk[r][c] = 0
|
26
|
+
@depth--
|
27
|
+
false
|
28
|
+
|
29
|
+
# Coffeescript does not support generators
|
30
|
+
moves: (r, c) ->
|
31
|
+
res = []
|
32
|
+
for [sr, sc] in shifts
|
33
|
+
r1 = r + sr
|
34
|
+
if 0 <= r1 < @n
|
35
|
+
c1 = c + sc
|
36
|
+
if 0 <= c1 < @n && @desk[r1][c1]==0
|
37
|
+
res.push [r1, c1]
|
38
|
+
res
|
39
|
+
|
40
|
+
toString: ->
|
41
|
+
res = []
|
42
|
+
for r in [0...@n]
|
43
|
+
res.push ( (if x==0 then ' .' else pad(x,3)) for x in @desk[r]).join('')
|
44
|
+
res.join "\n"
|
45
|
+
|
46
|
+
return (n, left) ->
|
47
|
+
new Solver(n, left).toString()
|
48
|
+
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require './tools'
|
2
|
+
|
3
|
+
class Solver
|
4
|
+
|
5
|
+
def initialize rank, leave_free=0
|
6
|
+
@n, @nn = rank, rank*rank
|
7
|
+
@n2 = @n+@n
|
8
|
+
@leave_free = leave_free
|
9
|
+
@desk = []
|
10
|
+
@n.times { @desk << [0] * @n }
|
11
|
+
@depth = 0
|
12
|
+
solve 0, 0
|
13
|
+
end
|
14
|
+
|
15
|
+
def solve r, c
|
16
|
+
@desk[r][c] = (@depth+=1)
|
17
|
+
return true if @depth == @nn-@leave_free
|
18
|
+
moves(r, c) { |r1, c1|
|
19
|
+
return true if solve(r1, c1)
|
20
|
+
}
|
21
|
+
@desk[r][c] = 0
|
22
|
+
@depth -= 1
|
23
|
+
false
|
24
|
+
end
|
25
|
+
|
26
|
+
@@shifts = [[-2, +1], [-1, +2], [+1, +2], [+2, +1], [+2, -1], [-2, -1]]
|
27
|
+
|
28
|
+
def moves r, c
|
29
|
+
@@shifts.each { |sr, sc|
|
30
|
+
r1 = r + sr
|
31
|
+
if r1 >= 0 && r1 < @n
|
32
|
+
c1 = c + sc
|
33
|
+
if c1 >= 0 && c1 < @n
|
34
|
+
yield r1, c1 if @desk[r1][c1] == 0
|
35
|
+
end
|
36
|
+
end
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
def to_s
|
41
|
+
res = []
|
42
|
+
@n.times do |row|
|
43
|
+
res << @n.times.map { |col|
|
44
|
+
d = @desk[row][col]
|
45
|
+
d == 0 ? ' .' : ("%3d" % d)
|
46
|
+
}.join('')
|
47
|
+
end
|
48
|
+
res.join "\n"
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
cs = js_context.eval coffee(:knightsmove)
|
54
|
+
|
55
|
+
N, L = 7, 3
|
56
|
+
|
57
|
+
res1 = res2 = 0
|
58
|
+
timing('total') {
|
59
|
+
tt = []
|
60
|
+
tt << Thread.start { timing('ruby') { res1 = Solver.new(N, L).to_s } }
|
61
|
+
tt << Thread.start { timing('coffee') { res2 = cs.call(N, L) } }
|
62
|
+
tt.each &:join
|
63
|
+
}
|
64
|
+
|
65
|
+
if res1 != res2
|
66
|
+
puts "WRONG RESULTS test data can not be trusted"
|
67
|
+
puts "Ruby:\n#{res1}"
|
68
|
+
puts "Coffee:\n#{res2}"
|
69
|
+
end
|
70
|
+
|
@@ -0,0 +1,12 @@
|
|
1
|
+
return (text) ->
|
2
|
+
words = {}
|
3
|
+
freq = []
|
4
|
+
for w in text.split(/\s+/)
|
5
|
+
w = w.toLowerCase()
|
6
|
+
continue if w == 'which' || w == 'from' || w.length < 4
|
7
|
+
w = w[2..-1]
|
8
|
+
unless (rec = words[w])?.count++
|
9
|
+
freq.push (words[w] = { word: w, count: 1})
|
10
|
+
freq.sort (a,b) ->
|
11
|
+
b.count - a.count
|
12
|
+
freq[0..10]
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'h8'
|
2
|
+
require 'pp'
|
3
|
+
require './tools'
|
4
|
+
|
5
|
+
def process_text text
|
6
|
+
words = {}
|
7
|
+
text.split(/\s+/).each { |w|
|
8
|
+
w.downcase!
|
9
|
+
next if w == 'which' || w == 'from' || w.length < 4
|
10
|
+
w = w[2..-1]
|
11
|
+
rec = words[w] ||= { word: w, count: 0 }
|
12
|
+
rec[:count] += 1
|
13
|
+
}
|
14
|
+
words.values.sort { |a, b| b[:count] <=> a[:count] }[0..10]
|
15
|
+
end
|
16
|
+
|
17
|
+
base = File.dirname(File.expand_path(__FILE__))
|
18
|
+
|
19
|
+
text = open(base+'/big.txt').read
|
20
|
+
text = text * 2
|
21
|
+
|
22
|
+
cxt = js_context
|
23
|
+
|
24
|
+
coffee_process = cxt.eval coffee(:process_text)
|
25
|
+
|
26
|
+
coffee_res = ruby_res = nil
|
27
|
+
|
28
|
+
t1 = Thread.start {
|
29
|
+
timing "ruby" do
|
30
|
+
ruby_res = process_text text
|
31
|
+
end
|
32
|
+
}
|
33
|
+
|
34
|
+
t2 = Thread.start {
|
35
|
+
timing "coffee" do
|
36
|
+
coffee_res = coffee_process.call text
|
37
|
+
end
|
38
|
+
}
|
39
|
+
|
40
|
+
timing 'total' do
|
41
|
+
t1.join
|
42
|
+
t2.join
|
43
|
+
end
|
44
|
+
|
45
|
+
# pp coffee_res.to_ruby[0..4]
|
46
|
+
# pp ruby_res[0..4]
|
47
|
+
5.times { |n|
|
48
|
+
coffee_res[n].word == ruby_res[n][:word] or raise "Words are different"
|
49
|
+
coffee_res[n].count == ruby_res[n][:count] or raise "counts are different"
|
50
|
+
}
|
51
|
+
|
data/benchmark/tools.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'h8'
|
2
|
+
|
3
|
+
def timing name
|
4
|
+
s = Time.now
|
5
|
+
yield
|
6
|
+
puts "#{name}\t: #{Time.now - s}"
|
7
|
+
rescue
|
8
|
+
puts "*** #{$!}"
|
9
|
+
raise
|
10
|
+
end
|
11
|
+
|
12
|
+
def js_context
|
13
|
+
cxt = H8::Context.new
|
14
|
+
cxt[:print] = -> (*args) { puts args.join(' ') }
|
15
|
+
cxt
|
16
|
+
end
|
17
|
+
|
18
|
+
def coffee script_file_name
|
19
|
+
@base ||= File.dirname(File.expand_path(__FILE__))
|
20
|
+
H8::Coffee.compile open("#{@base}/#{script_file_name}.coffee").read
|
21
|
+
end
|
data/bin/h8
ADDED
data/ext/h8/h8.cpp
CHANGED
@@ -99,14 +99,16 @@ void h8::H8::invoke(v8::Handle<v8::Script> script, Local<Value>& result) {
|
|
99
99
|
#endif
|
100
100
|
}
|
101
101
|
|
102
|
-
v8::Handle<v8::Value> h8::H8::eval(const char* script_utf, unsigned max_ms) {
|
102
|
+
v8::Handle<v8::Value> h8::H8::eval(const char* script_utf, unsigned max_ms,const char* source_name) {
|
103
103
|
v8::EscapableHandleScope escape(isolate);
|
104
104
|
Local<Value> result;
|
105
105
|
|
106
106
|
Handle<v8::String> script_source = String::NewFromUtf8(isolate, script_utf);
|
107
107
|
v8::Handle<v8::Script> script;
|
108
108
|
JsCatcher try_catch(this);
|
109
|
-
|
109
|
+
if( source_name == NULL)
|
110
|
+
source_name = "eval";
|
111
|
+
v8::ScriptOrigin origin(String::NewFromUtf8(isolate, source_name));
|
110
112
|
|
111
113
|
script = v8::Script::Compile(script_source, &origin);
|
112
114
|
|
data/ext/h8/h8.h
CHANGED
@@ -125,11 +125,11 @@ public:
|
|
125
125
|
* to this value, JsTimeoutError will be thrown if exceeded
|
126
126
|
* \return the value returned by the script.
|
127
127
|
*/
|
128
|
-
Handle<Value> eval(const char* script_utf, unsigned max_ms = 0);
|
128
|
+
Handle<Value> eval(const char* script_utf, unsigned max_ms = 0,const char* script_name=NULL);
|
129
129
|
|
130
|
-
VALUE eval_to_ruby(const char* script_utf, int timeout = 0) {
|
130
|
+
VALUE eval_to_ruby(const char* script_utf, int timeout = 0,const char* script_name=NULL) {
|
131
131
|
// TODO: throw ruby exception on error
|
132
|
-
return to_ruby(eval(script_utf, timeout));
|
132
|
+
return to_ruby(eval(script_utf, timeout, script_name));
|
133
133
|
}
|
134
134
|
|
135
135
|
Handle<Context> getContext() {
|
@@ -159,6 +159,11 @@ public:
|
|
159
159
|
getContext()->Global()->Set(js(name), to_js(value));
|
160
160
|
}
|
161
161
|
|
162
|
+
void gc() {
|
163
|
+
// puts("H8 GC");
|
164
|
+
while(!isolate->IdleNotification(500)) {}
|
165
|
+
}
|
166
|
+
|
162
167
|
Local<Value> to_js(VALUE ruby_value) {
|
163
168
|
switch (TYPE(ruby_value)) {
|
164
169
|
case T_STRING:
|
data/ext/h8/js_gate.h
CHANGED
@@ -135,6 +135,9 @@ public:
|
|
135
135
|
object()->Set(v8_name, h8->to_js(value));
|
136
136
|
}
|
137
137
|
|
138
|
+
/**
|
139
|
+
* Access indexed property from ruby environment
|
140
|
+
*/
|
138
141
|
VALUE get_index(VALUE index) {
|
139
142
|
H8::Scope scope(h8);
|
140
143
|
return h8->to_ruby(object()->Get(NUM2INT(index)));
|
@@ -148,11 +151,18 @@ public:
|
|
148
151
|
return value()->IsString() ? Qtrue : Qfalse;
|
149
152
|
}
|
150
153
|
|
154
|
+
/**
|
155
|
+
* True if the wrapped object is a function
|
156
|
+
*/
|
151
157
|
VALUE is_function() {
|
152
158
|
H8::Scope scope(h8);
|
153
159
|
return value()->IsFunction();
|
154
160
|
}
|
155
161
|
|
162
|
+
/**
|
163
|
+
* Usually unneeded function, as H8 converts undefined values to
|
164
|
+
* H8::Undefined
|
165
|
+
*/
|
156
166
|
VALUE is_undefined() {
|
157
167
|
H8::Scope scope(h8);
|
158
168
|
return value()->IsUndefined() ? Qtrue : Qfalse;
|
@@ -166,11 +176,18 @@ public:
|
|
166
176
|
return apply(h8->getContext()->Global(), args);
|
167
177
|
}
|
168
178
|
|
179
|
+
/**
|
180
|
+
* apply wrapped function to a given 'this' value and arguments
|
181
|
+
* wrapped in the ruby array
|
182
|
+
*/
|
169
183
|
VALUE apply(VALUE self, VALUE args) const {
|
170
184
|
H8::Scope scope(h8);
|
171
185
|
return apply(h8->gateObject(self), args);
|
172
186
|
}
|
173
187
|
|
188
|
+
/**
|
189
|
+
* @return bound ruby H8::Context instance
|
190
|
+
*/
|
174
191
|
VALUE ruby_context() const {
|
175
192
|
return h8->ruby_context();
|
176
193
|
}
|
@@ -185,6 +202,8 @@ public:
|
|
185
202
|
persistent_value.Reset();
|
186
203
|
AllocatedResource::free();
|
187
204
|
h8 = 0;
|
205
|
+
// It is used no more, and it will be GC'd by ruby, until then
|
206
|
+
// we can not delete it!
|
188
207
|
}
|
189
208
|
|
190
209
|
virtual void rb_mark_gc() {
|
@@ -202,9 +221,11 @@ public:
|
|
202
221
|
virtual ~JsGate() {
|
203
222
|
if( h8 ) {
|
204
223
|
Locker l(h8->getIsolate());
|
224
|
+
// puts("~JsGate1");
|
205
225
|
persistent_value.Reset();
|
206
226
|
}
|
207
227
|
else {
|
228
|
+
// puts("~JsGate2");
|
208
229
|
persistent_value.Reset();
|
209
230
|
}
|
210
231
|
}
|
@@ -220,6 +241,7 @@ private:
|
|
220
241
|
}
|
221
242
|
|
222
243
|
#include "ruby_gate.h"
|
244
|
+
#include <ruby/encoding.h>
|
223
245
|
|
224
246
|
template<class T>
|
225
247
|
VALUE h8::JsGate::to_ruby(H8* h8, const Handle<T>& value) {
|
@@ -228,7 +250,9 @@ VALUE h8::JsGate::to_ruby(H8* h8, const Handle<T>& value) {
|
|
228
250
|
if (v->IsString()) {
|
229
251
|
H8::Scope scope(h8);
|
230
252
|
String::Utf8Value res(v);
|
231
|
-
|
253
|
+
if( *res )
|
254
|
+
return rb_enc_str_new(*res, res.length(), rb_utf8_encoding());
|
255
|
+
return Qnil;
|
232
256
|
}
|
233
257
|
if (v->IsInt32()) {
|
234
258
|
return INT2FIX(v->Int32Value());
|
data/ext/h8/main.cpp
CHANGED
@@ -29,7 +29,7 @@ VALUE protect_ruby(const std::function<VALUE()> &block) {
|
|
29
29
|
}
|
30
30
|
|
31
31
|
static void rvalue_free(void* ptr) {
|
32
|
-
delete (JsGate*) ptr;
|
32
|
+
delete ((JsGate*) ptr);
|
33
33
|
}
|
34
34
|
|
35
35
|
static void rvalue_mark(void* ptr) {
|
@@ -126,21 +126,27 @@ inline H8* rc(VALUE self) {
|
|
126
126
|
return prcxt;
|
127
127
|
}
|
128
128
|
|
129
|
-
static VALUE context_eval(VALUE self, VALUE script,VALUE timeout) {
|
129
|
+
static VALUE context_eval(VALUE self, VALUE script,VALUE timeout,VALUE script_name_ruby) {
|
130
130
|
return protect_ruby([&] {
|
131
131
|
H8* cxt = rc(self);// v8::Locker l(cxt->getIsolate());
|
132
132
|
H8::Scope s(cxt);
|
133
|
-
|
133
|
+
const char* script_name = script_name_ruby != Qnil ? StringValueCStr(script_name_ruby) : NULL;
|
134
|
+
return cxt->eval_to_ruby(StringValueCStr(script), FIX2INT(timeout), script_name);
|
134
135
|
});
|
135
136
|
}
|
136
137
|
|
137
138
|
static VALUE context_set_var(VALUE self, VALUE name, VALUE value) {
|
138
|
-
return protect_ruby([
|
139
|
+
return protect_ruby([&] {
|
139
140
|
rc(self)->set_var(name, value);
|
140
141
|
return Qnil;
|
141
142
|
});
|
142
143
|
}
|
143
144
|
|
145
|
+
static VALUE context_force_gc(VALUE self) {
|
146
|
+
rc(self)->gc();
|
147
|
+
return Qnil;
|
148
|
+
}
|
149
|
+
|
144
150
|
static void context_free(void* ptr) {
|
145
151
|
delete (H8*) ptr;
|
146
152
|
}
|
@@ -176,9 +182,10 @@ void Init_h8(void) {
|
|
176
182
|
context_class = rb_define_class_under(h8, "Context", rb_cObject);
|
177
183
|
ruby_gate_class = rb_define_class_under(h8, "RubyGate", rb_cObject);
|
178
184
|
rb_define_alloc_func(context_class, context_alloc);
|
179
|
-
rb_define_method(context_class, "_eval", (ruby_method) context_eval,
|
185
|
+
rb_define_method(context_class, "_eval", (ruby_method) context_eval, 3);
|
180
186
|
rb_define_method(context_class, "_set_var", (ruby_method) context_set_var,
|
181
187
|
2);
|
188
|
+
rb_define_method(context_class, "javascript_gc", (ruby_method) context_force_gc, 0);
|
182
189
|
|
183
190
|
value_class = rb_define_class_under(h8, "Value", rb_cObject);
|
184
191
|
rb_define_alloc_func(value_class, rvalue_alloc);
|
data/ext/h8/object_wrap.h
CHANGED
@@ -26,111 +26,105 @@
|
|
26
26
|
#include <include/v8.h>
|
27
27
|
#include <assert.h>
|
28
28
|
|
29
|
-
|
30
29
|
namespace h8 {
|
31
30
|
|
32
31
|
class ObjectWrap {
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
wrap->handle_.Reset();
|
130
|
-
delete wrap;
|
131
|
-
}
|
132
|
-
|
133
|
-
v8::Persistent<v8::Object> handle_;
|
32
|
+
public:
|
33
|
+
ObjectWrap() {
|
34
|
+
refs_ = 0;
|
35
|
+
}
|
36
|
+
|
37
|
+
virtual ~ObjectWrap() {
|
38
|
+
if (persistent().IsEmpty())
|
39
|
+
return;
|
40
|
+
assert(persistent().IsNearDeath());
|
41
|
+
persistent().ClearWeak();
|
42
|
+
persistent().Reset();
|
43
|
+
}
|
44
|
+
|
45
|
+
template<class T>
|
46
|
+
static inline T* Unwrap(v8::Handle<v8::Object> handle) {
|
47
|
+
assert(!handle.IsEmpty());
|
48
|
+
assert(handle->InternalFieldCount() > 0);
|
49
|
+
// Cast to ObjectWrap before casting to T. A direct cast from void
|
50
|
+
// to T won't work right when T has more than one base class.
|
51
|
+
void* ptr = handle->GetAlignedPointerFromInternalField(0);
|
52
|
+
ObjectWrap* wrap = static_cast<ObjectWrap*>(ptr);
|
53
|
+
return static_cast<T*>(wrap);
|
54
|
+
}
|
55
|
+
|
56
|
+
inline v8::Local<v8::Object> handle() {
|
57
|
+
return handle(v8::Isolate::GetCurrent());
|
58
|
+
}
|
59
|
+
|
60
|
+
inline v8::Local<v8::Object> handle(v8::Isolate* isolate) {
|
61
|
+
return v8::Local<v8::Object>::New(isolate, persistent());
|
62
|
+
}
|
63
|
+
|
64
|
+
inline v8::Persistent<v8::Object>& persistent() {
|
65
|
+
return handle_;
|
66
|
+
}
|
67
|
+
|
68
|
+
protected:
|
69
|
+
inline void Wrap(v8::Handle<v8::Object> handle) {
|
70
|
+
assert(persistent().IsEmpty());
|
71
|
+
assert(handle->InternalFieldCount() > 0);
|
72
|
+
handle->SetAlignedPointerInInternalField(0, this);
|
73
|
+
persistent().Reset(v8::Isolate::GetCurrent(), handle);
|
74
|
+
MakeWeak();
|
75
|
+
}
|
76
|
+
|
77
|
+
inline void MakeWeak(void) {
|
78
|
+
persistent().SetWeak(this, WeakCallback);
|
79
|
+
persistent().MarkIndependent();
|
80
|
+
}
|
81
|
+
|
82
|
+
/* Ref() marks the object as being attached to an event loop.
|
83
|
+
* Refed objects will not be garbage collected, even if
|
84
|
+
* all references are lost.
|
85
|
+
*/
|
86
|
+
virtual void Ref() {
|
87
|
+
assert(!persistent().IsEmpty());
|
88
|
+
persistent().ClearWeak();
|
89
|
+
refs_++;
|
90
|
+
}
|
91
|
+
|
92
|
+
/* Unref() marks an object as detached from the event loop. This is its
|
93
|
+
* default state. When an object with a "weak" reference changes from
|
94
|
+
* attached to detached state it will be freed. Be careful not to access
|
95
|
+
* the object after making this call as it might be gone!
|
96
|
+
* (A "weak reference" means an object that only has a
|
97
|
+
* persistant handle.)
|
98
|
+
*
|
99
|
+
* DO NOT CALL THIS FROM DESTRUCTOR
|
100
|
+
*/
|
101
|
+
virtual void Unref() {
|
102
|
+
assert(!persistent().IsEmpty());
|
103
|
+
assert(!persistent().IsWeak());
|
104
|
+
assert(refs_ > 0);
|
105
|
+
if (--refs_ == 0)
|
106
|
+
MakeWeak();
|
107
|
+
}
|
108
|
+
|
109
|
+
int refs_; // ro
|
110
|
+
|
111
|
+
private:
|
112
|
+
static void WeakCallback(
|
113
|
+
const v8::WeakCallbackData<v8::Object, ObjectWrap>& data) {
|
114
|
+
puts("WEAK CALLBACK!!");
|
115
|
+
v8::Isolate* isolate = data.GetIsolate();
|
116
|
+
v8::HandleScope scope(isolate);
|
117
|
+
ObjectWrap* wrap = data.GetParameter();
|
118
|
+
assert(wrap->refs_ == 0);
|
119
|
+
assert(wrap->handle_.IsNearDeath());
|
120
|
+
assert(
|
121
|
+
data.GetValue()
|
122
|
+
== v8::Local<v8::Object>::New(isolate, wrap->handle_));
|
123
|
+
wrap->handle_.Reset();
|
124
|
+
delete wrap;
|
125
|
+
}
|
126
|
+
|
127
|
+
v8::Persistent<v8::Object> handle_;
|
134
128
|
};
|
135
129
|
|
136
130
|
} // namespace node
|