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

Incorrect hiding of the string metatable

Started by Eric, 30 December 2012 - 10:15 PM
Eric #1
Posted 30 December 2012 - 11:15 PM
At the moment, in bios.lua, the string metatable is hidden like this:


local nativegetmetatable = getmetatable
local nativetype = type
local nativeerror = error
function getmetatable( _t )
	if nativetype( _t ) == "string" then
		nativeerror( "Attempt to access string metatable", 2 )
		return nil
	end
	return nativegetmetatable( _t )
end

This is a bad way to approach the problem - in normal lua, the contract of `getmetatable` is to never error. For instance, I might want to check if a value has a metatable, without wanting to do anything to it.

Lua provides a builtin way to protect metatables - the `__metatable` metafield:


getmetatable("").__metatable = "The string metatable is protected"

print(getmetatable("")) -- The string metatable is protected
CoolisTheName007 #2
Posted 30 December 2012 - 11:24 PM
They know about the __metatable field in general, beats me why the __metatable field isn't used for protecting the string metatable.
Eric #3
Posted 31 December 2012 - 12:34 AM
Thinking some more, it would be more useful to actually expose the string metatable in a readonly manner, by:
  • Making a copy of the metatable. This has the advantage of being iterable, but the disadvantage of silently failing when the metatable is modified.
  • 
    local stringmt = getmetatable("")
    local clonedmt = {}
    stringmt.__metatable = clonedmt
    for k, v in pairs(stringmt) do
        clonedmt[k] = v
    end
  • Proxying access, firing an error on modification. Yes, rawset can be used, but that won't actually modify the real metatable. This has the disadvantage of making the table non-iterable, but the advantage of failing loudly.
  • 
    local stringmt = getmetatable("")
    stringmt.__metatable = setmetatable({}, {
        -- the `__index = stringmt` shorthand is unsuitable here
        __index = function(_, k)
            return stringmt[k]
        end,
        __newindex = function(_, k, v)
            nativeerror("The string metatable cannot be changed")
        end
    })
immibis #4
Posted 31 December 2012 - 02:25 AM
The string metatable is shared between computers. I can't see your code on my phone, so I don't know if that affects it.
Eric #5
Posted 02 January 2013 - 03:52 AM
The string metatable is shared between computers.

Wait, really? Is that the only thing shared between computers/
immibis #6
Posted 02 January 2013 - 01:57 PM
The string metatable is shared between computers.

Wait, really? Is that the only thing shared between computers/
I think so. It's a static field in LuaJ so there's no easy way for CC to unshare it.
natedogith1 #7
Posted 07 January 2013 - 09:48 PM
I don't see a reason to prevent getting the string metatable, it's not like the value can be changed outside of java
Cloudy #8
Posted 08 January 2013 - 12:25 AM
Except it can.
immibis #9
Posted 08 January 2013 - 12:48 AM
I don't see a reason to prevent getting the string metatable, it's not like the value can be changed outside of java

getmetatable("").__index.sub = os.reboot

That would reboot something whenever someone does aStringVariable:sub(…)
Not sure if it'll reboot the computer that put it in the metatable or the one running the sub command.
Eric #10
Posted 08 January 2013 - 07:45 AM
Except it can.
Not if you get it in a readonly way, like the two ways I suggested.

getmetatable("").__index.sub = os.reboot

That would reboot something whenever someone does aStringVariable:sub(…)
Not sure if it'll reboot the computer that put it in the metatable or the one running the sub command.

That particular example is no worse than `string.sub = os.reboot`, which is currently allowed - worse yet, all the bios code uses `string.func(s, …)` not `s:func(…)`, so this breaks things at a deeper level.

The case which would really break things is `getmetatable("").__concat = os.reboot`


Again, none of these are problems if the metatable is readonly (instead of inaccesible)
Cloudy #11
Posted 08 January 2013 - 08:17 AM
Why is that example no worse? One propagates to all computers - the other stays on one computer.
Eric #12
Posted 08 January 2013 - 08:22 AM
Why is that example no worse? One propagates to all computers - the other stays on one computer.

Whoops, forgot about the "The string metatable is shared between computers." bit
natedogith1 #13
Posted 08 January 2013 - 02:47 PM
Ok, so I was being stupid and assumed that sense there was no setmetatable method, the metatable couldn't be changed
immibis #14
Posted 08 January 2013 - 08:51 PM
The case which would really break things is `getmetatable("").__concat = os.reboot`
IIRC all string operations are built-in except for indexing, and don't go through the metatable.