What does it mean, in Lua, to assign an undefined identifier to an undeclared variable?

StackOverflow https://stackoverflow.com/questions/3949534

  •  08-10-2019
  •  | 
  •  

Question

I was perusing an Lua code file, and the very top of the file contains:

    1  | TradeSkillFrameReset = TradeSkillFrame_LoadUI;
    2  | 
    3  | TradeSkillFrame_LoadUI = function()
    4  |    TradeSkillFrameReset();
            ...
    112|    TradeSkillFrame_LoadUI = TradeSkillFrameReset;
            ...
    114| end;

The very first line they are doing an assignment:

TradeSkillFrameReset = TradeSkillFrame_LoadUI;

At this point in the code file they are assigning an undeclaraed identifier (TradeSkillFrame_LoadUI) to an undeclared variable (TradeSkillFrameReset). Is this allowed in Lua?

  • is variable declaration implicit?
  • are all undeclared identifiers assumed to be forward declarations?

The more confusing thing is that the first TradeSkillFrame_LoadUI seems to do is call itself:

TradeSkillFrame_LoadUI = function()
   TradeSkillFrameReset();

How is this not an infinite loop?


Edit: Added remaining references to TradeSkillFrameReset and TradeSkillFrame_LoadUI.

Was it helpful?

Solution

If TradeSkillFrame_LoadUI is a global function, the first statement captures this function in TradeSkillFrameReset.

The assigment to TradeSkillFrame_LoadUI then replaces the global function with a new one, that at first calls the original function through the TradeSkillFrameReset reference.

This pattern is called "function hooking", here is some more information about the general technique and some World of Warcraft Lua environment specific details (where this script may come from, according to the function's name)

Some example code, to make this clear:

function SomeGlobalFunction()
    print("I'm the original global function")
end

OrigFunction = SomeGlobalFunction

SomeGlobalFunction = function()
    OrigFunction()
    print("And here's the modfied one...")
end

--...
SomeGlobalFunction()

this will print the following output:

I'm the original global function

And here's the modfied one...

As a side note, Lua's name is not all capitalized.

OTHER TIPS

On the first line, since TradeSkillFrame_LoadUI is a non-initialized variable, then this first line:

TradeSkillFrameReset = TradeSkillFrame_LoadUI;

Is the same as making:

TradeSkillFrameReset = nil

The function below that doesn't enter an infinite loop because TradeSkillFrameReset isn't actually 'pointing to anything' there.

My guess is that later on in the code, it gets properly initialized. Like so:

TradeSkillFrameReset = TradeSkillFrame_LoadUI;

TradeSkillFrame_LoadUI = function()  -- 1. function declaration
   TradeSkillFrameReset();

   [...snip...]
end;

[... snip ...]
TradeSkillFrameReset = somethingElse -- 2. initialized to something else
TradeSkillFrame_LoadUI()             -- 3. function invocation

The trick is that you can define a function on 1, and use the value of a variable on 3, while changing it on 2.

Let me know if this is not clear enough.

Unless the behavior of global variables has been modified in the environment that you're using has been modified, the statement does absolutely nothing.

local TradeSkillFrameReset = TradeSkillFrame_LoadUI

Would have an effect, creating a local variable "slot" and making code past that point use the local variable slot for TradeSkillFrameReset rather than doing a global variable lookup for it. However, as it is in the code you posted, it simply assigns the global variable to nil, which has the effect of erasing it, in other words, no effect if it was already nil.

Reasons it could be there:

1) The environment the code is running in does something special when a global variable assignment is done, rather than the default behavior of a simple assignment. I don't think this is likely though, as if there was any special behavior the lookup of the nil TradeSkillFrame_LoadUI would probably cause an error.

2) The most probable reason is simply for readability. It's to let you know that TradeSkillFrameReset will be properly assigned to later buried somewhere in the code where you won't notice it as readily.

The main thing to take away is that it's easy for Lua code to inject global variables into other Lua code. I've used a lot of Lua where they use the environment to add global vars. Just because a var isn't defined in that file most assuredly does not mean that it does not exist. It doesn't even have to be defined as a global variable in another file, because you can inject into the environment table programmatically as if it were any other table.

I would guess its doing something similar to Javascript's function hoisting, which you can read about here: Referencing a JavaScript value before it is declared - can someone explain this

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top