| 39 | } |
| 40 | |
| 41 | public pushValue(thread: Thread, { target }: Decoration<TableType>, userdata?: Map<any, number>): boolean { |
| 42 | if (typeof target !== 'object' || target === null) { |
| 43 | return false |
| 44 | } |
| 45 | |
| 46 | // This is a map of JS objects to luaL references. |
| 47 | const seenMap = userdata || new Map<any, number>() |
| 48 | const existingReference = seenMap.get(target) |
| 49 | if (existingReference !== undefined) { |
| 50 | thread.lua.lua_rawgeti(thread.address, LUA_REGISTRYINDEX, BigInt(existingReference)) |
| 51 | return true |
| 52 | } |
| 53 | |
| 54 | try { |
| 55 | const tableIndex = thread.getTop() + 1 |
| 56 | |
| 57 | const createTable = (arrayCount: number, keyCount: number): void => { |
| 58 | thread.lua.lua_createtable(thread.address, arrayCount, keyCount) |
| 59 | const ref = thread.lua.luaL_ref(thread.address, LUA_REGISTRYINDEX) |
| 60 | seenMap.set(target, ref) |
| 61 | thread.lua.lua_rawgeti(thread.address, LUA_REGISTRYINDEX, BigInt(ref)) |
| 62 | } |
| 63 | |
| 64 | if (Array.isArray(target)) { |
| 65 | createTable(target.length, 0) |
| 66 | |
| 67 | for (let i = 0; i < target.length; i++) { |
| 68 | thread.pushValue(i + 1, seenMap) |
| 69 | thread.pushValue(target[i], seenMap) |
| 70 | |
| 71 | thread.lua.lua_settable(thread.address, tableIndex) |
| 72 | } |
| 73 | } else { |
| 74 | createTable(0, Object.getOwnPropertyNames(target).length) |
| 75 | |
| 76 | for (const key in target) { |
| 77 | thread.pushValue(key, seenMap) |
| 78 | thread.pushValue((target as Record<string, any>)[key], seenMap) |
| 79 | |
| 80 | thread.lua.lua_settable(thread.address, tableIndex) |
| 81 | } |
| 82 | } |
| 83 | } finally { |
| 84 | if (userdata === undefined) { |
| 85 | for (const reference of seenMap.values()) { |
| 86 | thread.lua.luaL_unref(thread.address, LUA_REGISTRYINDEX, reference) |
| 87 | } |
| 88 | } |
| 89 | } |
| 90 | |
| 91 | return true |
| 92 | } |
| 93 | |
| 94 | private readTableKeys(thread: Thread, index: number): string[] { |
| 95 | const keys = [] |