Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions spec/lua/object/coroutine_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,19 @@ module Lua
lua.close
end

it "yields multiple values in order" do
lua = Lua.load
t = lua.run %q(
return coroutine.create(function()
coroutine.yield(1, 2, "three")
end)
)

co = t.as(Lua::Coroutine)
co.resume.should eq [1.0, 2.0, "three"]
lua.close
end

it "can return an error" do
lua = Lua.load
t = lua.run %q(
Expand Down
11 changes: 11 additions & 0 deletions spec/lua/object/function_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,17 @@ module Lua
sum.call(2, 3).should be_nil
end

it "returns multiple values in order" do
swap = Lua::Stack.new.run(%q(
function swap(x, y)
return y, x
end

return swap
)).as(Lua::Function)
swap.call(1, 2).should eq [2.0, 1.0]
end

it "can call a function without arguments" do
f = Lua::Stack.new.run(%q(
function ff()
Expand Down
30 changes: 30 additions & 0 deletions spec/lua/stack/chunk_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,21 @@ module Lua::StackMixin
s.close
end

context "with multiple return values" do
it "returns the values in order" do
s = Stack.new
s.run(%q( return 1, 2, "three" )).should eq [1.0, 2.0, "three"]
s.close
end

it "keeps nil values and leaves the stack clean" do
s = Stack.new
s.run(%q( return 5, nil )).should eq [5.0, nil]
s.size.should eq 0
s.close
end
end

context "with multibyte UTF-8 characters" do
it "keeps the full chunk when a comment contains multibyte characters" do
s = Stack.new
Expand All @@ -60,6 +75,21 @@ module Lua::StackMixin
s.close
end
end

context "with embedded zero bytes" do
it "returns the full string" do
s = Stack.new
s.run(%q( return "a\0b" )).should eq "a\u{0}b"
s.close
end

it "passes the full string to Lua" do
s = Stack.new
s.set_global("x", "a\u{0}b")
s.run(%q( return #x )).should eq 3
s.close
end
end
end

describe "#run" do
Expand Down
19 changes: 19 additions & 0 deletions spec/lua/stack_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ module Lua
Stack.new.tap(&.<< :message)[1].should eq "message"
end

it "can push string with embedded zero bytes" do
Stack.new.tap(&.<< "a\u{0}b")[1].should eq "a\u{0}b"
end

it "can push array" do
r = Stack.new.tap(&.<< %w[lua is cool])[1].as(Table).map { |_, v| v }
r.should eq %w[lua is cool]
Expand Down Expand Up @@ -141,6 +145,21 @@ module Lua
it "returns nil when stack is empty" do
Stack.new.pop.should be_nil
end

it "removes a nil element from the top of the stack" do
stack = Stack.new.tap(&.<< nil)
stack.pop.should be_nil
stack.size.should eq 0
end

it "does not lose values below a popped nil" do
stack = Stack.new
stack << 5
stack << nil
stack.pop.should be_nil
stack.pop.should eq 5.0
stack.size.should eq 0
end
end

describe "#remove" do
Expand Down
3 changes: 2 additions & 1 deletion src/lua/callable.cr
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,8 @@ module LuaCallable

def self.__index(state : LibLua::State) : Int32 # __index(t,k)
stack = Lua::Stack.new(state, :all)
key = String.new LibLua.tolstring(state, -1, nil)
chars = LibLua.tolstring(state, -1, out len)
key = String.new(chars.as(UInt8*), len)
data = LibLua.touserdata(state, -2).as(LuaCallable*)
pointer = data.value
val = pointer._index(key)
Expand Down
20 changes: 15 additions & 5 deletions src/lua/stack.cr
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,9 @@ module Lua
when Int then LibLua.pushinteger(@state, o)
when Float then LibLua.pushnumber(@state, o)
when Bool then LibLua.pushboolean(@state, o ? 1 : 0)
when Char then LibLua.pushstring(@state, o.to_s)
when String then LibLua.pushstring(@state, o)
when Symbol then LibLua.pushstring(@state, o.to_s)
when Char then push_string o.to_s
when String then push_string o
when Symbol then push_string o.to_s
when Array, Tuple then pushtable(o.to_a)
when Hash, NamedTuple then pushtable(o.to_h)
when Proc(LibLua::State, Int32) then pushclosure(o.as(Proc))
Expand Down Expand Up @@ -119,7 +119,7 @@ module Lua
when TYPE::TNIL, TYPE::TNONE then nil
when TYPE::TBOOLEAN then LibLua.toboolean(@state, pos) == 1
when TYPE::TNUMBER then LibLua.tonumberx(@state, pos, nil)
when TYPE::TSTRING then String.new LibLua.tolstring(@state, pos, nil)
when TYPE::TSTRING then string_at(pos)
when TYPE::TTABLE then Table.new self, reference(pos)
when TYPE::TFUNCTION then Function.new self, reference(pos)
when TYPE::TTHREAD then Coroutine.new Stack.new(LibLua.tothread(@state, pos), libs.to_a)
Expand Down Expand Up @@ -202,7 +202,8 @@ module Lua
# stack.size # => 0
# ```
def pop
top.try &.tap { remove }
return if size == 0
top.tap { remove }
end

# Removes n elements from the stack.
Expand All @@ -219,5 +220,14 @@ module Lua
n = n < 0 ? 0 : [n, size].min
LibLua.settop(@state, -n - 1)
end

private def push_string(s : String)
LibLua.pushlstring(@state, s, s.bytesize)
end

private def string_at(pos)
chars = LibLua.tolstring(@state, pos, out len)
String.new(chars.as(UInt8*), len)
end
end
end
3 changes: 1 addition & 2 deletions src/lua/stack/coroutine_support.cr
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,13 @@ module Lua::StackMixin

# Starts and resumes a coroutine in the given thread
protected def resume(*args)
thread_pos = size
args.each { |arg| self.<< arg }

nres = 0
res = CALL.new LibLua.resume(@state, nil, args.size, pointerof(nres))
raise error(res, pop) if res > CALL::YIELD

pick_results thread_pos
pick_results size - nres + 1
end

# Returns the status of the current thread.
Expand Down
3 changes: 2 additions & 1 deletion src/lua/stack/util.cr
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ module Lua
end

protected def pick_results(start, finish = size)
elements = (start..finish).map { pop.as(::Lua::Type) }
elements = (start..finish).map { |pos| self[pos].as(::Lua::Type) }
remove elements.size
elements.size > 1 ? elements : elements.first?
end
end
Expand Down
Loading