Search code examples
functionluacopydeep-copyluajit

deepcopying function variable gives unexpected results


I have an object that I create using the following function

local function newObject(functionVariable)
    ...
    functionVariable = functionVariable or nop
    ...
    return setmetatable({
        ...
        functionVariable         = functionVariable,
        ...
    }, Objectmt)
end

When I deepcopy this object using this

local function deepcopy(t)
    if t == nil then return nil end

    if type(t) == 'table' then
            local copy = {}

            for k, v in pairs(t) do
                    copy[k] = deepcopy(v)
            end
            setmetatable(copy, deepcopy(getmetatable(t)))
            return copy
    else -- number, string, boolean, etc
            return t
    end
end

and load in the object using this

for k, v in pairs(state.objectsTable) do objectsTable[k] = v end

the function variable is totally wrong. It isn't the function that was passed into the object anymore and yeilds unexpected results


Solution

  • There are a lot of things that could be going wrong, and not enough information to say for sure. However, there are some things I can see that could be wrong with your copy, and one in particular that seems problematic.

    Your deepcopy function will copy the metatable of the object:

    -- snip...
    setmetatable(copy, deepcopy(getmetatable(t)))
    -- ...
    

    However, it is a fairly common practice for objects of the same type to share the same metatable, and moreover, your code seems to do this (although without seeing how Objectmt is defined, it isn't clear). Other code can then, for example, determine if something is an object by doing:

    function isObject(obj)
      return getmetatable(obj)==Objectmt
    end
    

    This will fail for your copy, because the metatable is no longer the same (even if it has the same contents).

    This could be fixed by having a different version of deepcopy (or modifying the existing one) to reuse the metatable:

    -- snip...
    setmetatable(copy, getmetatable(t))
    -- ...
    

    If that is not the problem, then are are some other things to consider:

    • Your deepcopy function does not copy table keys. In some cases copying keys could be important, but that doesn't seem to be the case for any of the code you've shown.
    • Your deepcopy function will not copy functions. This could be important if you use functions with mutable upvalues. Again, this isn't the case for any of the code you've shown.
    • It is possible that there are other tables besides the metatable that ought to be copied by reference rather than by value. To know which is appropriate, you must understand the way they are used.
    • There could be userdata or other less common types (e.g. coroutines) that are currently being copied by reference that would need to be copied by value. Some things might not be copyable by value.
    • There might be something in the data you are copying that ought not by copied at all, for example, a unique identifier.
    • The problem might not be with the copy at all.