Search code examples
lualuaj

Making global environment access-only (Lua)


I embedded Lua and want scripts to be able to read the global table but not automatically write to it so two scripts can write variables with the same name without overwriting eachother but still being able to add stuff to the global table. I can't really explain it better then this:

Script 1

var1 = "foo"
_G.var2 = "bar"

Script 2

print(var1) -- Prints nil
print(var2) -- Prints 'bar'

How I tried to accomplish this is by doing something like this (The 'scripts' being a function)

newScript = function(content)
    Script = loadstring(content)()
    env = setmetatable({},{__index = _G})
    setfenv(Script,env)
    return Script
end

My Lua binding is LuaJ, for the sake of giving all information here is that code too:

private LuaValue newScript(String content){
        LuaTable envMt = new LuaTable();
        envMt.set(INDEX, _G);
        LuaTable env = new LuaTable();
        env.setmetatable(envMt);

        LuaClosure func = (LuaClosure) _G.get("loadstring").call(valueOf(content));

        thread = new LuaThread(func,env);
        thread.resume(NIL);
        return thread;
}

Solution

  • Here's a function I use to return a read-only table:

    function ro_table (t)
      local t = t
      if t then
        return setmetatable({}, 
          { __index=t, 
            __newindex= function(_,_,_) error ("Attempt to modify read-only table") end, 
          })
      else
        return nil
      end
    end
    

    So for your code, you'd have the following:

    newScript = function(content)
        Script = loadstring(content)()
        setfenv(Script,ro_table(_G))
        return Script
    end
    

    Note that this does not work recursively, so if you have any table defined as a global (or even any of the built-in functions) the contents can be changed, but the table itself cannot be replaced.