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

Replacing System Level Apis

Started by BigTwisty, 12 September 2013 - 08:40 PM
BigTwisty #1
Posted 12 September 2013 - 10:40 PM
I am trying to wrap certain system level APIs in order to control an application's access to things my OS is managing. To test this concept, I have the following code:

newTerm = {}
for k,v in pairs(term) do
  newTerm[k] = function(...) return v(...) end
end
oldTerm = _G.term
_G.term = newTerm
print("The quick brown fox jumped over the lazy dog")
_G.term = oldTerm

This works fine. As soon as I try to replace one of the functions with a non-standard wrapped version, the system crashes to reboot.

newTerm = {}
for k,v in pairs(term) do
  newTerm[k] = function(...) return v(...) end
end
newTerm.write = function(...)
  args = {...}
  for i = 1,#args do
	if type(args[i]) == "string" then
	  args[i] = args[i]:upper()
	end
  end
  return term.write(unpack(args))
end
oldTerm = _G.term
_G.term = newTerm
print("The quick brown fox jumped over the lazy dog")
_G.term = oldTerm

This should simply allow normal activity on the screen, but printing everything in caps. Obviously this is just a proof of concept, but it should work. Any ideas?
PixelToast #2
Posted 12 September 2013 - 10:48 PM
you are doing it wrong

local newTerm = {}
local oldTerm = term
for k,v in pairs(term) do
  newTerm[k] = v
end
newTerm.write = function(text)
  return oldTerm.write((text or ""):upper())
end
term=newTerm
print("The quick brown fox jumped over the lazy dog")
term=oldTerm

EDIT: i derped, forgot () after :upper
Yevano #3
Posted 12 September 2013 - 10:53 PM
I'm pretty sure you're getting a stack overflow. Your custom function calls the function from the global term, but at the time of invocation term is set to your custom term, so the function just calls itself over and over again. You could fix this by directly referencing oldTerm from your custom function.
BigTwisty #4
Posted 12 September 2013 - 11:09 PM
That worked. I was under the impression that as _G.term was still set to the old term at the point where this function was established, the new function would be compiled with the old function reference.
Xemrox #5
Posted 13 September 2013 - 05:16 PM
how about envmapping?

local customEnv = {}
customEnv.term = {}
customEnv.term.write = function() print("magic") end
setmetatable(customEnv, { __index = _G })
local userfunc = loadfile("testfile")
setfenv(userfunc, customEnv)
userfunc()
BigTwisty #6
Posted 13 September 2013 - 07:09 PM
That looks like it could be a more elegant solution than swapping out globals between each running application. Give each application its own environment! I love it! You'z guyz is geniuses…

Now for this example to be functional, wouldn't it have to assign _G.term as a metatable __index for customEnv.term? I wouldn't think the metatable for customEnv would handle lower level tables as well… Am I wrong?

Also, what if that application spawns another application? Would that secondary app default back to the default environment, thus allowing it access to the old terminal?
Symmetryc #7
Posted 16 September 2013 - 06:11 PM
Now for this example to be functional, wouldn't it have to assign _G.term as a metatable __index for customEnv.term? I wouldn't think the metatable for customEnv would handle lower level tables as well… Am I wrong?
Not entirely sure of what you're asking, but does this help?

local a = {}
a.b = {}
a.b.c = 5
local b = setmetatable({}, {__index = a})
print(b.b.c) --> 5

How this works:
Lua checks for b.b.c and sees that it is nil/attempt to index nil/whatever, then it goes over to its metatable and sees that there is an __index table. It then checks this table for the same thing. Since there is an a.b.c, Lua uses that instead of b.b.c. Hope this helps!
BigTwisty #8
Posted 22 September 2013 - 12:21 AM

local a = {}
a.b = {}
a.b.c = 5
local b = setmetatable({}, {__index = a})
print(b.b.c) --> 5
The reason this code works is that b.b doesn't exist, prompting lua to go looking for the __index.

In Xem's code, he defines customEnv.term as a table and gives it a member, write(). Then he sets the __index for customEnv. The problem with this is that if the user tries to access something like term.setCursorPos(), lua sees that customEnv HAS a member called term and doesn't check the __index. Then you get an error, because the new term only had one member, write, and no __index. Hence term would need its own metatable with __index set to _G.term.