Merge branch 'centau:main' into main

This commit is contained in:
alicesaidhi 2025-04-15 22:21:31 +02:00 committed by GitHub
commit 92cf612cef
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 221 additions and 45 deletions

View file

@ -86,6 +86,9 @@ local Instance = {} :: any do
local proxies = {} :: { [Data]: userdata? }
setmetatable(proxies :: any, { __mode = "v" })
-- allocate variables for the metamethods as __index and get_proxy cross refrences each other
local __index, __newindex
local function get_data(userdata: userdata): Data
local function f(userdata: userdata): ProxyMT
return getmetatable(userdata :: any)
@ -94,6 +97,19 @@ local Instance = {} :: any do
return f(userdata).data
end
local function get_proxy(data: Data): userdata
return proxies[data] or (function()
local userdata = newproxy(true)
local proxy = getmetatable(userdata)
proxy.proxy = userdata
proxy.data = data
proxy.__index = __index
proxy.__newindex = __newindex
proxies[data] = userdata
return userdata
end)()
end
local function is_instance(value: unknown): boolean
local mt = getmetatable(value :: any)
return mt and mt.data and mt.data.type == "Instance"
@ -101,16 +117,16 @@ local Instance = {} :: any do
local methods = {}
local function __index(userdata: userdata, property: string): ()
__index = function(userdata: userdata, property: string): ()
local data = get_data(userdata)
return if methods[property] then methods[property]
elseif property == "Name" then data.name
elseif property == "Parent" then data.parent
elseif property == "Parent" then (data.parent and get_proxy(data.parent))
elseif property == "Destroying" then data.destroying
else data.properties[property]
end
local function __newindex(userdata: userdata, property: string, value: unknown)
__newindex = function(userdata: userdata, property: string, value: unknown)
local data = get_data(userdata)
if property == "Name" then
if type(value) ~= "string" then error("name must be a string", 2) end
@ -135,19 +151,6 @@ local Instance = {} :: any do
end
end
local function get_proxy(data: Data): userdata
return proxies[data] or (function()
local userdata = newproxy(true)
local proxy = getmetatable(userdata)
proxy.proxy = userdata
proxy.data = data
proxy.__index = __index
proxy.__newindex = __newindex
proxies[data] = userdata
return userdata
end)()
end
function Instance.new(class: string): Instance
local data = {
name = "UNNAMED",

View file

@ -759,6 +759,13 @@ TEST("create()", wrap_root(function()
CHECK(text.Text == "test")
end
do CASE "set nested parent"
local frame = create "Frame" {}
local text = create "TextLavel" { { Parent = frame } }
CHECK(frame:GetChildren()[1] == text)
CHECK(text.Parent == frame)
end
do CASE "nested deferred"
local text = create "TextLabel" {
{
@ -819,6 +826,20 @@ TEST("create()", wrap_root(function()
CHECK(frame:FindFirstChild "G")
end
do CASE "set false as child"
create "Frame" {
false
}
create "Frame" {
function() return false end
}
create "Frame" {
function() return { false } end
}
end
do CASE "binding properties to source"
local name = source("Hi")
local text = source("Bye")
@ -1073,20 +1094,147 @@ TEST("show()", wrap_root(function()
local show = vide.show
local root = vide.root
do CASE "main"
-- uses switch() internally, more extensive testing of scoping not needed
local value = source("truey" :: unknown)
do CASE "show component"
local input = source(true)
local function one() return 1 end
local output = show(input, one)
CHECK(output() == 1)
input(false)
CHECK(output() == nil)
end
do CASE "fallback component"
local input = source(true)
local function one() return 1 end
local function two() return 2 end
local output = show(value, one, two)
local output = show(input, one, two)
CHECK(output() == 1)
value(nil)
input(false)
CHECK(output() == 2)
end
do CASE "alt"
do CASE "updating truth to truthy does not rerun"
local input = source(1)
local count = 0
local function component()
count += 1
return 1
end
local output = show(input, component)
CHECK(count == 1)
CHECK(output() == 1)
input(2)
CHECK(count == 1)
end
do CASE "updating source passed to component"
local input = source(1 :: number?)
local count = 0
show(input :: () -> number?, function(value: () -> number)
vide.cleanup(function() print "destroyed" end)
effect(function()
local v = value()
count += 1
CHECK(v == count)
if v ~= count then error(count) end
end)
return true
end)
input(2)
CHECK(count == 2)
input(3)
CHECK(count == 3)
input(nil)
CHECK(count == 3)
end
do CASE "special strict case"
type Weapon = {
id: string,
enchant: string?
}
vide.strict = true
local count = 0
local branch = 0
local weapon = source(nil :: Weapon?)
effect(function()
weapon()
end)
effect(function()
weapon()
end)
show(weapon, function(weapon: () -> Weapon)
local enchant = function() return weapon().enchant end
show(enchant, function(enchant: () -> string)
effect(function()
local e = enchant()
count += 1
CHECK(e ~= nil)
if branch == 1 then
CHECK(e == "fire")
elseif branch == 2 then
CHECK(e == "poison")
end
end)
return {}
end)
return {}
end)
effect(function()
weapon()
end)
effect(function()
weapon()
end)
branch = 1
weapon { id = "1", enchant = "fire" }
CHECK(count == 8)
branch = 2
weapon { id = "1", enchant = "poison" }
CHECK(count == 10)
weapon { id = "1", enchant = nil }
CHECK(count == 10)
branch = 1
weapon { id = "1", enchant = "fire" }
CHECK(count == 14)
weapon(nil)
branch = 2
weapon { id = "1", enchant = "poison" }
CHECK(count == 22)
vide.strict = false
end
do CASE "alt" -- todo: move test
local visible = vide.source(true)
local count = vide.source(0)
@ -1109,7 +1257,6 @@ TEST("show()", wrap_root(function()
destroyed += 1
end)
end)
return nil
end)
end)
end)
@ -2722,7 +2869,6 @@ TEST("strict", wrap_root(function()
vide.cleanup(function() count_2 += 1 end)
return {}
end)
return nil
end)
end)
end)
@ -2749,7 +2895,6 @@ TEST("strict", wrap_root(function()
vide.cleanup(function() count_2 += 1 end)
return {}
end)
return nil
end)
end)
end)