• chapter8_1 编译执行和错误


    1、编译

      前面介绍的,dofile是一个运行lua代码块的基本操作,实际上它是一个辅助函数。

    loadfile才真正做了核心的工作。dofile(打开文件,执行里面的代码块)和loadfile(从文件或stdin中获取代码块)实际不运行代码,

    只是编译,然后将结果作为一个函数返回。loadfile与dofile不同的 是它不会引发错误,只是返回错误值并不处理错误。

    一般dofile定义:

    function dofile(filename)
        local f = assert(loadfile(filename))   --如果loadfile失败,assert就会引发一条错误
        return f()
    end

    通常,如果我们要多次运行一个文件,只需用loadfile一次,再多次调用它的返回结果就可以了。

    相对于dofile来说,它只编译一次,开销就小得多。

    与loadfile类似的函数load,它是从字符串中读取代码,而不是文件。

    f = load("i = i + 1")

    f变成了一个函数,每次调用就执行"i = i + 1"

    i = 0
    f();print(i)    --> 1
    f();print(i)    --> 2

    使用load的时候要小心,它的开销大,并且会返回一些难以理解的代码。

    如果你想塑造一个便捷但粗糙的dostring(load and run a chunk):

    load(s)()   -- 有语法错误,load返回nil就会报错
    
    --优化的版本
    
    assert(load(s))()

    一般将load用于字面字符串是没有意义的:

    f = load("i = i + 1")
    
    --基本上等价于
    
    f = function() i = i + 1 end

    但是第二行要快的多,因为它只编译了一次,而第一行每次调用load都要重新编译。

    因为load编译的时候不涉及词法域,所以两者还是有区别的:

    i  =  32
    local i = 0
    g = load("i = i + 1 ; print(i)")        -- 字符串,访问的是全局  i
    f = function() i = i + 1 ;print(i) end  -- 函数块,访问的是local  i
    g()          --> 33
    f()          --> 1

    函数 f 访问的是local  i

    函数 g 访问的是全局  i

    因为load总是在全局环境去编译代码块。

    load最典型的用处是执行外部代码,那些位于程序之外的代码( 就像加载配置文件config一样,之前疑惑的地方 )。

    有很多地方需要用户自定义函数,就要求输入函数代码,然后调用load去求值。

    如果要对一个表达式求值,则必须在其之前添加return,这样才能构成一条语句,返回表达式的值:

    print"enter your expression:"        -- 输入 1 + 2
    local r = io.read()
    local func = assert(load("return " .. r))
    print("the value is " .. func())       -- the value is 3

    因为load返回的是函数,所有可以多次调用:

    print "enter function to be plotted(with variable 'x'):"  --输入一个数字,因为string.rep(第二个参数要数字,如果输入a或者b之类的 返回值为nil)
    local r = io.read()
    local f = assert(load("return " .. r))
    for i = 1,20 do 
        x = i      
        print(string.rep("*",f()))
    end

    loadfile的实质也是调用load,load接收一个"读取器函数--reader function",并在内部调用它来获取程序块:

    它可以分几次返回一个程序块,load会反复调用它,直到返回nil。

    下面的例子就等于loadfile:

    f = load(io.lines(filename,"*L")) --每次调用io.lines,都返回一个函数
    
    --下面的代码更高效
    
    f = load(io.lines(filename,1024))

    Lua把这些独立的代码块当作匿名函数的函数体。比如load(“a = 1”)等于:

    function (...) a = 1 end

    代码块也可以声明local变量:

    f = load("local a = 10; print(a+20)")
    f()   --> 30

    有了这些特点:可以重写上面的例子,避免用全局变量:

    print "enter function to be plotted(with variable 'x'):"
    local r = io.read()
    local f = assert(load("local x = ...;return " .. r)) --把x声明为local变量,当调用f(i)时,...就变成了参数i。
    for i = 1,20 do
        print(string.rep("*",f(i)))
    end

    load不会引发错误,在错误情况中,load会返回nil及一条错误消息。

    print (load("i i"))
    --> nil   [string "i i"]:1: syntax error near 'i'

    此外,这些函数不会有其他副作用。

    有一个误解,认为加载了一块代码,就定义了其中的函数。

    在Lua中函数定义是一种赋值操作,也就是说,它们是在运行时才完成的操作:

    一个foo.lua:

    --file 'foo.lua'
    function foo(x)
        print(x)
    end

    运行它:

    f = loadfile("foo.lua")

    运行之后,foo函数只是编译,还没有被定义。为了定义它,必须运行这个chunk:

    print(foo)        --> nil
    f()               -- defines 'foo',即运行它
    foo("ok")         --> ok

    此外,若需要在一个商业品质的程序中执行外部代码,还要处理加载程序块时报告的错误。

    如果代码是不可信的,还要在一个保护环境下执行。

  • 相关阅读:
    [工控安全][原创]施耐德某PLC模块用户密码相关漏洞
    [工控安全][原创]施耐德某PLC模块敏感信息泄露漏洞
    [工控安全][原创]西门子PLC固件逆向之定位s7comm协议的一个切入口
    [安全工具][原创]保存IDA Pro中生成的函数调用关系(图)
    [工控安全][原创]西门子PLC固件逆向之socket API(总览)
    [工控安全][原创]面向开环控制的震网病毒恶意载荷探究
    [工控安全][翻译]Rogue7:西门子s7comm-plus协议全解析
    [工控/IOT安全][笔记]ARM设备固件装载基址定位的研究
    Tor源码阅读与改造(一)
    Java WebDriver 使用经验
  • 原文地址:https://www.cnblogs.com/daiker/p/5813314.html
Copyright © 2020-2023  润新知