mirror of
https://github.com/centau/vide.git
synced 2026-08-20 14:41:37 +00:00
Try improve error reporting
This commit is contained in:
parent
3b22f6ccf9
commit
58a31a1b32
16 changed files with 164 additions and 55 deletions
|
|
@ -1,7 +1,6 @@
|
|||
local typeof = game and typeof or require "../test/mock".typeof :: never
|
||||
|
||||
local flags = require "./flags"
|
||||
local throw = require "./throw"
|
||||
local implicit_effect = require "./implicit_effect"
|
||||
local _, is_action = require "./action"()
|
||||
local graph = require "./graph"
|
||||
|
|
@ -67,7 +66,7 @@ local function process_properties(properties: Map<unknown, unknown>, instance: I
|
|||
if type(property) == "string" then
|
||||
if flags.strict then -- check for duplicate property assignment at nesting depth
|
||||
if cache.nested_debug[depth][property] then
|
||||
throw(`duplicate property {property} at depth {depth}`)
|
||||
error(`duplicate property {property} at depth {depth}`, 0)
|
||||
end
|
||||
cache.nested_debug[depth][property] = true
|
||||
end
|
||||
|
|
@ -104,7 +103,7 @@ end
|
|||
-- applies table of nested properties to an instance using full vide semantics
|
||||
local function apply<T>(instance: T & Instance, properties: { [unknown]: unknown }): T
|
||||
if not properties then
|
||||
throw("attempt to call a constructor returned by create() with no properties")
|
||||
error "attempt to call a constructor returned by create() with no properties"
|
||||
end
|
||||
|
||||
-- queue parent assignment if any for last
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
local flags = require "./flags"
|
||||
local throw = require "./throw"
|
||||
local graph = require "./graph"
|
||||
|
||||
local function batch(setter: () -> ())
|
||||
|
|
@ -11,14 +10,14 @@ local function batch(setter: () -> ())
|
|||
from = graph.get_update_queue_length()
|
||||
end
|
||||
|
||||
local ok, err: string? = pcall(setter)
|
||||
local ok, err: string? = xpcall(setter, debug.traceback)
|
||||
|
||||
if not already_batching then
|
||||
flags.batch = false
|
||||
graph.flush_update_queue(from)
|
||||
end
|
||||
|
||||
if not ok then throw(`error occured while batching updates: {err}`) end
|
||||
if not ok then error(`error occured while batching updates: {err}`, 0) end
|
||||
end
|
||||
|
||||
return batch
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
local typeof = game and typeof or require "../test/mock".typeof :: never
|
||||
|
||||
local throw = require "./throw"
|
||||
local graph = require "./graph"
|
||||
local get_scope = graph.get_scope
|
||||
local push_cleanup = graph.push_cleanup
|
||||
|
|
@ -14,14 +13,14 @@ local function helper(obj: any)
|
|||
elseif obj.disconnect then function() obj:disconnect() end
|
||||
elseif obj.Destroy then function() obj:Destroy() end
|
||||
elseif obj.Disconnect then function() obj:Disconnect() end
|
||||
else throw("cannot cleanup given object")
|
||||
else error "cannot cleanup given object"
|
||||
end
|
||||
|
||||
local function cleanup(value: unknown)
|
||||
local scope = get_scope()
|
||||
|
||||
if not scope then
|
||||
throw "cannot cleanup outside a stable or reactive scope"
|
||||
error "cannot cleanup outside a stable or reactive scope"
|
||||
end; assert(scope)
|
||||
|
||||
if type(value) == "function" then
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
local throw = require "./throw"
|
||||
local graph = require "./graph"
|
||||
type Node<T> = graph.Node<T>
|
||||
local create_node = graph.create_node
|
||||
|
|
@ -44,10 +43,10 @@ local function context<T>(...: T): Context<T>
|
|||
if has_default ~= nil then
|
||||
return default_value
|
||||
else
|
||||
throw("attempt to get context when no context is set and no default context is set")
|
||||
error("attempt to get context when no context is set and no default context is set", 0)
|
||||
end
|
||||
else -- set
|
||||
if not scope then return throw("attempt to set context outside of a vide scope") end
|
||||
if not scope then return error("attempt to set context outside of a vide scope", 0) end
|
||||
|
||||
local value, component = ...
|
||||
|
||||
|
|
@ -62,7 +61,7 @@ local function context<T>(...: T): Context<T>
|
|||
pop_scope()
|
||||
|
||||
if not ok then
|
||||
throw(`error while running context:\n\n{result}`)
|
||||
error(`error while running context:\n\n{result}`, 0)
|
||||
end
|
||||
|
||||
return result
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
local typeof = game and typeof or require "../test/mock".typeof :: never
|
||||
local Instance = game and Instance or require "../test/mock".Instance :: never
|
||||
|
||||
local throw = require "./throw"
|
||||
local defaults = require "./defaults"
|
||||
local apply = require "./apply"
|
||||
|
||||
|
|
@ -10,7 +9,7 @@ local ctor_cache = {} :: { [string]: () -> Instance }
|
|||
setmetatable(ctor_cache :: any, {
|
||||
__index = function(self, class)
|
||||
local ok, instance: Instance = pcall(Instance.new, class :: any)
|
||||
if not ok then throw(`invalid class name, could not create instance of class { class }`) end
|
||||
if not ok then error(`invalid class name, could not create instance of class { class }`, 0) end
|
||||
|
||||
local default: { [string]: unknown }? = defaults[class]
|
||||
if default then
|
||||
|
|
@ -35,7 +34,7 @@ end
|
|||
local function clone_instance(instance: Instance)
|
||||
return function(properties: Props): Instance
|
||||
local clone = instance:Clone()
|
||||
if not clone then throw "attempt to clone a non-archivable instance" end
|
||||
if not clone then error "attempt to clone a non-archivable instance" end
|
||||
return apply(clone, properties)
|
||||
end
|
||||
end
|
||||
|
|
@ -47,7 +46,7 @@ local function create(class_or_instance: string | Instance, props: Props?): ((Pr
|
|||
elseif typeof(class_or_instance) == "Instance" then
|
||||
result = clone_instance(class_or_instance)
|
||||
else
|
||||
throw("bad argument #1, expected string or instance, got " .. typeof(class_or_instance))
|
||||
error("bad argument #1, expected string or instance, got " .. typeof(class_or_instance), 0)
|
||||
return nil :: never
|
||||
end
|
||||
if props then
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
local throw = require "./throw"
|
||||
local flags = require "./flags"
|
||||
|
||||
export type SourceNode<T> = {
|
||||
|
|
@ -22,9 +21,23 @@ export type Node<T> = {
|
|||
|
||||
local scopes = { n = 0 } :: { [number]: Node<any>, n: number } -- scopes stack
|
||||
|
||||
local function efn(err: string)
|
||||
local trace = debug.traceback(err, 2)
|
||||
|
||||
if string.find(err, "^effect error stacktrace") then -- if effect error is nested
|
||||
trace = string.gsub(" " .. trace, "\n", function() -- indent entire error
|
||||
return "\n "
|
||||
end)
|
||||
end
|
||||
|
||||
trace ..= "\nsource update stacktrace:"
|
||||
return trace
|
||||
end
|
||||
|
||||
local function ycall<T, U>(fn: (T) -> U, arg: T): (boolean, string|U)
|
||||
|
||||
local thread = coroutine.create(xpcall)
|
||||
local function efn(err: string) return debug.traceback(err, 3) end
|
||||
--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)
|
||||
|
|
@ -45,9 +58,9 @@ local function assert_stable_scope(): Node<unknown>
|
|||
|
||||
if not scope then
|
||||
local caller_name = debug.info(2, "n")
|
||||
return throw(`cannot use {caller_name}() outside a stable or reactive scope`)
|
||||
return error(`cannot use {caller_name}() outside a stable or reactive scope`, 0)
|
||||
elseif scope.effect then
|
||||
throw("cannot create a new reactive scope inside another reactive scope")
|
||||
error("cannot create a new reactive scope inside another reactive scope", 0)
|
||||
end
|
||||
|
||||
return scope
|
||||
|
|
@ -81,8 +94,8 @@ end
|
|||
local function flush_cleanups<T>(node: Node<T>)
|
||||
if node.cleanups then
|
||||
for _, fn in next, node.cleanups do
|
||||
local ok, err: string? = pcall(fn)
|
||||
if not ok then throw(`cleanup error: {err}`) end
|
||||
local ok, err: string? = xpcall(fn, debug.traceback)
|
||||
if not ok then error(`cleanup error: {err}`, 0) end
|
||||
end
|
||||
|
||||
table.clear(node.cleanups)
|
||||
|
|
@ -107,7 +120,7 @@ end
|
|||
|
||||
local function destroy<T>(node: Node<T>)
|
||||
if flags.strict and table.find(scopes, node) then
|
||||
throw("attempt to destroy an active scope")
|
||||
error("attempt to destroy an active scope", 0)
|
||||
end
|
||||
|
||||
flush_cleanups(node)
|
||||
|
|
@ -150,7 +163,7 @@ local function evaluate_node<T>(node: Node<T>)
|
|||
if not ok then
|
||||
table.clear(update_queue)
|
||||
update_queue.n = 0
|
||||
throw(`effect stacktrace:\n{new_value :: string}`)
|
||||
error(`effect error stacktrace\n{new_value :: string}`, 0)
|
||||
end
|
||||
|
||||
node.cache = new_value :: T
|
||||
|
|
@ -170,7 +183,7 @@ local function evaluate_node<T>(node: Node<T>)
|
|||
if not ok then
|
||||
table.clear(update_queue)
|
||||
update_queue.n = 0
|
||||
throw(`effect stacktrace:\n{new_value}\n`)
|
||||
error(`effect error:\n{new_value}\n`, 0)
|
||||
end
|
||||
|
||||
node.cache = new_value
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ local indexes, values = require "./maps"()
|
|||
local spring, update_springs = require "./spring"()
|
||||
local action = require "./action"()
|
||||
local changed = require "./changed"
|
||||
local throw = require "./throw"
|
||||
local flags = require "./flags"
|
||||
|
||||
export type Source<T> = source.Source<T>
|
||||
|
|
@ -98,7 +97,7 @@ local vide = {
|
|||
setmetatable(vide :: any, {
|
||||
__index = function(_, index: unknown): ()
|
||||
if flags[index] == nil then
|
||||
throw(`{tostring(index)} is not a valid member of vide`)
|
||||
error(`{tostring(index)} is not a valid member of vide`, 0)
|
||||
else
|
||||
return flags[index]
|
||||
end
|
||||
|
|
@ -106,7 +105,7 @@ setmetatable(vide :: any, {
|
|||
|
||||
__newindex = function(_, index: unknown, value: unknown)
|
||||
if flags[index] == nil then
|
||||
throw(`{tostring(index)} is not a valid member of vide`)
|
||||
error(`{tostring(index)} is not a valid member of vide, 0`)
|
||||
else
|
||||
flags[index] = value
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
local throw = require "./throw"
|
||||
local flags = require "./flags"
|
||||
local graph = require "./graph"
|
||||
type Node<T> = graph.Node<T>
|
||||
|
|
@ -20,7 +19,7 @@ local function check_primitives(t: {})
|
|||
|
||||
for _, v in next, t do
|
||||
if type(v) == "table" or type(v) == "userdata" or type(v) == "function" then continue end
|
||||
throw("table source map cannot return primitives")
|
||||
error("table source map cannot return primitives", 0)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
@ -71,7 +70,7 @@ local function indexes<K, VI, VO>(input: () -> Map<K, VI>, transform: (() -> VI,
|
|||
|
||||
push_scope(scope)
|
||||
|
||||
local ok, result = pcall(transform, function()
|
||||
local ok, result = xpcall(transform, debug.traceback, function()
|
||||
push_child_to_scope(node)
|
||||
return node.cache
|
||||
end, i)
|
||||
|
|
@ -132,7 +131,7 @@ local function values<K, VI, VO>(input: () -> Map<K, VI>, transform: (VI, () ->
|
|||
local cache = {}
|
||||
for _, v in next, data do
|
||||
if cache[v] ~= nil then
|
||||
throw "duplicate table value detected"
|
||||
error "duplicate table value detected"
|
||||
end
|
||||
cache[v] = true
|
||||
end
|
||||
|
|
@ -154,7 +153,7 @@ local function values<K, VI, VO>(input: () -> Map<K, VI>, transform: (VI, () ->
|
|||
|
||||
push_scope(scope)
|
||||
|
||||
local ok, result = pcall(transform, v, function()
|
||||
local ok, result = xpcall(transform, debug.traceback, v, function()
|
||||
push_child_to_scope(node)
|
||||
return node.cache
|
||||
end)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
local throw = require "./throw"
|
||||
local graph = require "./graph"
|
||||
type Node<T> = graph.Node<T>
|
||||
local create_node = graph.create_node
|
||||
|
|
@ -14,21 +13,20 @@ local function root<T...>(fn: (destroy: () -> ()) -> T...): (() -> (), T...)
|
|||
refs[node] = true -- prevent gc of root node
|
||||
|
||||
local destroy = function()
|
||||
if not refs[node] then throw "root already destroyed" end
|
||||
if not refs[node] then error "root already destroyed" end
|
||||
refs[node] = nil
|
||||
destroy(node)
|
||||
end
|
||||
|
||||
push_scope(node)
|
||||
|
||||
local function efn(err: string) return debug.traceback(err, 3) end
|
||||
local result = { xpcall(fn, efn, destroy) }
|
||||
local result = { xpcall(fn, debug.traceback, destroy) }
|
||||
|
||||
pop_scope()
|
||||
|
||||
if not result[1] then
|
||||
destroy()
|
||||
throw(`error while running root():\n\n{result[2]}`)
|
||||
error(`error while running root():\n\n{result[2]}`, 0)
|
||||
end
|
||||
|
||||
return destroy, unpack(result :: any, 2)
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ export type Source<T> = (() -> T) & ((value: T) -> T)
|
|||
local function source<T>(initial_value: T): Source<T>
|
||||
local node = create_source_node(initial_value)
|
||||
|
||||
return function(...): T
|
||||
local function update_source(...): T
|
||||
if select("#", ...) == 0 then -- no args were given
|
||||
push_child_to_scope(node)
|
||||
return node.cache
|
||||
|
|
@ -24,6 +24,8 @@ local function source<T>(initial_value: T): Source<T>
|
|||
update_descendants(node)
|
||||
return v
|
||||
end
|
||||
|
||||
return update_source
|
||||
end
|
||||
|
||||
return source :: (<T>(initial_value: T) -> Source<T>) & (<T>() -> Source<T>)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
local throw = require "./throw"
|
||||
local graph = require "./graph"
|
||||
type Node<T> = graph.Node<T>
|
||||
type SourceNode<T> = graph.SourceNode<T>
|
||||
|
|
@ -114,7 +113,7 @@ local vec6_to_type = {
|
|||
|
||||
local invalid_type = {
|
||||
__index = function(_, t: string)
|
||||
throw(`cannot spring type {t}`)
|
||||
error(`cannot spring type {t}`, 0)
|
||||
end
|
||||
}
|
||||
|
||||
|
|
@ -141,7 +140,7 @@ local function spring<T>(source: () -> T, period: number?, damping_ratio: number
|
|||
-- todo: is there a solution other than reducing step size?
|
||||
-- todo: this does not catch all solver exploding cases
|
||||
if c > UPDATE_RATE*2 then -- solver will explode if this is true
|
||||
throw("spring damping too high, consider reducing damping or increasing period")
|
||||
error("spring damping too high, consider reducing damping or increasing period", 0)
|
||||
end
|
||||
|
||||
local data: SpringState<T> = {
|
||||
|
|
@ -263,8 +262,6 @@ local function step_springs(dt: number)
|
|||
end
|
||||
end
|
||||
|
||||
local remove_queue = {}
|
||||
|
||||
local function update_spring_sources()
|
||||
for data, output in springs do
|
||||
local x0_123, x1_123, v_123,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
local throw = require "./throw"
|
||||
local graph = require "./graph"
|
||||
type Node<T> = graph.Node<T>
|
||||
type SourceNode<T> = graph.SourceNode<T>
|
||||
|
|
@ -32,7 +31,7 @@ local function switch<T, U>(source: () -> T): (map: Map<T, ((() -> U)?)>) -> ()
|
|||
if component == nil then return nil end
|
||||
|
||||
if type(component) ~= "function" then
|
||||
throw "map must map a value to a function"
|
||||
error "map must map a value to a function"
|
||||
end
|
||||
|
||||
local new_scope = create_node(owner, false, false)
|
||||
|
|
@ -40,7 +39,7 @@ local function switch<T, U>(source: () -> T): (map: Map<T, ((() -> U)?)>) -> ()
|
|||
|
||||
push_scope(new_scope)
|
||||
|
||||
local ok, result = pcall(component)
|
||||
local ok, result = xpcall(component, debug.traceback)
|
||||
|
||||
pop_scope()
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +0,0 @@
|
|||
local function VIDE_ASSERT(msg): any
|
||||
error(msg, 0)
|
||||
end
|
||||
|
||||
return VIDE_ASSERT
|
||||
|
|
@ -10,13 +10,13 @@ local function untrack<T>(source: () -> T): T
|
|||
local effect = scope.effect
|
||||
scope.effect = false
|
||||
|
||||
local ok, result = pcall(source)
|
||||
local ok, result = xpcall(source, debug.traceback)
|
||||
|
||||
scope.effect = effect :: () -> ()
|
||||
|
||||
if not ok then error(result, 0) end
|
||||
|
||||
return result
|
||||
return result :: T
|
||||
else
|
||||
return source()
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue