Nmap/Lua 5.2
New features in Lua 5.2 of particular interest for NSE
- New hexadecimal escape sequences for strings. Scripts may now use, for example, "\x0a" instead of "\010".
- Lexical environments with _ENV. Environments are now simply upvalues of a function and you may introduce a new environment into a scope by creating a new local named _ENV with a lifetime that expires at the end of the scope. For example:
local print = print function foo () a = 5 do local _ENV = {} print(a) --> nil a = 6 print(a) --> 6 end print(a) --> 5 end
You may also do things like:
local print = print function foo (_ENV) print(a) end foo({a = 5}) --> 5 foo({a = 6}) --> 6
The biggest gotcha you may experience is figuring out that changing the environment for one function may also change the environment of others. For example:
local print = print _ENV = {foo = 1} local function helper () print(foo) end function foo () _ENV = nil end foo() helper() --> attempt to index upvalue '_ENV' (a nil value)
The old Lua 5.1 environments are completely gone. getfenv and setfenv are gone as a result.
- Libraries must be required and stored in locals by scripts. The require function no longer puts libraries in the global namespace once required. So instead of:
require "http"
You must do:
local http = require "http"
The days of getting away with forgetting a require are also over. :)
- Coroutines may yield across most C call boundaries. One of the simplest examples of such an error in 5.1:
$ lua Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio > F = coroutine.wrap(function(...) > > return pcall(function(...) >> return coroutine.yield(...) >> end, ...) >> end) > print(F("Hello")) false attempt to yield across metamethod/C-call boundary
This changes allows for coroutines in generic for loop iterators which yield outside of the for loop. For example:
-- This example does not handle EOF or errors. local function lines (socket) while true do coroutine.yield(socket:receive_buf("\r?\n", false)) end end for line in lines(socket) do --> do something end
- Lua 5.2 comes with a default bit library called bit32.
- Lua closures are now cached. The ramifications are best illustrated:
$ lua51 Lua 5.1.4 Copyright (C) 1994-2008 Lua.org, PUC-Rio > for i = 1, 2 do print(function() end) end function: 0x2104ad0 function: 0x2104fe0
$ lua52 Lua 5.2.0 Copyright (C) 1994-2011 Lua.org, PUC-Rio > for i = 1, 2 do print(function() end) end function: 0x1d51070 function: 0x1d51070
Notice the closures are equivalent (and we could confirm equality using ==). An example where the closures change:
$ lua52 Lua 5.2.0 Copyright (C) 1994-2011 Lua.org, PUC-Rio > for i = 1, 2 do print(function() return i end) end function: 0x796070 function: 0x796d30
This produces distinct closures because the upvalues are different. Namely, the i local is recreated on each iteration of the loop so each closure references a different i.
It is an optimization to cache closures. This optimization lessens the performance impact of an anonymous function used in a loop. For example:
-- Escape non-graphical characters function escape (str) return (str:gsub("[^%w%s%p]", function (a) return ("\\x%02x"):format(a:byte()) end)); end
In Lua 5.1, if the above escape function is called in a loop, it would create a closure for the call to gsub each time. These become "garbage" because they are only used once. The way to correct this is to pull the function out into a local above the escape function:
-- Lua 5.1 optimization for escape function do local function helper (a) return ("\\x%02x"):format(a:byte()) end function escape (str) return (str:gsub("[^%w%s%p]", helper)); end end
In Lua 5.2, this is largely unnecessary as the closure is cached for future use.
- Lua 5.2 now supports goto. Be sure the Raptor fences aren't out before use.
::forever:: goto forever
The goto mechanism was added partially to solve the lack of a continue statement and nested break.
for i = 1, 10 do if i % 2 == 0 then goto cont end ::cont:: end
for i = 1, 10 do for j = 1, 10 do for k = 1, 10 do if i + j + k == 99 then goto done end end end end ::done::
- __pairs and __ipairs metamethods. You can now define how an object is iterated over using the pairs and ipairs functions.
$ lua52 Lua 5.2.0 Copyright (C) 1994-2011 Lua.org, PUC-Rio > t = {1, 2, 3, a = 1} > setmetatable(t, {__ipairs = pairs}) -- make ipairs work like pairs > for i, v in ipairs(t) do print(i, v) end 1 1 2 2 3 3 a 1