diff --git a/README.md b/README.md index 6a8b915..9acb3f3 100644 --- a/README.md +++ b/README.md @@ -6,15 +6,15 @@ ## From -[ceifa/wasmoon](https://github.com/ceifa/wasmoon) +[ceifa/wasmoon](https://github.com/ceifa/wasmoon) This repository has made some modifications based on this repository, adapting it to the lua5.1 version. At the same time, some functions of this repository have been optimized/adapted/adjusted. This package aims to provide a way to: -- Embed Lua to any Node.js, Deno or Web Application. -- Run lua code in any operational system -- Interop Lua and JS without memory leaks (including the DOM) +- Embed Lua to any Node.js, Deno or Web Application. +- Run lua code in any operational system +- Interop Lua and JS without memory leaks (including the DOM) ## API Usage @@ -40,6 +40,50 @@ try { } ``` +### About data interaction + +Regarding the issue of interacting between JavaScript objects and Lua tables, the previous solution before version 1.18.0 was to perform a one-time conversion, such as: + +```js +const obj = { name: 233 }; +lua.ctx.obj = obj; // table +const o = lua.ctx.obj; // object, but not the same object as obj, it is a new object with the same value but different reference. +``` + +This approach has some problems. First of all, Lua tables are more flexible in that they allow keys of any type and their array indices start from 1. + +Furthermore, it is not possible to achieve data binding. After adding a JavaScript object to Lua, it is not possible to manipulate the Lua table from the JavaScript layer. Also, modifying an extracted table in JavaScript does not affect the values of the table by modifying the JavaScript object. + +Therefore, starting from version 1.18.0, when attempting to inject a plainObject from JavaScript into the Lua environment, a table will be created just like before. However, when exporting a table from Lua, instead of trying to convert it into an object as before, a proxy class called "LuaTable" will be generated which allows arbitrary index and newindex operations on its underlying Lua table. + +The "LuaTable" class provides a series of methods: + +- `$get` for getting values because when indexing an object in JavaScript, + the key will be automatically converted to string and cannot access keys of number type properly. +- `$set` for setting values for similar reasons as above. +- `$detach` similar operation as before version 1.18.0, + returns a Map detached from the Lua environment (can pass parameters to return an object or array). +- `$istable` used for determining if it is a table. +- `$getRef` gets the index of this table in lua's registry. + +It can be used like this: + +```js +import { Lua } from '../dist/index.js'; +// This file was created as a sandbox to test and debug on vscode + +const lua = await Lua.create(); + +const obj = {}; +lua.ctx.obj = obj; + +const o = lua.ctx.obj; +o.name = 23333; +console.log(o.name); // 23333 + +lua.doStringSync('print(obj.name)'); // 23333 +``` + ## CLI Usage Although Wasmoon has been designed to be embedded, you can run it on command line as well, but, if you want something more robust on this, we recommend to take a look at [demoon](https://github.com/ceifa/demoon). @@ -50,8 +94,8 @@ $: wasmoon [options] [file] [args] Available options are: -- `-l`: Include a file or directory -- `-i`: Enter interactive mode after running the files +- `-l`: Include a file or directory +- `-i`: Enter interactive mode after running the files ### Example @@ -219,7 +263,7 @@ print("res", res:await()) Which will throw an error like this: -``` js +```js Error: Lua Error(ErrorRun/2): cannot resume dead coroutine at Thread.assertOk (/home/tstableford/projects/wasmoon/dist/index.js:409:23) at Thread. (/home/tstableford/projects/wasmoon/dist/index.js:142:22) @@ -229,7 +273,7 @@ Error: Lua Error(ErrorRun/2): cannot resume dead coroutine Or like this: -``` js +```js attempt to yield across a C-call boundary ``` diff --git a/README_zhcn.md b/README_zhcn.md new file mode 100644 index 0000000..1d336af --- /dev/null +++ b/README_zhcn.md @@ -0,0 +1,38 @@ +### 关于数据交互 + +关于js的object与lua的table交互的问题,1.18.0以前的方案是做一次性的转换,如 + +```js +const obj = { name: 233 }; +lua.ctx.obj = obj; //table +const o = lua.ctx.obj; // object,但是和obj不是一个对象,是一个值相同但是引用不同的新对象。 +``` + +这样会存在一些问题,首先lua的table相对更奔放一些,它允许任意类型的键,以及他的数组索引是从1开始的。 +以及无法实现数据绑定,即把js的object加入lua之后,无法再从js层面操作lua的table。以及在js修改提取出的table后无法通过修改js的对象影响table的值。 +所以从1.18.0开始,当尝试把js的plainObject注入lua环境中时,像之前一样会创建一个table。但是当从lua中导出一个table时,不会再尝试将其组成一个对象,而是生成一个代理的LuaTable类,可以对其进行任意的index,newindex操作以修改lua内的table。 +这个LuaTable类提供了一系列方法: + +- `$get` 获取值,因为js如果对对象index的时候,key会被自动转换为string。无法正常访问number类型的键。 +- `$set` 设置值,理由同上 +- `$detach` 类似1.18.0以前的操作,返回一个与lua环境脱钩的Map(可以传入参数返回object或者array) +- `$istable` 用于判断是不是一个table +- `$getRef` 获取table在lua环境注册表中的索引 + +可以像这样使用: + +```js +import { Lua } from '../dist/index.js'; +// This file was created as a sandbox to test and debug on vscode + +const lua = await Lua.create(); + +const obj = {}; +lua.ctx.obj = obj; + +const o = lua.ctx.obj; +o.name = 23333; +console.log(o.name); // 23333 + +lua.doStringSync('print(obj.name)'); // 23333 +``` diff --git a/package.json b/package.json index 210c1a7..28c20bc 100755 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "wasmoon-lua5.1", - "version": "1.17.2", + "version": "1.18.0", "description": "A real lua 5.1 VM with JS bindings made with webassembly", "main": "dist/index.js", "scripts": { diff --git a/src/table.ts b/src/table.ts index acd3473..d9825f8 100644 --- a/src/table.ts +++ b/src/table.ts @@ -1,6 +1,6 @@ +import { DictType, mapTransform } from './utils/map-transform'; import { LUA_REGISTRYINDEX, LuaType } from './definitions'; import LuaThread from './thread'; -import { DictType, mapTransform } from './utils/map-transform'; export class LuaTable { private thread: LuaThread; @@ -16,6 +16,34 @@ export class LuaTable { this.pointer = pointer; } + public $get(key: any): any { + return this.getTableValue(key); + } + + public $set(key: any, value: any): boolean { + return this.setTableValue(key, value); + } + + public $istable(): true { + return true; + } + + public $getRef(): number { + return this.ref; + } + + public $detach(dictType?: DictType): Map { + this.thread.luaApi.lua_rawgeti(this.thread.address, LUA_REGISTRYINDEX, this.ref); + let map = this.detachTable(-1); + this.thread.pop(); + map = mapTransform(map, { dictType: dictType ?? DictType.Map }) as Map; + return map; + } + + public toString(): string { + return `[LuaTable *${this.ref} 0x${this.pointer.toString(16)}]`; + } + private getTableValue(key: any): any { this.thread.luaApi.lua_rawgeti(this.thread.address, LUA_REGISTRYINDEX, this.ref); this.thread.pushValue(key); @@ -38,9 +66,13 @@ export class LuaTable { private detachTable(index: number, refs?: Map): Map { index = this.thread.luaApi.lua_absindex(this.thread.address, index); - if (!refs) refs = new Map(); + if (!refs) { + refs = new Map(); + } const pointer = this.thread.luaApi.lua_topointer(this.thread.address, index); - if (refs.has(pointer)) return refs.get(pointer); + if (refs.has(pointer)) { + return refs.get(pointer); + } const result = new Map(); refs.set(pointer, result); @@ -53,39 +85,11 @@ export class LuaTable { } return result; } - - public $get(key: any): any { - return this.getTableValue(key); - } - - public $set(key: any, value: any): boolean { - return this.setTableValue(key, value); - } - - public $istable(): true { - return true; - } - - public $getRef(): number { - return this.ref; - } - - public $detach(dictType?: DictType): Map { - this.thread.luaApi.lua_rawgeti(this.thread.address, LUA_REGISTRYINDEX, this.ref); - let map = this.detachTable(-1); - this.thread.pop(); - map = mapTransform(map, { dictType: dictType ?? DictType.Map }) as Map; - return map; - } - - public toString(): string { - return `[LuaTable *${this.ref} 0x${this.pointer.toString(16)}]`; - } } export const getTable = (thread: LuaThread, index: number): LuaTable => { // 根据内存地址,判断是否已经get过,如果有直接从引用表中返回 - let pointer = thread.luaApi.lua_topointer(thread.address, index); + const pointer = thread.luaApi.lua_topointer(thread.address, index); if (thread.luaApi.pointerRefs.has(pointer)) { return thread.luaApi.pointerRefs.get(pointer).proxy; } @@ -97,9 +101,15 @@ export const getTable = (thread: LuaThread, index: number): LuaTable => { const table = new LuaTable(thread, ref, pointer); const { proxy, revoke } = Proxy.revocable(table, { get: (target, key) => { - if (target[key]) return target[key]; - if (key === Symbol.toStringTag) return () => 'LuaTable'; - if (typeof key === 'symbol') return undefined; + if (target[key]) { + return target[key]; + } + if (key === Symbol.toStringTag) { + return () => 'LuaTable'; + } + if (typeof key === 'symbol') { + return undefined; + } return target.$get(key); }, set: (target, key, value) => target.$set(key, value), diff --git a/test/debug.mjs b/test/debug.mjs index c47f4f8..403d5ab 100644 --- a/test/debug.mjs +++ b/test/debug.mjs @@ -1,4 +1,4 @@ -import { LUA_REGISTRYINDEX, Lua } from '../dist/index.js'; +import { Lua } from '../dist/index.js'; // This file was created as a sandbox to test and debug on vscode const lua = await Lua.create(); @@ -7,9 +7,7 @@ const obj = {}; lua.ctx.obj = obj; const o = lua.ctx.obj; -o.name = 114514; +o.name = 23333; console.log(o.name); -lua.doStringSync(` - print(obj.name) -`); +lua.doStringSync('print(obj.name)');