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

Support Lua require

Started by DrSpork, 18 November 2012 - 11:39 AM
DrSpork #1
Posted 18 November 2012 - 12:39 PM
Provided the Lua engine you use supports it, the require statement provides a more convenient and robust way to import functions vs. CC's built in API system (e.g. Metatables work properly, Can change the namespace the file uses, etc.)

It would make it much easier to program complex systems if require statements could be used.
Cloudy #2
Posted 18 November 2012 - 12:50 PM
The LuaJ library supports it - but it is disabled (for good reason, it allowed Java classes to be loaded including the LuaJ native IO). I do however plan to allow API's to have metatables providing it can be done without causing any security issues (highly likely).
DrSpork #3
Posted 18 November 2012 - 12:57 PM
Ahh, OK. Thanks for the response. Sucks that the LuaJ version has that many security holes.
Cloudy #4
Posted 18 November 2012 - 01:04 PM
To be quite honest it would be easy to code require yourself in Lua - but once loadAPI supports metatables all should be fine.
DrSpork #5
Posted 18 November 2012 - 01:33 PM
To be quite honest it would be easy to code require yourself in Lua - but once loadAPI supports metatables all should be fine.
Any articles on this you know of the top of your head to link me to? My current project could use this.
Sebra #6
Posted 18 November 2012 - 07:31 PM
To be quite honest it would be easy to code require yourself in Lua - but once loadAPI supports metatables all should be fine.
Will this work or will it become security hole?
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 tAPImt=getmetatable(tENV)
  if tAPImt.__index == _G then
	tAPImt.__index = nil
  end

  _G[sName] = tENV
  tAPIsLoading[sName] = nil
  return true
end
immibis #7
Posted 18 November 2012 - 07:53 PM
To be quite honest it would be easy to code require yourself in Lua - but once loadAPI supports metatables all should be fine.
Will this work or will it become security hole?
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 tAPImt=getmetatable(tENV)
  if tAPImt.__index == _G then
	tAPImt.__index = nil
  end

  _G[sName] = tENV
  tAPIsLoading[sName] = nil
  return true
end

It will stop the API from being able to access _G once it's loaded (unless it saves it in a local variable)
Sebra #8
Posted 18 November 2012 - 08:41 PM
It will stop the API from being able to access _G once it's loaded (unless it saves it in a local variable)
Thanks. I did not thought about it :)/>/>
Would this variant be better? API can return value needed or old default scheme used. No APIs needs to be rewritten.
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 )
	local tAPI = fnAPI()
	if not tAPI then
	  tAPI = {}
	  for k,v in pairs( tEnv ) do
		tAPI[k] = v
	  end
	end
	_G[sName] = tAPI
	tAPIsLoading[sName] = nil
	return true
  else
	printError( err )
	tAPIsLoading[sName] = nil
	return false
  end
end
immibis #9
Posted 19 November 2012 - 12:55 AM
It will stop the API from being able to access _G once it's loaded (unless it saves it in a local variable)
Thanks. I did not thought about it :)/>/>
Would this variant be better? API can return value needed or old default scheme used. No APIs needs to be rewritten.
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 )
	local tAPI = fnAPI()
	if not tAPI then
	  tAPI = {}
	  for k,v in pairs( tEnv ) do
		tAPI[k] = v
	  end
	end
	_G[sName] = tAPI
	tAPIsLoading[sName] = nil
	return true
  else
	printError( err )
	tAPIsLoading[sName] = nil
	return false
  end
end
That would work
billysback #10
Posted 19 November 2012 - 02:24 AM
This was creater proir to seeing this suggestion but as it's kind of relevant I thought I would post it;
Would this suffice or are you looking for something more/less/different?
http://www.computercraft.info/forums2/index.php?/topic/6101-api-classy-classes-in-lua-v11-inheritance/
Cloudy #11
Posted 19 November 2012 - 02:33 AM
It will stop the API from being able to access _G once it's loaded (unless it saves it in a local variable)
Thanks. I did not thought about it :)/>/>
Would this variant be better? API can return value needed or old default scheme used. No APIs needs to be rewritten.
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 )
	local tAPI = fnAPI()
	if not tAPI then
	  tAPI = {}
	  for k,v in pairs( tEnv ) do
		tAPI[k] = v
	  end
	end
	_G[sName] = tAPI
	tAPIsLoading[sName] = nil
	return true
  else
	printError( err )
	tAPIsLoading[sName] = nil
	return false
  end
end

I like it! I'll speak to dan and see what he thinks.
Sebra #12
Posted 19 November 2012 - 03:34 AM
Thanks! Yet another time :)/>/>
DrSpork #13
Posted 19 November 2012 - 06:48 AM
I tried using that version of loadAPI (renaming it and dropping it into my file), and it loads OK, but metatables still break. I can still get the metatable using getmetatable(), and I can do getmetatable(object).functionName(), but not object.functionName().
Orwell #14
Posted 19 November 2012 - 09:22 AM
I tried using that version of loadAPI (renaming it and dropping it into my file), and it loads OK, but metatables still break. I can still get the metatable using getmetatable(), and I can do getmetatable(object).functionName(), but not object.functionName().
That's because you would do object:functionName(), right? And the function would have to be defined like that in the API as well. The dot notation works only for the metatables themselves. You could of course create the objects with the functions as a member, that's a whole different deal.
Sebra #15
Posted 19 November 2012 - 09:32 AM
I tried using that version of loadAPI (renaming it and dropping it into my file), and it loads OK, but metatables still break. I can still get the metatable using getmetatable(), and I can do getmetatable(object).functionName(), but not object.functionName().
Have you __index in metatable?
DrSpork #16
Posted 19 November 2012 - 10:02 AM
Have you __index in metatable?
Yes , I do. It works fine when run from the same file. I know you use : to call the function but using . allows you to print out the id of the function (cause I was testing if it existed by doing print(object.functionName))

EDIT: I just checked and it works fine if I dofile() in the api file. I lose the namespacing though :)/>/>

EDIT 2: Never mind, fixed, now it works with that version of os.LoadAPI. I had it defined like this:


Class = {}
Class:function(a,:D/>/>
	SOMETHING
end

And turns out it only works if you do


Class = {
	function = function(self,a,:D/>/>{
		  SOMETHING
	}
}
CoolisTheName007 #17
Posted 20 November 2012 - 11:45 AM
I just finished part of my plan to implement a proper require, which is providing an efficient search function that can use glob's.
EDIT: just noticed require doesn't do that much, it just replaces ? wih the file to be loaded..oh well, I'll call it an improvement!
Check it out here if interested. It's also there in github, folow the link!
CoolisTheName007 #18
Posted 22 November 2012 - 01:37 PM
snip

I've implemented require. Check it out if interested:
http://www.computercraft.info/forums2/index.php?/topic/6286-loadreq-api-require-implemented-in-ccsome-goodies/