Find Global Symbols

Introduction

This channel demonstrates how to identify unwanted globals (functions and variables) in your code. Global functions and variables can be accessed from every Lua file within a translator instance. The problem with this is that they can cause a conflict with other globals (of the same name) in other Lua files.

Because of this we strongly recommend it as best practice to only use local variables in your code.

Unfortunately Lua variables are global by default and you need to use the local keyword to enforce local scope. Therefore in practice it is very easy to omit the local keyword on the occasional function or variable, and unfortunately it is not obvious when this happens. This is where this tool is very helpful as it detects global variables and raises an error,  we strongly recommend running it before you release software.

There are very few scenarios where using globals is a good idea, we have not seen any yet. Potentially global variables could be used is to share data between multiple channels, but this can be better achieved by sharing data with files or a database (unless performance is absolutely critical).

If you have any questions please contact us at support@interfaceware.com.

Note: This type of “global conflict error” can be particularly insidious, as in the future someone can retroactively introduce a bug to your code by using a global of the same name as one used in your channel or module. And if the conflicting code is developed and tested on another server (that does not contain your module/channel), the problem may not be detected until late in testing cycle (i.e., on a staging server) – or possibly only when the code goes live.

Using the Code [top]

  • Import the Find Global Symbols channel from the Builtin: Iguana Tools repository
  • Experiment with the code to find out how it works
  • Then add the module to your Translator project
  • Copy the require statement from the channel and add it at the top of your script
    Note: This module uses require to return a single function
  • Adapt the code to your own requirements
  • Paste this command into the main() function, it will raise an error containing the name of the first global it detects:
    Note: This combines the require and calling the function into a single statement (no separate require is needed)

    require('tool.global.find')()
    • If an error is raised then fix the problem by placing “local” before the global named in the error
    • Repeat the process until there is no error (= no globals)
    • Delete the command, and remove the shared module from your channel
  • Alternatively use CheckForGlobals() and the require statement separately
  • Interactive scripting help is included for this module

This is the github code for the main module:

How it works [top]

In Lua functions and variables global by default.

So if you declare functions or variables like this the will be global in scope:

Info = {}

function Foo()

end

This means that the variable Info and the function Foo can be accessed from every Lua file within a translator instance.

Why is that a bad idea? The issue is that it makes scripts much harder to maintain because if you have another file which also tries to use the Info or Foo names then they collide with the usage of the symbols here. For this reason it’s much better practice to keep everything local in scope so that it’s very clear what the dependencies for each module are. The same names can be re-used in many places without any interference.

This code resolves the issue by using the local keyword to enforce local scope for the variable and function:

local Info = {}

local function Foo()

end

The problem with this is that it’s very easy to forget just one instance of a function and quite hard to find the place that you missed using the local keyword.

This is where this tool is very helpful for finding any global symbols that you might have created. It iterates through the global table context _G and looks for new symbols that wouldn’t normally be there. If it finds one then it throws an error.

So if you include it in your project and call the check function it will be like the canary in the coal mine and warn you if you have created a new global symbol.

Here we can see how it detected MyUnintendedGlobalFunction(), because it was not declared as local:

There are actually three global declarations (can you spot the other two), if we fix them all by adding local at the start of each line, then the error goes away:

Tada!

One last trick. Since the require for this module just returns a function, the following one liner can be used to copy paste into the main function to check a translator instance for global symbols:
Note: The “extra” pair of brackets at the end are used to immediately call the function returned by require.

   require('tool.global.find')()

More information [top]