This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
apo's profile picture

Metatable inconsistency

Started by apo, 08 July 2013 - 04:28 PM
apo #1
Posted 08 July 2013 - 06:28 PM
I was trying to make a json library work with computercraft to use as a part of my project but I faced some errors. After some debugging I found this to be the cause of the problem.



It seems like in computercraft setmetatable(new, meta) converts the __index from a table to a function in the returned new. Any idea why this is?
Lyqyd #2
Posted 08 July 2013 - 08:09 PM
Split into new topics.

I'm curious as to how this would be causing problems.
Bubba #3
Posted 08 July 2013 - 08:13 PM
My guess is that your library makes changes to the metatable values through the table returned by getmetatable, correct?

It is a bit odd that this would be happening, but it must be something that's happening under the hood of LuaJ. You could reset the metatable every time and edit the original whenever you want to make a change, but that seems like a crappy work-around. Unfortunately I'm not really sure that there's anything that can be done about this other than update to the newest version of LuaJ (If this bug has even been fixed), but for reasons of backward-compatibility this will not happen.
MysticT #4
Posted 08 July 2013 - 08:28 PM
This is not a bug in LuaJ, it is done in the bios. The setmetatable function is "replaced" with this:

local nativesetmetatable = setmetatable
function setmetatable( _o, _t )
  if _t and type(_t) == "table" then
    local idx = rawget( _t, "__index" )
    if idx and type( idx ) == "table" then
      rawset( _t, "__index", function( t, k ) return idx[k] end )
    end
    local newidx = rawget( _t, "__newindex" )
    if newidx and type( newidx ) == "table" then
      rawset( _t, "__newindex", function( t, k, v ) newidx[k] = v end )
    end
  end
  return nativesetmetatable( _o, _t )
end
I don't know why this is done, but there must be a reason why they added it.
apo #5
Posted 09 July 2013 - 01:31 AM
I'm curious as to how this would be causing problems.
The library I'm using is here: http://regex.info/code/JSON.lua

Both the encode and decode methods check the following

function OBJDEF:encode(value, etc)
   if type(self) ~= 'table' or self.__index ~= OBJDEF then
	  OBJDEF:onEncodeError("JSON:encode must be called in method format", etc)
   end
The second condition fails in computercraft.

The second condition exists to make sure that the method is called correctly. You're supposed to call it with
json:encode(tabletoencode)
But you could call it accidentally with
json.encode(tabletoencode)

If you did that the method would carry on happily thinking that tabletoencode was self, rather than json. It would then explode with a confusing error. The check prevents that.

So why does it fail in computercraft? Well it makes sure it's called the right way by confirming self.__index == OBJDEF. This works because earlier on OBJDEF.__index was explicity set to OBJDEF, then set as self's metatable. But under computercraft when self's metatable is set, OBJDEF.__index is converted to a function, and suddenly self.__index and OBJDEF reference completely different things.

Wow that was a mouthful. I'll just give you the code in the spoiler. It returns the json object when you load the script with json = (loadfile "JSON.lua")()
Spoiler

local VERSION = 20130120.6  -- version history at end of file
local OBJDEF = { VERSION = VERSION }
...
OBJDEF.onDecodeOfNilError  = OBJDEF.onDecodeError
OBJDEF.onDecodeOfHTMLError = OBJDEF.onDecodeError
...
OBJDEF.__index = OBJDEF

function OBJDEF:new(args)
   local new = { }

   if args then
	  for key, val in pairs(args) do
		 new[key] = val
	  end
   end

   return setmetatable(new, OBJDEF)
end

return OBJDEF:new()
Lyqyd #6
Posted 09 July 2013 - 01:40 AM
Would it be problematic to set OBJDEF's metatable to OBJDEF? If you did so, in ComputerCraft, the function value that replaces the __index table value should be present in each new metatable. Of course, you then have to modify the conditional to check self.__index against OBJDEF.__index. That may be a viable workaround that keeps the functionality of the check without needing to modify the bios.
apo #7
Posted 09 July 2013 - 02:00 AM
That could work. It honestly doesn't matter to me whether I have the checks so for my own use I just deleted them for a quick solution. I was mostly just curious what was going on :)/>