From The Blog: Check for those pesky globals!

Anyone who has used Lua for a while learns that global variables are not our friends. Since any mention of a variable that hasn’t been set is assumed to be a reference to a global whose value is nil, that means the silliest typo (e.g. varl vs. var1) can wreak havoc in the most finely-tuned code. As such, best practice is to avoid the use of global variables in Lua code entirely.

There’s a module which has been around for a long time called strict.lua — available here and many other places — which will check for the use of globals and throw an error if it finds any. Some developers don’t like it because throwing errors means it’s hard to run on a large body of existing code, but if you’re starting a new project it can save a lot of time.

For this reason, we’ve now built a similar capability directly into the Corona Simulator. With the following API call, you can now get warnings about the use of globals so you can eliminate them permanently:

Runtime:setCheckGlobals( true )

Doing so will give you warnings like this:

Apr 15 12:27:22.155 WARNING: reading global nil 'onTapLogo' stack traceback: /Users/gimli/corona/metagame/main.lua:91: in function 'setupTapHandler' /Users/gimli/corona/metagame/main.lua:96: in main chunk

Pass false to the same function to turn the feature off. Since it only issues warnings, it’s easier to use in existing code than strict.lua.

System globals

Alongside this feature comes a new “safety net” built into the Corona Simulator which prevents the inadvertent overwriting of system globals and the attendant chaos that would ensue. Some of you will be familiar with the issues an innocent line of code like this can cause:

debug = true

This has the effect of disabling all errors in your project because it overwrites the system library debug with a boolean value. Hours can be wasted if you’re unlucky enough to do this, because nothing makes sense anymore.

Fortunately, Corona SDK now prevents overwriting of system globals (e.g. debug, display, print, etc.) and issues an error message like this:

Apr 15 12:27:21.875 ERROR: cannot overwrite system library 'debug' stack traceback: /Users/gimli/corona/metagame/main.lua:9: in main chunk

Of course, you can still override things like print() if you really want to, but you have to think about it a little more carefully and make your override local to your project code:

local debugMode = true local cachePrint = print local function print(...) -- Note local definition if debugMode then cachePrint(unpack(arg)) end end

Note that this doesn’t affect customizing system libraries with new APIs or member variables since that doesn’t involve overwriting the system global with another value, but rather it just amends its existing attributes.

Most people don’t need to worry about it, but if you’re deep in the heart of Lua and are accessing the _G globals table, you can still do so via Runtime._G, but this is advanced usage and the safety net comes off. And, if you really, truly need to change the value of a system global, you can always use Lua’s rawset() (but if you really need to do it, you’d know this already).

Conclusion

The features outlined in this article are available in Daily Build 2016.2866 and later. Happy hunting for globals!

View the full article

I’ve been running into a few “false positives” while running the setCheckGlobals.

An example below in regards to socket.http

Runtime:setCheckGlobals(true) -- Load the relevant LuaSocket modules local http = require( "socket.http" ) local ltn12 = require( "ltn12" ) -- Create local file for saving data local path = system.pathForFile( "hello.png", system.DocumentsDirectory ) local myFile = io.open( path, "w+b" ) -- Request remote file and save data to local file http.request{ url = "http://www.coronalabs.com/demo/hello.png", sink = ltn12.sink.file(myFile), } -- Display local file local testImage = display.newImage( "hello.png", system.DocumentsDirectory )

outputs:

21:20:15.368 WARNING: setting global table 'socket' 21:20:15.368 stack traceback: 21:20:15.368 [C]: ? 21:20:15.368 [C]: in function 'require' 21:20:15.368 ?: in function 'require' 21:20:15.368 ?: in main chunk 21:20:15.368 [C]: ? 21:20:15.368 [C]: in function 'require' 21:20:15.368 ?: in function 'require' 21:20:15.368 ?: in main chunk 21:20:15.368 [C]: ? 21:20:15.368 [C]: in function 'require' 21:20:15.368 ?: in function 'require' 21:20:15.368 C:\Users\norib\Documents\Corona Projects\test1\main.lua:5: in main chunk 21:20:15.368 WARNING: setting global table 'socket' 21:20:15.368 stack traceback: 21:20:15.368 [C]: in function 'module' 21:20:15.368 ?: in main chunk 21:20:15.368 [C]: ? 21:20:15.368 [C]: in function 'require' 21:20:15.368 ?: in function 'require' 21:20:15.368 ?: in main chunk 21:20:15.368 [C]: ? 21:20:15.368 [C]: in function 'require' 21:20:15.368 ?: in function 'require' 21:20:15.368 C:\Users\norib\Documents\Corona Projects\test1\main.lua:5: in main chunk 21:20:15.368 WARNING: setting global table 'ltn12' 21:20:15.368 stack traceback: 21:20:15.368 [C]: in function 'module' 21:20:15.368 ?: in main chunk 21:20:15.368 [C]: ? 21:20:15.368 [C]: in function 'require' 21:20:15.368 ?: in function 'require' 21:20:15.368 ?: in main chunk 21:20:15.368 [C]: ? 21:20:15.368 [C]: in function 'require' 21:20:15.368 ?: in function 'require' 21:20:15.368 C:\Users\norib\Documents\Corona Projects\test1\main.lua:5: in main chunk 21:20:15.368 WARNING: setting global table 'mime' 21:20:15.368 stack traceback: 21:20:15.368 [C]: ? 21:20:15.368 [C]: in function 'require' 21:20:15.368 ?: in function 'require' 21:20:15.368 ?: in main chunk 21:20:15.368 [C]: ? 21:20:15.368 [C]: in function 'require' 21:20:15.368 ?: in function 'require' 21:20:15.368 ?: in main chunk 21:20:15.368 [C]: ? 21:20:15.368 [C]: in function 'require' 21:20:15.368 ?: in function 'require' 21:20:15.368 C:\Users\norib\Documents\Corona Projects\test1\main.lua:5: in main chunk 21:20:15.368 WARNING: setting global table 'socket' 21:20:15.368 stack traceback: 21:20:15.368 [C]: in function 'module' 21:20:15.368 ?: in main chunk 21:20:15.368 [C]: ? 21:20:15.368 [C]: in function 'require' 21:20:15.368 ?: in function 'require' 21:20:15.368 C:\Users\norib\Documents\Corona Projects\test1\main.lua:5: in main chunk

Now I could easily put the Runtime:setCheckGlobals(true) below the requires in main.lua, and it will be fine for just that file… but if i had different scenes using composer and not in main.lua, it will produce the same false positive output.  When I used strict.lua i only would declare it once in my main.lua and it was good throughout different scenes/modules.

along those lines, tho a true positive, the physics library has long leaked the global “physics”

Letting the engineer’s know.

This is because the Lua Socket library is implemented in fairly old school Lua.  I’m figuring out a fix.

Is there any way to disable the “now prevents overwriting of system globals” for the whole project? If not, you’ve just royally screwed our suite of business apps and we’re going to have to rewrite tens of thousands of lines of code across 20+ modules per app.

It’s fine to try and enforce ‘good practice’ on people, but you NEED to provide the ability to switch it off for backwards compatibility.

One option is to use an older daily build or the last release build until you have time to adapt to this change.

Just out of curiosity, why are you overwriting system globals? What is the use case? Which ones are you overwriting?

Rob

Rob, it suits me to use it in the way I’m using it. I’ll live with the perceived ‘bad practice’.

I don’t really want to ‘adjust to the change’, because what that entails is me having to alter 10,000 or more lines of code.

FYI, I’m overwriting print in this particular instance, and up until now, it worked absolutely perfectly.

That’s not to say I don’t override other system functions to do my bidding. Actually, it’s one of the strengths of lua as far as I’m concerned, and you just took that away from us :frowning:

I know what your reply is going to be… so let me just preempt that by saying that I really don’t want to “think about it a little more carefully and make your override local to your project code”. That’s fine when you’re starting a blank project, but this is a mature business app we’ve been developing in Corona for a number of years, and you guys just broke it.

Unless you want to supply a few guys for a few hundred man hours to come and refactor our code to suit coding practices you insist we adopt?..

We haven’t taken anything away, we’ve just made it much harder to shoot yourself in the foot (which was a recurring issue).  As the original article says:

… but if you’re deep in the heart of Lua and are accessing the _G globals table, you can still do so via Runtime._G, but this is advanced usage and the safety net comes off. And, if you really, truly need to change the value of a system global, you can always use Lua’s rawset() (but if you really need to do it, you’d know this already).

If it isn’t clear from this how to globally override print() then feel free to send me a PM with your current code and I’ll translate it for you.

Not exactly sure what you’d like to see of my current code? All half a million lines of code over 22 modules? Because that’s what I’m asking for; I’d like that my overwritten Print function worked seamlessly across all of that in tomorrows Corona the way it worked yesterday without you arbitrarily deciding to break 4 years worth of development because you suddenly figured you’d introduce some mandatory coding standards.

So yeah, I don’t really see how my giving you code would make any difference. Make what worked before work again please.

Even if I have to put a switch at the top of my main.lua saying iUnderstandImABadBadCoderAndReallyShouldntDoItLikeThis(“true”)

(obviously I tried using Runtime._G.Print, but it DOES NOT work the same as it did before)

I have a require(“utilities”) that takes care of overwrites like print.  I just had to prepend Runtime._G.nameoffunction

here’s an snippet of what I have. 

--- Overwrites the print function and adds a check to see if it is a table, then output it like print\_r -- @param ... -- @usage print(variableName) -- @see print\_r function Runtime.\_G.print(...) --Cycle through each argument for i=1, #arg do if(arg[i]) then --If a table, output it like print\_r if(type(arg[i]) == "table" or type(arg[i]) == "function") then Runtime.\_G.print\_r(arg[i]) else \_oldPrint(arg[i]) end end end end

But immediately calling print() afterwards does not work… If you aren’t using composer and going to print() in the main.lua before the first scene transition, you’ll need to put some sort of short delay to output it.  I’m sure someone else has a better way to do it, but i don’t print anything inside the main.lua, but in the composer scenes afterwards.

Edit: user error on my part (see next 2 posts)

@JWiow  I sent you a PM.  We can resolve this to your satisfaction.

@noriega  I’m not completely sure what you mean by “immediately calling print() afterwards does not work” …

I created utilities.lua and put your code above in it (I removed the Runtime._G. from the print_r() call as that doesn’t make sense; print_r() is presumably defined in your  utilities.lua  file, it’s not a system global).

I added this to my main.lua and got exactly what I should:

local utilities = require('utilities') local myTable = { one = 1, two = 2, three = 3 } print(myTable)

What am I doing differently?

+1  This is actually a *feature* of the language – regardless that beginners might “abuse” it.  Don’t let anyone tell you it’s “bad practice” - the Lua designers actually intended and encourage such things (and overriding print is a very common use case, given that Lua is embedded in such a wide variety of environments).

The current reasoning seems a bit like saying that Linux shouldn’t have “rm” because someone might accidentally delete something.

I’d prefer if these sort of “kid gloves” / “protect me from myself” -type features were optional, defaulting to off so as to avoid the type of breaking change as currently being discussed.  Developers could then toggle them on for a dev-time check as desired, or off for production. Or just leave it out and let us implement it ourselves as needed (fe, I’ve long used Niklas Frykholm’s GLOBAL_lock() approach)

Apologizes.  I was trying to test jwios’s theory in my current codebase earlier in the day, but realized there’s another utility that captures the print and doesn’t output unless a debug flag is set.  I just so happened to put that print right before the debug flag was set. Explains why the extra frame is needed to be processed prior to printing.  I have edited my last post for anyone scrolling through.  The Runtime._G.print is working properly in my case.

Absolutely agree, @davebollinger.

Getting quite sick of stuff (in general!) being ‘dumbed down’, for ‘my own benefit’ when the net result is that I have to do twice as much work to accomplish the same thing to work round it. That appears to be the case with IT in general, since everyone and their dog thinks they can ‘do computers’ now. Yeah - you can ‘do computers’ now because they operate more like a friggin’ toy, and as a side effect, anyone who wants to do anything remotely advanced has to jump through major hoops! Grr. 

But being a coder takes a certain discipline, and one of those is ‘this breaks stuff. So don’t do that, k?’… 

I don’t care if they switch it on by default for all new projects. But it NEEDS an option to turn it off. I really, really don’t need ‘protecting from myself’ or hand holding. I’ve been coding for 30+ years, coding in lua for 12 years and in Corona since 2009. Sure, still learning all the time… But I do know enough to know how to not ‘shoot myself in the foot’. 

What I need is my code to work like it worked before from one build to the next, or all the benefits of Corona become null and void and it’d be a lot safer to code natively, even if we had to maintain multiple codebases for different platforms.

I’ve been running into a few “false positives” while running the setCheckGlobals.

An example below in regards to socket.http

