mirror of
https://github.com/centau/vide.git
synced 2026-08-20 14:41:37 +00:00
Improve error reporting
This commit is contained in:
parent
14f8d38a35
commit
7bae2517cd
7 changed files with 50 additions and 119 deletions
|
|
@ -1,35 +1,12 @@
|
|||
if not game then script = require "test/relative-string" end
|
||||
|
||||
local trace = require(script.Parent.trace)
|
||||
local flags = require(script.Parent.flags)
|
||||
local graph = require(script.Parent.graph)
|
||||
type Node<T> = graph.Node<T>
|
||||
local create_node = graph.create_node
|
||||
local assert_stable_scope = graph.assert_stable_scope
|
||||
local evaluate_node = graph.evaluate_node
|
||||
|
||||
function create_binding<T>(updater: (T) -> T, binding: T)
|
||||
if flags.strict then
|
||||
-- track bind creation trace
|
||||
local fn = updater
|
||||
local bind_trace = debug.traceback(nil, trace()-1)
|
||||
updater = function(...)
|
||||
local ok, result = xpcall(fn, function(err: string)
|
||||
return err
|
||||
end, ...)
|
||||
|
||||
if not ok then
|
||||
local btype =
|
||||
if (binding :: any).property then (binding :: any).property
|
||||
elseif (binding :: any).parent then "Parent"
|
||||
else "children"
|
||||
error(`PROPERTY BINDING ERROR: Property {btype}\n{result}\nBIND CREATION TRACE:\n{bind_trace}`, 0)
|
||||
end
|
||||
|
||||
return result
|
||||
end
|
||||
end
|
||||
|
||||
function create_implicit_effect<T>(updater: (T) -> T, binding: T)
|
||||
evaluate_node(create_node(assert_stable_scope(), updater, binding))
|
||||
end
|
||||
|
||||
|
|
@ -39,7 +16,7 @@ type PropertyBinding = {
|
|||
source: () -> unknown
|
||||
}
|
||||
|
||||
local function update_property(p: PropertyBinding)
|
||||
local function update_property_effect(p: PropertyBinding)
|
||||
(p.instance :: any)[p.property] = p.source()
|
||||
return p
|
||||
end
|
||||
|
|
@ -49,7 +26,7 @@ type ParentBinding = {
|
|||
parent: () -> Instance
|
||||
}
|
||||
|
||||
local function update_parent(p: ParentBinding)
|
||||
local function update_parent_effect(p: ParentBinding)
|
||||
p.instance.Parent = p.parent()
|
||||
return p
|
||||
end
|
||||
|
|
@ -61,7 +38,7 @@ type ChildrenBinding = {
|
|||
children: () -> Instance | { Instance }
|
||||
}
|
||||
|
||||
local function update_children(p: ChildrenBinding)
|
||||
local function update_children_effect(p: ChildrenBinding)
|
||||
local cur_children_set: { [Instance]: true } = p.cur_children_set -- cache of all children parented before update
|
||||
local new_child_set: { [Instance]: true } = p.new_children_set -- cache of all children parented after update
|
||||
|
||||
|
|
@ -94,7 +71,7 @@ end
|
|||
|
||||
return {
|
||||
property = function(instance, property, source)
|
||||
return create_binding(update_property, {
|
||||
return create_implicit_effect(update_property_effect, {
|
||||
instance = instance,
|
||||
property = property,
|
||||
source = source
|
||||
|
|
@ -102,14 +79,14 @@ return {
|
|||
end,
|
||||
|
||||
parent = function(instance, parent)
|
||||
return create_binding(update_parent, {
|
||||
return create_implicit_effect(update_parent_effect, {
|
||||
instance = instance,
|
||||
parent = parent
|
||||
})
|
||||
end,
|
||||
|
||||
children = function(instance, children)
|
||||
return create_binding(update_children, {
|
||||
return create_implicit_effect(update_children_effect, {
|
||||
instance = instance,
|
||||
cur_children_set = {},
|
||||
new_children_set = {},
|
||||
|
|
|
|||
|
|
@ -23,13 +23,14 @@ export type Node<T> = {
|
|||
local scopes = { n = 0 } :: { [number]: Node<any>, n: number } -- scopes stack
|
||||
|
||||
local function ycall<T, U>(fn: (T) -> U, arg: T): (boolean, string|U)
|
||||
local thread = coroutine.create(pcall)
|
||||
local resume_ok, run_ok, result = coroutine.resume(thread, fn, arg)
|
||||
local thread = coroutine.create(xpcall)
|
||||
local function efn(err: string) return debug.traceback(err, 3) end
|
||||
local resume_ok, run_ok, result = coroutine.resume(thread, fn, efn, arg)
|
||||
|
||||
assert(resume_ok)
|
||||
|
||||
if coroutine.status(thread) ~= "dead" then
|
||||
return false, "attempt to yield in reactive scope"
|
||||
return false, debug.traceback(thread, "attempt to yield in reactive scope")
|
||||
end
|
||||
|
||||
return run_ok, result
|
||||
|
|
@ -129,41 +130,47 @@ end
|
|||
local update_queue = { n = 0 } :: { n: number, [number]: Node<any> }
|
||||
|
||||
local function evaluate_node<T>(node: Node<T>)
|
||||
local cur_value = node.cache
|
||||
|
||||
if flags.strict then
|
||||
flush_cleanups(node)
|
||||
destroy_owned(node)
|
||||
|
||||
push_scope(node)
|
||||
|
||||
local ok, new_value = ycall(node.effect :: (T) -> T, cur_value)
|
||||
|
||||
pop_scope()
|
||||
|
||||
if not ok then throw(new_value :: string) end
|
||||
|
||||
node.cache = new_value :: T
|
||||
end
|
||||
local ok, cur_value, new_value
|
||||
for i = 1, 2 do
|
||||
cur_value = node.cache
|
||||
|
||||
flush_cleanups(node)
|
||||
destroy_owned(node)
|
||||
|
||||
push_scope(node)
|
||||
|
||||
local ok, new_value = pcall(node.effect :: (T) -> T, node.cache)
|
||||
|
||||
ok, new_value = ycall(node.effect :: (T) -> T, cur_value)
|
||||
pop_scope()
|
||||
|
||||
if not ok then
|
||||
table.clear(update_queue)
|
||||
update_queue.n = 0
|
||||
throw(`side-effect error from source update\n{new_value}`)
|
||||
throw(`effect stacktrace:\n{new_value :: string}`)
|
||||
end
|
||||
|
||||
node.cache = new_value :: T
|
||||
end
|
||||
|
||||
return cur_value ~= new_value
|
||||
else
|
||||
local cur_value = node.cache
|
||||
|
||||
flush_cleanups(node)
|
||||
destroy_owned(node)
|
||||
|
||||
push_scope(node)
|
||||
local ok, new_value = pcall(node.effect :: (T) -> T, node.cache)
|
||||
pop_scope()
|
||||
|
||||
if not ok then
|
||||
table.clear(update_queue)
|
||||
update_queue.n = 0
|
||||
throw(`effect stacktrace:\n{new_value}\n`)
|
||||
end
|
||||
|
||||
node.cache = new_value
|
||||
|
||||
return cur_value ~= new_value
|
||||
end
|
||||
end
|
||||
|
||||
local function queue_children_for_update<T>(node: SourceNode<T>)
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ local function root<T...>(fn: (destroy: () -> ()) -> T...): T...
|
|||
|
||||
if not result[1] then
|
||||
refs[node] = nil
|
||||
throw(`mount error\n{result[2]}`)
|
||||
throw(`error while running root():\n\n{result[2]}`)
|
||||
end
|
||||
|
||||
return unpack(result :: any, 2)
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
if not game then script = require "test/relative-string" end
|
||||
|
||||
local trace = require(script.Parent.trace)
|
||||
|
||||
local function throw(msg): any
|
||||
error(msg, trace() - 1)
|
||||
local function VIDE_ASSERT(msg): any
|
||||
error(msg, 0)
|
||||
end
|
||||
|
||||
return throw
|
||||
return VIDE_ASSERT
|
||||
|
|
|
|||
|
|
@ -1,29 +0,0 @@
|
|||
-- returns path to file as an array with each directory
|
||||
-- accounts for Roblox and Luau contexts
|
||||
local function get_path(s)
|
||||
if string.sub(s, #s - 4, #s) == ".luau" then
|
||||
s = string.sub(s, 1, #s - 5)
|
||||
end
|
||||
|
||||
return string.split(s, string.match(s, "%w+/") and "/" or ".")
|
||||
end
|
||||
|
||||
-- get directory of vide root
|
||||
local root do
|
||||
local path = get_path(debug.info(1, "s"))
|
||||
root = path[#path - 1]
|
||||
end
|
||||
|
||||
-- finds the first stack depth outside of any vide library function
|
||||
return function(): number
|
||||
local stack = 1
|
||||
|
||||
local path = get_path(debug.info(stack, "s"))
|
||||
|
||||
while path[#path] == root or path[#path - 1] == root do
|
||||
stack += 1
|
||||
path = get_path(debug.info(stack, "s"))
|
||||
end
|
||||
|
||||
return stack
|
||||
end
|
||||
|
|
@ -26,7 +26,6 @@ end
|
|||
|
||||
local N = 2^18 -- 262144
|
||||
|
||||
|
||||
TITLE "sources"
|
||||
|
||||
BENCH("create source", function()
|
||||
|
|
@ -449,27 +448,6 @@ end)
|
|||
|
||||
N *= 1024
|
||||
|
||||
TITLE "cleanup"
|
||||
|
||||
ROOT_BENCH("register new cleanup", function()
|
||||
local cleanup = cleanup
|
||||
|
||||
local cleaner = function() end
|
||||
|
||||
local callers = {}
|
||||
|
||||
for i = 1, N do
|
||||
callers[i] = function(fn, v)
|
||||
fn(v)
|
||||
return i -- return unique upvalue to ensure unique closure
|
||||
end
|
||||
end
|
||||
|
||||
for i = 1, START(N) do
|
||||
callers[i](cleanup, cleaner)
|
||||
end
|
||||
end)
|
||||
|
||||
TITLE "aggregate"
|
||||
|
||||
do
|
||||
|
|
|
|||
|
|
@ -218,8 +218,8 @@ TEST("graph", function()
|
|||
do
|
||||
local c = get_children(selected)
|
||||
CHECK(#c == 2)
|
||||
CHECK(table.find(c, bind1))
|
||||
CHECK(table.find(c, bind2))
|
||||
CHECK(table.find(c, bind1 :: any))
|
||||
CHECK(table.find(c, bind2 :: any))
|
||||
end
|
||||
|
||||
do
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue