Lua5.3 注册表 _G _ENV

来源:http://blog.csdn.net/murisly/article/details/46518551

注册表的描述,借用PIL中的一段话:

registry 一直位于一个由 LUA_REGISTRYINDEX 定义的值所对应的假索引(pseudo-index)的位置。一个假索引除了他对应的值不在栈中之外,其他都类似于栈中的索引。Lua API 中大部分接受索引作为参数的函数,也都可以接受假索引作为参数—除了那些操作栈本身的函数,比如 lua_remove,lua_insert。例如,为了获取以键值 "Key" 保

存在 registry 中的值,使用下面的代码:

  1. lua_pushstring(L, "Key");
  2. lua_gettable(L, LUA_REGISTRYINDEX);

由于这个表是所有的lua库所共享的,所以key值也一定要注意。云大也给了一些去key值的参考方法

函数中,取注册表键值有这样的代码,可以看出注册表存储在 global_State 结构的 l_registry 变量中

  1. static TValue *index2addr (lua_State *L, int idx)
  2. else if (idx == LUA_REGISTRYINDEX)  /*注册表索引*/
  3. return &G(L)->l_registry;

注册表这个量在lua CApi可以访问,lua脚本中访问不了。Lua中可以访问的是 _G 这个全局变量。

  1. for i,v in pairs(_G) do
  2. print("name:", i, ", type:", type(v));
  3. end

这可以看到当前全局量里面所包含的值。那么全局量保存在那个位置呢?可以在lua_getglobal的源代码

  1. LUA_API void lua_getglobal (lua_State *L, const char *var) {
  2. Table *reg = hvalue(&G(L)->l_registry);
  3. const TValue *gt;  /* global table */
  4. lua_lock(L);
  5. gt = luaH_getint(reg, LUA_RIDX_GLOBALS);
  6. setsvalue2s(L, L->top++, luaS_new(L, var));
  7. luaV_gettable(L, gt, L->top - 1, L->top - 1);
  8. lua_unlock(L);
  9. }

可以看出获得全局变量,先是获得注册表的值,然后注册表中key为 LUA_RIDX_GLOBALS 的表就是全局表。Lua.h中定义了这个宏。

  1. #define LUA_RIDX_MAINTHREAD 1
  2. #define LUA_RIDX_GLOBALS    2
  3. #define LUA_RIDX_LAST       LUA_RIDX_GLOBALS

当前也就是构建了这样的一个状态

再来看看 _ENV 这个量

Print(_G);   -->table:003C27D8

Print(_ENV); -->table:003C27D8

这里看出两个指向的是同一个table。那么这两个是什么关系呢?Lua官方说明文档中:

When Lua loads a chunk, the default value for its _ENV upvalue is the global environment (see load).

Therefore, by default, free names in Lua code refer to entries in the global environment

(and, therefore, they are also called global variables).

Moreover, all standard libraries are loaded in the global environment and some functions there operate on that environment.

You can use load (or loadfile) to load a chunk with a different environment.

(In C, you have to load the chunk and then change the value of its first upvalue.)

关于chuck的解释:

The unit of compilation of Lua is called a chunk. Syntactically, a chunk is simply a block。

A chunk can be stored in a file or in a string inside the host program.

To execute a chunk, Lua first loads it, precompiling the chunk's code into instructions for a virtual machine,

and then Lua executes the compiled code with an interpreter for the virtual machine.

一个chunk就是lua的一个解释单元,可以存储在文件或者字符串中。对于每一个chunk,都有一个叫_ENV的upvalue,此时_ENV的初值就是_G。在chunk内的函数,都会有这个upvalue值。修改当前的chunk的 _ENV,也就修改了_G(这段话解释有问题,_ENV 和 _G 都只是一个表table的名字, 改变_ENV的指向并不会对_G有影响, 仅仅_ENV的初值是_G而已),那么在该代码块中加入的非local变量,可以直接通过名称在其他chunk中访问到(当然该chunk的_ENV 也得是 _G)。

改变_G的指向, 此时_G是一个空表,_ENV的初始值是未改变前的表_G。
程序可以正常运行,print函数和_G表,以及全家变量a 都是可以从 _ENV表中正常查找到。

a =  -- 全局变量
print("_ENV --->", _ENV, "_G --->", _G)
_G = {}
print("_ENV --->", _ENV, "_G --->", _G)
print("global a is: ", a)
print("_ENV a is: ", _ENV.a)
print("_G a is: ", _G.a)

改变_ENV的指向, 此时_ENV是一个空表,表_G没有任何变化。
程序可以不能正常运行,print函数和_G表,以及全家变量a 都不能从 _ENV表中查找到。

a =  -- 全局变量
print("_ENV --->", _ENV, "_G --->", _G)
_ENV = {}
print("_ENV --->", _ENV, "_G --->", _G)
print("global a is: ", a)
print("_ENV a is: ", _ENV.a)
print("_G a is: ", _G.a)

所以不规范得命名很容易影响其他模块。为了避免这种情况,变量尽量申请为local类型。利用_G和表元表和元方法,即可以强制声明全局变量。

  1. local oldprint = print;
  2. local oldload = load;
  3. local oldpairs = pairs;
  4. local _tenv = {};
  5. _tenv.tprint = print;
  6. _tenv.tpairs = pairs;
  7. oldprint(_ENV);    -->table: 00A888D8
  8. oldprint(_ENV._G); -->table: 00A888D8
  9. oldprint(_G);      -->table: 00A888D8
  10. local _tg = _G;  -->_G 通过 _tg 保存起来
  11. _G = {};
  12. _ENV = _tenv;
  13. x = 1;
  14. for i,v in tpairs(_ENV) do
  15. tprint(i);   -->tprint
  16. end           -->x
  17. -->tpairs
  18. oldprint(_ENV); -->table: 00A8E660
  19. oldprint(_G);   -->nil(此时这两个表指向了不同的地方)
  20. --test这个chunk内,使用的 _ENV 是 _tenv。这里 _ENV 添加了变量 y
  21. local a = oldload("y = 1; for i,v in tpairs(_ENV) do tprint(i); end", "test", "t", _tenv);
  22. a();
  23. --访问另一个chunk,使用同样的 _ENV , y值可以直接访问
  24. local b = oldload("y = y + 1;", "test", "t", _tenv);
  25. b();
  26. oldprint(_tenv.y); -->2
  27. oldprint(_ENV.y);  -->2
  28. oldprint(_tg.y) -->nil

这里可以看出,当前chunk 的 _ENV 改变了,不会改变全局的 _G(除非此时 _ENV 就是指向的 _G)。在加载一个chunk的时候可以设置 _ENV,说明可以让chunk在特定的环境中运行。获取一个独立运行环境的函数,可以修改 _ENV 。

  1. function testbar(env)
  2. local _ENV = env;
  3. _ENV.x = 1;
  4. return function ()
  5. return _ENV.x;
  6. end
  7. end
  8. local env = {};
  9. local f = testbar(env);
  10. print(_ENV.x);
  11. print(f());

来源 http://www.jianshu.com/p/e35ed633bc21

在lua中,至少从5.2开始就不再有全局变量:

当你写a = 1的时候,其实被编译成 _ENV.a = 1

但是一直以为_ENV是全局environment,其实不然,lua手册中说:

every chunk is compiled in the scope of an external local variable called _ENV (see §3.3.2), so _ENV itself is never a global name in a chunk.

这里的external local variable就是upvalue,也就是_ENV是当前chunk的upvalue。

When Lua compiles a chunk, it initializes the value of its _ENV upvalue with the global environment (see load).

当lua编译一个chunk的时候,如果不指定的话,默认使用全局environment初始化它的upvalue _ENV(其实就是引用),它是隐式声明的一个upvalue,看load声明:

load (ld [, source [, mode [, env]])
loadfile ([filename [, mode [, env]])

所以_ENV是语法糖,它在lua编译的作为upvalue被隐式声明,并且可以由外部指定。
通过下面代码可以验证:

t.lua
local _env = {tostring=tostring,print=print, debug=debug}
print ("_ENV = "..tostring(_ENV))
print ("_env = "..tostring(_env))
local f, err = loadfile("m.lua", "bt", _env)
assert(f, err)
local m = f(f)
m.test()
m.lua
local f = ...
local m = {} function m.test()
local i =1
while true do
local n, v = debug.getupvalue(f, i)
if not n then
break
elseif n == "_ENV" then
print ("_ENV = "..tostring(v))
break
end
i = i+1
end
end return m
输出
_ENV = table: 0x132f5f0
_env = table: 0x1335f90
_ENV = table: 0x1335f90

查看指令码可以看到SETUPVAL:

t.lua
_ENV = nil
$ luac -l t.lua
main <t.lua:0,0> (3 instructions at 0x245b3c0)
0+ params, 2 slots, 1 upvalue, 0 locals, 0 constants, 0 functions
1 [47] LOADNIL 0 0
2 [47] SETUPVAL 0 0 ; _ENV
3 [47] RETURN 0 1

ps:
全局environment在C Registry(LUA_REGISTRYINDEX):LUA_RIDX_GLOBALS: At this index the registry has the global environment.
_G在初始化的时候被复制为全局environment

test_env.lua:

local local_var = "local_var"           -- 该变量为该模块的局部变量
global_var = "global_var" -- 该变量为全局变量注册在 _G 中
-- print("_ENV.local_var:", _ENV.local_var) -- 这句证明 local 变量不存在于 _ENV 中 test_env = {} -- 如果这里设为 local 则不能在 require 一次后在其他文件中直接调用 test_env 后面会说明
setmetatable(test_env, {__index = _G}) -- 设置 _G 是为了使用 print 函数还有验证上面的 global_var
_ENV = test_env -- 改变当前环境 local env_local_var = "env_local_var"
env_globar_var = "env_global_var" -- 注意这里不是存在 _G 而是在 _ENV print("local_var:", local_var)
print("global_var:", global_var) print("env_local_var:", env_local_var)
print("env_global_var:", env_global_var) print("test_env:", test_env)
print("_G:", _G) print("_G.local_var = nil result:", _G.local_var)
print("_G.global_var = global_var result:", _G.global_var)
print("_G.test_env = test_env(addr) result:", _G.test_env)
print("_G.env_local_var = nil result:", _G.env_local_var) -- 该变量为局部变量所以 _G 中没有
print("_G.env_global_var = nil result:", _G.env_global_var) -- 该变量在 _ENV 中 print("_ENV._G = _G(addr) result:", _ENV._G)
print("_ENV.local_var = nil result:", _G.local_var)
print("_ENV.env_local_var = nil result:", _ENV.env_local_var)
print("_ENV.env_global_var = env_global_var result:", _ENV.env_global_var) return test_env

test.lua:

local env = require "test_env"

print("")
print("")
print("env: ", env)
print("test_env: ", test_env)
print("_G.test_env: ", _G.test_env) print("env.local_var = nil result:", env.local_var)
print("env.global_var = global_var result:", env.global_var) -- 这里的 global_var 其实是 _G.global_var print("_G.global_var = global_var result:", _G.global_var) print("env.env_local_var = nil result:", _G.env_local_var)
print("env.env_global_var = nil result:", _G.env_global_var) print("test_env.env_local_var = nil result:", test_env.env_local_var) -- 这里是 _G.test_env.env_local_var
print("test_env.env_global_var = env_global_var result:", test_env.env_global_var) -- 这里是 _G.test_env.env_local_var 如果test_env.lua中 test_env 变量前面加上 local 这里就不能这么用

Lua 5.3 _ENV table include function list (Exclude _G table and package table)

xpcall	 ==>> 	function: 00A80D70
require ==>> function: 003935F0
assert ==>> function: 00A80A60
pairs ==>> function: 00A805D0
type ==>> function: 00A80490
ipairs ==>> function: 00A80630
rawequal ==>> function: 00A800C0
tostring ==>> function: 00A80E50
print ==>> function: 00A7F990
select ==>> function: 00A80B40
next ==>> function: 00A80530
rawlen ==>> function: 00A80150
setmetatable ==>> function: 00A7FFD0
getmetatable ==>> function: 00A7FF30
rawset ==>> function: 00A80290
dofile ==>> function: 00A80990
rawget ==>> function: 00A80200
tonumber ==>> function: 00A7FC10
load ==>> function: 00A807C0
loadfile ==>> function: 00A806D0
_VERSION ==>> Lua 5.3
collectgarbage ==>> function: 00A80330
error ==>> function: 00A7FE60
pcall ==>> function: 00A80CB0 ------------>>> utf8 ==>> table: 00393910
len ==>> function: 00AB4790
charpattern ==>> [ -?鬩[€-縘*
char ==>> function: 00AB4CF0
offset ==>> function: 00AB4E30
codepoint ==>> function: 00AB4A20
codes ==>> function: 00AB51F0
<<<------------ utf8 ==>> table: 00393910 ------------>>> arg ==>> table: 00393AA0
0 ==>> E:\vsproj\lua1\mytest2.lua
-1 ==>> C:\lua-5.3.4\bin\lua53.exe
<<<------------ arg ==>> table: 00393AA0 ------------>>> debug ==>> table: 003939B0
getupvalue ==>> function: 00A86FE0
setmetatable ==>> function: 00A86500
upvaluejoin ==>> function: 00A87130
upvalueid ==>> function: 00A870A0
gethook ==>> function: 00A85E20
traceback ==>> function: 00A862A0
sethook ==>> function: 00A85B70
setlocal ==>> function: 00A86DE0
debug ==>> function: 00A86020
setuservalue ==>> function: 00A86630
getinfo ==>> function: 00A866C0
getlocal ==>> function: 00A86B80
setupvalue ==>> function: 00A87030
getmetatable ==>> function: 00A86480
getuservalue ==>> function: 00A865B0
getregistry ==>> function: 00A86420
<<<------------ debug ==>> table: 003939B0 ------------>>> table ==>> table: 00393640
unpack ==>> function: 00AAFBD0
remove ==>> function: 00AAF310
sort ==>> function: 00AAF030
concat ==>> function: 00AAF860
move ==>> function: 00AAF4E0
insert ==>> function: 00AAF140
pack ==>> function: 00AAFAE0
<<<------------ table ==>> table: 00393640 ------------>>> os ==>> table: 00393780
setlocale ==>> function: 00A9D5F0
remove ==>> function: 00A9CBF0
difftime ==>> function: 00A9D540
execute ==>> function: 00A9CB30
date ==>> function: 00A9CF80
getenv ==>> function: 00A9CE80
clock ==>> function: 00A9CF00
rename ==>> function: 00A9CCA0
time ==>> function: 00A9D300
tmpname ==>> function: 00A9CD70
exit ==>> function: 00A9D6B0
<<<------------ os ==>> table: 00393780 ------------>>> math ==>> table: 003938C0
mininteger ==>> -9223372036854775808
fmod ==>> function: 00A97A60
floor ==>> function: 00A978C0
ult ==>> function: 00A97E10
tan ==>> function: 00A975B0
abs ==>> function: 00A973E0
log ==>> function: 00A97ED0
sin ==>> function: 00A974D0
rad ==>> function: 00A98150
acos ==>> function: 00A97690
maxinteger ==>> 9223372036854775807
ceil ==>> function: 00A97990
exp ==>> function: 00A98050
random ==>> function: 00A983A0
atan ==>> function: 00A97700
modf ==>> function: 00A97BF0
cos ==>> function: 00A97540
max ==>> function: 00A982C0
pi ==>> 3.1415926535898
type ==>> function: 00A986A0
sqrt ==>> function: 00A97DA0
asin ==>> function: 00A97620
tointeger ==>> function: 00A977C0
randomseed ==>> function: 00A98610
huge ==>> 1.#INF
min ==>> function: 00A981E0
deg ==>> function: 00A980C0
<<<------------ math ==>> table: 003938C0 ------------>>> io ==>> table: 00393690
stdin ==>> file (0FD49468)
lines ==>> function: 00A919B0
stdout ==>> file (0FD49488)
close ==>> function: 00A914A0
stderr ==>> file (0FD494A8)
write ==>> function: 00A91BE0
read ==>> function: 00A91B10
output ==>> function: 00A918E0
tmpfile ==>> function: 00A917D0
type ==>> function: 00A91330
flush ==>> function: 00A91F40
popen ==>> function: 00A916D0
input ==>> function: 00A91880
open ==>> function: 00A915B0
<<<------------ io ==>> table: 00393690 ------------>>> string ==>> table: 003937D0
len ==>> function: 00AA6380
format ==>> function: 00AA6760
char ==>> function: 00AA7690
dump ==>> function: 00AA7830
reverse ==>> function: 00AA6610
lower ==>> function: 00AA6E20
sub ==>> function: 00AA6440
upper ==>> function: 00AA6F80
rep ==>> function: 00AA70E0
byte ==>> function: 00AA7410
gmatch ==>> function: 00AA8AF0
find ==>> function: 00AA8A50
match ==>> function: 00AA8AA0
unpack ==>> function: 00AA8550
packsize ==>> function: 00AA8370
gsub ==>> function: 00AA8C60
pack ==>> function: 00AA7980
<<<------------ string ==>> table: 003937D0 ------------>>> coroutine ==>> table: 00393230
wrap ==>> function: 00A85450
resume ==>> function: 00A852C0
isyieldable ==>> function: 00A856D0
running ==>> function: 00A85740
status ==>> function: 00A85530
yield ==>> function: 00A854C0
create ==>> function: 00A853B0
<<<------------ coroutine ==>> table: 00393230 ------------>>> package ==>> table: 003930A0
<<<------------ package ==>> table: 003930A0 ------------>>> _G ==>> table: 00390D60
<<<------------ _G ==>> table: 00390D60

Lua5.3 注册表 _G _ENV的更多相关文章

  1. Win.ini和注册表的读取写入

    最近在做打包的工作,应用程序的配置信息可以放在注册表文件中,但是在以前的16位操作系统下,配置信息放在Win.ini文件中.下面介绍一下Win.ini文件的读写方法和注册表的编程. 先介绍下Win.i ...

  2. 卸载oracle之后,如何清除注册表

    之前卸载了oracle,今天偶然间发现,在服务和应用程序里面,还残存着之前的oracle服务.原来,还需要去清理下注册表. 在开始菜单的这个框里面 输入regedit,进入注册表.找到这个目录 HKE ...

  3. 利用注册表在右键添加VS15的快捷方式打开文件夹

    1.简介 最近安装VS15 Preview 5,本版本可以打开"文件夹" 是否可以向Visual Studio Code一样在文件夹或文件右键菜单添加"Open with ...

  4. 修改策略组/注册表 屏蔽Win10升级解决方法

    一.Windows非家庭版 第1步:按Win+R键调出运行对话框,输入命令“gpedit.msc”,按回车键启动组策略编辑器. 第2步:依次定位到“计算机配置→管理模板→Windows组件→Windo ...

  5. Windows 7安装软件时无法将注册值写入注册表的处理方法

    1. 我们来确认一下,有没有安装什么软件把注册表给封了.如杀毒软件,防火墙等.把这些软件关了之后,再安装软件试试:如果不行,就把杀毒软件卸载了,再安装软件试试. 2. 更改组策略设置 步骤: 开始-运 ...

  6. MFC操作注册表

    1.创建和修改注册表 BOOL CTestToolCtr::GetHkey(CString strHkey, HKEY& hkey) { == strHkey.CompareNoCase(_T ...

  7. Win 通过修改注册表把CapsLock映射为Rshift

    成品: REGEDIT4     [HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layout] "Scancod ...

  8. js通过注册表找到本地软件安装路径并且执行

    场景:用js执行本地的安装软件,如果不存在就执行安装 操作步骤: 1.前台js代码 <script type="text/javascript"> function e ...

  9. 修改注册表 去除Windows快捷方式图标小箭头

    一些朋友不喜欢Windows系统中快捷方式图标上面的小箭头,下面介绍如何修改注册表去除快捷方式图标上的小箭头. 1.开始->运行->输入regedit,启动注册表编辑器,然后; 2.依次展 ...

随机推荐

  1. 【java】java反射初探 ——“当类也照起镜子”

    反射的作用   开门见山地说说反射的作用 1.为我们提供了全面的分析类信息的能力 2.动态加载类   我理解的“反射”的意义 (仅个人理解) 我理解的java反射机制就是: 提供一套完善而强大的API ...

  2. React获得真实的DOM操作

    真实的DOM操作 ------------------------------------------------------------------------------------------- ...

  3. Django(博客系统):按照时间分层筛选“/blog/article/?create_time__year=2017”,出现问题:Database returned an invalid datetime value. Are time zone definitions for your database installed?

    问题背景 添加文章时间没问题,但为了设定博客文章按照时间分层筛选(创建时间的年份.年月&月份来搜索文章),我在blog这个django app的admin.py的ArticleAdmin类中做 ...

  4. redis C接口hiredis 简单函数使用介绍

    hiredis是redis数据库的C接口,目前只能在linux下使用,几个基本的函数就可以操作redis数据库了. 函数原型:redisContext *redisConnect(const char ...

  5. 简单Elixir游戏服设计-创建玩家模型

    删除model.ex 创建玩家模型 player.ex, 简单化,只有唯一标识,昵称,金币,够用了. 选择 map 代表数据,是为了扩展数据结构,方便增加功能.struct也是可以的. add_num ...

  6. 【JAVA零基础入门系列】Day8 Java的控制流程

    什么是控制流程?简单来说就是控制程序运行逻辑的,因为程序一般而言不会直接一步运行到底,而是需要加上一些判断,一些循环等等.举个栗子,就好比你准备出门买个苹果,把这个过程当成程序的话,可能需要先判断一下 ...

  7. 从头编写 asp.net core 2.0 web api 基础框架 (4) EF配置

    第1部分:http://www.cnblogs.com/cgzl/p/7637250.html 第2部分:http://www.cnblogs.com/cgzl/p/7640077.html 第3部分 ...

  8. MVC中使用Echart后台加载数据 实现饼图、折线图、全国地图数据,单击双击事件等

    @{ Layout = null; } @if (false) { <script src="~/Js/jquery-easyui-1.5/jquery.min.js"> ...

  9. JavaWeb之response响应中文乱码问题

    response向页面响应中文乱码问题  字节流 * 有可能乱码,与中文转换成字节数组.浏览器打开的默认字符编码有关 * 解决方式:将中文转成字节数组的时候和浏览器默认打开的时候采用的字符集一致 re ...

  10. 正则表达式与grep和sed

    正则表达式与grep和sed 目录 1.正则表达式 2.grep 3.sed grep和sed需要正则表达式,我们需要注意的正则表达式与通配符用法的区分. 1.正则表达式 REGEXP,正则表达式:由 ...