In Lua code such as
for key, value1 in pairs(frame.args) do value2 = frame.args[key] ...
value2 will be nil when the argument name is a string containing an integer between 10^14 and 2^63, as with:
{{#invoke:module|func|100000000000000=foo}}This may be surprising for users.
LuaEngine::getAllExpandedArguments() returns the arguments with the name in the array key, so PHP implicitly converts the name to an integer which is then converted to a Lua number. In mw.lua, getExpandedArgument() normalizes the name with name = tostring( name ), however in Lua 5.1 tostring() uses sprintf("%.14g") and thus the name is converted to 1e+14.
14 digits of precision is unusually few for a double-precision number, and this is bound to cause problems elsewhere. We can in principle make tostring() do whatever we want. Lua 5.3 introduced an integer subtype, allowing tostring() to convert large integers to strings without loss:
Lua 5.3.6 Copyright (C) 1994-2020 Lua.org, PUC-Rio > =tostring(100000000000000) 100000000000000 > =tostring(100000000000000.0) 1e+14
And Lua 5.5 introduced iterative conversion of float to string with the goal of lossless conversion:
Lua 5.5.1 Copyright (C) 1994-2026 Lua.org, PUC-Rio > tostring(100000000000000.0) 100000000000000.0
We can in principle backport something like this. Or we can upgrade Lua (T178146) which will implicitly fix it.
Alternatively:
- __pairs could convert numeric keys to strings using an appropriate format. PHP could help with this by providing named and numbered arguments in separate arrays. This could improve performance since PPTemplateFrame_Hash::getArguments() needs to merge the two underlying arrays.
- mw.lua's getExpandedArgument() could do a better job of conversion. But frame.args[1e14] is inherently weird and broken. Numbered arguments are allocated sequentially so cannot have large indexes. Named arguments have string names.
- The argument name could be passed back to PHP as a number, since PHP does a better job of converting numbers to strings than Lua.
As a workaround, modules can convert the name to a string themselves, with e.g. ("%d"):format(key).