Skip to content

Commit

Permalink
Improve error reporting
Browse files Browse the repository at this point in the history
  • Loading branch information
centau committed Jul 15, 2024
1 parent 14f8d38 commit 7bae251
Show file tree
Hide file tree
Showing 7 changed files with 50 additions and 119 deletions.
37 changes: 7 additions & 30 deletions src/bind.luau
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -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
Expand All @@ -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
Expand All @@ -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

Expand Down Expand Up @@ -94,22 +71,22 @@ 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
})
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 = {},
Expand Down
67 changes: 37 additions & 30 deletions src/graph.luau
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
local ok, cur_value, new_value
for i = 1, 2 do
cur_value = node.cache

flush_cleanups(node)
destroy_owned(node)

push_scope(node)
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(`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 = ycall(node.effect :: (T) -> T, cur_value)

local ok, new_value = pcall(node.effect :: (T) -> T, node.cache)
pop_scope()

if not ok then throw(new_value :: string) end

node.cache = new_value :: T
end

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(`side-effect error from source update\n{new_value}`)
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

node.cache = new_value

return cur_value ~= new_value
end

local function queue_children_for_update<T>(node: SourceNode<T>)
Expand Down
2 changes: 1 addition & 1 deletion src/root.luau
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
8 changes: 3 additions & 5 deletions src/throw.luau
Original file line number Diff line number Diff line change
@@ -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
29 changes: 0 additions & 29 deletions src/trace.luau

This file was deleted.

22 changes: 0 additions & 22 deletions test/benchmark.luau
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ end

local N = 2^18 -- 262144


TITLE "sources"

BENCH("create source", function()
Expand Down Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions test/tests.luau
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 7bae251

Please sign in to comment.