Refactor visibility handling: centralize visibility constants and improve error messaging for access violations
This commit is contained in:
parent
c7237452df
commit
1a492d9b9a
43
oop/oop.lua
43
oop/oop.lua
@ -1,5 +1,11 @@
|
||||
local oop = {}
|
||||
|
||||
oop.Visibility = {
|
||||
PUBLIC = "public",
|
||||
PROTECTED = "protected",
|
||||
PRIVATE = "private"
|
||||
}
|
||||
|
||||
local tpack = table.pack or function(...)
|
||||
return { n = select("#", ...), ... }
|
||||
end
|
||||
@ -30,12 +36,12 @@ oop._call_stack = {}
|
||||
|
||||
local function normalize_visibility(visibility)
|
||||
if visibility == nil then
|
||||
return "public"
|
||||
return oop.Visibility.PUBLIC
|
||||
end
|
||||
if visibility == "public" or visibility == "protected" or visibility == "private" then
|
||||
if visibility == oop.Visibility.PUBLIC or visibility == oop.Visibility.PROTECTED or visibility == oop.Visibility.PRIVATE then
|
||||
return visibility
|
||||
end
|
||||
error("visibility must be 'public', 'protected', or 'private'")
|
||||
error("visibility must be Visibility.PUBLIC, Visibility.PROTECTED, or Visibility.PRIVATE")
|
||||
end
|
||||
|
||||
local function is_class_table(target)
|
||||
@ -58,21 +64,36 @@ local function current_caller()
|
||||
end
|
||||
|
||||
local function can_access(owner, visibility, caller)
|
||||
if visibility == "public" then
|
||||
if visibility == oop.Visibility.PUBLIC then
|
||||
return true
|
||||
end
|
||||
if not caller or not owner then
|
||||
return false
|
||||
end
|
||||
if visibility == "private" then
|
||||
if visibility == oop.Visibility.PRIVATE then
|
||||
return caller == owner
|
||||
end
|
||||
if visibility == "protected" then
|
||||
if visibility == oop.Visibility.PROTECTED then
|
||||
return oop.issubclass(caller, owner)
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function class_name(cls)
|
||||
if type(cls) ~= "table" then
|
||||
return tostring(cls)
|
||||
end
|
||||
return rawget(cls, "__name") or "Anonymous"
|
||||
end
|
||||
|
||||
local function visibility_error(kind, member, visibility, owner, caller)
|
||||
local from = caller and class_name(caller) or "outside"
|
||||
local where = owner and class_name(owner) or "unknown"
|
||||
local msg = "access violation: " .. kind .. " '" .. tostring(member) .. "' is " .. visibility
|
||||
.. " in " .. where .. " (from " .. from .. ")"
|
||||
error(msg, 3)
|
||||
end
|
||||
|
||||
function oop.setattr(target, key, value, visibility)
|
||||
target[key] = value
|
||||
if is_class_table(target) then
|
||||
@ -135,7 +156,7 @@ function lookup_visibility(cls, key)
|
||||
end
|
||||
cur = rawget(cur, "__base")
|
||||
end
|
||||
return "public", nil
|
||||
return oop.Visibility.PUBLIC, nil
|
||||
end
|
||||
|
||||
function oop.class(def, base)
|
||||
@ -143,6 +164,7 @@ function oop.class(def, base)
|
||||
cls.__name = "Anonymous"
|
||||
cls.__base = base
|
||||
cls.__visibility = {}
|
||||
cls.visibility = oop.Visibility
|
||||
cls.__index = function(self, key)
|
||||
local fields = rawget(self, "__fields")
|
||||
if fields then
|
||||
@ -150,7 +172,7 @@ function oop.class(def, base)
|
||||
if field_val ~= nil then
|
||||
local vis, owner = lookup_visibility(cls, key)
|
||||
if not can_access(owner, vis, current_caller()) then
|
||||
error("attempt to access " .. vis .. " member '" .. tostring(key) .. "'")
|
||||
visibility_error("field", key, vis, owner, current_caller())
|
||||
end
|
||||
return field_val
|
||||
end
|
||||
@ -163,7 +185,7 @@ function oop.class(def, base)
|
||||
local vis, vis_owner = lookup_visibility(cls, key)
|
||||
local access_owner = vis_owner or owner
|
||||
if not can_access(access_owner, vis, current_caller()) then
|
||||
error("attempt to access " .. vis .. " member '" .. tostring(key) .. "'")
|
||||
visibility_error("member", key, vis, access_owner, current_caller())
|
||||
end
|
||||
if type(val) == "function" then
|
||||
return function(...)
|
||||
@ -179,7 +201,7 @@ function oop.class(def, base)
|
||||
end
|
||||
local vis, owner = lookup_visibility(cls, key)
|
||||
if owner and not can_access(owner, vis, current_caller()) then
|
||||
error("attempt to set " .. vis .. " member '" .. tostring(key) .. "'")
|
||||
visibility_error("field", key, vis, owner, current_caller())
|
||||
end
|
||||
local fields = rawget(self, "__fields")
|
||||
if not fields then
|
||||
@ -284,6 +306,7 @@ function oop.install(env)
|
||||
target.setattr = oop.setattr
|
||||
target.isinstance = oop.isinstance
|
||||
target.issubclass = oop.issubclass
|
||||
target.Visibility = oop.Visibility
|
||||
end
|
||||
|
||||
oop.install()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user