rufus-lua 1.1.1 → 1.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.txt +12 -0
- data/CREDITS.txt +8 -0
- data/README.md +82 -1
- data/lib/rufus/lua/error.rb +8 -4
- data/lib/rufus/lua/lib.rb +10 -2
- data/lib/rufus/lua/objects.rb +3 -2
- data/lib/rufus/lua/state.rb +93 -35
- data/lib/rufus/lua/utils.rb +1 -0
- data/lib/rufus/lua/version.rb +1 -1
- data/spec/coroutines_spec.rb +49 -3
- data/spec/error_handler_spec.rb +222 -0
- data/spec/eval_spec.rb +41 -1
- data/spec/rfunctions_spec.rb +15 -2
- data/spec/string_spec.rb +14 -0
- data/spec/utils_spec.rb +9 -0
- metadata +3 -2
data/CHANGELOG.txt
CHANGED
@@ -2,6 +2,18 @@
|
|
2
2
|
= rufus-lua CHANGELOG.txt
|
3
3
|
|
4
4
|
|
5
|
+
== rufus-lua - 1.1.2 released 2014/09/13
|
6
|
+
|
7
|
+
- properly turn Ruby nils into Lua nils (Christopher Saunders)
|
8
|
+
- get rid of Lib.strlen (as suggested by Stas Ukolov)
|
9
|
+
- let Ruby callbacks accept Lua functions as arguments (Gian Perrone)
|
10
|
+
- add #set_error_handler (based on an initial request by Nathanael Jones)
|
11
|
+
- fix unstack issue when returning table (https://github.com/lywaterman)
|
12
|
+
- use "{filename}:#{lineno}" as eval chunk name (suggested by Nathanael Jones)
|
13
|
+
- wrap Ruby callbacks in CallbackState (Nathanael Jones)
|
14
|
+
- accept Ruby symbols containing \0 character (Nathanael Jones)
|
15
|
+
|
16
|
+
|
5
17
|
== rufus-lua - 1.1.1 released 2014/07/01
|
6
18
|
|
7
19
|
- move from bacon to rspec
|
data/CREDITS.txt
CHANGED
@@ -11,9 +11,17 @@ Scott Persinger https://github.com/scottpersinger
|
|
11
11
|
|
12
12
|
== contributors
|
13
13
|
|
14
|
+
Christopher Saunders https://github.com/csaunders
|
15
|
+
Gian Perrone https://github.com/quidrex
|
14
16
|
Matthew Nielsen https://github.com/xunker
|
15
17
|
Nathanael Jones https://github.com/nathanaeljones
|
16
18
|
|
19
|
+
|
20
|
+
== feedback
|
21
|
+
|
22
|
+
Stas Ukolov https://github.com/ukoloff
|
23
|
+
|
24
|
+
|
17
25
|
== inspiration
|
18
26
|
|
19
27
|
http://rubyluabridge.rubyforge.org/
|
data/README.md
CHANGED
@@ -168,6 +168,87 @@ lua = Rufus::Lua::State.new
|
|
168
168
|
lua.eval('print("hello")', nil, 'myluastuff/hello.lua', 77)
|
169
169
|
```
|
170
170
|
|
171
|
+
### set_error_handler
|
172
|
+
|
173
|
+
`set_error_handler` gives a little bit of control on how error messages are prepared when errors occur in the Lua interpreter.
|
174
|
+
|
175
|
+
Here are set of examples for each of the possible error handler kind: Lua code, Ruby block, `:traceback`.
|
176
|
+
|
177
|
+
```ruby
|
178
|
+
require 'rufus-lua'
|
179
|
+
|
180
|
+
lua = Rufus::Lua::State.new
|
181
|
+
|
182
|
+
#
|
183
|
+
# no error handler
|
184
|
+
|
185
|
+
begin
|
186
|
+
lua.eval('error("ouch!")')
|
187
|
+
rescue => e
|
188
|
+
puts(e)
|
189
|
+
end
|
190
|
+
# --> eval:pcall : '[string "line"]:1: ouch!' (2 LUA_ERRRUN)
|
191
|
+
|
192
|
+
#
|
193
|
+
# Lua error handler
|
194
|
+
|
195
|
+
lua.set_error_handler(%{
|
196
|
+
function (msg)
|
197
|
+
return 'something went wrong: ' .. string.gmatch(msg, ": (.+)$")()
|
198
|
+
end
|
199
|
+
})
|
200
|
+
|
201
|
+
begin
|
202
|
+
lua.eval('error("ouch!")')
|
203
|
+
rescue => e
|
204
|
+
puts(e)
|
205
|
+
end
|
206
|
+
# --> eval:pcall : 'something went wrong: ouch!' (2 LUA_ERRRUN)
|
207
|
+
|
208
|
+
#
|
209
|
+
# Ruby block error handler
|
210
|
+
|
211
|
+
lua.set_error_handler do |msg|
|
212
|
+
([ msg.split.last ] * 3).join(' ')
|
213
|
+
end
|
214
|
+
|
215
|
+
begin
|
216
|
+
lua.eval('error("ouch!")')
|
217
|
+
rescue => e
|
218
|
+
puts(e)
|
219
|
+
end
|
220
|
+
# --> eval:pcall : 'ouch! ouch! ouch!' (2 LUA_ERRRUN)
|
221
|
+
|
222
|
+
#
|
223
|
+
# prepackaged :traceback handler
|
224
|
+
|
225
|
+
lua.set_error_handler(:traceback)
|
226
|
+
|
227
|
+
begin
|
228
|
+
lua.eval('error("ouch!")')
|
229
|
+
rescue => e
|
230
|
+
puts(e)
|
231
|
+
end
|
232
|
+
# -->
|
233
|
+
# eval:pcall : '[string "line"]:1: ouch!
|
234
|
+
# stack traceback:
|
235
|
+
# [C]: in function 'error'
|
236
|
+
# [string "line"]:1: in main chunk' (2 LUA_ERRRUN)
|
237
|
+
|
238
|
+
#
|
239
|
+
# unset the error handler
|
240
|
+
|
241
|
+
lua.set_error_handler(nil)
|
242
|
+
|
243
|
+
begin
|
244
|
+
lua.eval('error("ouch!")')
|
245
|
+
rescue => e
|
246
|
+
puts(e)
|
247
|
+
end
|
248
|
+
# --> eval:pcall : '[string "line"]:1: ouch!' (2 LUA_ERRRUN)
|
249
|
+
# (back to default)
|
250
|
+
```
|
251
|
+
|
171
252
|
|
172
253
|
## compiling liblua.dylib
|
173
254
|
|
@@ -216,7 +297,7 @@ sudo cp src/liblua.dylib /usr/local/lib/liblua.5.1.4.dylib
|
|
216
297
|
|
217
298
|
## tested with
|
218
299
|
|
219
|
-
ruby 1.
|
300
|
+
ruby 1.9.1p0, jruby 1.2.0
|
220
301
|
jruby 1.1.6 has an issue with errors raised inside of Ruby functions (callbacks)
|
221
302
|
|
222
303
|
ruby-ffi 0.4.0 and 0.5.0
|
data/lib/rufus/lua/error.rb
CHANGED
@@ -25,25 +25,29 @@
|
|
25
25
|
|
26
26
|
module Rufus::Lua
|
27
27
|
|
28
|
+
# error codes from 0 to 5
|
29
|
+
#
|
30
|
+
LUA_ERRS = %w[ 0 LUA_YIELD LUA_ERRRUN LUA_ERRSYNTAX LUA_ERRMEM LUA_ERRERR ]
|
31
|
+
|
28
32
|
#
|
29
33
|
# An error class for rufus-lua.
|
30
34
|
#
|
31
35
|
class LuaError < RuntimeError
|
32
36
|
|
33
37
|
attr_reader :kind, :errcode, :msg
|
34
|
-
attr_reader :
|
38
|
+
attr_reader :bndng, :filename, :lineno
|
35
39
|
|
36
40
|
attr_reader :original_backtrace
|
37
41
|
|
38
|
-
def initialize(kind, errcode, msg,
|
42
|
+
def initialize(kind, errcode, msg, bndng, filename, lineno)
|
39
43
|
|
40
|
-
super("#{kind} : '#{msg}' (#{errcode})")
|
44
|
+
super("#{kind} : '#{msg}' (#{errcode} #{LUA_ERRS[errcode]})")
|
41
45
|
|
42
46
|
@kind = kind
|
43
47
|
@errcode = errcode
|
44
48
|
@msg = msg
|
45
49
|
|
46
|
-
@
|
50
|
+
@bndng = bndng
|
47
51
|
@filename = filename
|
48
52
|
@lineno = lineno
|
49
53
|
end
|
data/lib/rufus/lua/lib.rb
CHANGED
@@ -77,8 +77,6 @@ module Lua
|
|
77
77
|
#
|
78
78
|
# attach functions
|
79
79
|
|
80
|
-
attach_function :strlen, [ :string ], :int
|
81
|
-
|
82
80
|
attach_function :lua_close, [ :pointer ], :void
|
83
81
|
|
84
82
|
attach_function :luaL_openlibs, [ :pointer ], :void
|
@@ -118,6 +116,16 @@ module Lua
|
|
118
116
|
attach_function :lua_pushstring, [ :pointer, :string ], :pointer
|
119
117
|
attach_function :lua_pushlstring, [ :pointer, :pointer, :int ], :pointer
|
120
118
|
|
119
|
+
attach_function :lua_remove, [ :pointer, :int ], :void
|
120
|
+
# removes the value at the given stack index, shifting down all elts above
|
121
|
+
|
122
|
+
#attach_function :lua_pushvalue, [ :pointer, :int ], :void
|
123
|
+
# pushes a copy of the value at the given index to the top of the stack
|
124
|
+
#attach_function :lua_insert, [ :pointer, :int ], :void
|
125
|
+
# moves the top elt to the given index, shifting up all elts above
|
126
|
+
#attach_function :lua_replace, [ :pointer, :int ], :void
|
127
|
+
# pops the top elt and override the elt at given index with it
|
128
|
+
|
121
129
|
attach_function :lua_rawgeti, [ :pointer, :int, :int ], :void
|
122
130
|
|
123
131
|
attach_function :luaL_newstate, [], :pointer
|
data/lib/rufus/lua/objects.rb
CHANGED
@@ -42,6 +42,7 @@ module Rufus::Lua
|
|
42
42
|
@pointer = pointer
|
43
43
|
@ref = Lib.luaL_ref(@pointer, LUA_REGISTRYINDEX)
|
44
44
|
# this pops the object out of the stack !
|
45
|
+
@error_handler = 0
|
45
46
|
end
|
46
47
|
|
47
48
|
# Frees the reference to this object
|
@@ -60,7 +61,7 @@ module Rufus::Lua
|
|
60
61
|
#
|
61
62
|
def load_onto_stack
|
62
63
|
|
63
|
-
|
64
|
+
raise RuntimeError.new(
|
64
65
|
"#{self.class} got freed, cannot re-access it directly"
|
65
66
|
) unless @ref
|
66
67
|
|
@@ -286,7 +287,7 @@ module Rufus::Lua
|
|
286
287
|
h = self.to_h
|
287
288
|
|
288
289
|
pure && h.keys.find { |k| not [ Float ].include?(k.class) } &&
|
289
|
-
|
290
|
+
fail('cannot turn hash into array, some keys are not numbers')
|
290
291
|
|
291
292
|
a_keys = (1..objlen).to_a.collect { |k| k.to_f }
|
292
293
|
keys = a_keys + (h.keys - a_keys)
|
data/lib/rufus/lua/state.rb
CHANGED
@@ -86,14 +86,15 @@ module Rufus::Lua
|
|
86
86
|
|
87
87
|
# This method holds the 'eval' mechanism.
|
88
88
|
#
|
89
|
-
def loadstring_and_call(s,
|
89
|
+
def loadstring_and_call(s, bndng, filename, lineno)
|
90
90
|
|
91
91
|
bottom = stack_top
|
92
|
+
chunk = filename ? "#{filename}:#{lineno}" : 'line'
|
92
93
|
|
93
|
-
err = Lib.luaL_loadbuffer(@pointer, s,
|
94
|
-
fail_if_error('eval:compile', err,
|
94
|
+
err = Lib.luaL_loadbuffer(@pointer, s, s.length, chunk)
|
95
|
+
fail_if_error('eval:compile', err, bndng, filename, lineno)
|
95
96
|
|
96
|
-
pcall(bottom, 0,
|
97
|
+
pcall(bottom, 0, bndng, filename, lineno) # arg_count is set to 0
|
97
98
|
end
|
98
99
|
|
99
100
|
# Returns a string representation of the state's stack.
|
@@ -188,7 +189,8 @@ module Rufus::Lua
|
|
188
189
|
def stack_pop
|
189
190
|
|
190
191
|
r = stack_fetch
|
191
|
-
stack_unstack
|
192
|
+
stack_unstack if r.class != Rufus::Lua::Table
|
193
|
+
|
192
194
|
r
|
193
195
|
end
|
194
196
|
|
@@ -200,7 +202,7 @@ module Rufus::Lua
|
|
200
202
|
new_top = 0 if new_top < 0
|
201
203
|
#
|
202
204
|
# there are no safeguard in Lua, setting top to -2 work well
|
203
|
-
# when the stack is
|
205
|
+
# when the stack is crowded, but it has bad side effects when the
|
204
206
|
# stack is empty... Now safeguarding by ourselves.
|
205
207
|
|
206
208
|
Lib.lua_settop(@pointer, new_top)
|
@@ -222,8 +224,8 @@ module Rufus::Lua
|
|
222
224
|
when Fixnum then Lib.lua_pushinteger(@pointer, o)
|
223
225
|
when Float then Lib.lua_pushnumber(@pointer, o)
|
224
226
|
|
225
|
-
when String then Lib.lua_pushlstring(@pointer, o, o.
|
226
|
-
when Symbol then Lib.
|
227
|
+
when String then Lib.lua_pushlstring(@pointer, o, o.bytesize)
|
228
|
+
when Symbol then Lib.lua_pushlstring(@pointer, o.to_s, o.to_s.bytesize)
|
227
229
|
|
228
230
|
when Hash then stack_push_hash(o)
|
229
231
|
when Array then stack_push_array(o)
|
@@ -269,6 +271,27 @@ module Rufus::Lua
|
|
269
271
|
Lib.lua_getfield(@pointer, LUA_GLOBALSINDEX, name)
|
270
272
|
end
|
271
273
|
|
274
|
+
# Loads a field of the table currently on the top of the stack
|
275
|
+
#
|
276
|
+
def stack_load_field(name)
|
277
|
+
|
278
|
+
Lib.lua_getfield(@pointer, -1, name)
|
279
|
+
end
|
280
|
+
|
281
|
+
# Loads a path (for example "debug.traceback") on top of the stack.
|
282
|
+
#
|
283
|
+
def stack_load_path(path)
|
284
|
+
|
285
|
+
ps = path.split('.')
|
286
|
+
|
287
|
+
stack_load_global(ps.shift)
|
288
|
+
|
289
|
+
while ps.first
|
290
|
+
stack_load_field(ps.shift)
|
291
|
+
Lib.lua_remove(@pointer, -2)
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
272
295
|
# Loads the Lua object registered with the given ref on top of the stack
|
273
296
|
#
|
274
297
|
def stack_load_ref(ref)
|
@@ -294,14 +317,14 @@ module Rufus::Lua
|
|
294
317
|
#
|
295
318
|
# Will raise an error in case of failure.
|
296
319
|
#
|
297
|
-
def pcall(stack_bottom, arg_count,
|
320
|
+
def pcall(stack_bottom, arg_count, bndng, filename, lineno)
|
298
321
|
|
299
322
|
#err = Lib.lua_pcall(@pointer, 0, 1, 0)
|
300
|
-
#
|
301
|
-
# rest of the time
|
323
|
+
# When there's only 1 return value.
|
324
|
+
# Use LUA_MULTRET (-1) the rest of the time
|
302
325
|
|
303
|
-
err = Lib.lua_pcall(@pointer, arg_count, LUA_MULTRET,
|
304
|
-
fail_if_error('eval:pcall', err,
|
326
|
+
err = Lib.lua_pcall(@pointer, arg_count, LUA_MULTRET, @error_handler)
|
327
|
+
fail_if_error('eval:pcall', err, bndng, filename, lineno)
|
305
328
|
|
306
329
|
return_result(stack_bottom)
|
307
330
|
end
|
@@ -320,21 +343,14 @@ module Rufus::Lua
|
|
320
343
|
# This method will raise an error with err > 0, else it will immediately
|
321
344
|
# return.
|
322
345
|
#
|
323
|
-
def fail_if_error(kind, err,
|
346
|
+
def fail_if_error(kind, err, bndng, filename, lineno)
|
324
347
|
|
325
348
|
return if err < 1
|
326
349
|
|
327
|
-
# TODO :
|
328
|
-
#
|
329
|
-
# LUA_ERRRUN: a runtime error.
|
330
|
-
# LUA_ERRMEM: memory allocation error. For such errors, Lua does not call
|
331
|
-
# the error handler function.
|
332
|
-
# LUA_ERRERR: error while running the error handler function.
|
333
|
-
|
334
350
|
s = Lib.lua_tolstring(@pointer, -1, nil).read_string
|
335
351
|
Lib.lua_settop(@pointer, -2)
|
336
352
|
|
337
|
-
fail LuaError.new(kind, err, s,
|
353
|
+
fail LuaError.new(kind, err, s, bndng, filename, lineno)
|
338
354
|
end
|
339
355
|
|
340
356
|
# Given the name of a Lua global variable, will return its value (or nil
|
@@ -347,6 +363,17 @@ module Rufus::Lua
|
|
347
363
|
end
|
348
364
|
end
|
349
365
|
|
366
|
+
class CallbackState
|
367
|
+
include StateMixin
|
368
|
+
|
369
|
+
def initialize(pointer)
|
370
|
+
|
371
|
+
@pointer = pointer
|
372
|
+
@callbacks = []
|
373
|
+
@error_handler = 0
|
374
|
+
end
|
375
|
+
end
|
376
|
+
|
350
377
|
#
|
351
378
|
# A Lua state, wraps a Lua runtime.
|
352
379
|
#
|
@@ -389,13 +416,44 @@ module Rufus::Lua
|
|
389
416
|
# garbage collection (Scott).
|
390
417
|
|
391
418
|
@callbacks = []
|
419
|
+
|
420
|
+
@error_handler = 0
|
421
|
+
end
|
422
|
+
|
423
|
+
def set_error_handler(lua_code=nil, &block)
|
424
|
+
|
425
|
+
if lua_code == nil && block
|
426
|
+
|
427
|
+
function('_ruby_error_handler', &block)
|
428
|
+
stack_load_path('_ruby_error_handler')
|
429
|
+
@error_handler = stack_top
|
430
|
+
|
431
|
+
elsif lua_code == nil
|
432
|
+
|
433
|
+
Lib.lua_remove(@pointer, @error_handler) if @error_handler > 0
|
434
|
+
@error_handler = 0
|
435
|
+
|
436
|
+
elsif lua_code == :traceback
|
437
|
+
|
438
|
+
stack_load_path('debug.traceback')
|
439
|
+
@error_handler = stack_top
|
440
|
+
|
441
|
+
else
|
442
|
+
|
443
|
+
lua_code = lua_code.strip
|
444
|
+
lua_code = 'return ' + lua_code unless lua_code.match(/^return\s+/)
|
445
|
+
|
446
|
+
r = self.eval(lua_code)
|
447
|
+
r.send(:load_onto_stack)
|
448
|
+
@error_handler = stack_top
|
449
|
+
end
|
392
450
|
end
|
393
451
|
|
394
452
|
# Evaluates a piece (string) of Lua code within the state.
|
395
453
|
#
|
396
|
-
def eval(s,
|
454
|
+
def eval(s, bndng=nil, filename=nil, lineno=nil)
|
397
455
|
|
398
|
-
loadstring_and_call(s,
|
456
|
+
loadstring_and_call(s, bndng, filename, lineno)
|
399
457
|
end
|
400
458
|
|
401
459
|
# Returns a value set at the 'global' level in the state.
|
@@ -462,24 +520,24 @@ module Rufus::Lua
|
|
462
520
|
#
|
463
521
|
def function(name, opts={}, &block)
|
464
522
|
|
465
|
-
|
523
|
+
fail 'please pass a block for the body of the function' unless block
|
466
524
|
|
467
525
|
to_ruby = opts[:to_ruby]
|
468
526
|
|
469
527
|
callback = Proc.new do |state|
|
470
528
|
|
529
|
+
s = CallbackState.new(state)
|
471
530
|
args = []
|
472
531
|
|
473
532
|
loop do
|
474
533
|
|
475
|
-
break if stack_top == 0 # never touch stack[0] !!
|
534
|
+
break if s.stack_top == 0 # never touch stack[0] !!
|
476
535
|
|
477
|
-
arg = stack_fetch
|
478
|
-
break if arg.class == Rufus::Lua::Function
|
536
|
+
arg = s.stack_fetch
|
479
537
|
|
480
538
|
args.unshift(arg)
|
481
539
|
|
482
|
-
stack_unstack unless args.first.is_a?(Rufus::Lua::
|
540
|
+
s.stack_unstack unless args.first.is_a?(Rufus::Lua::Ref)
|
483
541
|
end
|
484
542
|
|
485
543
|
while args.size < block.arity
|
@@ -491,7 +549,7 @@ module Rufus::Lua
|
|
491
549
|
|
492
550
|
result = block.call(*args)
|
493
551
|
|
494
|
-
stack_push(result)
|
552
|
+
s.stack_push(result)
|
495
553
|
|
496
554
|
1
|
497
555
|
end
|
@@ -538,7 +596,7 @@ module Rufus::Lua
|
|
538
596
|
#
|
539
597
|
def close
|
540
598
|
|
541
|
-
|
599
|
+
fail 'State already closed' unless @pointer
|
542
600
|
Lib.lua_close(@pointer)
|
543
601
|
@pointer = nil
|
544
602
|
end
|
@@ -547,7 +605,7 @@ module Rufus::Lua
|
|
547
605
|
#
|
548
606
|
def gc_count
|
549
607
|
|
550
|
-
|
608
|
+
fail 'State got closed, cannot proceed' unless @pointer
|
551
609
|
Lib.lua_gc(@pointer, LUA_GCCOUNT, 0)
|
552
610
|
end
|
553
611
|
|
@@ -555,7 +613,7 @@ module Rufus::Lua
|
|
555
613
|
#
|
556
614
|
def gc_collect!
|
557
615
|
|
558
|
-
|
616
|
+
fail 'State got closed, cannot proceed' unless @pointer
|
559
617
|
Lib.lua_gc(@pointer, LUA_GCCOLLECT, 0)
|
560
618
|
end
|
561
619
|
|
@@ -563,7 +621,7 @@ module Rufus::Lua
|
|
563
621
|
#
|
564
622
|
def gc_stop
|
565
623
|
|
566
|
-
|
624
|
+
fail 'State got closed, cannot proceed' unless @pointer
|
567
625
|
Lib.lua_gc(@pointer, LUA_GCSTOP, 0)
|
568
626
|
end
|
569
627
|
|
@@ -571,7 +629,7 @@ module Rufus::Lua
|
|
571
629
|
#
|
572
630
|
def gc_resume
|
573
631
|
|
574
|
-
|
632
|
+
fail 'State got closed, cannot proceed' unless @pointer
|
575
633
|
Lib.lua_gc(@pointer, LUA_GCRESTART, 0)
|
576
634
|
end
|
577
635
|
|
data/lib/rufus/lua/utils.rb
CHANGED
data/lib/rufus/lua/version.rb
CHANGED
data/spec/coroutines_spec.rb
CHANGED
@@ -68,15 +68,12 @@ describe Rufus::Lua::State do
|
|
68
68
|
#
|
69
69
|
it 'yields the right value' do
|
70
70
|
|
71
|
-
pending 'yield across callback no worky on Lua 5.1.x'
|
72
|
-
|
73
71
|
@s.function :host_function do
|
74
72
|
'success'
|
75
73
|
end
|
76
74
|
r = @s.eval(%{
|
77
75
|
function routine()
|
78
76
|
coroutine.yield(host_function())
|
79
|
-
--coroutine.yield("success") -- that works :-(
|
80
77
|
end
|
81
78
|
co = coroutine.create(routine)
|
82
79
|
a, b = coroutine.resume(co)
|
@@ -85,6 +82,55 @@ describe Rufus::Lua::State do
|
|
85
82
|
|
86
83
|
expect(r).to eq([ true, 'success' ])
|
87
84
|
end
|
85
|
+
|
86
|
+
it 'executes a ruby function within a coroutine' do
|
87
|
+
|
88
|
+
run_count = 0
|
89
|
+
|
90
|
+
@s.function :host_function do
|
91
|
+
run_count += 1
|
92
|
+
end
|
93
|
+
r = @s.eval(%{
|
94
|
+
function routine()
|
95
|
+
host_function()
|
96
|
+
coroutine.yield()
|
97
|
+
host_function()
|
98
|
+
coroutine.yield()
|
99
|
+
end
|
100
|
+
co = coroutine.create(routine)
|
101
|
+
a, b = coroutine.resume(co)
|
102
|
+
a, b = coroutine.resume(co)
|
103
|
+
return { a, b }
|
104
|
+
}).to_ruby
|
105
|
+
|
106
|
+
expect(r).to eq([ true])
|
107
|
+
expect(run_count).to eq(2)
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'executes a ruby function (with arguments) within a coroutine' do
|
111
|
+
|
112
|
+
run_count = 0
|
113
|
+
last_arguments = nil
|
114
|
+
|
115
|
+
@s.function :host_function, { :to_ruby => true } do |*args|
|
116
|
+
run_count += 1
|
117
|
+
last_arguments = args
|
118
|
+
0
|
119
|
+
end
|
120
|
+
r = @s.eval(%{
|
121
|
+
function routine()
|
122
|
+
host_function("hi")
|
123
|
+
coroutine.yield()
|
124
|
+
end
|
125
|
+
co = coroutine.create(routine)
|
126
|
+
a, b = coroutine.resume(co)
|
127
|
+
return { a, b }
|
128
|
+
}).to_ruby
|
129
|
+
|
130
|
+
expect(r).to eq([ true ])
|
131
|
+
expect(run_count).to eq(1)
|
132
|
+
expect(last_arguments).to eq([ "hi" ])
|
133
|
+
end
|
88
134
|
end
|
89
135
|
end
|
90
136
|
|
@@ -0,0 +1,222 @@
|
|
1
|
+
|
2
|
+
#
|
3
|
+
# Specifying rufus-lua
|
4
|
+
#
|
5
|
+
# Tue Jul 22 21:07:10 JST 2014
|
6
|
+
#
|
7
|
+
|
8
|
+
require 'spec_base'
|
9
|
+
|
10
|
+
|
11
|
+
describe 'State and error handler' do
|
12
|
+
|
13
|
+
before do
|
14
|
+
@s = Rufus::Lua::State.new
|
15
|
+
end
|
16
|
+
after do
|
17
|
+
@s.close
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '#set_error_handler(lua_code)' do
|
21
|
+
|
22
|
+
it 'registers a function as error handler' do
|
23
|
+
|
24
|
+
le = nil
|
25
|
+
|
26
|
+
@s.set_error_handler(%{
|
27
|
+
function (e)
|
28
|
+
return e .. '\\n' .. debug.traceback()
|
29
|
+
end
|
30
|
+
})
|
31
|
+
|
32
|
+
@s.eval(%{
|
33
|
+
function f ()
|
34
|
+
error("in f")
|
35
|
+
end
|
36
|
+
}, nil, 'mystuff.lua', 77)
|
37
|
+
begin
|
38
|
+
@s.eval('f()', nil, 'mymain.lua', 88)
|
39
|
+
rescue Rufus::Lua::LuaError => le
|
40
|
+
end
|
41
|
+
|
42
|
+
expect(le.message).to eq(%{
|
43
|
+
eval:pcall : '[string "mystuff.lua:77"]:3: in f
|
44
|
+
stack traceback:
|
45
|
+
[string "line"]:2: in function <[string "line"]:1>
|
46
|
+
[C]: in function 'error'
|
47
|
+
[string "mystuff.lua:77"]:3: in function 'f'
|
48
|
+
[string "mymain.lua:88"]:1: in main chunk' (2 LUA_ERRRUN)
|
49
|
+
}.strip)
|
50
|
+
|
51
|
+
expect(@s.send(:stack_top)).to eq(1)
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'sets the error handler in a permanent way' do
|
55
|
+
|
56
|
+
le = nil
|
57
|
+
|
58
|
+
@s.set_error_handler(%{
|
59
|
+
function (e)
|
60
|
+
return 'something went wrong: ' .. string.gmatch(e, ": (.+)$")()
|
61
|
+
end
|
62
|
+
})
|
63
|
+
|
64
|
+
begin
|
65
|
+
@s.eval('error("a")')
|
66
|
+
rescue Rufus::Lua::LuaError => le
|
67
|
+
end
|
68
|
+
|
69
|
+
expect(le.msg).to eq('something went wrong: a')
|
70
|
+
|
71
|
+
begin
|
72
|
+
@s.eval('error("b")')
|
73
|
+
rescue Rufus::Lua::LuaError => le
|
74
|
+
end
|
75
|
+
|
76
|
+
expect(le.msg).to eq('something went wrong: b')
|
77
|
+
end
|
78
|
+
|
79
|
+
context 'CallbackState' do
|
80
|
+
|
81
|
+
# Setting an error handler on the calling state or even directly
|
82
|
+
# on the CallbackState doesn't have any effect.
|
83
|
+
# Any error handler seems bypassed.
|
84
|
+
|
85
|
+
it 'has no effect' do
|
86
|
+
|
87
|
+
e = nil
|
88
|
+
|
89
|
+
@s.set_error_handler(%{
|
90
|
+
function (e) return 'bad: ' .. string.gmatch(e, ": (.+)$")() end
|
91
|
+
})
|
92
|
+
f = @s.function(:do_fail) { fail('in style') }
|
93
|
+
begin
|
94
|
+
@s.eval('do_fail()')
|
95
|
+
rescue Exception => e
|
96
|
+
end
|
97
|
+
|
98
|
+
expect(e.class).to eq(RuntimeError)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
describe '#set_error_handler(:traceback)' do
|
104
|
+
|
105
|
+
it 'sets a vanilla debug.traceback() error handler' do
|
106
|
+
|
107
|
+
le = nil
|
108
|
+
|
109
|
+
@s.set_error_handler(:traceback)
|
110
|
+
|
111
|
+
@s.eval(%{
|
112
|
+
function f ()
|
113
|
+
error("in f")
|
114
|
+
end
|
115
|
+
}, nil, 'mystuff.lua', 77)
|
116
|
+
begin
|
117
|
+
@s.eval('f()', nil, 'mymain.lua', 88)
|
118
|
+
rescue Rufus::Lua::LuaError => le
|
119
|
+
end
|
120
|
+
|
121
|
+
expect(le.message).to eq(%{
|
122
|
+
eval:pcall : '[string "mystuff.lua:77"]:3: in f
|
123
|
+
stack traceback:
|
124
|
+
[C]: in function 'error'
|
125
|
+
[string "mystuff.lua:77"]:3: in function 'f'
|
126
|
+
[string "mymain.lua:88"]:1: in main chunk' (2 LUA_ERRRUN)
|
127
|
+
}.strip)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
describe '#set_error_handler(:backtrace)' do
|
132
|
+
|
133
|
+
it 'sets a special handler that provides a merged Ruby then Lua backtrace'
|
134
|
+
# does it make any sense?
|
135
|
+
end
|
136
|
+
|
137
|
+
describe '#set_error_handler(&ruby_block)' do
|
138
|
+
|
139
|
+
it 'sets a Ruby callback as handler' do
|
140
|
+
|
141
|
+
le = nil
|
142
|
+
|
143
|
+
@s.set_error_handler do |msg|
|
144
|
+
([ msg.split.last ] * 3).join(' ')
|
145
|
+
end
|
146
|
+
|
147
|
+
begin
|
148
|
+
@s.eval('error("tora")')
|
149
|
+
rescue Rufus::Lua::LuaError => le
|
150
|
+
end
|
151
|
+
|
152
|
+
expect(le.msg).to eq('tora tora tora')
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
describe '#set_error_handler(nil)' do
|
157
|
+
|
158
|
+
it 'unsets the current error handler' do
|
159
|
+
|
160
|
+
le = nil
|
161
|
+
|
162
|
+
# set
|
163
|
+
|
164
|
+
@s.set_error_handler(%{
|
165
|
+
function (e)
|
166
|
+
return 'something went wrong: ' .. string.gmatch(e, ": (.+)$")()
|
167
|
+
end
|
168
|
+
})
|
169
|
+
|
170
|
+
begin
|
171
|
+
@s.eval('error("a")')
|
172
|
+
rescue Rufus::Lua::LuaError => le
|
173
|
+
end
|
174
|
+
|
175
|
+
expect(le.msg).to eq('something went wrong: a')
|
176
|
+
|
177
|
+
# unset
|
178
|
+
|
179
|
+
@s.set_error_handler(nil)
|
180
|
+
|
181
|
+
begin
|
182
|
+
@s.eval('error("b")')
|
183
|
+
rescue Rufus::Lua::LuaError => le
|
184
|
+
end
|
185
|
+
|
186
|
+
expect(le.msg).to eq('[string "line"]:1: b')
|
187
|
+
expect(@s.send(:stack_top)).to eq(0)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
describe '#set_error_handler(x)' do
|
192
|
+
|
193
|
+
it 'pops the previous handler from the stack' do
|
194
|
+
|
195
|
+
le = nil
|
196
|
+
|
197
|
+
expect(@s.send(:stack_top)).to eq(0)
|
198
|
+
|
199
|
+
@s.set_error_handler(%{ function (e) return "a" end })
|
200
|
+
#@s.set_error_handler(:traceback)
|
201
|
+
#@s.set_error_handler do |msg| return msg * 2; end
|
202
|
+
|
203
|
+
#@s.send(:print_stack)
|
204
|
+
|
205
|
+
expect(@s.send(:stack_top)).to eq(1)
|
206
|
+
|
207
|
+
@s.set_error_handler(%{ function (e) return "b" end })
|
208
|
+
|
209
|
+
#@s.send(:print_stack)
|
210
|
+
|
211
|
+
expect(@s.send(:stack_top)).to eq(1)
|
212
|
+
|
213
|
+
begin
|
214
|
+
@s.eval('error("Z")')
|
215
|
+
rescue Rufus::Lua::LuaError => le
|
216
|
+
end
|
217
|
+
|
218
|
+
expect(le.msg).to eq('b')
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
data/spec/eval_spec.rb
CHANGED
@@ -72,6 +72,28 @@ describe Rufus::Lua::State do
|
|
72
72
|
expect(@s.eval('return 1, 2')).to eq [ 1.0, 2.0 ]
|
73
73
|
end
|
74
74
|
|
75
|
+
it 'returns multiple values (tables included)' do
|
76
|
+
|
77
|
+
r = @s.eval('return 1, 2, {}')
|
78
|
+
|
79
|
+
expect(r.class).to eq Array
|
80
|
+
expect(r[0, 2]).to eq [ 1.0, 2.0 ]
|
81
|
+
expect(r[2].class).to eq Rufus::Lua::Table
|
82
|
+
expect(r[2].size).to eq 0
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'returns multiple values (tables included) (take 2)' do
|
86
|
+
|
87
|
+
r = @s.eval('return { "hello", "world" }, 2, { 3 }')
|
88
|
+
|
89
|
+
expect(r[0].class).to eq Rufus::Lua::Table
|
90
|
+
expect(r[0][1]).to eq 'hello'
|
91
|
+
expect(r[0][2]).to eq 'world'
|
92
|
+
expect(r[1]).to eq 2.0
|
93
|
+
expect(r[2].class).to eq Rufus::Lua::Table
|
94
|
+
expect(r[2][1]).to eq 3.0
|
95
|
+
end
|
96
|
+
|
75
97
|
it 'returns false' do
|
76
98
|
|
77
99
|
expect(@s.eval('return false')).to eq false
|
@@ -82,6 +104,17 @@ describe Rufus::Lua::State do
|
|
82
104
|
expect(@s.eval('return true')).to eq true
|
83
105
|
end
|
84
106
|
|
107
|
+
it 'returns tables' do
|
108
|
+
|
109
|
+
r = @s.eval('return { "hello", "world", 2 }')
|
110
|
+
|
111
|
+
expect(r.class).to eq Rufus::Lua::Table
|
112
|
+
expect(r[0]).to eq nil
|
113
|
+
expect(r[1]).to eq 'hello'
|
114
|
+
expect(r[2]).to eq 'world'
|
115
|
+
expect(r[3]).to eq 2.0
|
116
|
+
end
|
117
|
+
|
85
118
|
it 'accepts a binding optional argument'
|
86
119
|
|
87
120
|
it 'accepts a filename and a lineno optional arguments' do
|
@@ -92,6 +125,10 @@ describe Rufus::Lua::State do
|
|
92
125
|
rescue Rufus::Lua::LuaError => le
|
93
126
|
end
|
94
127
|
|
128
|
+
expect(le.kind).to eq('eval:pcall')
|
129
|
+
expect(le.msg).to eq('[string "/nada/virtual.lua:63"]:1: 77')
|
130
|
+
expect(le.errcode).to eq(2)
|
131
|
+
|
95
132
|
expect(le.filename).to eq('/nada/virtual.lua')
|
96
133
|
expect(le.lineno).to eq(63)
|
97
134
|
|
@@ -113,8 +150,11 @@ describe Rufus::Lua::State do
|
|
113
150
|
expect(le.msg).to eq('[string "line"]:1: 77')
|
114
151
|
expect(le.errcode).to eq(2)
|
115
152
|
|
153
|
+
expect(le.message).to eq(
|
154
|
+
"eval:pcall : '[string \"line\"]:1: 77' (2 LUA_ERRRUN)")
|
155
|
+
|
116
156
|
expect(le.filename).to eq(__FILE__)
|
117
|
-
expect(le.lineno).to eq(__LINE__ -
|
157
|
+
expect(le.lineno).to eq(__LINE__ - 12)
|
118
158
|
|
119
159
|
expect(le.original_backtrace.first).to match(/\/lua\/state\.rb:/)
|
120
160
|
expect(le.backtrace.first).to match(/\/eval_spec\.rb:/)
|
data/spec/rfunctions_spec.rb
CHANGED
@@ -52,6 +52,19 @@ describe Rufus::Lua::State do
|
|
52
52
|
expect(message).to eq 'obandegozaimasu'
|
53
53
|
end
|
54
54
|
|
55
|
+
it 'works with function arguments' do
|
56
|
+
|
57
|
+
message = nil
|
58
|
+
|
59
|
+
@s.function :exec do |fun|
|
60
|
+
message = fun.call()
|
61
|
+
end
|
62
|
+
|
63
|
+
@s.eval("exec(function() return 'foobar' end)")
|
64
|
+
|
65
|
+
expect(message).to eq 'foobar'
|
66
|
+
end
|
67
|
+
|
55
68
|
it 'binds functions inside of Lua tables' do
|
56
69
|
|
57
70
|
@s.eval('lib = {}')
|
@@ -147,11 +160,11 @@ describe Rufus::Lua::State do
|
|
147
160
|
it 'raise exceptions (Ruby -> Lua -> Ruby and back)' do
|
148
161
|
|
149
162
|
@s.function :do_fail do
|
150
|
-
raise
|
163
|
+
raise 'fail!'
|
151
164
|
end
|
152
165
|
|
153
166
|
expect(lambda {
|
154
|
-
@s.eval(
|
167
|
+
@s.eval('return do_fail()')
|
155
168
|
}).to raise_error(RuntimeError)
|
156
169
|
end
|
157
170
|
|
data/spec/string_spec.rb
CHANGED
@@ -41,6 +41,20 @@ describe 'lua strings' do
|
|
41
41
|
|
42
42
|
expect(f.call(s).to_h).to eq({ 's' => s, 'l' => 8.0 })
|
43
43
|
end
|
44
|
+
|
45
|
+
it 'accept symbols containing the \0 character' do
|
46
|
+
|
47
|
+
s = [ 65, 66, 0, 67, 0, 0, 68, 0 ].pack('c*').to_sym
|
48
|
+
|
49
|
+
f = @s.eval(%{
|
50
|
+
f = function(s)
|
51
|
+
return { s = s, l = string.len(s) }
|
52
|
+
end
|
53
|
+
return f
|
54
|
+
})
|
55
|
+
|
56
|
+
expect(f.call(s).to_h).to eq({ 's' => s.to_s, 'l' => 8.0 })
|
57
|
+
end
|
44
58
|
end
|
45
59
|
end
|
46
60
|
|
data/spec/utils_spec.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rufus-lua
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2014-
|
14
|
+
date: 2014-09-12 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: ffi
|
@@ -85,6 +85,7 @@ files:
|
|
85
85
|
- spec/eval_spec.rb
|
86
86
|
- spec/state_spec.rb
|
87
87
|
- spec/spec_base.rb
|
88
|
+
- spec/error_handler_spec.rb
|
88
89
|
- spec/string_spec.rb
|
89
90
|
- spec/gc_spec.rb
|
90
91
|
- spec/lib_spec.rb
|