multiple lua files with same-named functions

classic Classic list List threaded Threaded
14 messages Options
Reply | Threaded
Open this post in threaded view
|

multiple lua files with same-named functions

Raymond Jacobs
Greetings All,
 
I am having a bit of an issue with the system I've made.
 
I have a single lua_State with a number of registered C functions in it.
 
whenever I want to execute another script file, I make a new co-routine (from C),
I load the lua code into this new co-routine, and then call a function on it.
 
the issue is that all of these different script files have functions of the same name,
and it seems that if i have previously loaded a script with the function 'onCreate' in it,
and I load a new script which doesn't have an onCreate function (yet it tries to call it) it will find
the previously loaded onCreate function, I would rather that it say onCreate doesn't exist (since it is not in the script I've loaded),
but it seems whatever code i load all of the functions get stored into the global state, is this right?
 
and if so how can I solve it? a brute force idea is to clear all the functions from the global state before loading a new script
but that sounds awful =/
 
any info is appreciated, thanks!
 
Raymond Jacobs
Owner,
Ethereal Darkness Interactive
Reply | Threaded
Open this post in threaded view
|

Re: multiple lua files with same-named functions

Andreas Stenius-2
Take a look at setfenv() / lua_setfenv() in the lua manual.

Raymond Jacobs skrev:
Greetings All,
I am having a bit of an issue with the system I've made. I have a single lua_State with a number of registered C functions in it. whenever I want to execute another script file, I make a new co-routine (from C), I load the lua code into this new co-routine, and then call a function on it. the issue is that all of these different script files have functions of the same name, and it seems that if i have previously loaded a script with the function 'onCreate' in it, and I load a new script which doesn't have an onCreate function (yet it tries to call it) it will find the previously loaded onCreate function, I would rather that it say onCreate doesn't exist (since it is not in the script I've loaded), but it seems whatever code i load all of the functions get stored into the global state, is this right? and if so how can I solve it? a brute force idea is to clear all the functions from the global state before loading a new script
but that sounds awful =/
any info is appreciated, thanks! Raymond Jacobs
Owner,
Ethereal Darkness Interactive
www.edigames.com <http://www.edigames.com>


Reply | Threaded
Open this post in threaded view
|

Re: multiple lua files with same-named functions

Goran Zoricic
In reply to this post by Raymond Jacobs
The solution we use is to wrap each script file inside 'function Main() ... end; Main();'. Then you can declare all functions as 'local' inside that scope. You can also use 'function self:Main() ... end;', and declare all functions inside as 'self:SomeFunction', adding those functions to the 'self' object, if you have one... This doesn't place the functions in the global environment, making function names reusable.
 
Cheers,
 
Goran.
 
--
The absolute law of the universe: everything depends
 
 
----- Original Message -----
Sent: Friday, 05 May, 2006 15:52
Subject: multiple lua files with same-named functions

Greetings All,
 
I am having a bit of an issue with the system I've made.
 
I have a single lua_State with a number of registered C functions in it.
 
whenever I want to execute another script file, I make a new co-routine (from C),
I load the lua code into this new co-routine, and then call a function on it.
 
the issue is that all of these different script files have functions of the same name,
and it seems that if i have previously loaded a script with the function 'onCreate' in it,
and I load a new script which doesn't have an onCreate function (yet it tries to call it) it will find
the previously loaded onCreate function, I would rather that it say onCreate doesn't exist (since it is not in the script I've loaded),
but it seems whatever code i load all of the functions get stored into the global state, is this right?
 
and if so how can I solve it? a brute force idea is to clear all the functions from the global state before loading a new script
but that sounds awful =/
 
any info is appreciated, thanks!
 
Raymond Jacobs
Owner,
Ethereal Darkness Interactive
Reply | Threaded
Open this post in threaded view
|

Re: multiple lua files with same-named functions

Luiz Henrique de Figueiredo
In reply to this post by Raymond Jacobs
> and if so how can I solve it? a brute force idea is to clear all the
> functions from the global state before loading a new script

You can run each new script in a brand new Lua state or you can set the
environment of each script after loading it but before running it to a
new table. In the second case, you'll probably want to set an __index
metamethod in the new environment to point to the global environment.
--lhf

Reply | Threaded
Open this post in threaded view
|

Re: multiple lua files with same-named functions

David Given
In reply to this post by Goran Zoricic
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Goran Zoricic wrote:
> The solution we use is to wrap each script file inside 'function Main() ... end; Main();'. Then you can declare all functions as 'local' inside that scope. You can also use 'function self:Main() ... end;', and declare all functions inside as 'self:SomeFunction', adding those functions to the 'self' object, if you have one... This doesn't place the functions in the global environment, making function names reusable.

You don't actually need that dummy function --- a script is a chunk,
which has its own lexical scope, which means that you can use local
variables in it.

- --
+- David Given --McQ-+ "Preacher, don't the Bible have some pretty
|  [hidden email]    | specific things to say about killing?" "Quite
| ([hidden email]) | specific. It is, however, somewhat fuzzier on the
+- www.cowlark.com --+ subject of kneecaps." --- Firefly, _War Stories_
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.1 (GNU/Linux)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iD8DBQFEW2Iwf9E0noFvlzgRAvq8AJ4xoyy+Egewqn78qrYwGQh+z0HxcwCg1K/z
qiDWyS8An1CpcvyWkuEwZHg=
=YU5m
-----END PGP SIGNATURE-----

Reply | Threaded
Open this post in threaded view
|

RE: multiple lua files with same-named functions

Jerome Vuarand-2
In reply to this post by Raymond Jacobs
You can use something like that (tested) :
 
---- foo.lua ----
-- declare helper functions
function push_env_table(t)
 local g = getfenv(0)
    setmetatable(t, {__index=g})
 setfenv(0, t)
end
 
function pop_env_table()
 local t = getfenv(0)
    local g = getmetatable(t).__index
 setfenv(0, g)
end
 
-- declare local stuff
foo = "foo"
function ffoo()
 return "ffoo"
end
 
-- loading a file require 4 lines, you could make a function of it
envbar = {}
push_env_table(envbar)
require("bar")
pop_env_table()
 
-- to use a file simply prepend the env table you defined
print(foo, envbar.foo)
print(ffoo(), envbar.ffoo())
--------------------
 
---- bar.lua ----
foo = "bar"
function ffoo()
 return "fbar"
end
--------------------


De : [hidden email] [mailto:[hidden email]] De la part de Raymond Jacobs
Envoyé : 5 mai 2006 09:52
À : Lua list
Objet : multiple lua files with same-named functions

Greetings All,
 
I am having a bit of an issue with the system I've made.
 
I have a single lua_State with a number of registered C functions in it.
 
whenever I want to execute another script file, I make a new co-routine (from C),
I load the lua code into this new co-routine, and then call a function on it.
 
the issue is that all of these different script files have functions of the same name,
and it seems that if i have previously loaded a script with the function 'onCreate' in it,
and I load a new script which doesn't have an onCreate function (yet it tries to call it) it will find
the previously loaded onCreate function, I would rather that it say onCreate doesn't exist (since it is not in the script I've loaded),
but it seems whatever code i load all of the functions get stored into the global state, is this right?
 
and if so how can I solve it? a brute force idea is to clear all the functions from the global state before loading a new script
but that sounds awful =/
 
any info is appreciated, thanks!
 
Raymond Jacobs
Owner,
Ethereal Darkness Interactive
Reply | Threaded
Open this post in threaded view
|

Re: multiple lua files with same-named functions

Raymond Jacobs
In reply to this post by Luiz Henrique de Figueiredo
This sounds potentialy viable (the second idea), however I am still pretty new to lua,
could you explain in more detail how I would do it?
 
-Raymond

 
On 5/5/06, Luiz Henrique de Figueiredo <[hidden email]> wrote:
> and if so how can I solve it? a brute force idea is to clear all the
> functions from the global state before loading a new script

You can run each new script in a brand new Lua state or you can set the
environment of each script after loading it but before running it to a
new table. In the second case, you'll probably want to set an __index
metamethod in the new environment to point to the global environment.
--lhf

Reply | Threaded
Open this post in threaded view
|

Re: multiple lua files with same-named functions

Daniel Collins
In reply to this post by Raymond Jacobs
> This sounds potentially viable (the second idea), however I am still
pretty new to lua, 
> could you explain in more detail how I would do it? 

Jerome's post shows a neat way to set the environment before loading the
file. That way you don't need to modify the files at all. So you can
have them run in a shared environment if needed, or they can have their
own environment. I would probably put Jerome's solution in a function
(untested):

function requireNewEnv(file, env)

	function push_env_table(t)
 		local g = getfenv(0)
    		setmetatable(t, {__index=g})
 		setfenv(0, t)
	end
 
	function pop_env_table()
 		local t = getfenv(0)
    		local g = getmetatable(t).__index
 		setfenv(0, g)
	end

	newenv = env or {}
	push_env_table(newenv)
	require(file)
	pop_env_table()
end

I will be facing a similar situation to this in my project, but haven't
built my engine sufficiently to have needed a solution. I am tending
towards using a new lua state for each level. It is slightly more heavy
handed and requires me to load all my libraries again, but that really
doesn't take that long in the context of loading a game level. And since
my game is running on mobile phones, I really need to keep the memory
use in check. Also, once I have finished a level, I will not ever need
any of its functions again while another level is running, so there is
no reason to keep those functions in memory while the new level runs
with a new environment table.

- DC



Reply | Threaded
Open this post in threaded view
|

Re: multiple lua files with same-named functions

Jerome Vuarand
Actually if you limit push_env_table and pop_env_table scope to a
function, it is better to set them as local. That prevent loaded
module from redefining pop_env_table.

Now that I see a concrete example, a question comes to my mind : does
a called function (in that case require) has any mean to access the
local variables of one of its caller ? I would say that no since the
local variables are not part of any environment. Is that right ?

2006/5/5, Daniel Collins <[hidden email]>:

> This sounds potentially viable (the second idea), however I am still
pretty new to lua,
> could you explain in more detail how I would do it?

Jerome's post shows a neat way to set the environment before loading the
file. That way you don't need to modify the files at all. So you can
have them run in a shared environment if needed, or they can have their
own environment. I would probably put Jerome's solution in a function
(untested):

function requireNewEnv(file, env)

        function push_env_table(t)
                local g = getfenv(0)
                setmetatable(t, {__index=g})
                setfenv(0, t)
        end

        function pop_env_table()
                local t = getfenv(0)
                local g = getmetatable(t).__index
                setfenv(0, g)
        end

        newenv = env or {}
        push_env_table(newenv)
        require(file)
        pop_env_table()
end

I will be facing a similar situation to this in my project, but haven't
built my engine sufficiently to have needed a solution. I am tending
towards using a new lua state for each level. It is slightly more heavy
handed and requires me to load all my libraries again, but that really
doesn't take that long in the context of loading a game level. And since
my game is running on mobile phones, I really need to keep the memory
use in check. Also, once I have finished a level, I will not ever need
any of its functions again while another level is running, so there is
no reason to keep those functions in memory while the new level runs
with a new environment table.

- DC





Reply | Threaded
Open this post in threaded view
|

Re: multiple lua files with same-named functions

Aaron Brown
JÃrÃme Vuarand wrote:

Now that I see a concrete example, a question comes to my
mind : does a called function (in that case require) has
any mean to access the local variables of one of its
caller ? I would say that no since the local variables are
not part of any environment. Is that right ?

Right.  You can always tell whether a local variable is
accessible from a certain spot by looking at the source
code: the local's scope needs to start above the spot in
question and end after it, which rules out (for instance) a
spot and a local variable in different files.

There is a cheat for this, though -- debug.getlocal can be
used to get a caller's locals (or those of the caller's
caller, and so on down the stack).

--
Aaron
Reply | Threaded
Open this post in threaded view
|

Re: multiple lua files with same-named functions

Raymond Jacobs
In reply to this post by Daniel Collins
I am trying to do all of this from C,
so that example doesn't really help me,
I especially don't know what __index is =/

as I understand it, i want to set my environment to a, 'temporary' one,
load the code, and then set it back (so i get all of my nice global variables and registred C functions and such)
is that right?  and if so how would I acomplish that in C?

thanks,

-Raymond

On 5/5/06, Daniel Collins <[hidden email]> wrote:

> This sounds potentially viable (the second idea), however I am still
pretty new to lua,
> could you explain in more detail how I would do it?

Jerome's post shows a neat way to set the environment before loading the
file. That way you don't need to modify the files at all. So you can
have them run in a shared environment if needed, or they can have their
own environment. I would probably put Jerome's solution in a function
(untested):

function requireNewEnv(file, env)

        function push_env_table(t)
                local g = getfenv(0)
                setmetatable(t, {__index=g})
                setfenv(0, t)
        end

        function pop_env_table()
                local t = getfenv(0)
                local g = getmetatable(t).__index
                setfenv(0, g)
        end

        newenv = env or {}
        push_env_table(newenv)
        require(file)
        pop_env_table()
end

I will be facing a similar situation to this in my project, but haven't
built my engine sufficiently to have needed a solution. I am tending
towards using a new lua state for each level. It is slightly more heavy
handed and requires me to load all my libraries again, but that really
doesn't take that long in the context of loading a game level. And since
my game is running on mobile phones, I really need to keep the memory
use in check. Also, once I have finished a level, I will not ever need
any of its functions again while another level is running, so there is
no reason to keep those functions in memory while the new level runs
with a new environment table.

- DC



Reply | Threaded
Open this post in threaded view
|

Re: multiple lua files with same-named functions

Adrian Sietsma
In reply to this post by Daniel Collins
Daniel Collins wrote:

Jerome's post shows a neat way to set the environment before loading the
file. That way you don't need to modify the files at all. So you can
have them run in a shared environment if needed, or they can have their
own environment. I would probably put Jerome's solution in a function
(untested):


That is also a neat way to do lua config files.
<lua>
local config = {
		-- fill in defaults
		}

local cfgfile="foo.cfg"
local f, err = loadfile(cfgfile)
if f then
    setfenv(f,config)
    f()
else
   print("config file ("..cfgfile..") load failed :"..tostring(err))
   print("... using default config")
end
</lua>

The config file then just has Lua statements :

name="fred"
number=1024
namelen=#name
-- etc

When the config file is executed, all of its 'globals' are stored in the environment, eg the config table. As lhf pointed out, you need to set a meta-index to _G for the config file to be able to call any lua functions.

Adrian

Reply | Threaded
Open this post in threaded view
|

Re: multiple lua files with same-named functions

Javier Guerra Giraldez
In reply to this post by Raymond Jacobs
On Friday 05 May 2006 10:47 pm, Raymond Jacobs wrote:
> I am trying to do all of this from C,
> so that example doesn't really help me,
> I especially don't know what __index is =/
>
> as I understand it, i want to set my environment to a, 'temporary' one,
> load the code, and then set it back (so i get all of my nice global
> variables and registred C functions and such)
> is that right?  and if so how would I acomplish that in C?

in fact, it's easiest to do all from Lua, if you REALLY need it from C, you 
could do a prototype in Lua and then port it to C.

i'll try to explain most of the concepts here, first in Lua, and then try to 
show how to port it to C.

first, when you load a file, what you get is a function representing the whole 
chunk. assume that the file defines some functions, like this:

----- script.lua --------

function foo (x)
  return x * 2
end

function bar (t)
  return foo(t.x)
