- H-198(사용자 안): 상류 스탬프(dep:_track)를 fn 직전으로 + Get을 수렴까지의 재시작 루프로 — 계약 강화: 모든 Get이 fn 도중 변경(재진입·게이트 유보)을 같은 호출 안에서 수렴시켜 항상 최신 반환. 매 패스 자기 dep Set은 UB. state-epoch-plan §4 세 절 + H-85 bullet 정정, spec.state 6 새 계약, spec.gate 10(리뷰의 영구 stale 재현 → 같은 Get에서 99 + flush 통지만) - H-186(b): 교차 인스턴스 값 혼용 UB 문서화 — architecture 13번 + content-map §4 22번(H-116 이웃), 코드 주석. M5 재검토 - H-205(a): Modifier 가드 level 2→3(직접 Get이면 유저 호출부), spec.state 12 단언 - H-208: Ref:Set 스냅샷을 table.clone + 집합 병합으로(사용자 — 더 싸고 dedup 공짜) - H-209: src 전 파일 pairs/ipairs → generalized iteration(사용자 — 최적화로 더 빠름; 메타테이블 있는 테이블의 raw 순회 실측 확인). 문서 의사코드 표기는 H-178과 같은 급으로 무변경 - H-211: Relate:SetWeak의 캐스트 없는 setmetatable 대입이 IDE(strict)에서 TypeError(플레인 luau-analyze는 솔버 차로 조용) → 로컬 주석 + :: any 경유 - §4 열린 문항 0, 코드 마커 0. 감사 2라운드 수렴(확실 1·의심 1 → 0) Co-authored-by: qwreey <me@qwreey.moe> Claude-Session: https://claude.ai/code/session_01LF78pXeFGD1ZSVD3ifteYG
92 lines
2.3 KiB
Text
92 lines
2.3 KiB
Text
--[[
|
|
Relate — inst를 weak 키로 하는 범용 릴레이션 프리미티브.
|
|
`.claude/base/relate-plan.md` "API"/"실제 구조" 절 그대로 구현.
|
|
]]
|
|
|
|
local QuadTypes = require("../roblox_packages/quad_types")
|
|
|
|
-- 타입의 소스는 `quad-types`(`Quad.Relate` 필드가 같은 타입을 써야 함) — 여기선 재export만.
|
|
export type Relate = QuadTypes.Relate
|
|
|
|
type Bucket = {
|
|
StrongMap: { [any]: any }?,
|
|
WeakMap: { [any]: any }?,
|
|
}
|
|
|
|
-- 모든 WeakMap이 공유하는 단일 메타테이블 — 매번 새로 만들 이유가 없음
|
|
local WEAK_VALUE_MT = { __mode = "v" }
|
|
|
|
local RelateImpl = {}
|
|
RelateImpl.__index = RelateImpl
|
|
|
|
local function getBucket(self, inst: any): Bucket?
|
|
return self.buckets[inst]
|
|
end
|
|
|
|
local function getOrCreateBucket(self, inst: any): Bucket
|
|
local bucket = self.buckets[inst]
|
|
if bucket == nil then
|
|
bucket = {} :: Bucket
|
|
self.buckets[inst] = bucket
|
|
end
|
|
return bucket
|
|
end
|
|
|
|
function RelateImpl:SetStrong(inst: any, key: any, value: any)
|
|
local bucket = getOrCreateBucket(self, inst)
|
|
local strongMap = bucket.StrongMap
|
|
if strongMap == nil then
|
|
local newMap = {}
|
|
bucket.StrongMap = newMap
|
|
newMap[key] = value
|
|
return
|
|
end
|
|
strongMap[key] = value
|
|
end
|
|
|
|
function RelateImpl:GetStrong(inst: any, key: any): any?
|
|
local bucket = getBucket(self, inst)
|
|
if bucket == nil then
|
|
return nil
|
|
end
|
|
local strongMap = bucket.StrongMap
|
|
if strongMap == nil then
|
|
return nil
|
|
end
|
|
return strongMap[key]
|
|
end
|
|
|
|
function RelateImpl:SetWeak(inst: any, key: any, value: any)
|
|
local bucket = getOrCreateBucket(self, inst)
|
|
local weakMap = bucket.WeakMap
|
|
if weakMap == nil then
|
|
-- `:: any` detour: the metatable-carrying type `{ @metatable WEAK_VALUE_MT, {} }`
|
|
-- is not a subtype of the plain indexer `Bucket.WeakMap` expects (`H-211`).
|
|
local newMap: { [any]: any } = setmetatable({}, WEAK_VALUE_MT) :: any
|
|
bucket.WeakMap = newMap
|
|
newMap[key] = value
|
|
return
|
|
end
|
|
weakMap[key] = value
|
|
end
|
|
|
|
function RelateImpl:GetWeak(inst: any, key: any): any?
|
|
local bucket = getBucket(self, inst)
|
|
if bucket == nil then
|
|
return nil
|
|
end
|
|
local weakMap = bucket.WeakMap
|
|
if weakMap == nil then
|
|
return nil
|
|
end
|
|
return weakMap[key]
|
|
end
|
|
|
|
local function Relate(): Relate
|
|
local self = setmetatable({
|
|
buckets = setmetatable({}, { __mode = "k" }) :: { [any]: Bucket },
|
|
}, RelateImpl)
|
|
return (self :: any) :: Relate
|
|
end
|
|
|
|
return Relate
|