h8 0.2.2 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+
@@ -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
@@ -0,0 +1,12 @@
1
+ #!/bin/env ruby
2
+ require 'h8'
3
+ require 'h8/command'
4
+
5
+ cmd = nil
6
+ begin
7
+ cmd = H8::Command.new(*ARGV)
8
+ rescue Exception => e
9
+ STDERR.puts "Error: #{e}\n\n#{e.backtrace.join("\n")}"
10
+ STDERR.puts cmd.usage
11
+ exit 10
12
+ end
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
- v8::ScriptOrigin origin(String::NewFromUtf8(isolate, "eval"));
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
- return *res ? rb_str_new2(*res) : Qnil;
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
- return cxt->eval_to_ruby(StringValueCStr(script), FIX2INT(timeout));
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, 2);
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
- public:
34
- ObjectWrap() {
35
- refs_ = 0;
36
- }
37
-
38
-
39
- virtual ~ObjectWrap() {
40
- if (persistent().IsEmpty())
41
- return;
42
- assert(persistent().IsNearDeath());
43
- persistent().ClearWeak();
44
- persistent().Reset();
45
- }
46
-
47
-
48
- template <class T>
49
- static inline T* Unwrap(v8::Handle<v8::Object> handle) {
50
- assert(!handle.IsEmpty());
51
- assert(handle->InternalFieldCount() > 0);
52
- // Cast to ObjectWrap before casting to T. A direct cast from void
53
- // to T won't work right when T has more than one base class.
54
- void* ptr = handle->GetAlignedPointerFromInternalField(0);
55
- ObjectWrap* wrap = static_cast<ObjectWrap*>(ptr);
56
- return static_cast<T*>(wrap);
57
- }
58
-
59
-
60
- inline v8::Local<v8::Object> handle() {
61
- return handle(v8::Isolate::GetCurrent());
62
- }
63
-
64
-
65
- inline v8::Local<v8::Object> handle(v8::Isolate* isolate) {
66
- return v8::Local<v8::Object>::New(isolate, persistent());
67
- }
68
-
69
-
70
- inline v8::Persistent<v8::Object>& persistent() {
71
- return handle_;
72
- }
73
-
74
-
75
- protected:
76
- inline void Wrap(v8::Handle<v8::Object> handle) {
77
- assert(persistent().IsEmpty());
78
- assert(handle->InternalFieldCount() > 0);
79
- handle->SetAlignedPointerInInternalField(0, this);
80
- persistent().Reset(v8::Isolate::GetCurrent(), handle);
81
- MakeWeak();
82
- }
83
-
84
-
85
- inline void MakeWeak(void) {
86
- persistent().SetWeak(this, WeakCallback);
87
- persistent().MarkIndependent();
88
- }
89
-
90
- /* Ref() marks the object as being attached to an event loop.
91
- * Refed objects will not be garbage collected, even if
92
- * all references are lost.
93
- */
94
- virtual void Ref() {
95
- assert(!persistent().IsEmpty());
96
- persistent().ClearWeak();
97
- refs_++;
98
- }
99
-
100
- /* Unref() marks an object as detached from the event loop. This is its
101
- * default state. When an object with a "weak" reference changes from
102
- * attached to detached state it will be freed. Be careful not to access
103
- * the object after making this call as it might be gone!
104
- * (A "weak reference" means an object that only has a
105
- * persistant handle.)
106
- *
107
- * DO NOT CALL THIS FROM DESTRUCTOR
108
- */
109
- virtual void Unref() {
110
- assert(!persistent().IsEmpty());
111
- assert(!persistent().IsWeak());
112
- assert(refs_ > 0);
113
- if (--refs_ == 0)
114
- MakeWeak();
115
- }
116
-
117
- int refs_; // ro
118
-
119
- private:
120
- static void WeakCallback(
121
- const v8::WeakCallbackData<v8::Object, ObjectWrap>& data) {
122
- v8::Isolate* isolate = data.GetIsolate();
123
- v8::HandleScope scope(isolate);
124
- ObjectWrap* wrap = data.GetParameter();
125
- assert(wrap->refs_ == 0);
126
- assert(wrap->handle_.IsNearDeath());
127
- assert(
128
- data.GetValue() == v8::Local<v8::Object>::New(isolate, wrap->handle_));
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