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

persistence of local variables

Started by aaa, 13 July 2013 - 01:20 PM
aaa #1
Posted 13 July 2013 - 03:20 PM
Hi!

[I've CC 1.5]
I usually read the built in programs for some inspiration, and I found something I can hardly explain. In the shell, there are local variables like sPath or tEnv declared at the beginning which give the paths to search the programs, or the environment used at this level of shell, and everything goes well, we can access to sPath with shell.path and shell.setPath, and tEnv is well used while running programs, new shells included.

And now, in the bios, before the declaration of os.loadAPI, there is a local table declared to ensure that an API is not loaded twice. But that doesn't work at all! When I call os.loadAPI on the same API twice in the lua prompt, or even twice on a row in the same program, I do not raise the error "API "..sName.." is already being loaded". I'm very curious to know how it works. I haven't yet tried to load twice the same API in the bios, cause I didn't fell like changing it, but I think this could be very informative.

My hypothesis, though I do not convince myself, is that the bios isn't compiled by a regular loadstring (maybe by a Java one).

definition of os.loadAPI in the bios :
Spoiler
local tAPIsLoading = {}
function os.loadAPI( _sPath )
	local sName = fs.getName( _sPath )
	if tAPIsLoading[sName] == true then
		printError( "API "..sName.." is already being loaded" )
		return false
	end
	tAPIsLoading[sName] = true
		
	local tEnv = {}
	setmetatable( tEnv, { __index = _G } )
	local fnAPI, err = loadfile( _sPath )
	if fnAPI then
		setfenv( fnAPI, tEnv )
		fnAPI()
	else
		printError( err )
		tAPIsLoading[sName] = nil
		return false
	end
	
	local tAPI = {}
	for k,v in pairs( tEnv ) do
		tAPI[k] =  v
	end
	
	_G[sName] = tAPI	
	tAPIsLoading[sName] = nil
	return true
end

definition of sPath in the shell program :
Spoiler
local sPath = (parentShell and parentShell.path()) or ".:/rom/programs"
shell.path and shell.setPath :
Spoiler

function shell.path()
	return sPath
end

function shell.setPath( _sPath )
	sPath = _sPath
end
Shell.programs using sPath :
Spoiler


function shell.programs( _bIncludeHidden )
local tItems = {}

-- Add programs from the path
	for sPath in string.gmatch(sPath, "[^:]+") do
	 sPath = shell.resolve( sPath )
  if fs.isDir( sPath ) then
   local tList = fs.list( sPath )
   for n,sFile in pairs( tList ) do
	if not fs.isDir( fs.combine( sPath, sFile ) ) and
	   (_bIncludeHidden or string.sub( sFile, 1, 1 ) ~= ".") then
	 tItems[ sFile ] = true
	end
   end
  end
	end
-- Sort and return
local tItemList = {}
for sItem, b in pairs( tItems ) do
  table.insert( tItemList, sItem )
end
table.sort( tItemList )
return tItemList
end
Grim Reaper #2
Posted 13 July 2013 - 05:35 PM
You almost answered your own question ;)/>

In the definition of os.loadAPI, the last few lines remove the API from the loading queue when it has been loaded properly.

tAPIsLoading[sName] = nil

When you call os.loadAPI from the lua prompt, I believe, you're running the code entered as a function inside of the lua prompt program which is just a function being compiled from a file and run from the shell. When you define variables using the lua prompt without the local keyword, you're adding that variable to the function environment of the lua prompt as a function being executed by the shell which allows you to access it in further calls as a global variable.
aaa #3
Posted 14 July 2013 - 02:50 AM
oh, I didn't saw that line.. everything is clear now, thank you very much.
So I guess tAPIsLoading do not prevent from overwriting API, just from ambiguously loading it twice simultaneously (which rarely happen, methinks)
Grim Reaper #4
Posted 14 July 2013 - 03:03 AM
Exactly. That occurs when you're loading an API and the compilation of the API file fails while loading the API. If you try to reload it after that, the os.loadAPI function will throw that error ;)/>
Lyqyd #5
Posted 14 July 2013 - 06:33 AM
It prevents APIs from loading themselves.
aaa #6
Posted 14 July 2013 - 07:43 AM
It prevents APIs from loading themselves.
I got it. Like #ifndef #define, in more simple because we can use non already defined functions in the code.

Well, I used to reboot the computer to reinstall my personal APIs during debugging, so I never raised the error. In my great ignorance, I even override the loadAPI and that message. Well, I've removed my creepy os.loadAPI function and made a small program (actually two lines, but too many to type at any compilation) to reload an API without rebooting, and raised the error! I was quite happy to know why and to see it works as expected (the API loading was interrupted). Finally, I had a small line to unloadAPI to say "ok, it's not loading any more". Unless you enjoy loading APIs in parallel of the shell, I think it still does its job, preventing APIs loading themselves.