Movatterモバイル変換


[0]ホーム

URL:


leafo.net

My projects

View more →

Recent guides

View all →

Recent posts

Cloning a function in Lua

PostedJuly 08, 2015 by leafo (@moonscript) · Tags: lua
Tweet

Have you ever considered whether functions in Lua are mutable or not. In Lua,objects are mutable because properties and metatables can be changed. Stringsand numbers are examples of types that aren’t mutable:string libraryfunctions return new strings, numeric operators return a new numbers.

Functions and mutability

For something to be mutable it must have state that can be changed. Dependingon the version of Lua, there are either two possibilities:

  • Lua 5.1: The function environment and the upvalues are mutable
  • Lua 5.2 and above: the upvalues are mutable

Lua 5.2 replaced the function environment with a specially named upvaluecalled_ENV. You can read more about this in my companion guide:Implementing setfenv in Lua.

When assigning a function to a new variable it is not copied. Just liketables, function values are actually pointers to a function.

locala=function()endlocalb=a-- these point to the same functionassert(a==b)

This is commonly confused withpass by reference. Pass by reference isslightly different. Lua uses pass by value, it’s just that some values arepointers to the same object.

Why clone a function?

A cloned function will allow you to change state without affecting other codethat is holding references to the original function.

You might think that because Lua is a single threaded language you can modifythe state of the function while it executes, then put it back. This would be trueif Lua didn’t have coroutines.

A running function might yield at any point in execution, and in that time thefunction could have its state changed before the coroutine resumes.

string.dump andloadstring

Thestring.dump function returns a binary representation of a function as astring. By dumping a function to a string and then reloading it you've createda clone of the function:

localfunctionsay_hi()print("Hi!")endlocalsay_hi_clone=loadstring(string.dump(say_hi))say_hi_clone()--> Hi!assert(say_hi~=say_hi_clone)

This works in the previous example but it’s not entirely correct. What aboutupvalues? An upvalue’s reference can not be encoded into the string dump andpreserved when it’s loaded again.

localmessage="Hello"localfunctionsay_message()print("message: "..tostring(message))endlocalsay_message_clone=loadstring(string.dump(say_message))say_message_clone()-- message: nil

A new set of upvalues is created for the loaded function, and they all point tonil.

Preserving upvalues

Lua 5.2 and above give two ways to set upvalues on a function:debug.setupvalue anddebug.upvaluejoin. As we discovered inthesetfenv implementation guide, upvalues are shared among multiplefunctions. Changes to the values pointed to by an upvalue should reflect in allthe functions that have access. For that reasondebug.upvaluejoin will beused to connect the original function’s upvalues to the new function.

debug.upvaluejoin takes two pairs of function and upvalue index. Since onefunction is a clone of the other, the upvalue positions will be the same. It’sjust a matter of iterating through all the valid upvalue indexes and joiningthem.

localmessage="Hello"localfunctionsay_message()print("message: "..tostring(message))endlocalsay_message_clone=loadstring(string.dump(say_message))locali=1whiletruedo-- see if i is a valid upvalue indexlocalname=debug.getupvalue(say_message,i)ifnotnamethenbreakend-- join the clone and the originaldebug.upvaluejoin(say_message_clone,i,say_message,i)i=i+1end-- the clone now has a functional upvaluesay_message_clone()-- message: Hellomessage="MoonScript"say_message_clone()-- message: MoonScript

clone_function implementation

Now all that’s left is to write a generic function for cloning any function:

localfunctionclone_function(fn)localdumped=string.dump(fn)localcloned=loadstring(dumped)locali=1whiletruedolocalname=debug.getupvalue(fn,i)ifnotnamethenbreakenddebug.upvaluejoin(cloned,i,fn,i)i=i+1endreturnclonedend

Handling Lua 5.1

As far as I know Lua 5.1 does not provide a way tojoin upvalues. LuaJIT doesprovite an implementation ofdebug.upvaluejoin though, so that may handle anyLua runtimes you run code in.

The best alternative for Lua 5.1 is to usedebug.setupvalue. The result willbe a function that works, but because the upvalues aren’t connected some hardto debug issues may result.

Here are some more guides tagged 'lua'
PostedApril 26, 2020
PostedJune 09, 2016
PostedJanuary 28, 2016
PostedJanuary 24, 2016
PostedAugust 08, 2015
PostedJuly 08, 2015
PostedJuly 05, 2015
PostedJuly 04, 2015
PostedJuly 04, 2015

leafo.net · Generated Sun Oct 8 13:02:35 2023 bySitegenmastodon.social/@leafo


[8]ページ先頭

©2009-2025 Movatter.jp