import { Decoration } from '../decoration' ; import { LUA_REGISTRYINDEX , LuaType } from '../types' ; import Global from '../global' ; import Thread from '../thread' ; import TypeExtension from '../type-extension' ; export type TableType = Record < any , any > | any [ ] ; class TableTypeExtension extends TypeExtension < TableType > { public constructor ( thread ) { super ( thread , 'js_table' ) ; } public close ( ) { } public isType ( _thread , _index , type ) { return type === LuaType . Table ; } public getValue ( thread , index , userdata ? ) { const seenMap = userdata || new Map < number , Record < string , string > > ( ) ; const pointer = thread . lua . lua_topointer ( thread . address , index ) ; let table = seenMap . get ( pointer ) ; if ( ! table ) { table = { } ; seenMap . set ( pointer , table ) ; this . tableToObject ( thread , index , seenMap , table ) ; } const keys = Object . keys ( table ) ; const isSequential = keys . length > 0 && keys . every ( ( key , index ) => key === String ( index + 1 ) ) ; return isSequential ? Object . values ( table ) : table ; } public pushValue ( thread , { target } , userdata ? ) { if ( typeof target !== 'object' || target === null ) { return false ; } const seenMap = userdata || new Map < any , number > ( ) ; const existingReference = seenMap . get ( target ) ; if ( existingReference !== undefined ) { thread . lua . lua_rawgeti ( thread . address , LUA_REGISTRYINDEX , existingReference ) ; return true ; } try { const tableIndex = thread . getTop ( ) + 1 ; const createTable = ( arrayCount , keyCount ) : void => { thread . lua . lua_createtable ( thread . address , arrayCount , keyCount ) ; const ref = thread . lua . luaL_ref ( thread . address , LUA_REGISTRYINDEX ) ; seenMap . set ( target , ref ) ; thread . lua . lua_rawgeti ( thread . address , LUA_REGISTRYINDEX , ref ) ; } ; if ( Array . isArray ( target ) ) { createTable ( target . length , 0 ) ; for ( let i = 0 ; i < target . length ; i ++ ) { thread . pushValue ( i + 1 , seenMap ) ; thread . pushValue ( target [ i ] , seenMap ) ; thread . lua . lua_settable ( thread . address , tableIndex ) ; } } else { createTable ( 0 , Object . getOwnPropertyNames ( target ) . length ) ; for ( const key in target ) { thread . pushValue ( key , seenMap ) ; thread . pushValue ( ( target as Record < string , any > ) [ key ] , seenMap ) ; thread . lua . lua_settable ( thread . address , tableIndex ) ; } } } finally { if ( userdata === undefined ) { for ( const reference of seenMap . values ( ) ) { thread . lua . luaL_unref ( thread . address , LUA_REGISTRYINDEX , reference ) ; } } } return true ; } private tableToObject ( thread , index , seenMap , table ) { thread . lua . lua_pushnil ( thread . address ) ; while ( thread . lua . lua_next ( thread . address , index ) ) { const key = thread . lua . luaL_tolstring ( thread . address , - 2 , null ) ; thread . pop ( ) ; const value = thread . getValue ( - 1 , undefined , seenMap ) ; table [ key ] = value ; thread . pop ( ) ; } } } export default function createTypeExtension ( thread ) { return new TableTypeExtension ( thread ) ; }