You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The code above does work as-is, but I would personally not recommend using that. Instead, the code below is more robust / "correct", since it handles some additional edge cases.
--- Check whether this polyfill is actually needed before inserting it. It is--- only required when using Lua 5.1, as well as not using LuaJIT with Lua 5.2--- compatibility mode enabled. This is mainly here to avoid doing unnecessary--- computations when creating a cross-version script.
const _len_0 =if(_G._VERSION =="Lua 5.1")and(_G.rawlen ==nil)importtype,error,rawget,select, debug from_G
global rawlen =(object)->#object
const _getmetatable_0 =do
const gloabl_getmetatable =_G.getmetatable
--- The regular / global `getmetatable()`-function can be overloaded--- using the `__metatable`-field of a metatable. This is basically--- never used in practice (because why on earth would you do this?),--- but for the sake of correctness, this wrapper at least prevents--- issues with `__metatable` being set to an unexpected type.
const wrapper =(object)->
const metatable = gloabl_getmetatable(object)iftype(metatable)=="table"
metatable
elsenil--- If possible, `debug.getmetatable()` is used instead of the--- global `getmetatable()`-function. This is because the latter--- could get tricked by something like this:------ ```yuescript--- tb = {--- <metatable>: {--- <len>: () => 5--- }--- <len>: () => 3--- }--- --- --- This may incorrectly print 5 instead of 3 or 0--- print(#getmetatable(tb))--- ```------ Note that some environments disable access to the `debug`-library.if(type(debug)=="table")and(type(debug.getmetatable)=="function")debug.getmetatableelse
wrapper
const get_function_name =if(type(debug)=="table")and(type(debug.getinfo)=="function")import getinfo from debug
()-> getinfo(2,"n").name
else()->"_len_0"(...)->do
const argc =select("#",...)if argc !=1
const function_name = get_function_name()error("function %s() expected exactly one argument, got: %d"::format(
function_name
argc
))
const object =...do--- Throw an error if the type of `object` doesn't support getting--- it's length.
const type_of_object =type(object)if type_of_object notin["string","table","userdata"]--- This is the same error message that Lua would throw when--- doing something like this:------ ```lua--- print(#(false))--- ```error("attempt to get length of a %s value"::format(
type_of_object
))
const metatable = _getmetatable_0(object)--- `object` has no metatable -> return its raw length.if metatable ==nilreturn#object
--- Use `rawget()` to prevent being tricked by `__index`, e.g.:------ ```yuescript--- tb = {--- <>: {--- <index>: {--- __len: () => 7--- }--- }--- }------ --- This will incorrectly print 7 instead of throwing an error--- print(getmetatable(tb).__len(tb))--- ```
const len_meta =rawget(metatable,"__len")--- `object` has no `__len`-metamethod -> return its raw length.--- Note: `__len` might also be a callable table or userdata-object--- instead of a function, which is why no strict type check is--- performed here.if len_meta ==nilreturn#object
len_meta(object)else(object)->#object
The length-operator isn't overloadable in PUC Lua 5.1 at all, and can only be overloaded in LuaJIT when using a build with the compilation-flag
-DLUAJIT_ENABLE_LUA52COMPAT
enabled (which isn't the case for basically all pre-built distributions).I would appreciate it if a polyfill were to be automatically inlined when targeting Lua 5.1. A basic version could look like this:
It could then be inserted whenever getting the length of an object, i.e.:
would compile to
The code above does work as-is, but I would personally not recommend using that. Instead, the code below is more robust / "correct", since it handles some additional edge cases.
And here's a bunch of tests:
The text was updated successfully, but these errors were encountered: