Edit: The roblox given error message actually tells the wrong function name even if I’m not using debug.info, but I get the correct function name in studio (I made it error in studio as well by setting the variable to be nil). However, the situation where the code works correctly in studio and errors in live game seems to only happen when I do use debug.info.
- What do you want to achieve?
I made a class system and for debugging purposes I made a function local getClassName
in the modulescript called Class. The class constructor Class.new
calls getClassName
and stores the result in a table.
Here's the code of the modulescript Class.
local Class = {}
local metadata = {}
local classes = {}
local classNamesForClasses = {}
local function getClassName()
local classModuleFullName = debug.info(3, "s")
return string.reverse(string.split(string.reverse(classModuleFullName), ".")[1])
end
function Class.new(classData, parentClass)
assert(typeof(classData) == "table", "classData needs to be a table.")
--assert(typeof(isAbstract) == "boolean", "isAbstract needs to be a boolean.")
assert(typeof(parentClass) == "table" or typeof(parentClass) == "nil", "parentClass needs to be either a table (class) or nil")
local objectProperties = classData.objectProperties
assert(objectProperties ~= nil, "no object properties")
local staticProperties = classData.staticProperties
assert(staticProperties ~= nil, "no static properties")
for propertyName, property in pairs(objectProperties) do
assert(property.get ~= nil or property.set ~= nil, "Property cannot be both unreadable and unwritable.")
-- Actually, there's probably no problem in this if the method is a static method.
--assert(classData[propertyName] == nil, "Having a method and an object property with the same name is not allowed.")
end
for propertyName, property in pairs(staticProperties) do
assert(property.get ~= nil or property.set ~= nil, "Property cannot be both unreadable and unwritable.")
assert(classData[propertyName] == nil, "Having a method and a static property with the same name is not allowed.")
end
local self = {}
metadata[self] = {
classData = classData,
--isAbstract = isAbstract,
parentClass = parentClass
}
classes[classData] = self
classNamesForClasses[self] = getClassName()
return setmetatable(self, Class)
end
local function tryGetMethod(class, key)
local currentClass = class
repeat
local classMetadata = metadata[currentClass]
local classData = classMetadata.classData
--assert(classData.staticProperties[key] == nil, "This is a property, not a method.")
if classData.staticProperties[key] ~= nil then
-- this is a property
return nil
end
-- Now it is known that this is not a property.
local method = classData[key]
--assert(method ~= nil, "Method does not exist.")
if typeof(method) == "function" then
return method
else
-- either this is a field (private field because this system does not support public field (also doesn't support private properties))
-- or this is nil (nothing)
currentClass = classMetadata.parentClass
end
until currentClass == nil
return nil
end
function Class:__index(key)
-- Object properties may be needed by the Object module, but static properties are only needed by this module,
-- and this module can access them without indexing the class object itself.
if key == "objectProperties" then
return metadata[self].classData.objectProperties
elseif key == "staticProperties" then
error("no access")
end
local currentClass = self
repeat
local classData = metadata[currentClass].classData
local staticProperty = classData.staticProperties[key]
if staticProperty ~= nil then
assert(staticProperty.get ~= nil, "This property cannot be read.")
return staticProperty.get()
end
local method = tryGetMethod(currentClass, key)
if method ~= nil then
return method
end
currentClass = Class.getParentClass(currentClass)
until currentClass == nil
error("No property or method with this name exists.")
end
function Class:__newindex(key, value)
assert(key ~= "objectProperties" and key ~= "staticProperties", "The tables 'objectProperties' and 'staticProperties' cannot be changed to new ones.")
local currentClass = self
repeat
local classData = metadata[currentClass].classData
local staticProperty = classData.staticProperties[key]
if staticProperty ~= nil then
assert(staticProperty.set ~= nil, "This property cannot be set.")
staticProperty.set(metadata[currentClass].classData, value)
return
end
currentClass = Class.getParentClass(currentClass)
until currentClass == nil
error("This property does not exist.")
end
function Class.hasMethod(class, key)
return tryGetMethod(class, key) ~= nil
end
-- should only be used by the Object class, not by any other scripts or module scripts
function Class.getMethod(class, methodName)
local method = tryGetMethod(class, methodName)
assert(method ~= nil, "This object method does not exist")
return method
end
-- should only be used by the Object class, not by any other scripts or module scripts
function Class.getParentClass(class)
return metadata[class].parentClass
end
--[[
function Class.isAbstract(class)
return metadata[class].isAbstract
end
--]]
function Class.getClass(classData)
local class = classes[classData]
assert(class ~= nil, "There is no class corresponding to this classData.")
return class
end
-- can be used for calling parent class getter function in child class setter
function Class.getGetter(class, propertyName, isObjectProperty)
local classData = metadata[class].classData
local property = (isObjectProperty and classData.objectProperties or classData.staticProperties)[propertyName]
assert(property ~= nil, "Property doesn't exist.")
assert(property.get ~= nil, "Property is writeonly")
return property.get
end
-- can be used for calling parent class setter function in child class setter
function Class.getSetter(class, propertyName, isObjectProperty)
local classData = metadata[class].classData
local property = (isObjectProperty and classData.objectProperties or classData.staticProperties)[propertyName]
assert(property ~= nil, "Property doesn't exist.")
assert(property.set ~= nil, "Property is readonly")
return property.set
end
function Class.getClassName(class)
return classNamesForClasses[class]
end
return Class
- What is the issue?
When I’m testing in studio, there’s no issue. However, when I publish the place and play it, there’s an error on both the client and the server.
- What solutions have you tried so far? Did you look for solutions on the Developer Hub?
In order to minimize possible effects of my other code, I made a new (server)script and a new modulescript for investigating this.
DebugInfoProblemModule:
local DebugInfoProblemModule = {}
local function problemFunction()
local scriptFullName = debug.info(3, "s")
local returnValue = string.reverse(scriptFullName)
return returnValue
end
function DebugInfoProblemModule.problemFunctionCaller()
local scriptFullNameReverse = problemFunction()
end
return DebugInfoProblemModule
DebugInfoProblemScript
local debugInfoProblemFolder = script.Parent
local DebugInfoProblemModule = require(debugInfoProblemFolder.DebugInfoProblemModule)
DebugInfoProblemModule.problemFunctionCaller()
The error message (server) in the developer console now looks like this.
As I said, I do not get this error in studio, only when playing in the published place. Another interesting thing is that the error message says Line 5 - function problemFunctionCaller
, although line 5 is in the local function problemFunction
.
So that describes the initial problem. Now, when I edited the code, I noticed some surprising things. Here’s the module with an addition.
local DebugInfoProblemModule = {}
local function problemFunction()
local scriptFullName = debug.info(3, "s")
if scriptFullName == nil then
error("scriptFullName == nil")
end
local returnValue = string.reverse(scriptFullName)
return returnValue
end
function DebugInfoProblemModule.problemFunctionCaller()
local scriptFullNameReverse = problemFunction()
end
return DebugInfoProblemModule
And here’s the error message that again says problemFunctionCaller
.
Now, if I add either tostring(tostring(scriptFullName))
, print(tostring(scriptFullName))
or debug.info(1, "n")
, there’s no error anymore. In the first and the third case, output is empty. Below is the output in the second case.
The odd thing here is the lack of error. Here’s the code for the first case tostring(tostring(scriptFullName))
:
local DebugInfoProblemModule = {}
local function problemFunction()
local scriptFullName = debug.info(3, "s")
tostring(tostring(scriptFullName))
if scriptFullName == nil then
error("scriptFullName == nil")
end
local returnValue = string.reverse(scriptFullName)
return returnValue
end
function DebugInfoProblemModule.problemFunctionCaller()
local scriptFullNameReverse = problemFunction()
end
return DebugInfoProblemModule
I made the other two additions to the same location in the code (only one of these three additions was included at a time). There are also other alternative additions that have the same effect (no error).
Also, when adding just tostring(scriptFullName)
or print(scriptFullName)
(no nested function calls) I get my own error (scriptFullName == nil
).
Based on my knowledge of the functions I used in the three additions, none of these additions should change the value of the variable scriptFullName
.
I’m wondering if something in my own code is causing these odd things or if debug.info does something I’m not aware of that causes this.