关于名称:本软件名称致敬 Minecraft 模组加载器 Forge 与 Fabric——由 LuaForge Studio 更名为 LuaFabric Studio,应用图标也同步更换为 Fabric 风格的织布意象,寓意如模组加载器一般,为 Lua 生态加载无限可能。
-
结构页(原「文件」页更名):项目文件树,按项目根目录惰性展开、目录在前字母序;行布局为 折叠指示器(折叠
menu-right/ 展开menu-down)+ 文件(夹)图标(文件file/ 文件夹folder/ 展开folder-open)+ 名称文本,子叶点击带波纹;单击文件选中高亮(单选互斥),右上角悬浮操作栏(多层底色区分于页面)仅选中.lua时显示,点击 golf 图标直达拉起该 lua(newActivity直调、绕过拦截确认) -
浮球拖动修复:主浮窗 END 重力下横向拖动方向反转(向右拖左移)——X 坐标现按距右缘距离递减修正
-
newActivity 拦截强化:
-
重放支持尾部数组参数打包:luajava 将 Lua 多参展开为
(String, Object[]),resolveOverload现先定长精确匹配、无命中再尾部打包匹配,修复「允许跳转」后no overload matches args String, Long / String, String重放失败 -
「跳转后关闭当前界面」开关配色修复:MaterialSwitch 开启态拇指改
onPrimary,不再与轨道同色成整块
-
-
设置页重构:折叠卡片分组(输出 / 拦截 / 报错,默认全部折叠,样式与环境页一致);移除「控制台设置」标题;「元数据展示」由文本开关改为 MaterialSwitch;新增「拦截界面跳转/结束请求」总开关(默认开,关闭后 newActivity / finish 全部放行);修复进页崩溃(ScrollView 单子布局冲突)
-
输出页:移除「元数据:开/关」「仅事件:开/关」「清空」文字开关,改为置顶居左纯图标工具栏——
calendar-blank(仅事件开启时切calendar-check)+trash-can(清空),功能不变 -
环境页:环境信息折叠卡新增「绑定布局」键值对——显式
setContentView("xxx")字符串参数命中.aly、或按文件 require 记录找到项目下同名.aly(如layout.aly,loadlayout内部 require 经 traced 捕获)时显示基于项目根目录的相对路径(./layout.aly);否则「内联布局」/「无布局」 -
调试页:移除文件树(文件树并入结构页),仅保留「重启项目」「重建当前文件」
-
修复:
-
「使用 Toast 输出 Lua 侧错误」开关无效:Lua 运行时错误经
doFile/doString/doAsset等sendMsg直出 toast,绕过设置门控;现由LuaActivityMainHandler 按设置统一门控,开关关闭后 Lua 侧错误(含线程内)不再以 Toast 回显,错误仍正常入 F1 缓冲与浮球角标 -
主线程
print全部丢失:import.lua将全局print覆盖为 Lua 闭包直调activity.sendMsg,绕过 LuaPrint / 控制台采集;改为保存原print(LuaPrint)透传,主线程顶层输出(含类型解析)回归控制台,同时消除重复输出
-
-
版本号更新:
versionCode 26090901/versionName 26.09.09-alpha -
newActivity 拦截(F3)重写:单会话单弹窗 + 折叠卡片列表
-
同一会话内多次
activity.newActivity调用汇入同一弹窗,同签名重复请求自动丢弃;须先单选一条,点「允许跳转」放行该条,其余取消 -
折叠卡片:圆角 12dp、间距 4dp;左侧为相对项目根目录的文件路径,右侧为 chevron 折叠指示图标(请求未携带参数时不显示图标);默认全部折叠,点图标折叠 / 展开同样计为选中
-
携带参数:「携带参数:」独占头行,参数按「参数名 + Lua 类型」键值对排版(自动命名参数不显示名),条目间以不贴两侧的细分割线分隔;展开区背景左右收窄,避免遮盖卡片圆角
-
选中高亮:点卡片非图标区域即选中,背景 + 主色描边突出;弹窗背景与「允许 / 取消」文本颜色跟随应用外观主题
-
-
修复:
-
控制台输出归属错乱:新会话开启时清空全局缓冲;旧会话按项目 SHA-256 哈希归档(
sessions/<项目哈希>_<时间戳>/outputs.txt),Logcat 日志写入项目独立目录,互不串台 -
Lua 运行时错误未汇入 F1 输出缓冲(type=error):接线桥接层
onError入口 -
拦截方法名比对失败(cacheKey 带类前缀):抽取裸方法名后再与
newActivity比对 -
返回旧界面浮球需点两次 / 旧浮窗残留:宿主销毁时切换至存活成员页,页面恢复时同步宿主引用
-
-
版本号更新:
versionCode 26090801/versionName 26.09.08 -
新增调试控制台(仅调试模式项目启用,由
manifest.json的application.debugmode控制,默认零资源初始化;代码仅存在于 app 模块,不随用户软件打包,未注册桥实现时全部钩子空操作):-
浮球入口:可拖动、点击展开面板,音量 - 键随时隐藏/显示;配色跟随应用外观设置(主题 / 深色模式 / 动态取色);运行崩溃时浮球变红提示
-
面板:底部弹出式面板,页签含 输出 / 文件 / 环境 / Logcat / 调试 / 设置;头部加粗标题 + 最小化 / 完全关闭图标按钮;禁用拖拽收起,避免与页签内滚动冲突;打开面板时浮球自动隐藏,收起后恢复
-
输出页(F1):每个 lua 文件独立会话缓冲,print / toast / snackbar / 错误统一汇入;两级类型解析(Lua 类型 + Java 具体类型 / 预览);条目可展开完整元数据(时间 / 线程 / 相对文件);支持多选复制、导出(每条空行分隔、恒含完整时间戳)、清空当前文件缓冲;「元数据」开关与「仅事件」过滤
-
文件页(F2):当前文件相对路径 + 布局三态识别(aly 项目路径 / 内联布局 / 无布局),单击 / 长按复制
-
newActivity 拦截(F3):
activity.newActivity弹窗阻塞确认(可取消阻断);并发同文件同参数请求丢弃、不同参数列表单选跳转;长参数弹窗录入 -
环境页(F4):会话启动探测 Lua 版本 + JIT;按文件跟踪 require / bindClass;分为 环境信息 / Lua 模块 / 原生库 / Java 类库 四类折叠卡片(圆角 12dp、卡片间距 4dp);Java 库反射展示全方法签名,C / Lua 库列出函数名与参数个数
-
Logcat 页(F5):调试运行起至停止期间后台常驻记录至
logcat_<项目>_<毫秒时间戳>.log;RecyclerView 懒加载渲染 + 「仅看错误」过滤;崩溃独立记录至 crash/ 目录 -
事件(F6):仅 Lua 侧显式调用
activity.runFunc且事件实际触发时记录,含相对路径 / 毫秒时间戳 / 参数摘要(并入输出页「仅事件」开关) -
调试页(F7):项目文件树,点选文件调起「注入参数」结构化键值表单(string / number / boolean / table 类型下拉,table 树状折叠编辑),以 JSON 全局注入后启动;「重启项目」(新会话 + 旧会话归档)与「重建当前文件」双功能;会话归档至
sessions/<项目>_<时间戳>/outputs.txt -
设置页:元数据展示开关(完整时间 · 线程 · 类型解析)与解析深度(1 浅 / 2 中 / 3 深),实时生效
-
-
修复:
-
Lua 模块误分类(yyjson / aes 等原生模块混入):原生模块判定改用
debug.getinfo的what字段,而非source -
原生库误分类(loadmenu 标准库与项目内 lua 模块 app.config 等混入):loadmenu 归入内置库,过滤
com.androlua./com.google.前缀,Java 类库仅扫描项目libs/*.dex -
Lua 版本显示 "Lua Lua 5.5":去除冗余前缀
-
require 探测原生模块闪退(require 返回非 table 时按 Table 解引用致 SIGSEGV):加类型守卫 + 栈平衡修复
-
设置页闪退(重复 addView)
-
newActivity 后旧浮窗 / 面板残留:接线
onActivityDestroyed清理死引用并重建,会话跨 Activity 保留 -
布局助手等工具场景启动不再误开控制台会话(console_disable 门控)
-
折叠卡片箭头由文字符号改为矢量图标(chevron-left / chevron-down)
-
-
版本号更新:
versionCode 26090501/versionName 26.09.05 -
构建环境升级:
-
Gradle 9.1.0 → 9.5.0(AGP 8 需要 Gradle ≤ 9.5)
-
CMake 3.31.6 → 4.4.3
-
Java 17 → 21(app / core-apk / signer 的
sourceCompatibility与jvmTarget) -
minSdk24 / 23 → 29 -
gradle.properties固定 JDK 21 构建(AGP 8 不支持 JDK ≥ 26)
-
-
修复项目属性页长按查看文档时闪退:注册
MarkwonInlineParserPlugin(新增markwon-inline-parser依赖),表格 / 删除线 / LaTeX 插件可正常使用 -
文档弹窗内容增加 12dp 内边距,并支持长按选区复制
-
新增
require "yyjson"高性能 JSON 模块(自包含 C 绑定,基于 yyjson),提供read/decode/encode/ JSON Pointer / 读改写文档等接口
-
版本号更新:
versionCode 26082202/versionName 26.08.22-alpha -
修复旧配置缺失
providers字段导致的闪退(反序列化空值兜底) -
AI 添加供应商弹窗:获取模型需同时填写 API 请求地址与 API Key
-
支持解析带
/chat/completions后缀的 API 地址,自动处理末尾斜杠 -
第三方 AI 中转站获取模型列表失败时增强日志输出
-
删除
MaterialTextField组件 -
删除 native 层
smgr模块(renamefile/getdatadir等函数)
-
侧边栏"赞助"与"设置"位置互换
-
侧边栏新增"手册"页(Material MenuBook 图标,页面暂空置,后续填充)
-
版本号更新:
versionCode 26082201/versionName 26.08.22 -
AI 聊天:
-
修复点击历史对话闪退(旧数据
summary/messages空检查 + LazyColumn 唯一 key) -
修复软键盘输入括号失败、光标左移(输入框本地状态化 + 保留输入法 composition)
-
修复右下角置底按钮无法完全置底(滚动对齐末条消息底部)
-
修复 AI 无法使用记忆工具函数(agent 循环内每轮重建 API 消息,工具调用/结果正确回传)
-
系统提示词写明当前环境为 LuaFabric Studio
-
修复 AI 消息 Markdown/LaTeX 渲染闪退(注册 MarkwonInlineParserPlugin)
-
LaTeX 预处理兼容
\(...\)/\[...\]写法 -
表格文字溢出修复(
includeFontPadding+ TablePlugin) -
工具/SKILL 注入修复(系统提示词包含工具/技能/记忆)
-
工具函数从系统提示词移至 API 请求
tools字段,避免 token 浪费 -
上下文压缩:消息 ≥30 条时压缩旧消息为滚动摘要,保留最近 20 条,摘要持久化到历史
-
标题栏改为下钻式二级菜单(ChevronRight 图标)
-
输入框输入
/弹出技能补全列表 -
AI 标签页右下角新增回到顶部/底部按钮
-
修复文件树/AI 标签切换导致对话状态与模型选中态重置(SaveableStateHolder)
-
修复选择技能后光标被挪到开头(TextFieldValue 管理光标)
-
历史记录打开对话自动滚动到底部
-
Markdown 渲染从 WebView 迁移至 Markwon(移除 html 预览资源)
-
-
构建:
-
修复 Release 构建 R8 报错(Markwon 可选依赖 SVG/GIF 缺失类添加
-dontwarn规则) -
修复 Release lint 报错(默认 locale 补充缺失的
licenses_title字符串)
-
-
修复
require "socket"无法加载的问题(新增luaopen_socket别名符号) -
修复返回键退出后重进项目进度条卡死(初始化/加载逻辑判断优化)
-
侧边栏"赞助"与"关于"位置互换
-
关于页面 Licenses 列表替换为折叠式更新日志
-
编辑器侧滑栏底部新增底栏:文件树/AI 两个图标按钮,支持切换侧滑栏内容
-
代码补全初始加载完成后正确切换确定性进度条(修复进度条卡在 0% 的问题)
-
新增赞助页(设置页进入):展示赞赏二维码,支持保存到相册、拉起微信扫一扫
-
向导页新增「应用列表权限」条目(行为探测已安装应用数),点击跳转应用详情页
-
新增
require "memory"动态内存管理模块(自动/手动 GC、泄漏监测、Java 堆清理) -
新增
require "lanes"多线程库(linda 消息传递、真线程并行执行)-
适配 fork 版
luaL_pushresult的栈占位差异,lanes 函数按值正确传递 -
支持 assets 内 require:
require "xxx"自动从assets/与assets/lua/加载(memory、lanes 等)
-
-
修复
require仅能加载 assets 根目录模块的问题(新增assets/lua/回退路径) -
修复向导页点击「应用列表权限」卡片无反应的问题(改为跳转应用详情页)
-
修复
lsmgrlib对luajava.so的外部符号依赖(内嵌 isJavaObject 实现) -
修复 lgc/lobject 中
strchr潜在越界读取(改为带长度memchr) -
新增 README「lanes 多线程教程」与「memory 内存管理」章节
-
软件更名:LuaForge Studio → LuaFabric Studio(名称来源 Minecraft 模组加载器 Forge/Fabric 的梗,图标同步更换)
-
移除无用旧库,显著减小安装体积:删除 libbase64、libencrypt、libmd5、libyyjson
-
cjson 升级至 2.1.0.11(修复旧版问题、支持更多配置项与特殊值,接口文档见下文「Lua 库接口文档」)
-
包名迁移:
-
应用包名
com.luaforge.studio→com.luafabric.studio.falling -
工具库包名
com.luaforge.studio.utils.*→muling.views.tool.utils.* -
组件包名 →
com.luafabric.studio.falling.core.widget.textfield
-
-
存储路径变更:
/storage/emulated/0/LuaForge-Studio/→/storage/emulated/0/LuaFabric-Studio/- 注意:不提供自动迁移,旧项目需手动移动到新路径
-
修复 RecyclerView 适配器只能添加 8 项的问题(Lua 表通过
push()传递) -
修复全新安装时无存储权限导致的日志写入报错(日志文件改为应用私有目录)
-
修复
import模块缺失问题(Splash 启动时幂等解包 lua 运行库) -
版本号切换为年份命名:
versionName 26.08.18/versionCode 260818(alpha 版:26.08.18-alpha/26081801) -
新增
libmmkv.so(MMKV v2.4.1 高性能键值存储)Lua 绑定,支持类型化读写、过期时间、多实例 -
新增
libzstd.so(Zstandard 1.6.0 压缩)Lua 绑定,支持压缩/解压/流式接口 -
新增 README「Lua 库接口文档」章节,涵盖 mmkv / zstd / aes / sodium / cjson / ffi
-
修复应用内构建 APK 签名失败问题(签名密钥凭据与代码不匹配,更换新签名密钥)
-
新增
libdecrypt.so,支持 Lua 加密脚本自动解密加载-
提供
decrypt.loadfile()/decrypt.dofile()/decrypt.load()接口 -
兼容
.luae加密格式及普通.lua/.luac文件 -
复用
ldump.c/lundump.c保护配置,加解密算法一致
-
- 支持 Maven 依赖自动下载与打包(JAR 类型)
-
优化补全数据加载机制:检测到版本变更时自动重建补全数据缓存,确保代码补全与新版本一致
-
优化应用启动流程
- 增加
com.luafabric.studio.falling.core.widget.textfield.MaterialTextField组件
- 增加
onActivityReenter回调函数
本文档基于 Lua 5.5 的深度魔改版本,全面支持现代编程语言的语法特性。主要特性包括:
-
增强型语法结构:支持 try-catch-finally 异常处理、switch-case 多分支、defer 延迟执行、when 条件分支、continue 跳转。
-
现代化操作符:三元运算符、复合赋值、可选链、空值合并、管道运算符。
-
函数式编程增强:Lambda 表达式(匿名函数)简洁语法。
-
完整的特性组合:所有扩展语法可以无缝嵌套使用。
| 符号 | 等价于/描述 | 说明 | ||
|---|---|---|---|---|
? : |
if-else 三元 | 条件运算符 (cond) ? true_expr : false_expr |
||
?. |
安全访问 | 可选链操作符,避免 nil 报错 | ||
?? |
空值合并 | 左侧为 nil 时返回右侧值 | ||
| ` | >` | 管道操作符 | 将值传入函数 `value | > func` |
! |
not |
逻辑非 | ||
!= |
~= |
不等于 | ||
&& |
and |
逻辑与 | ||
| ` | ` | or |
逻辑或 | |
$ |
local |
局部变量声明缩写 | ||
@ |
:: |
标签声明(用于 goto) |
支持完整的 C 风格复合赋值操作符:
| 操作符 | 示例 | 等价于 | |||
|---|---|---|---|---|---|
+= |
a += 5 |
a = a + 5 |
|||
-= |
a -= 3 |
a = a - 3 |
|||
*= |
a *= 2 |
a = a * 2 |
|||
/= |
a /= 4 |
a = a / 4 |
|||
//= |
a //= 2 |
a = a // 2 |
|||
%= |
a %= 3 |
a = a % 3 |
|||
^= |
a ^= 2 |
a = a ^ 2 |
|||
..= |
s ..= "x" |
s = s .. "x" |
|||
&= |
bits &= mask |
bits = bits & mask |
|||
| ` | =` | `flags | = 0x01` | `flags = flags | 0x01` |
<<= |
a <<= 2 |
a = a << 2 |
|||
>>= |
a >>= 1 |
a = a >> 1 |
示例代码:
$ counter = 10
counter += 5 -- 15
counter *= 2 -- 30
$ message = "Hello"
message ..= " World" -- "Hello World"
$ flags = 0b1100
flags &= 0b1010 -- 0b1000简洁的条件表达式,支持嵌套使用。
语法格式:
(condition) ? true_expression : false_expression示例代码:
$ age = 18
$ status = (age >= 18) ? "adult" : "minor"
print(status) -- adult
-- 嵌套使用
$ score = 85
$ grade = score >= 90 ? "A" : score >= 80 ? "B" : "C"
print(grade) -- B安全访问嵌套对象属性,避免因中间值为 nil 而抛出错误。
语法格式:
obj?.field -- 安全访问字段
obj?.[index] -- 安全访问数组元素
obj?.method?() -- 安全调用方法示例代码:
$ user = {
profile = {
name = "Alice",
settings = { theme = "dark" }
}
}
print(user?.profile?.name) -- Alice
print(user?.profile?.address?.city) -- nil(不会报错)
print(user?.nonexist?.field) -- nil
-- 与空值合并结合使用
$ city = user?.profile?.address?.city ?? "unknown"
print(city) -- unknown当左侧值为 nil 时返回右侧值,否则返回左侧值。
语法格式:
value ?? default_value示例代码:
$ name = nil
$ display = name ?? "Anonymous" -- "Anonymous"
$ count = 0
$ result = count ?? 100 -- 0(0 不是 nil)
$ a = nil; $ b = nil; $ c = 42
$ value = a ?? b ?? c ?? 0 -- 42简洁的匿名函数定义语法,支持多种写法。
语法格式:
\参数列表 -> 表达式 -- 单表达式自动返回
\参数列表 => 语句块 -- 多语句需显式 return
lambda 参数列表 -> 表达式 -- 完整写法示例代码:
-- 基础用法
$ add = \x, y -> x + y
print(add(3, 5)) -- 8
$ square = \x -> x * x
print(square(4)) -- 16
-- 多语句块
$ complex = \x, y -> do
$ temp = x + y
return temp * 2
end
-- 闭包
$ factor = 3
$ multiplier = \x -> x * factor
print(multiplier(5)) -- 15
-- 高阶函数
$ make_adder = \n -> \x -> x + n
$ add5 = make_adder(5)
print(add5(10)) -- 15
-- 与管道结合
$ numbers = {1, 2, 3, 4, 5}
$ doubled = map(numbers, \x -> x * 2)将值从左到右传递通过一系列函数,提高代码可读性。
语法格式:
value |> function1 |> function2 |> function3示例代码:
$ double = \x -> x * 2
$ add1 = \x -> x + 1
$ square = \x -> x * x
$ result = 5 |> double |> add1 |> square
print(result) -- ((5*2)+1)^2 = 121
-- 与三元结合
$ value = (x > 0) ? x : 0 |> double |> add1
-- 与可选链结合
$ user = { score = 80 }
$ level = user?.score |> \s -> s >= 60 ? "pass" : "fail"完整的异常处理机制,支持错误捕获和资源清理。
语法格式:
try
-- 可能抛出错误的代码
error("something wrong")
catch (error_variable)
-- 错误处理代码
finally
-- 无论是否出错都会执行的清理代码
end示例代码:
-- 基础用法
try
$ file = io.open("data.txt", "r")
$ content = file:read("*a")
print(content)
catch (err)
print("Error reading file:", err)
finally
if file then file:close() end
end
-- 嵌套使用
try
print("Outer try")
try
error("inner error")
catch (e)
print("Inner catch:", e)
error("rethrown")
finally
print("Inner finally")
end
catch (e)
print("Outer catch:", e)
finally
print("Outer finally")
end
-- 与 return 结合
function test()
try
return "from try"
catch (e)
return "from catch"
finally
print("finally runs before return")
end
end多分支选择结构,支持多值匹配。
语法格式:
switch expression do
case value1 then
-- 代码块
case value2, value3 then
-- 多值匹配
default
-- 默认分支
end示例代码:
$ command = "start"
switch command do
case "start", "run" then
print("Starting...")
-- 执行启动逻辑
case "stop", "halt" then
print("Stopping...")
case "restart" then
print("Restarting...")
default
print("Unknown command:", command)
end
-- 数值匹配
$ score = 85
switch math.floor(score / 10) do
case 9, 10 then
print("Grade A")
case 8 then
print("Grade B")
case 7 then
print("Grade C")
default
print("Grade D")
end延迟执行,在作用域结束时自动运行,常用于资源释放。
语法格式:
defer statement end示例代码:
function processFile(filename)
$ file = io.open(filename, "r")
defer file:close() end
-- 无论发生什么,file:close() 都会在函数退出前执行
$ data = file:read("*a")
if #data == 0 then
return nil -- defer 仍会执行
end
return process(data)
end
-- 多个 defer 按后进先出顺序执行
function test()
defer print("first") end
defer print("second") end
defer print("third") end
print("body")
end
-- 输出顺序: body, third, second, first
-- 在块中使用
do
$ resource = acquire()
defer release(resource) end
-- 使用资源
end -- 退出块时自动释放简洁的条件分支,类似多路 if-elseif。
语法格式:
when condition1 then
-- 代码块
case condition2 then
-- 代码块
else
-- 默认分支
end示例代码:
$ temperature = 25
when temperature > 30 then
print("Hot")
case temperature > 20 then
print("Warm")
case temperature > 10 then
print("Cool")
else
print("Cold")
end
-- 与逻辑操作符结合
$ age = 25
$ hasLicense = true
when age >= 18 && hasLicense then
print("Can drive")
case age >= 18 && !hasLicense then
print("Need license")
else
print("Too young")
end跳过当前循环迭代,进入下一次循环。
语法格式:
continue -- 在 for, while, repeat 循环中使用示例代码:
-- for 循环
for i = 1, 10 do
if i % 2 == 0 then
continue -- 跳过偶数
end
print("odd:", i) -- 输出 1,3,5,7,9
end
-- while 循环
$ i = 0
while i < 10 do
i = i + 1
if i == 5 then
continue -- 跳过 5
end
print(i) -- 输出 1,2,3,4,6,7,8,9,10
end
-- repeat 循环
$ j = 0
repeat
j = j + 1
if j == 3 then
continue
end
print(j) -- 输出 1,2,4,5
until j >= 5
-- 嵌套循环
for i = 1, 3 do
for j = 1, 3 do
if j == 2 then
continue -- 只跳过内层循环的当前迭代
end
print(i, j)
end
end使用 $ 快速声明局部变量。
语法格式:
$ variable = value
$ var1, var2 = value1, value2示例代码:
$ name = "Lua"
$ x, y = 10, 20
$ result = x + y
-- 与三元结合
$ max = (x > y) ? x : y
-- 在块中使用
do
$ temp = calculate()
print(temp)
end
-- temp 在这里不可访问使用 @ 声明标签,支持 goto 跳转。
语法格式:
@label@ -- 标签声明
goto label示例代码:
-- 循环模拟
$ count = 1
@start@
print("count:", count)
count = count + 1
if count <= 3 then
goto start
end
-- 错误处理
$ success, err = pcall(function()
if some_condition then
goto error_handler
end
-- 正常逻辑
return
@error_handler@
print("Error occurred")
end)所有扩展语法可以无缝组合,实现简洁而强大的代码。
-- 复杂数据处理管道
$ processUserData = \data -> do
try
$ result = data?.users
?.[0]
?.profile
?.name ?? "anonymous"
|> upper
|> \name -> name .. " (" .. (data?.version ?? 1) .. ")"
$ counter = 0
counter += (result != "anonymous") ? 10 : 0
when counter > 5 then
print("High priority user")
case counter > 0 then
print("Normal user")
else
print("Anonymous user")
end
return result
catch (e)
return "error: " .. e
finally
print("Processing completed")
end
end
-- 资源管理
function safeFileOperation(filename, operation)
$ file = io.open(filename, "r")
if !file then
return nil, "Cannot open file"
end
defer file:close() end
$ content = file:read("*a")
$ result = content
|> operation
|> \r -> r ?? "no result"
return result
end
-- 状态机
$ state = "initial"
while true do
when state == "initial" then
print("Initializing...")
state = "running"
case state == "running" then
print("Running...")
state = (counter++ > 10) ? "finished" : "running"
case state == "finished" then
print("Finished")
break
else
print("Unknown state:", state)
break
end
end
-- 错误处理与清理
try
$ conn = createConnection()
defer conn:close() end
$ data = conn:query("SELECT * FROM users")
$ processed = data
|> filter(\u -> u.age > 18)
|> map(\u -> {
name = u.name |> upper,
adult = true
})
switch #processed do
case 0 then
print("No adult users")
case 1 then
print("One adult user:", processed[1].name)
default
print("Multiple adult users:", #processed)
end
catch (err)
print("Database error:", err)
finally
print("Database operation completed")
end| 特性类别 | 具体特性 | 语法示例 | ||
|---|---|---|---|---|
| 条件表达式 | 三元运算符 | (a>b) ? a : b |
||
| 空值合并 | value ?? default |
|||
| 可选链 | obj?.field?.[index] |
|||
| 赋值操作 | 复合赋值 | `+= -= *= /= //= %= ^= ..= &= | = <<= >>=` | |
| 逻辑操作 | 逻辑非 | !condition |
||
| 不等于 | a != b |
|||
| 逻辑与 | a && b |
|||
| 逻辑或 | `a | b` | ||
| 函数式编程 | Lambda | \x,y -> x+y |
||
| 管道 | `value | > func1 | > func2` | |
| 异常处理 | Try-Catch-Finally | try ... catch(e) ... finally ... end |
||
| 控制流 | Switch-Case | switch x do case v: ... default ... end |
||
| When | when c1 then ... case c2 then ... else ... end |
|||
| Continue | continue |
|||
| 资源管理 | Defer | defer cleanup() end |
||
| 语法糖 | 局部声明 | $ var = value |
||
| 标签 | @label@ |
所有库均为编译进 APK 的原生模块,通过 require 直接加载。
require "mmkv",基于 MMKV v2.4.1。
| 函数 | 说明 |
|---|---|
mmkv.version() |
返回版本号字符串,如 "2.4.1" |
mmkv.initialize(rootDir) |
初始化存储根目录,成功返回 true;失败返回 false, errmsg。首次读写前必须调用 |
mmkv.set(id, key, value[, expireSeconds]) |
写入值。value 支持 string / boolean / integer / number;传 nil 等价于删除该 key;expireSeconds 为过期秒数(可选,大于 0 时启用,到期自动删除) |
mmkv.get(id, key) |
读取值,key 不存在返回 nil。根据底层编码自动推断类型 |
mmkv.contains(id, key) |
返回 boolean,是否包含该 key |
mmkv.remove(id, key) |
删除该 key,返回 boolean |
mmkv.clear(id) |
清空该实例全部数据 |
mmkv.count(id) |
返回该实例的 key 数量 |
mmkv.totalSize(id) |
返回该实例存储文件大小(字节) |
mmkv.allKeys(id) |
返回全部 key 组成的表(数组) |
id 为存储实例名(如 "default"),不同 id 之间数据互相隔离。
local mmkv = require "mmkv"
mmkv.initialize("/data/data/com.luafabric.studio.falling/files/mmkv")
mmkv.set("default", "name", "Alice", 3600) -- 1 小时后过期
print(mmkv.get("default", "name")) -- Alice类型推断说明:get 依据底层编码推断类型,少数边界情形可能产生歧义:
-
boolean 优先于整数
0 / 1; -
大于 2^49 的整数可能被当作 number 读取;
-
恰好 4 字节的整数可能被当作 float 读取;
-
极端 float 字节序列可能被当作 string 读取。
建议 set / get 使用一致的类型读写。
require "zstd",基于 Zstandard v1.6.0。
| 函数 | 说明 |
|---|---|
zstd.version() |
返回版本号字符串,如 "1.6.0" |
zstd.compress(data[, level]) |
压缩,level 默认 3。成功返回压缩后字符串;失败返回 nil, errmsg |
zstd.decompress(packed) |
解压。失败返回 nil, errmsg(如 "not a valid zstd frame") |
zstd.compressBound(len) |
返回长度为 len 的数据压缩后的最大尺寸 |
zstd.decompressedSize(packed) |
返回解压后的原始尺寸;无法确定时返回 -1 |
zstd.minCLevel() / zstd.maxCLevel() |
压缩级别范围(实测 -131072 ~ 22) |
zstd.defaultCLevel() |
默认压缩级别(3) |
zstd.compressStream(data[, level]) |
流式压缩(结果与 compress 等价) |
zstd.decompressStream(packed) |
流式解压 |
local zstd = require "zstd"
local packed = zstd.compress(("A"):rep(5600), 3) -- 5600 字节 → 46 字节
local original = zstd.decompress(packed) -- 还原 5600 字节require "aes",内置 micro AES 实现(AES-256:密钥 32 字节,块大小 16 字节)。
常量:BLOCKSIZE=16、KEYLENGTH=32,及各模式长度常量 GCM_NONCE_LEN=12、GCM_TAG_LEN=16、CCM_NONCE_LEN=11、CCM_TAG_LEN=16、OCB_NONCE_LEN=12、OCB_TAG_LEN=16、EAX_NONCE_LEN=16、EAX_TAG_LEN=16、SIV_TAG_LEN=16、GCM_SIV_NONCE_LEN=12、GCM_SIV_TAG_LEN=16、POLY1305_TAG_LEN=16、CTR_IV_LENGTH=12。
加密自动补齐到块边界;解密时密文长度必须是 16 的整数倍。
| 函数 | 说明 |
|---|---|
aes.ecb_encrypt(key, data) / aes.ecb_decrypt(key, data) |
ECB |
aes.cbc_encrypt(key, iv, data) / aes.cbc_decrypt(key, iv, data) |
CBC,iv 16 字节 |
aes.cfb_encrypt(key, iv, data) / aes.cfb_decrypt(key, iv, data) |
CFB,iv 16 字节 |
aes.ofb_encrypt(key, iv, data) / aes.ofb_decrypt(key, iv, data) |
OFB,iv 16 字节(加解密相同) |
aes.ctr_crypt(key, iv, data) |
CTR,加解密共用,iv 12 字节 |
aes.xts_encrypt(keys, tweak, data) / aes.xts_decrypt(keys, tweak, data) |
XTS,keys 为两个 32 字节密钥拼接(64 字节),tweak 16 字节或空,数据至少 16 字节 |
输出为 密文 + 16 字节 tag;解密或认证失败时抛错。
| 函数 | 说明 |
|---|---|
aes.gcm_encrypt(key, nonce, aad, data) / aes.gcm_decrypt(...) |
GCM,nonce 12 字节 |
aes.ccm_encrypt(...) / aes.ccm_decrypt(...) |
CCM,nonce 11 字节 |
aes.ocb_encrypt(...) / aes.ocb_decrypt(...) |
OCB,nonce 12 字节 |
aes.eax_encrypt(...) / aes.eax_decrypt(...) |
EAX,nonce 16 字节 |
aes.gcm_siv_encrypt(...) / aes.gcm_siv_decrypt(...) |
GCM-SIV,nonce 12 字节 |
aes.siv_encrypt(keys, aad, data) / aes.siv_decrypt(keys, aad, data) |
SIV(RFC 5297),keys 64 字节,无独立 nonce(IV 内嵌于输出前 16 字节) |
| 函数 | 说明 |
|---|---|
aes.cmac(key, data) |
CMAC 消息认证码,返回 16 字节 |
aes.poly1305(keys, data) |
Poly1305-AES,keys 48 字节(32 字节 AES 密钥 + 16 字节 r) |
aes.key_wrap(kek, secret) / aes.key_unwrap(kek, wrapped) |
NIST SP 800-38F 密钥封装;kek 32 字节,secret 为 16 字节倍数 |
aes.fpe_encrypt(key, tweak, text) / aes.fpe_decrypt(key, tweak, text) |
FF1 保形加密(NIST SP 800-38G),默认字母表为数字 0-9,输出长度不变 |
local aes = require "aes"
local key = ("k"):rep(32) -- 32 字节密钥
local iv = ("i"):rep(16)
local ct = aes.cbc_encrypt(key, iv, "hello") -- 自动补齐到 16 字节
local pt = aes.cbc_decrypt(key, iv, ct)require "sodium",封装 libsodium 核心接口。
常量:KEYBYTES=32(secretbox 密钥)、NONCEBYTES=24、SIGN_PUBLICKEYBYTES=32、SIGN_SECRETKEYBYTES=64、PWHASH_SALTBYTES=16、B64_ORIGINAL、B64_URLSAFE(base64 变体参数)。
| 函数 | 说明 |
|---|---|
sodium.version() |
libsodium 版本号 |
sodium.randombytes(n) |
生成 n 字节安全随机数 |
sodium.bin2hex(bin) / sodium.hex2bin(hex[, ignore]) |
二进制 ↔ 十六进制(ignore 为需跳过的字符) |
sodium.bin2base64(bin[, variant]) / sodium.base642bin(b64[, variant[, ignore]]) |
二进制 ↔ Base64 |
sodium.generichash(msg[, out_len[, key]]) |
BLAKE2b 通用哈希,默认输出 32 字节 |
sodium.secretbox_easy(msg, nonce, key) |
XSalsa20-Poly1305 对称加密,输出含 16 字节 MAC |
sodium.secretbox_open_easy(ct, nonce, key) |
解密;密钥/nonce 错误或数据被篡改时报错 |
sodium.sign_keypair() |
生成 Ed25519 签名密钥对,返回 pubkey, secretkey |
sodium.sign_detached(msg, sk) |
签名,返回 64 字节签名 |
sodium.sign_verify_detached(sig, msg, pk) |
验签,返回 boolean |
sodium.scalarmult_base(scalar) |
Curve25519 标量乘基点,返回 32 字节公钥 |
sodium.pwhash(pw, salt[, out_len[, ops[, mem]]]) |
Argon2i 密码哈希;salt 16 字节,默认 ops/mem 为交互式参数 |
require "cjson",标准 lua-cjson 2.1.0.11(另有 cjson.safe 变体,错误时不抛异常)。
| 函数 | 说明 |
|---|---|
cjson.encode(value) |
Lua 值 → JSON 字符串 |
cjson.decode(json) |
JSON 字符串 → Lua 值 |
cjson.new() |
新建独立配置的 cjson 模块表 |
配置函数(标准 lua-cjson):encode_empty_table_as_object、decode_array_with_array_mt、decode_allow_comment、encode_sparse_array、encode_max_depth、decode_max_depth、encode_number_precision、encode_keep_buffer、encode_invalid_numbers、decode_invalid_numbers、encode_escape_forward_slash、encode_skip_unsupported_value_types、encode_indent。
特殊值:cjson.null、cjson.empty_array、cjson.array_mt、cjson.empty_array_mt,以及 cjson._NAME / cjson._VERSION。
require "ffi",LuaJIT 风格 FFI(cffi 移植),可在 Lua 中声明并调用任意 C 函数与结构体。
local ffi = require "ffi"
ffi.cdef[[
int strlen(const char *s);
]]
local lib = ffi.load("libc.so")
print(lib.strlen("hello")) -- 5| 函数 | 说明 |
|---|---|
ffi.cdef(decl) |
声明 C 类型与函数原型 |
ffi.load(name) |
加载动态库,返回库句柄 |
ffi.new(ct, ...) |
创建 cdata 对象(可带初值) |
ffi.cast(ct, value) |
类型转换 |
ffi.metatype(ct, mt) |
为 cdata 类型绑定元表 |
ffi.typeof(ct) |
获取类型对象 |
ffi.addressof(cd) |
取 cdata 地址 |
ffi.gc(cd, finalizer) |
设置析构回调 |
ffi.sizeof(ct) / ffi.alignof(ct) / ffi.offsetof(ct, field) |
尺寸 / 对齐 / 字段偏移 |
ffi.istype(ct, value) |
判断类型 |
ffi.errno([err]) |
读取 / 设置 errno |
ffi.string(cdata[, len]) |
cdata → Lua 字符串 |
ffi.copy(dst, src, len) |
内存拷贝 |
ffi.fill(dst, len[, c]) |
内存填充 |
ffi.toretval(...) / ffi.eval(...) / ffi.type(...) |
进阶工具 |
ffi.getLuaState() |
返回当前 lua_State 指针 |
ffi.luatopointer(v) |
任意 Lua 值 → 指针 |
环境常量:ffi.os(如 "Linux")、ffi.arch(如 "arm64")、ffi.abi(ABI 特性表)、ffi.nullptr、ffi.tonumber、ffi.L、ffi.INFO。
require "memory",纯 Lua 实现(Apache-2.0,作者 YuYuanJin),提供自动/手动 GC、泄漏监测、Java 堆清理。采用 Handler + 协程调度,不占用真实线程。
local memory = require "memory"
memory.start() -- 默认参数启动自动模式
print(memory.get_status()) -- { lua_kb=..., java_mb=..., total_mb=..., running=true, mode="auto", leak_warning=false }| 函数 | 说明 |
|---|---|
memory.start(cfg) |
启动自动模式。cfg 可选:alert(溢出警告 KB,默认 2000)、pause/stepmul(GC 阈值/步长,传 true 启用动态自适应)、cooling_time(冷却秒)、peak_memory(Lua 峰值 KB)、pressure_threshold(压力阀值 0~1)、interval(检查间隔 ms,默认 1000)、debug、java_gc(默认 true) |
memory.start_manual(cfg) |
手动模式启动,之后需自行周期调用 tick() |
memory.tick() |
手动模式单次检查(建议 500ms 以上间隔调用) |
memory.stop() |
停止模块并清理协程/Handler/JNI 引用 |
memory.monitoring() |
随时可调,返回 lua_kb, leak_warning, java_mb |
memory.force_gc() |
立即执行完整 GC(Lua 全量回收 + luajava.clear() + Java GC) |
memory.get_status() |
返回当前内存状态表 |
memory.on_destroy() |
页面销毁清理,在 onDestroy() 中调用 |
memory.help() |
打印完整帮助文档 |
require "lanes",基于 LuaLanes(4.0)移植的 Lua 真线程库。每个 lane 是独立的 Lua 状态(独立 GC),通过 linda(双向消息通道)跨线程传递数据。
local lanes = require "lanes"
-- 1. 创建生成器(可复用):lanes.gen([libs,][opts,] lane_func)
local gen = lanes.gen("", function(v) -- "" = 仅 base 库,见 8.3 陷阱
return v * 2
end)
-- 2. 生成并启动一个 lane 线程
local lane = gen(21)
-- 3. 等待结果:lane:join([timeout]) → (true, 返回值...) 或 (nil, 错误信息, status)
local ok, res = lane:join(5)
print(ok, res) -- true 42local lanes = require "lanes"
local linda = lanes.linda() -- 创建消息通道(跨线程共享)
local gen = lanes.gen("", function(v)
local k, x = linda:receive(5, "from-main") -- 等待主线程消息
linda:send("to-main", x * 2) -- 回传结果
return x + 100
end)
linda:send("from-main", 42) -- 主线程发送
local k1, v1 = linda:receive(4, "to-main") -- 接收 lane 回传
print(k1, v1) -- to-main 84
local ok, res = gen(42):join(4)
print(ok, res) -- true 142| 函数 | 说明 |
|---|---|
linda:send(key, value) |
发送消息(带 key 的 FIFO),不阻塞 |
linda:receive([timeout,] key...) |
等待指定 key 的消息。返回 (key, value),匹配任意一个 key 即返回;timeout 秒后无消息返回 (nil, "timeout"),超时时间到后未取走的消息留在通道中 |
linda:get([timeout,] key...) |
读取但不移除消息(peek),可多次读取同一消息 |
linda:set(key, value) |
设置单个值,之后 get/set 直接存取(不走 FIFO) |
linda:count() |
返回排队中的消息数 |
linda:broadcast(key, value[, limit]) |
广播给所有等待该 key 的 receive |
两个容易踩的坑(务必注意):
-
receive返回(key, value),不是(value)!第一个返回值是匹配到的 key,第二个才是数据:local k, v = linda:receive(1, "data") -- k == "data", v == 数据
-
超时必须是第一个参数(紧跟
receive之后):receive(5, "key")表示 5 秒超时;receive("key", 5)中的5会被当作第二个 key,导致无超时无限等待!
lanes.gen([libs_str|opt_tbl [, ...],] lane_func) ([...]) -> lane-
libs_str指定 lane 内可用的标准库(默认 nil = 一个库都没有):-
nil:无任何库(默认)——lane 里tostring/print/assert全为 nil -
"":仅 base 库(assert、print、tostring、pairs等) -
"math,os":命名库 + base(多个用逗号分隔) -
"*":全部标准库
-
-
opt_tbl(可选):priority(线程优先级 -3~+3)、globals(按值传入的全局变量表)、required(lane 内自动 require 的包表)、gc_cb(lane 句柄回收回调)、name(调试名) -
lane 函数中闭包捕获的 upvalue 按值传递,跨线程共享状态请使用 linda 或 mmkv
| 方法 | 说明 |
|---|---|
lane:join([timeout]) |
等待 lane 结束。成功返回 (true, 返回值...);失败返回 (nil, 错误信息, status) |
lane:status() |
返回状态字符串("running" / "done" / "error" / "cancelled" 等) |
lane:cancel() |
请求取消线程 |
lane:get_peer() |
获取线程关联的 linda(不常用) |
local lanes = require "lanes"
local linda = lanes.linda()
local producer = lanes.gen("", function(count)
for i = 1, count do
linda:send("jobs", i)
end
linda:send("done", true)
return "producer done"
end)
local consumer = lanes.gen("", function()
local sum = 0
while true do
local k, v = linda:receive(1, "jobs", "done")
if k == "done" then break end
sum = sum + v
end
return sum
end)
local p, c = producer(1000), consumer()
local ok1, r1 = p:join(10)
local ok2, sum = c:join(10)
print(ok1, r1, ok2, sum) -- true producer done true 500500