quad/.claude/audit/handtrace-round10-reference-impl/spikes/d20_indexOfElement_weak.luau
qwreey d2d67c7aeb
qa: 10라운드 광범위 탐사 완료 — 발견 H-150~H-157 (🔴 0 / 🟡 5 / 🟢 3), §4 배치 문항 7건 회신 대기
- 신선한 탐사자(fable) 단일 컨텍스트, 지시서 -round10-brief.md §2 레인 A·C 완료, B 부분, D ALL PASS
- audit/handtrace-round10-reference-impl/: round7 참조 구현을 현재 계약으로 갱신·재실행
  (부수: round7/ref9 _recompute 첫 인자 오류 발견·정정)
- base/·인덱스 레이어는 미변경 — 결정은 사용자 배치 회신 뒤 -round10-followup.md로

Co-authored-by: qwreey <me@qwreey.moe>
Claude-Session: https://claude.ai/code/session_01546hjsYNLSMZdHdPyTZaGb
2026-08-28 01:03:05 +09:00

40 lines
2.3 KiB
Text

--!nocheck
-- H-145: 최상위 Slot 교체(해제 setLength(inst,k,0))가 옛 Slot 키를 못 지워도 weak-key라 GC로 빠지는가.
-- 요소가 테이블이라 weak-key 자체는 동작한다 — Instance userdata 키의 거동은 여기서 못 본다(한계).
local q = require("./core10"); local D = require("./dispatch10")
local inst = q.newInst("inst")
local probe = setmetatable({}, {__mode = "k"})
local retract
do
local S1 = D.newSlot("S1", { {name="a"}, {name="b"} })
probe[S1] = true
retract = D.mountTop(inst, S1, 1)
local bk = D.getBookkeeping(inst)
print("마운트 후 bk(inst).indexOfElement[S1] =", bk.indexOfElement[S1], "lengthList[1] =", tostring(bk.lengthList[1]))
local S2 = D.newSlot("S2", { {name="c"} })
retract(S2) -- State<Slot> 교체: retractor(nextValue=S2) → 언마운트 + setLength(inst,1,0)
print("교체(해제) 후 indexOfElement[S1] =", bk.indexOfElement[S1], "(해제엔 요소 인자가 없어 안 지워짐)")
print(" S1 canExecute(_baseObserver) =", q.canExecute(S1._baseObserver), "| observers[1] =", tostring(bk.observers[1]))
end
retract = nil -- retractor 클로저가 S1을 잡고 있으므로 놓는다(SlotHandler 체인 슬롯이 교체된 것에 해당)
collectgarbage(); collectgarbage()
local alive = 0; for _ in pairs(probe) do alive += 1 end
local cnt = 0; for _ in pairs(D.getBookkeeping(inst).indexOfElement) do cnt += 1 end
print("참조 놓고 GC → 옛 Slot 생존", alive, "(기대 0) | indexOfElement 항목 수", cnt, "(기대 0)")
-- 마운트 중인 요소는 절대 빠지지 않아야 한다(보장자: _elements + gatedRecompute 캡처)
local inst2 = q.newInst("inst2")
local outer = D.newSlot("outer", {})
D.mountTop(inst2, outer, 1)
do
local inner = D.newSlot("inner", { {name="x"} })
D.rawAdd(outer, inner)
D.rawAdd(outer, {name="y"})
end
collectgarbage(); collectgarbage()
local bko = D.getBookkeeping(outer)
local keys = 0; for _ in pairs(bko.indexOfElement) do keys += 1 end
print("마운트 중 요소(로컬 참조 없음) GC 뒤 indexOfElement 항목", keys, "(기대 2 — _elements가 붙잡음)")
print(" inner.Length 변경 전파: outer.Length =", outer.Length:Get())
D.rawAdd(outer._elements[1], {name="z"})
print(" inner에 z 추가 → outer.Length =", outer.Length:Get(), "(기대 3)")