Runtime:setCheckGlobals(true) -- Load the relevant LuaSocket modules local http = require( "socket.http" ) local ltn12 = require( "ltn12" ) -- Create local file for saving data local path = system.pathForFile( "hello.png", system.DocumentsDirectory ) local myFile = io.open( path, "w+b" ) -- Request remote file and save data to local file http.request{ url = "http://www.coronalabs.com/demo/hello.png", sink = ltn12.sink.file(myFile), } -- Display local file local testImage = display.newImage( "hello.png", system.DocumentsDirectory )

outputs:

21:20:15.368 WARNING: setting global table 'socket' 21:20:15.368 stack traceback: 21:20:15.368 [C]: ? 21:20:15.368 [C]: in function 'require' 21:20:15.368 ?: in function 'require' 21:20:15.368 ?: in main chunk 21:20:15.368 [C]: ? 21:20:15.368 [C]: in function 'require' 21:20:15.368 ?: in function 'require' 21:20:15.368 ?: in main chunk 21:20:15.368 [C]: ? 21:20:15.368 [C]: in function 'require' 21:20:15.368 ?: in function 'require' 21:20:15.368 C:\Users\norib\Documents\Corona Projects\test1\main.lua:5: in main chunk 21:20:15.368 WARNING: setting global table 'socket' 21:20:15.368 stack traceback: 21:20:15.368 [C]: in function 'module' 21:20:15.368 ?: in main chunk 21:20:15.368 [C]: ? 21:20:15.368 [C]: in function 'require' 21:20:15.368 ?: in function 'require' 21:20:15.368 ?: in main chunk 21:20:15.368 [C]: ? 21:20:15.368 [C]: in function 'require' 21:20:15.368 ?: in function 'require' 21:20:15.368 C:\Users\norib\Documents\Corona Projects\test1\main.lua:5: in main chunk 21:20:15.368 WARNING: setting global table 'ltn12' 21:20:15.368 stack traceback: 21:20:15.368 [C]: in function 'module' 21:20:15.368 ?: in main chunk 21:20:15.368 [C]: ? 21:20:15.368 [C]: in function 'require' 21:20:15.368 ?: in function 'require' 21:20:15.368 ?: in main chunk 21:20:15.368 [C]: ? 21:20:15.368 [C]: in function 'require' 21:20:15.368 ?: in function 'require' 21:20:15.368 C:\Users\norib\Documents\Corona Projects\test1\main.lua:5: in main chunk 21:20:15.368 WARNING: setting global table 'mime' 21:20:15.368 stack traceback: 21:20:15.368 [C]: ? 21:20:15.368 [C]: in function 'require' 21:20:15.368 ?: in function 'require' 21:20:15.368 ?: in main chunk 21:20:15.368 [C]: ? 21:20:15.368 [C]: in function 'require' 21:20:15.368 ?: in function 'require' 21:20:15.368 ?: in main chunk 21:20:15.368 [C]: ? 21:20:15.368 [C]: in function 'require' 21:20:15.368 ?: in function 'require' 21:20:15.368 C:\Users\norib\Documents\Corona Projects\test1\main.lua:5: in main chunk 21:20:15.368 WARNING: setting global table 'socket' 21:20:15.368 stack traceback: 21:20:15.368 [C]: in function 'module' 21:20:15.368 ?: in main chunk 21:20:15.368 [C]: ? 21:20:15.368 [C]: in function 'require' 21:20:15.368 ?: in function 'require' 21:20:15.368 C:\Users\norib\Documents\Corona Projects\test1\main.lua:5: in main chunk

Now I could easily put the Runtime:setCheckGlobals(true) below the requires in main.lua, and it will be fine for just that file… but if i had different scenes using composer and not in main.lua, it will produce the same false positive output.  When I used strict.lua i only would declare it once in my main.lua and it was good throughout different scenes/modules.

along those lines, tho a true positive, the physics library has long leaked the global “physics”

Letting the engineer’s know.

This is because the Lua Socket library is implemented in fairly old school Lua.  I’m figuring out a fix.

Is there any way to disable the “now prevents overwriting of system globals” for the whole project? If not, you’ve just royally screwed our suite of business apps and we’re going to have to rewrite tens of thousands of lines of code across 20+ modules per app.

It’s fine to try and enforce ‘good practice’ on people, but you NEED to provide the ability to switch it off for backwards compatibility.