lua-users home
lua-l archive

[Date Prev][Date Next][Thread Prev][Thread Next] [Date Index] [Thread Index]


Peter Shook escribió:
> local ReadOnly = {
>   x = 5,
>   y = 'bob',
> }
>
> local function check(tab, name, value)
>    if rawget(ReadOnly, name) then
>      error(name ..' is a read only variable', 2)
>   end
>   rawset(tab, name, value)
> end

> setmetatable(_G, {__index=ReadOnly, __newindex=check})

There is no reason to do rawget(ReadOnly, name) since ReadOnly
has no metatable. (And if it did, it should probably be respected.)
ReadOnly[name] is considerably faster because it requires neither
a lookup in the environment table nor a function call.

Just a little suggestion -- I have noticed the spurious use of
rawget and rawset in a number of pieces of example code, so I
thought I would mention this.

An interesting variant of the above, which is not the solution to
the problem posed but has other uses. There are some obvious improvements,
including using the facility I put into the WeakTablesTutorial on how
to give globals printable names.
(see <http://lua-users.org/wiki/WeakTablesTutorial>)

function protect(defined)
  -- returns a new table which only allows keys to be set once,
  -- and a proxy table which can be used to override the
  -- restriction, in that order.
  -- This is a debugging tool, not a security tool.
  -- (although you can simply let the override proxy disappear)

  local function ban_reset(t, k, v)
    if defined[k] then
      error(tostring(k).." has already been defined")
     else
      defined[k] = v
    end
  end 

  local new_table = setmetatable({}, {
    __index = defined,
    __newindex = ban_reset
  })

  local function allow_reset(t, k, v)
    rawset(new_table, k, v)
  end

  local unprotected_proxy = setmetatable({}, {__newindex = allow_reset})

  return new_table, unprotected_proxy
end

-- Now, we apply it to the global table.
_G, setq = protect(getfenv(0))

-- Just setting _G is not enough to change the environment
setfenv(0, _G)

-- This is necessary to avoid "error handling error"
-- There ought to be a better way of dealing with this :)
setq._TRACEBACK = _TRACEBACK


Here's how it works:

> -- Here is how it works:
> =math
table: 00443550
> -- I can't redefine that
> math = nil
stdin:10: math has already been defined
stack traceback:
        [C]: in function `error'
        stdin:10: in function <stdin:8>
        stdin:1: in main chunk
        [C]: ?
> -- Of course, I could redefine a member
> sin = math.sin
> math.sin = function() print "gotcha" end
> =math.sin(3)
gotcha
> math.sin = sin
> -- Probably I should have recursively protected
> -- tables before I protected the globals
> -- Now, I can create any new global
> function foo(x) return "Hello, "..x end
> =foo "world"
Hello, world
> -- But I can't redefine it
> foo = 7
stdin:10: foo has already been defined
stack traceback:
        [C]: in function `error'
        stdin:10: in function <stdin:8>
        stdin:1: in main chunk
        [C]: ?
> -- If I want to create a redefinable global, I use setq
> -- (Table syntax is used rather than function syntax to avoid typing 
quotes)
> setq.bar = 7
> -- Now, the global is unprotected and it doesn't matter if I use
> -- setq or not on it.
> bar = 8
> setq.bar = 42
> =bar
42