擴展 lua require 的行為


擴展 lua require 的行為

 

來源 https://blog.codingnow.com/2015/10/lua_require_env.html

 

今天同事提了個需求,他希望可以給部分 lua 代碼(由策划編寫)做一個沙盒關起來。在 lua 里做沙盒很容易,只需要控制函數的環境就可以了。不過另一個附加需求是,這些代碼還可以直接利用 require 加載。

而我們又不想去修改系統的 api 接口,那么怎么做到這點呢?

首先, 我希望使用的時候看起來像這樣:

local xxx = require "xxx" (myEnv)

和傳統的 require 用法不同,可以在后面追加一個參數 myEnv 。這樣的話,每次 xxx 模塊被 require 時,它其實被重復運行一次,但會綁定不同的 _ENV 。

其次,既然模塊會被反復初始化,那么我們甚至還可以約定,每個這種沙盒封裝的模塊還可以接收 require 的傳入的額外參數。

做到這點很容易,我們只需要在 package.searchers 里追加一個自定義的 loader 然后並不返回加載的模塊 chunk ,而是做一個函數封裝。將 chunk 的運行推遲到傳入 myEnv 調用之后。

這樣,load chunk 本身還是依靠 require 的 package 機制緩存代碼的,只是每次調用后,重新綁定 _ENV 生成了一組新實例。

我在 gist 上貼了一組代碼實現這個特性:延遲綁定環境的 require 。

 

mymod.user.lua

local M = {}

function M.test(...)
    print(...)
end

return M

 

 

requirenv.lua

local package = package
local debug = debug

local function load_env(filename)
    local f,err = loadfile(filename)
    if f == nil then
        return err
    end
    return function()
        return function(env)
            if env then
                debug.setupvalue(f, 1, env)
            end
            return f(filename)
        end
    end
end

local function searcher_env(name)
    local filename, err = package.searchpath(name, package.upath)
    if filename == nil then
        return err
    else
        return load_env(filename)
    end
end

table.insert(package.searchers, searcher_env)

 

test.lua

require "requirenv"

package.upath = "./?.user.lua"

local myprint = print

local env = {
    print = function (...)
        myprint("hook", ...)
    end
}

local s = require "mymod"(env)

s.test "hello world"  -- hook hello world

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM