Implementing setfenv in Lua 5.2, 5.3, and above

2023-11-10

Implementing setfenv in Lua 5.2, 5.3, and above

An upvalue tutorial

Posted  July 08, 2015 by leafo ( @moonscript) · Tags: lua

If you've programmed in Lua 5.1 you've probably come across the function setfenv (and the associated getfenv). Lua 5.2 removed those functions favor of the _ENV variable. The _ENV variable is an interesting change, and can accomplish many of the same things, but it is not a direct replacement.

View the completed implementation or read on to learn about how it’s created.

The _ENV variable

The _ENV variable provides a lexical way of setting a function’s environment. In the sense that functions have access to lexically scoped variables created a closure, a function’s environment is set by the closest lexically set value of _ENV.

Free variables referenced by closures are called upvalues. The _ENVvariable is just an upvalue of a function that’s has the special purpose of being the place where global variable references are searched.

In order to re-implement setfenv, and set the function’s environment, that upvalue must be updated to point to a table to act as the environment.

Getting upvalues

Upvalues can be accessed and manipulated in Lua by using the debug functions debug.getupvaluedebug.setupvalue, and debug.upvaluejoin.

Lua upvalues are indexed by an integer, much like an array. The debug function debug.getupvalue(func, index) is used to access them.

Before implementing setfenv we'll examine a function’s upvalues. Here’s a basic function:

local number = 15
local function lucky()
  print("your lucky number: " .. number)
end

There is one local variable closed on the function, the variablenumber.

Here’s a chunk of code that will print out the upvalues. It works by enumerating positive integers until nil is returned bydebug.getupvalue.

-- print out the upvalues of the function lucky
local idx = 1
while true do
  local name, val = debug.getupvalue(lucky, idx)
  if not name then break end
  print(name, val)
  idx = idx + 1
end

This will produce something like:

_ENV    table: 0x11e39f0
number  15

There are two upvalues, _ENV and number. What’s the default value of _ENV? It’s equal to the global variable _G.

As a quick example, how many upvalues does this function have? (note the absence of local on number)

number = 15

local function lucky2()
  print("your lucky number: " .. number)
end

Here’s the output:

_ENV    table: 0x11e39f0

number is no longer an upvalue because it is now a global variable. When number is referenced inside of that function it will look within the _ENV upvalue, effectively the value of _ENV.number.

One last example, what’s the upvalue of this (empty) function:

local function no_so_lucky()
end

You might be thinking “Well, just _ENV right?”. In reality this function has no upvalues. A function only has an upvalue on _ENV if it references a global variable. In the first example, the function referenced print globally, and the second example referenced bothprint and number. This is worth keeping in mind if you're ever trying to debug upvalue code.

Setting upvalues

The function debug.setupvalue(func, index, value) sets an upvalue for a function. As mentioned above, upvalues are referenced by their index. In order to set an upvalue by name, the index first must be found.

Here is the snippet above generalized to return a single upvalue’s index by its name:

local function get_upvalue(fn, search_name)
  local idx = 1
  while true do
    local name, val = debug.getupvalue(fn, idx)
    if not name then break end
    if name == search_name then
      return idx, val
    end
    idx = idx + 1
  end
end

Now it’s trivial to change an upvalue of the example function:

local number = 15
local function lucky()
  print("your lucky number: " .. number)
end

debug.setupvalue(get_upvalue(lucky, "number"), 22)

lucky() --> your lucky number: 22

Taking it a step further you might the upvalue _ENV to replaceprint with something else.

debug.setupvalue(lucky, get_upvalue(lucky, "_ENV"), {
  print = function()
    print("no lucky number for you!")
  end
})

lucky()
-- uhhhh....

This produces a stack overflow error, there’s an infinite loop within print. It kinda makes sense, we did just re-define print to call print with our new environment.

The problem is that upvalues can be shared across many functions. When we set the upvalue on lucky we actually changed every function environment. We changed the global environment to point to a function that just has print in it.

If print is called on the top level, instead of calling lucky, and the same infinite loop will happen. This is because the global scope also shared the same _ENV.

Additionally, calling any other regularly available global function, like type, will fail since the new environment only provides print.

Here’s another example:

local number = 15
local function lucky()
  print("your lucky number: " .. number)
end

local function lucky2()
  print("your other number: " .. number)
end

debug.setupvalue(lucky, get_upvalue(lucky, "number"), 22)
lucky2() --> your other number: 22

There are two functions that refer to the same upvalue number. Calling debug.setupvalue in this case has the same effect as writingnumber = 22, which changes what the results of both of the functions.

Since debug.setupvalue doesn’t work we'll need a new approach.

Replacing upvalues

The debug.upvaluejoin function replaces what an upvalue refers to. You must specify another function and upvalue index to do the replacement.

By creating a new anonymous function with and upvalue set to what_ENV should be in lucky, the upvalue can be replaced without affecting everything else.

local new_env = {
  print = function()
    print("no lucky number for you!")
  end
}

debug.upvaluejoin(lucky, get_upvalue(lucky, "_ENV"),
  function() return new_env end, 1)

lucky() --> prints "no lucky number for you"

The last two arguments reference upvalue 1 of the anonymous function. Since the anonymous function only has one upvalue it’s guaranteed to be at index 1. It’s not necessary to look it up by name.

Now we're ready for a completed setfenv implementation.

setfenv implementation

local function setfenv(fn, env)
  local i = 1
  while true do
    local name = debug.getupvalue(fn, i)
    if name == "_ENV" then
      debug.upvaluejoin(fn, i, (function()
        return env
      end), 1)
      break
    elseif not name then
      break
    end

    i = i + 1
  end

  return fn
end

getfenv implementation

local function getfenv(fn)
  local i = 1
  while true do
    local name, val = debug.getupvalue(fn, i)
    if name == "_ENV" then
      return val
    elseif not name then
      break
    end
    i = i + 1
  end
end
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Implementing setfenv in Lua 5.2, 5.3, and above 的相关文章

  • Lua 中的内联条件(a == b ? "yes" : "no")?

    无论如何 Lua 中可以使用内联条件吗 Such as print blah a true blah nahblah Sure print blah a and blah or nahblah
  • lua_resume 的 from 参数的含义

    From Lua 5 2 参考手册 http www lua org manual 5 2 manual html lua resume int lua resume lua State L lua State from int nargs
  • 如何在 Lua 中实现 OO?

    Lua 没有内置对 OO 的支持 但它允许您自己构建它 您能否分享一些实现面向对象的方法 请为每个答案写一个例子 如果您有更多示例 请发布另一个答案 我喜欢将 OOP 视为容器 对象 内的数据封装以及可以使用该数据完成的操作子集 还有很多内
  • Lua userdata:无法同时进行数组访问和方法

    我遇到了这个人的问题 Lua userdata数组访问及方法 https stackoverflow com questions 26970316 lua userdata array access and methods 其中 当我设置用
  • Lua中如何在另一个表的表成员中搜索

    我正在编写一个 lua 程序 它有一个表 该表是另一个表的成员 当我向该成员表添加新日期时 一切正常 但是 当我想在该表中搜索时 无论我给出什么键 我总是会将最后一行添加到表中 如何在该成员表中正确搜索 Stream name functi
  • 如何使用 srlua 制作可执行的 Lua 脚本?

    我的主要目标是使我的 lua 文件成为可执行文件或使其成为咬代码 最好是两者皆有 我正在尝试 srlua 但在自述文件中它告诉我要做的事情 对于Windows 您需要首先创建srlua exe和glue exe 然后为每个 你想把Lua程序
  • 如何在 emacs lua-mode 中配置缩进?

    完整的 emacs 新手在这里 我在 Ubuntu 上使用 emacs 23 1 1emacs 入门套件 https github com technomancy emacs starter kit 我主要在 lua 模式下工作 安装了pa
  • Lua 访问表的键和值

    我想在关卡编辑器中读取 Lua 文件 这样我就可以以可视化格式显示其数据供用户编辑 如果我有一个像这样的 Lua 表 properties Speed 10 TurnSpeed 5 Speed显然是关键并且10价值 我知道如果我知道像这样的
  • 如何终止Lua脚本?

    如何终止 Lua 脚本 现在我在 exit 方面遇到问题 我不知道为什么 这更像是一个 Minecraft ComputerCraft 问题 因为它使用了包含的 API 这是我的代码 while true do if turtle dete
  • LuaJ 导入 Lua 方法

    我正在使用 LuaJ 并且我有一个 lua文件充满了一堆函数 如何通过 LuaJ 导入这些函数以在 Java 中使用 一种选择是将文件编译为 Java 代码并导入它 另一种方法是使用可嵌入解释器直接从 Java 代码中调用 Lua 文件 E
  • 如何让我的 add 命令找到第一个变量和第二个变量的值,然后将它们加在一起?

    vars values function open file lex file end function lex file local data io open file r for char in data lines do Print
  • 在 Awesome-wm 中为特定应用程序设置窗口布局

    如何配置很棒 以便它可以启动两个窗口对齐的新应用程序 如下所示 xxxxxxxxxx xxxxxxxxxx xxxxxxxxxx xxxxxxxxxx 其中 x 是 pidgin 中的对话窗口 是好友列表窗口 一般来说 我想指定右窗口的宽度
  • 如何使用 Lua 运行可执行文件?

    我有一个可执行文件想要使用 Lua 运行 我该怎么做 似乎无法在任何地方找到有关此的任何文档 您可以使用 Lua 原生的 执行 命令 Example os execute c temp program exe 资料来源 Lua 指南 os
  • 从命令行运行 vlc 扩展

    我有一个用 Lua 编写的 vlc 扩展 我知道如何从 GUI 运行它 查看 gt 我的扩展 我想从命令行运行它 这样我就不需要每次都启动X 它还没有实施 查看门票 3883 https trac videolan org vlc tick
  • Lua 中的贪婪/非贪婪模式匹配和可选后缀

    在 Lua 中 我正在尝试模式匹配和捕获 384 Critical Strike Reforged from Parry Chance as 384 Critical Strike 后缀在哪里 Reforged from s 是可选的 长版
  • Lua(命令行)执行后保持打开状态

    我已经广泛寻找这个但我似乎找不到它 有什么方法可以执行Lua通过双击脚本 在中执行它 Lua Command Line 并在执行后保持打开状态 例如 print Hello World 该代码可以编译并运行 但是如果我双击hello lua
  • lua-socket:unix 域套接字?

    我使用的是 lua socket 3 0rc1 3 Ubuntu Trusty 附带的 和 lua 5 1 我正在尝试监听 unix 域套接字 我能找到的唯一示例代码是this http lua users org lists lua l
  • 如何将 Lua 粘合到 C++ 代码?

    您使用 Luabind toLua 或其他库 如果使用 是哪一个 还是根本不使用 每种方法都有哪些优点和缺点 我不太同意 自己投票 将基本类型和静态 C 函数绑定到 Lua 是微不足道的 是的 但是当你开始处理表和元表时 情况就会发生变化
  • luajit2.0.0 -- 分段错误:11

    我使用一个简单的例子http lua users org wiki SimpleLuaApiExample http lua users org wiki SimpleLuaApiExample进行测试 该示例可以成功静态链接libluaj
  • 在 Lua 中更改元表会破坏冒号运算符

    在学习Lua的过程中 我借用了一些代码here http lua users org wiki StringIndexing使用字符串索引 正是这样 getmetatable index function str i return stri

随机推荐

  • SonarQube 9.x集成阿里p3c代码规范检测java代码;

    文章目录 前言 一 下载p3c pmd插件 二 sonarqube配置使用p3c规则检测 1 新建质量配置 2 将创建好的p3c检测规则设置为默认质量配置 注1 注2 前言 因为我们公司后端主用的是java语言 在进行sonar代码检测的时
  • LED驱动GPIO相关头文件简要分析

    常识 应用程序 gt 系统内核 gt 设备驱动 gt 硬件设备 设备驱动既是系统内核的下属 又是硬件设备的老大 在inux系统中用一个文件来代表一个设备 这个文件就叫设备文件 设备驱动的责任是将应用程序对设备文件 的打开 读 写 定位等操作
  • 当今世界依然屏蔽谷歌服务的五个国家

    附件里有一份数据表 转载于 https blog 51cto com mirage1993 1541008
  • 解决mysql不是内部或外部命令 环境变量

    1 当安装好MySQL后 安装MySQL时用户名和密码都设置的简单一点 比如我的用户名和密码都是root 针对学生练习而言 不要增加自己的练习难度 按键盘win r 输入cmd打开命令行模式 输入命令 gt gt gt mysql u ro
  • 添加域账号到客户机本地管理员组

    关键词 域账号 本地管理员组 域策略 需求描述 在域中 我们通常不会给普通域用户本地管理员身份 只会将特定域用户组加入客户机本地管理员组中 这里就涉及到了如何添加问题 既然在域中 我们多采用域策略实现以上需求 方法有二 方法1 1 在bat
  • Java面试复习体系总结

    https blog csdn net weixin 43314519 article details 112603595 utm medium distribute pc feed none task blog personrec tag
  • Java21天打卡day16-类1

    public class Person String Name int Age String Sex public void setName String name Name name public void setAge int age
  • 构建第一个fabric网络

    本例中使用的官方提供的fabric sample案例 在该网络中 cli客户端能够直接调用chaincode 在实际开发环境中 cli不允许直接调用chaincode 只有peer结点才能运行chaincode 该环境适用于调试开发环节 在
  • Delphi入门教程

    一 第一章Delphi6介绍 1 1概述 Delphi是基于Pascal语言的RAD快速应用程序开发工具 Rapid Application Development 为Windows系统下的可视化集成开发工具 它提供强大的VCL Visua
  • linux内核源码分析进程的管理与调度

    文章目录 一 进程管理 进程描写叙述符及任务结构 进程状态 进程创建 fork和vfork的差别 进程终止 二 进程调度 什么是调度 三 策略 I O消耗型和处理器消耗型的进程 进程优先级 时间片 进程抢占 调度算法 可运行队列 优先级数组
  • 在UE4虚幻引擎下使用VRPN接入optitrack(Motive)的实时数据

    好记性不如烂笔头 前两天刚刚实现了功能 现在把主要过程都记录一下以防忘记 写个博客记录一下 同时也供大家参考 这个测试工程的目的在于在UE4中利用VRPN实时接入Optitrack的运动追踪数据 操作手柄刚体运动数据 并以此驱动一个方块根据
  • RHEL系发行版 设置启动默认进入 命令行 或 图形 模式的方法

    这里假设你已经安装好了桌面环境的所有软件包了 使用如下命令 然后 重启 即可进入桌面环境 sudo systemctl set default graphical target 如果想将系统设置为 启动后 默认进入命令行的状态下 使用如下命
  • 单层LSTM和多层LSTM的输入与输出

    单层LSTM的输入与输出 RNN结构 对应的代码为 代码中没写偏置 上图是单层LSTM的输入输出结构图 其实它是由一个LSTM单元的一个展开 如下图所示 所以从左到右的每个LSTM Block只是对应一个时序中的不同的步 在第一个图中 输入
  • VS QT——ui不显示新添加成员(控件)

    场景1 新建项目 在ui里编辑之后 新添加的控件不显示代码提示 场景2 成员 ui 报错 不允许使用不完整的类型 因为是刚开始学 花了两三天才找到解决办法 网上方法 重新编译 ui文件 重新扫描解决方案 但是我这里还是无法解决 根本原因 缺
  • (二十三)admin-boot项目之captcha验证码整合

    二十三 captcha验证码整合 项目地址 https gitee com springzb admin boot 如果觉得不错 给个 star 简介 这是一个基础的企业级基础后端脚手架项目 主要由springboot为基础搭建 后期整合一
  • [转载] [Mark]分布式存储必读论文

    原文 http 50vip com 423 html 分布式存储泛指存储存储和管理数据的系统 与无状态的应用服务器不同 如何处理各种故障以保证数据一致 数据不丢 数据持续可用 是分布式存储系统的核心问题 也是极具挑战的问题 本文总结了分布式
  • Java架构直通车——基于Redis的Set NX实现分布式锁

    文章目录 实现原理 SetNx的缺陷 超时问题 单机 多机问题 实践 基于Redis的Set NX实现分布式锁 基于Redisson实现分布式锁 实现原理 我们先来看获取redis锁的set命令 SET resource name rand
  • 缠论中第49课:没必要参与操作级别及以上级别的下跌与超过操作级别的盘整,如何理解与应用?

    缠论中第49课 没需要介入操纵级别及以上司其余下降与胜过操纵级其余盘整 怎样领会与运用 比方你的操纵级别是30秒钟 那么你该当只介入30秒钟的飞腾大概30秒钟级其余进取盘整 一旦30秒钟飞腾和盘整中断 就要退出 不介入30秒钟大概以上司别
  • memcache linux 命令行,Linux下用命令行查询memcache的所有keys

    1 telnet 10 10 24 106 11211 2 stats items STAT items 23 number 2 STAT items 23 age 934861 STAT items 23 evicted 0 STAT i
  • Implementing setfenv in Lua 5.2, 5.3, and above

    Implementing setfenv in Lua 5 2 5 3 and above An upvalue tutorial Posted July 08 2015 by leafo moonscript Tags lua Tweet