Fix switch() not working in strict mode

Fixes #20
This commit is contained in:
Aaron Smith 2023-09-27 10:02:09 +01:00
parent fd2888afab
commit d07c705b64
3 changed files with 46 additions and 25 deletions

View file

@ -23,26 +23,17 @@ export type Node<T> = {
-- reactive scope stack -- reactive scope stack
local scopes = { n = 0 } :: { [number]: Node<any>, n: number } local scopes = { n = 0 } :: { [number]: Node<any>, n: number }
-- runs a given callback in a context that Luau does not allow yielding in local function ycall<T, U>(fn: (T) -> U, arg: T): (boolean, string|U)
local check_for_yield: <T...>(fn: (T...) -> (), T...) -> (boolean, string?) do local thread = coroutine.create(pcall)
local t = { __mode = "kv" } local resume_ok, run_ok, result = coroutine.resume(thread, fn, arg)
setmetatable(t, t)
check_for_yield = function(fn, ...: any) assert(resume_ok)
local args = { ... }
if coroutine.status(thread) ~= "dead" then
t.__unm = function(_) return false, "attempt to yield in reactive scope"
fn(unpack(args))
end
local ok, err: string? = pcall(function()
local _ = -t
end)
return ok, if err == "attempt to yield across metamethod/C-call boundary"
or err == "thread is not yieldable" then "yield occured"
else err
end end
return run_ok, result
end end
local function get_scope(): Node<unknown>? local function get_scope(): Node<unknown>?
@ -161,11 +152,13 @@ local function evaluate_node<T>(node: Node<T>)
open_scope(node) open_scope(node)
local ok, err = check_for_yield(node.effect :: (T) -> T, cur_value) local ok, new_value = ycall(node.effect :: (T) -> T, cur_value)
close_scope() close_scope()
if not ok then throw(err :: string) end if not ok then throw(new_value :: string) end
node.cache = new_value :: T
end end
run_cleanups(node) run_cleanups(node)
@ -173,7 +166,7 @@ local function evaluate_node<T>(node: Node<T>)
open_scope(node) open_scope(node)
local ok, new_value = pcall(node.effect :: (T) -> T, cur_value) local ok, new_value = pcall(node.effect :: (T) -> T, node.cache)
close_scope() close_scope()

View file

@ -53,7 +53,7 @@ local function switch<T, U>(source: () -> T): (map: Map<T, ((() -> U)?)>) -> ()
return result return result
end end
local node = create_node(nil :: any, update) local node = create_node(nil :: U?, update)
set_owner(node, owner) set_owner(node, owner)
evaluate_node(node) evaluate_node(node)

View file

@ -1067,9 +1067,6 @@ TEST("show()", wrap_root(function()
CHECK(outer == 2) CHECK(outer == 2)
CHECK(inner == 4) CHECK(inner == 4)
CHECK(destroyed == 3) CHECK(destroyed == 3)
end end
end)) end))
@ -1181,6 +1178,22 @@ TEST("switch()", wrap_root(function()
CHECK(n0 == n1) CHECK(n0 == n1)
end end
do CASE "strict"
vide.strict = true
local input = source(0)
local output = switch(input) {
[0] = function() return 0 end,
[1] = function() return 1 end,
}
CHECK(output() == 0)
input(1)
CHECK(output() == 1)
vide.strict = false
end
end)) end))
TEST("indexes()", wrap_root(function() TEST("indexes()", wrap_root(function()
@ -1364,6 +1377,21 @@ TEST("indexes()", wrap_root(function()
CHECK(updated[3] == 2) CHECK(updated[3] == 2)
CHECK(updated[4] == 2) CHECK(updated[4] == 2)
end end
do CASE "strict"
vide.strict = true
local input = source{1}
local output = indexes(input, function(v)
return { v }
end)
CHECK(output()[1][1]() == 1)
input{2}
CHECK(output()[1][1]() == 2)
vide.strict = false
end
end)) end))
TEST("values()", wrap_root(function() TEST("values()", wrap_root(function()