h8 0.2.2 → 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|