lualuajreadonly-variable

How to make global variables "immutable" in Lua/LuaJ?


Description

I'm doing a LuaJ program, and here's a lib script like this:

function foo()
    print("foo");
end

I want the foo function can be invoked in other scripts directly (no require), but performs immutable in different scripts. ( Even a script overrides it, it performs as the original way in other scripts. )

For example, here's the script 1:

foo = function()
    print("bar");
end

and here is the script 2:

foo();

What's done

I have saw these two questions. They do work but not the solution to this problem.

LuaJ How to avoid overriding existing entries in Globals table

Preventing Function Overriding in Lua Table

Making global environment access-only (Lua)


I tried loading lib every time exec a script, or set local _ENV, but because there may be further callbacks from Java to Lua, it doesn't work correctly.

I now handle it by create a Globals and load lib script every time load a script in Java like this:

    public static void main(String[] args) {
        loadAndCallViaDifferentEnv(libPath, script1);
        loadAndCallViaDifferentEnv(libPath, script2);
    }

    static void loadAndCallViaDifferentEnv(String libPath, String scriptPath) {
        Globals globals = JsePlatform.standardGlobals();
        globals.loadfile(libPath).call();
        globals.loadfile(scriptPath).call();
    }

It works well, but costs much. Is there a better way?


Solution

  • I assume you want to protect three functions from overwriting: foo1, foo2 and print

    -- define foo1 and foo2 inside protected table instead of as usual globals
    local protected = {}
    
    function protected.foo1()  
       print("foo1");
    end
    
    function protected.foo2()
       print("foo2");
    end
    
    -- if a function to be protected already exists, remove it from globals:
    protected.print = print
    print = nil
    
    -- Now set the metatable on globals
    setmetatable(_G, {
       __index = protected,
       __newindex =
          function(t, k, v)
             if not protected[k] then
                rawset(t, k, v)
             end
          end
    })
    

    Now you can invoke foo1, foo2 and print from other modules without require, but you can not overwrite them:

    -- the script 1:
    foo1 = function()
       print("bar");
    end
    foo1();   -- original protected.foo1() is invoked
    
    -- the script 2:
    foo1();   -- original protected.foo1() is invoked