rufus-lua 1.1.1 → 1.1.2
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.
- 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
|