end

--------------------

this Lua chunk in fact is two separate assignment statements:

----- script.lua -------

foo = function (x)
  return x*2
end

bar = function (t)
  return foo (t.x)
end

---------------------

to get the functions 'foo' and 'bar' defined, you have to execute the chunk 
you get when loading the file:

--- script loader (in lua) ----

local chunk = loadfile ("script.lua")      -- loads/compiles the lua chunk
chunk()    -- executes the chunk
foo(2)      -- calls a script function

------------------------------

if you do that, the script would set two global functions (foo and bar) in the 
global environment.  that's what you want to avoid.  to do that, just change 
the environment for the chunk function, like this:

--- script loader (in lua) ----

local scriptenv = {}   -- a new table (one for each script)
local chunk = loadfile ("script.lua")      -- loads/compiles the lua chunk
setfenv (chunk, scriptenv)    -- sets the 'global' env for the script
chunk()    -- executes the chunk

scriptenv.foo(2)      -- calls a script function

-----------------------------

now, the foo and bar variables won't be set in the 'real' global environment, 
but in a new 'private' global space.  you can use a new one for each script, 
or maybe if you have several groups you could use a different environment for 
each group.  scripts within one group would 'see' the same environment and 
could share global variables or functions.

but..... the localized environment is empty, the scripts can't access any 
function then don't define.  not even 'standard' ones, like print(), or 
open(), or any utility function you've defined for use.

the 'easy' solution is to make the private environment 'inherit' from the real 
global environment.  enter metatables

a metatable is a table associated with another object.  it defines some 
properties, like how to fetch and set for extra fields.  specifically, we 
need here the '__index' property.  if you set the '__index' field of a 
metatable, then it'll be used when trying to fetch a nonexistant key from the 
object that uses this metatable.  __index can contain either a function to be 
called to fetch the key, or (easier to use) a table to use as a 'fallback'.

to make the private environment inherit from the global one:

--- script loader (in lua) ----

local scriptenv = {}   -- a new table (one for each script)
local scriptenv_mt = {__index=_G}  -- the metatable for scriptenv
setmetatable (scriptenv, scriptenv_mt)

local chunk = loadfile ("script.lua")      -- loads/compiles the lua chunk
setfenv (chunk, scriptenv)    -- sets the 'global' env for the script
chunk()    -- executes the chunk

scriptenv.foo(2)      -- calls a script function

-----------------------------

now, when some code in the script tries to fetch a 'global' function (like 
print()), it won't find it in the environment, so Lua would look in the 
metatable, follow a link to the 'real' environment, and find it there.

this way, you can expose the whole 'global' environment, but at the same time 
protect it from being modified.  any 'global' changes the script does are 
limited to its private environment.

you can refine this a little if you want to define a small subset of 
functionality.  for example, you might not want the scripts being able to 
open files, or you want them to use a special version of print(), that sends 
data to your system, instead of going to the screen:

---------------------------------
local cage = {
  -- import some packages:
  string = string,  coroutine = coroutine,
  table = table, math = math,
  
  -- some 'global' functions:
  next = next, ipairs = ipairs, pairs = pairs,
  require = require, type = type,
  tonumber = tonumber, tostring = tostring,
  unpack = unpack, 
  setmetatable = setmetatable,
  getmetatable = getmetatable,

  -- modified global functions:
  print = myprint,
  error = myerror

  -- my own api:
  move = move
  kill = kill
}

local mt = {__index=cage}

function scriptloader (scriptname)
  local scriptenv = {}
  setmetatable (scriptenv, mt)
  
  local chunk  = loadfile (scriptname)
  setfenv (chunk, scriptenv)
  
  chunk ()

  return scriptenv
end
---------------------------

here you define a specific subset of Lua functionality, to be available to 
your scripts.  then, each script has it's own environment, that 'inherits' 
from this "cage".  the scriptloader() function loads a script file and 
executes it into this new private environment, and returns it.  if the script 
defines 'global' functions, they will be registered only in this new 
environment, without 'polluting' the real global environment.

you can have a table with the new environments, like this:

-------------
allscripts  = {}

allscripts[1] = scriptloader ("script.lua")
allscripts[2] = scriptloader ("secondscript.lua")

script[1].foo(x)      -- call the loaded function from first script
script[2].foo(x)      -- from second script
--------------

now......... you want to do this from C?  the easiest way is to just call 
scriptloader()

-- 
Javier

Attachment: pgp1Htk0ZQ4xt.pgp
Description: PGP signature

Reply | Threaded
Open this post in threaded view
|

Re: multiple lua files with same-named functions

Jeff Sheets
On Sat, 2006-05-06 at 09:22 -0500, Javier Guerra wrote:
> On Friday 05 May 2006 10:47 pm, Raymond Jacobs wrote:
> > I am trying to do all of this from C,
> > so that example doesn't really help me,
> > I especially don't know what __index is =/
> >
> > as I understand it, i want to set my environment to a, 'temporary' one,
> > load the code, and then set it back (so i get all of my nice global
> > variables and registred C functions and such)
> > is that right?  and if so how would I acomplish that in C?
> 
> in fact, it's easiest to do all from Lua, if you REALLY need it from C, you 
> could do a prototype in Lua and then port it to C.
> 
...
> now......... you want to do this from C?  the easiest way is to just call 
> scriptloader()
> 

That is an excellent explanation of how to sandbox scripts.  I
considered good enough to bookmark in my mailreader.  It ought to go
into the Wiki.

I'll probably end up using it, though it may be nice to be able to
specify different cages for different scripts.

Separate the cage and scriptloader section as follows:

-- Define the scriptloader function like this:
function scriptloader (scriptname,cage)
  local scriptenv = {}

  setmetatable (scriptenv, {__index=cage})
  
  local chunk  = loadfile (scriptname)
  setfenv (chunk, scriptenv)
  
  chunk ()

  return scriptenv
end


-- use the scriptloader function elsewhere like this:
local mycage = {
  -- import some packages:
  string = string,  coroutine = coroutine,
  table = table, math = math,
  
  -- some 'global' functions:
  next = next, ipairs = ipairs, pairs = pairs,
  require = require, type = type,
  tonumber = tonumber, tostring = tostring,
  unpack = unpack, 
  setmetatable = setmetatable,
  getmetatable = getmetatable,

  -- modified global functions:
  print = myprint,
  error = myerror

  -- my own api:
  move = move
  kill = kill
}

allscripts  = {}

allscripts[1] = scriptloader ("script.lua",mycage)
allscripts[2] = scriptloader ("secondscript.lua",mycage)

script[1].foo(x)      -- call the loaded function from first script
script[2].foo(x)      -- from second script

This allows you to create different cages/sandboxes depending on how
much access you want to give to the script.

Now, as to the question of making this in C...

Use bin2c to convert the scriptloader function into a .h file.  Include
this .h file to load the scriptloader function into your lua_State's
globals.  Then call the scriptloader function as follows:

lua_getfield(L,LUA_GLOBALSINDEX,"scriptloader");
lua_pushstring(L,"script.lua");
lua_getfield(L,LUA_GLOBALSINDEX,"mycage");
lua_call(L, 2, 1);

Note that you can use any method for getting the cage object into the
right stack position.  For instance, if you have the cage stored in the
Lua Registry, you could use:

lua_getfield(L,LUA_GLOBALSINDEX,"scriptloader");
lua_pushstring(L,"script.lua");
lua_getfield(L,LUA_REGISTRYINDEX,"myapp.mycage");
lua_call(L, 2, 1);

You could also modify it to use a Lua value on the stack, or modify it
to use lua_pcall instead.  The end result of the lua_call/lua_pcall is a
value on the stack, which is the return value of the scriptloader
function.

-- 


JJS
"If Ignorance is Bliss, I'll take the Pain."