mlua_codemp_patch/
table.rs

1use std::collections::HashSet;
2use std::fmt;
3use std::marker::PhantomData;
4use std::os::raw::c_void;
5use std::string::String as StdString;
6
7#[cfg(feature = "serialize")]
8use {
9    rustc_hash::FxHashSet,
10    serde::ser::{Serialize, SerializeMap, SerializeSeq, Serializer},
11    std::{cell::RefCell, rc::Rc, result::Result as StdResult},
12};
13
14use crate::error::{Error, Result};
15use crate::function::Function;
16use crate::state::{LuaGuard, RawLua};
17use crate::traits::ObjectLike;
18use crate::types::{Integer, ValueRef};
19use crate::util::{assert_stack, check_stack, StackGuard};
20use crate::value::{FromLua, FromLuaMulti, IntoLua, IntoLuaMulti, Nil, Value};
21
22#[cfg(feature = "async")]
23use futures_util::future::{self, Either, Future};
24
25/// Handle to an internal Lua table.
26#[derive(Clone)]
27pub struct Table(pub(crate) ValueRef);
28
29impl Table {
30    /// Sets a key-value pair in the table.
31    ///
32    /// If the value is `nil`, this will effectively remove the pair.
33    ///
34    /// This might invoke the `__newindex` metamethod. Use the [`raw_set`] method if that is not
35    /// desired.
36    ///
37    /// # Examples
38    ///
39    /// Export a value as a global to make it usable from Lua:
40    ///
41    /// ```
42    /// # use mlua::{Lua, Result};
43    /// # fn main() -> Result<()> {
44    /// # let lua = Lua::new();
45    /// let globals = lua.globals();
46    ///
47    /// globals.set("assertions", cfg!(debug_assertions))?;
48    ///
49    /// lua.load(r#"
50    ///     if assertions == true then
51    ///         -- ...
52    ///     elseif assertions == false then
53    ///         -- ...
54    ///     else
55    ///         error("assertions neither on nor off?")
56    ///     end
57    /// "#).exec()?;
58    /// # Ok(())
59    /// # }
60    /// ```
61    ///
62    /// [`raw_set`]: #method.raw_set
63    pub fn set(&self, key: impl IntoLua, value: impl IntoLua) -> Result<()> {
64        // Fast track (skip protected call)
65        if !self.has_metatable() {
66            return self.raw_set(key, value);
67        }
68
69        self.set_protected(key, value)
70    }
71
72    pub(crate) fn set_protected(&self, key: impl IntoLua, value: impl IntoLua) -> Result<()> {
73        let lua = self.0.lua.lock();
74        let state = lua.state();
75        unsafe {
76            let _sg = StackGuard::new(state);
77            check_stack(state, 5)?;
78
79            lua.push_ref(&self.0);
80            key.push_into_stack(&lua)?;
81            value.push_into_stack(&lua)?;
82            protect_lua!(state, 3, 0, fn(state) ffi::lua_settable(state, -3))
83        }
84    }
85
86    /// Gets the value associated to `key` from the table.
87    ///
88    /// If no value is associated to `key`, returns the `nil` value.
89    ///
90    /// This might invoke the `__index` metamethod. Use the [`raw_get`] method if that is not
91    /// desired.
92    ///
93    /// # Examples
94    ///
95    /// Query the version of the Lua interpreter:
96    ///
97    /// ```
98    /// # use mlua::{Lua, Result};
99    /// # fn main() -> Result<()> {
100    /// # let lua = Lua::new();
101    /// let globals = lua.globals();
102    ///
103    /// let version: String = globals.get("_VERSION")?;
104    /// println!("Lua version: {}", version);
105    /// # Ok(())
106    /// # }
107    /// ```
108    ///
109    /// [`raw_get`]: #method.raw_get
110    pub fn get<V: FromLua>(&self, key: impl IntoLua) -> Result<V> {
111        // Fast track (skip protected call)
112        if !self.has_metatable() {
113            return self.raw_get(key);
114        }
115
116        self.get_protected(key)
117    }
118
119    pub(crate) fn get_protected<V: FromLua>(&self, key: impl IntoLua) -> Result<V> {
120        let lua = self.0.lua.lock();
121        let state = lua.state();
122        unsafe {
123            let _sg = StackGuard::new(state);
124            check_stack(state, 4)?;
125
126            lua.push_ref(&self.0);
127            key.push_into_stack(&lua)?;
128            protect_lua!(state, 2, 1, fn(state) ffi::lua_gettable(state, -2))?;
129
130            V::from_stack(-1, &lua)
131        }
132    }
133
134    /// Checks whether the table contains a non-nil value for `key`.
135    ///
136    /// This might invoke the `__index` metamethod.
137    pub fn contains_key(&self, key: impl IntoLua) -> Result<bool> {
138        Ok(self.get::<Value>(key)? != Value::Nil)
139    }
140
141    /// Appends a value to the back of the table.
142    ///
143    /// This might invoke the `__len` and `__newindex` metamethods.
144    pub fn push(&self, value: impl IntoLua) -> Result<()> {
145        // Fast track (skip protected call)
146        if !self.has_metatable() {
147            return self.raw_push(value);
148        }
149
150        let lua = self.0.lua.lock();
151        let state = lua.state();
152        unsafe {
153            let _sg = StackGuard::new(state);
154            check_stack(state, 4)?;
155
156            lua.push_ref(&self.0);
157            value.push_into_stack(&lua)?;
158            protect_lua!(state, 2, 0, fn(state) {
159                let len = ffi::luaL_len(state, -2) as Integer;
160                ffi::lua_seti(state, -2, len + 1);
161            })?
162        }
163        Ok(())
164    }
165
166    /// Removes the last element from the table and returns it.
167    ///
168    /// This might invoke the `__len` and `__newindex` metamethods.
169    pub fn pop<V: FromLua>(&self) -> Result<V> {
170        // Fast track (skip protected call)
171        if !self.has_metatable() {
172            return self.raw_pop();
173        }
174
175        let lua = self.0.lua.lock();
176        let state = lua.state();
177        unsafe {
178            let _sg = StackGuard::new(state);
179            check_stack(state, 4)?;
180
181            lua.push_ref(&self.0);
182            protect_lua!(state, 1, 1, fn(state) {
183                let len = ffi::luaL_len(state, -1) as Integer;
184                ffi::lua_geti(state, -1, len);
185                ffi::lua_pushnil(state);
186                ffi::lua_seti(state, -3, len);
187            })?;
188            V::from_stack(-1, &lua)
189        }
190    }
191
192    /// Compares two tables for equality.
193    ///
194    /// Tables are compared by reference first.
195    /// If they are not primitively equals, then mlua will try to invoke the `__eq` metamethod.
196    /// mlua will check `self` first for the metamethod, then `other` if not found.
197    ///
198    /// # Examples
199    ///
200    /// Compare two tables using `__eq` metamethod:
201    ///
202    /// ```
203    /// # use mlua::{Lua, Result, Table};
204    /// # fn main() -> Result<()> {
205    /// # let lua = Lua::new();
206    /// let table1 = lua.create_table()?;
207    /// table1.set(1, "value")?;
208    ///
209    /// let table2 = lua.create_table()?;
210    /// table2.set(2, "value")?;
211    ///
212    /// let always_equals_mt = lua.create_table()?;
213    /// always_equals_mt.set("__eq", lua.create_function(|_, (_t1, _t2): (Table, Table)| Ok(true))?)?;
214    /// table2.set_metatable(Some(always_equals_mt));
215    ///
216    /// assert!(table1.equals(&table1.clone())?);
217    /// assert!(table1.equals(&table2)?);
218    /// # Ok(())
219    /// # }
220    /// ```
221    pub fn equals<T: AsRef<Self>>(&self, other: T) -> Result<bool> {
222        let other = other.as_ref();
223        if self == other {
224            return Ok(true);
225        }
226
227        // Compare using __eq metamethod if exists
228        // First, check the self for the metamethod.
229        // If self does not define it, then check the other table.
230        if let Some(mt) = self.get_metatable() {
231            if mt.contains_key("__eq")? {
232                return mt.get::<Function>("__eq")?.call((self, other));
233            }
234        }
235        if let Some(mt) = other.get_metatable() {
236            if mt.contains_key("__eq")? {
237                return mt.get::<Function>("__eq")?.call((self, other));
238            }
239        }
240
241        Ok(false)
242    }
243
244    /// Sets a key-value pair without invoking metamethods.
245    pub fn raw_set(&self, key: impl IntoLua, value: impl IntoLua) -> Result<()> {
246        #[cfg(feature = "luau")]
247        self.check_readonly_write()?;
248
249        let lua = self.0.lua.lock();
250        let state = lua.state();
251        unsafe {
252            let _sg = StackGuard::new(state);
253            check_stack(state, 5)?;
254
255            lua.push_ref(&self.0);
256            key.push_into_stack(&lua)?;
257            value.push_into_stack(&lua)?;
258
259            if lua.unlikely_memory_error() {
260                ffi::lua_rawset(state, -3);
261                ffi::lua_pop(state, 1);
262                Ok(())
263            } else {
264                protect_lua!(state, 3, 0, fn(state) ffi::lua_rawset(state, -3))
265            }
266        }
267    }
268
269    /// Gets the value associated to `key` without invoking metamethods.
270    pub fn raw_get<V: FromLua>(&self, key: impl IntoLua) -> Result<V> {
271        let lua = self.0.lua.lock();
272        let state = lua.state();
273        unsafe {
274            let _sg = StackGuard::new(state);
275            check_stack(state, 3)?;
276
277            lua.push_ref(&self.0);
278            key.push_into_stack(&lua)?;
279            ffi::lua_rawget(state, -2);
280
281            V::from_stack(-1, &lua)
282        }
283    }
284
285    /// Inserts element value at position `idx` to the table, shifting up the elements from
286    /// `table[idx]`. The worst case complexity is O(n), where n is the table length.
287    pub fn raw_insert(&self, idx: Integer, value: impl IntoLua) -> Result<()> {
288        let size = self.raw_len() as Integer;
289        if idx < 1 || idx > size + 1 {
290            return Err(Error::runtime("index out of bounds"));
291        }
292
293        let lua = self.0.lua.lock();
294        let state = lua.state();
295        unsafe {
296            let _sg = StackGuard::new(state);
297            check_stack(state, 5)?;
298
299            lua.push_ref(&self.0);
300            value.push_into_stack(&lua)?;
301            protect_lua!(state, 2, 0, |state| {
302                for i in (idx..=size).rev() {
303                    // table[i+1] = table[i]
304                    ffi::lua_rawgeti(state, -2, i);
305                    ffi::lua_rawseti(state, -3, i + 1);
306                }
307                ffi::lua_rawseti(state, -2, idx)
308            })
309        }
310    }
311
312    /// Appends a value to the back of the table without invoking metamethods.
313    pub fn raw_push(&self, value: impl IntoLua) -> Result<()> {
314        #[cfg(feature = "luau")]
315        self.check_readonly_write()?;
316
317        let lua = self.0.lua.lock();
318        let state = lua.state();
319        unsafe {
320            let _sg = StackGuard::new(state);
321            check_stack(state, 4)?;
322
323            lua.push_ref(&self.0);
324            value.push_into_stack(&lua)?;
325
326            unsafe fn callback(state: *mut ffi::lua_State) {
327                let len = ffi::lua_rawlen(state, -2) as Integer;
328                ffi::lua_rawseti(state, -2, len + 1);
329            }
330
331            if lua.unlikely_memory_error() {
332                callback(state);
333            } else {
334                protect_lua!(state, 2, 0, fn(state) callback(state))?;
335            }
336        }
337        Ok(())
338    }
339
340    /// Removes the last element from the table and returns it, without invoking metamethods.
341    pub fn raw_pop<V: FromLua>(&self) -> Result<V> {
342        #[cfg(feature = "luau")]
343        self.check_readonly_write()?;
344
345        let lua = self.0.lua.lock();
346        let state = lua.state();
347        unsafe {
348            let _sg = StackGuard::new(state);
349            check_stack(state, 3)?;
350
351            lua.push_ref(&self.0);
352            let len = ffi::lua_rawlen(state, -1) as Integer;
353            ffi::lua_rawgeti(state, -1, len);
354            // Set slot to nil (it must be safe to do)
355            ffi::lua_pushnil(state);
356            ffi::lua_rawseti(state, -3, len);
357
358            V::from_stack(-1, &lua)
359        }
360    }
361
362    /// Removes a key from the table.
363    ///
364    /// If `key` is an integer, mlua shifts down the elements from `table[key+1]`,
365    /// and erases element `table[key]`. The complexity is O(n) in the worst case,
366    /// where n is the table length.
367    ///
368    /// For other key types this is equivalent to setting `table[key] = nil`.
369    pub fn raw_remove(&self, key: impl IntoLua) -> Result<()> {
370        let lua = self.0.lua.lock();
371        let state = lua.state();
372        let key = key.into_lua(lua.lua())?;
373        match key {
374            Value::Integer(idx) => {
375                let size = self.raw_len() as Integer;
376                if idx < 1 || idx > size {
377                    return Err(Error::runtime("index out of bounds"));
378                }
379                unsafe {
380                    let _sg = StackGuard::new(state);
381                    check_stack(state, 4)?;
382
383                    lua.push_ref(&self.0);
384                    protect_lua!(state, 1, 0, |state| {
385                        for i in idx..size {
386                            ffi::lua_rawgeti(state, -1, i + 1);
387                            ffi::lua_rawseti(state, -2, i);
388                        }
389                        ffi::lua_pushnil(state);
390                        ffi::lua_rawseti(state, -2, size);
391                    })
392                }
393            }
394            _ => self.raw_set(key, Nil),
395        }
396    }
397
398    /// Clears the table, removing all keys and values from array and hash parts,
399    /// without invoking metamethods.
400    ///
401    /// This method is useful to clear the table while keeping its capacity.
402    pub fn clear(&self) -> Result<()> {
403        #[cfg(feature = "luau")]
404        self.check_readonly_write()?;
405
406        let lua = self.0.lua.lock();
407        unsafe {
408            #[cfg(feature = "luau")]
409            ffi::lua_cleartable(lua.ref_thread(), self.0.index);
410
411            #[cfg(not(feature = "luau"))]
412            {
413                let state = lua.state();
414                check_stack(state, 4)?;
415
416                lua.push_ref(&self.0);
417
418                // Clear array part
419                for i in 1..=ffi::lua_rawlen(state, -1) {
420                    ffi::lua_pushnil(state);
421                    ffi::lua_rawseti(state, -2, i as Integer);
422                }
423
424                // Clear hash part
425                // It must be safe as long as we don't use invalid keys
426                ffi::lua_pushnil(state);
427                while ffi::lua_next(state, -2) != 0 {
428                    ffi::lua_pop(state, 1); // pop value
429                    ffi::lua_pushvalue(state, -1); // copy key
430                    ffi::lua_pushnil(state);
431                    ffi::lua_rawset(state, -4);
432                }
433            }
434        }
435
436        Ok(())
437    }
438
439    /// Returns the result of the Lua `#` operator.
440    ///
441    /// This might invoke the `__len` metamethod. Use the [`raw_len`] method if that is not desired.
442    ///
443    /// [`raw_len`]: #method.raw_len
444    pub fn len(&self) -> Result<Integer> {
445        // Fast track (skip protected call)
446        if !self.has_metatable() {
447            return Ok(self.raw_len() as Integer);
448        }
449
450        let lua = self.0.lua.lock();
451        let state = lua.state();
452        unsafe {
453            let _sg = StackGuard::new(state);
454            check_stack(state, 4)?;
455
456            lua.push_ref(&self.0);
457            protect_lua!(state, 1, 0, |state| ffi::luaL_len(state, -1))
458        }
459    }
460
461    /// Returns the result of the Lua `#` operator, without invoking the `__len` metamethod.
462    pub fn raw_len(&self) -> usize {
463        let lua = self.0.lua.lock();
464        unsafe { ffi::lua_rawlen(lua.ref_thread(), self.0.index) }
465    }
466
467    /// Returns `true` if the table is empty, without invoking metamethods.
468    ///
469    /// It checks both the array part and the hash part.
470    pub fn is_empty(&self) -> bool {
471        // Check array part
472        if self.raw_len() != 0 {
473            return false;
474        }
475
476        // Check hash part
477        let lua = self.0.lua.lock();
478        let state = lua.state();
479        unsafe {
480            let _sg = StackGuard::new(state);
481            assert_stack(state, 4);
482
483            lua.push_ref(&self.0);
484            ffi::lua_pushnil(state);
485            if ffi::lua_next(state, -2) != 0 {
486                return false;
487            }
488        }
489
490        true
491    }
492
493    /// Returns a reference to the metatable of this table, or `None` if no metatable is set.
494    ///
495    /// Unlike the `getmetatable` Lua function, this method ignores the `__metatable` field.
496    pub fn get_metatable(&self) -> Option<Table> {
497        let lua = self.0.lua.lock();
498        let state = lua.state();
499        unsafe {
500            let _sg = StackGuard::new(state);
501            assert_stack(state, 2);
502
503            lua.push_ref(&self.0);
504            if ffi::lua_getmetatable(state, -1) == 0 {
505                None
506            } else {
507                Some(Table(lua.pop_ref()))
508            }
509        }
510    }
511
512    /// Sets or removes the metatable of this table.
513    ///
514    /// If `metatable` is `None`, the metatable is removed (if no metatable is set, this does
515    /// nothing).
516    pub fn set_metatable(&self, metatable: Option<Table>) {
517        // Workaround to throw readonly error without returning Result
518        #[cfg(feature = "luau")]
519        if self.is_readonly() {
520            panic!("attempt to modify a readonly table");
521        }
522
523        let lua = self.0.lua.lock();
524        let state = lua.state();
525        unsafe {
526            let _sg = StackGuard::new(state);
527            assert_stack(state, 2);
528
529            lua.push_ref(&self.0);
530            if let Some(metatable) = metatable {
531                lua.push_ref(&metatable.0);
532            } else {
533                ffi::lua_pushnil(state);
534            }
535            ffi::lua_setmetatable(state, -2);
536        }
537    }
538
539    /// Returns true if the table has metatable attached.
540    #[doc(hidden)]
541    #[inline]
542    pub fn has_metatable(&self) -> bool {
543        let lua = self.0.lua.lock();
544        let ref_thread = lua.ref_thread();
545        unsafe {
546            if ffi::lua_getmetatable(ref_thread, self.0.index) != 0 {
547                ffi::lua_pop(ref_thread, 1);
548                return true;
549            }
550        }
551        false
552    }
553
554    /// Sets `readonly` attribute on the table.
555    ///
556    /// Requires `feature = "luau"`
557    #[cfg(any(feature = "luau", doc))]
558    #[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
559    pub fn set_readonly(&self, enabled: bool) {
560        let lua = self.0.lua.lock();
561        let ref_thread = lua.ref_thread();
562        unsafe {
563            ffi::lua_setreadonly(ref_thread, self.0.index, enabled as _);
564            if !enabled {
565                // Reset "safeenv" flag
566                ffi::lua_setsafeenv(ref_thread, self.0.index, 0);
567            }
568        }
569    }
570
571    /// Returns `readonly` attribute of the table.
572    ///
573    /// Requires `feature = "luau"`
574    #[cfg(any(feature = "luau", doc))]
575    #[cfg_attr(docsrs, doc(cfg(feature = "luau")))]
576    pub fn is_readonly(&self) -> bool {
577        let lua = self.0.lua.lock();
578        let ref_thread = lua.ref_thread();
579        unsafe { ffi::lua_getreadonly(ref_thread, self.0.index) != 0 }
580    }
581
582    /// Converts this table to a generic C pointer.
583    ///
584    /// Different tables will give different pointers.
585    /// There is no way to convert the pointer back to its original value.
586    ///
587    /// Typically this function is used only for hashing and debug information.
588    #[inline]
589    pub fn to_pointer(&self) -> *const c_void {
590        self.0.to_pointer()
591    }
592
593    /// Returns an iterator over the pairs of the table.
594    ///
595    /// This works like the Lua `pairs` function, but does not invoke the `__pairs` metamethod.
596    ///
597    /// The pairs are wrapped in a [`Result`], since they are lazily converted to `K` and `V` types.
598    ///
599    /// # Examples
600    ///
601    /// Iterate over all globals:
602    ///
603    /// ```
604    /// # use mlua::{Lua, Result, Value};
605    /// # fn main() -> Result<()> {
606    /// # let lua = Lua::new();
607    /// let globals = lua.globals();
608    ///
609    /// for pair in globals.pairs::<Value, Value>() {
610    ///     let (key, value) = pair?;
611    /// #   let _ = (key, value);   // used
612    ///     // ...
613    /// }
614    /// # Ok(())
615    /// # }
616    /// ```
617    ///
618    /// [`Result`]: crate::Result
619    /// [Lua manual]: http://www.lua.org/manual/5.4/manual.html#pdf-next
620    pub fn pairs<K: FromLua, V: FromLua>(&self) -> TablePairs<K, V> {
621        TablePairs {
622            guard: self.0.lua.lock(),
623            table: self,
624            key: Some(Nil),
625            _phantom: PhantomData,
626        }
627    }
628
629    /// Iterates over the pairs of the table, invoking the given closure on each pair.
630    ///
631    /// This method is similar to [`Table::pairs`], but optimized for performance.
632    /// It does not invoke the `__pairs` metamethod.
633    pub fn for_each<K, V>(&self, mut f: impl FnMut(K, V) -> Result<()>) -> Result<()>
634    where
635        K: FromLua,
636        V: FromLua,
637    {
638        let lua = self.0.lua.lock();
639        let state = lua.state();
640        unsafe {
641            let _sg = StackGuard::new(state);
642            check_stack(state, 5)?;
643
644            lua.push_ref(&self.0);
645            ffi::lua_pushnil(state);
646            while ffi::lua_next(state, -2) != 0 {
647                let k = K::from_stack(-2, &lua)?;
648                let v = V::from_stack(-1, &lua)?;
649                f(k, v)?;
650                // Keep key for next iteration
651                ffi::lua_pop(state, 1);
652            }
653        }
654        Ok(())
655    }
656
657    /// Returns an iterator over all values in the sequence part of the table.
658    ///
659    /// The iterator will yield all values `t[1]`, `t[2]` and so on, until a `nil` value is
660    /// encountered. This mirrors the behavior of Lua's `ipairs` function but does not invoke
661    /// any metamethods.
662    ///
663    /// # Examples
664    ///
665    /// ```
666    /// # use mlua::{Lua, Result, Table};
667    /// # fn main() -> Result<()> {
668    /// # let lua = Lua::new();
669    /// let my_table: Table = lua.load(r#"
670    ///     {
671    ///         [1] = 4,
672    ///         [2] = 5,
673    ///         [4] = 7,
674    ///         key = 2
675    ///     }
676    /// "#).eval()?;
677    ///
678    /// let expected = [4, 5];
679    /// for (&expected, got) in expected.iter().zip(my_table.sequence_values::<u32>()) {
680    ///     assert_eq!(expected, got?);
681    /// }
682    /// # Ok(())
683    /// # }
684    /// ```
685    ///
686    /// [`pairs`]: #method.pairs
687    /// [`Result`]: crate::Result
688    /// [Lua manual]: http://www.lua.org/manual/5.4/manual.html#pdf-next
689    pub fn sequence_values<V: FromLua>(&self) -> TableSequence<V> {
690        TableSequence {
691            guard: self.0.lua.lock(),
692            table: self,
693            index: 1,
694            _phantom: PhantomData,
695        }
696    }
697
698    #[cfg(feature = "serialize")]
699    pub(crate) fn for_each_value<V>(&self, mut f: impl FnMut(V) -> Result<()>) -> Result<()>
700    where
701        V: FromLua,
702    {
703        let lua = self.0.lua.lock();
704        let state = lua.state();
705        unsafe {
706            let _sg = StackGuard::new(state);
707            check_stack(state, 4)?;
708
709            lua.push_ref(&self.0);
710            let len = ffi::lua_rawlen(state, -1);
711            for i in 1..=len {
712                ffi::lua_rawgeti(state, -1, i as _);
713                f(V::from_stack(-1, &lua)?)?;
714                ffi::lua_pop(state, 1);
715            }
716        }
717        Ok(())
718    }
719
720    /// Sets element value at position `idx` without invoking metamethods.
721    #[doc(hidden)]
722    pub fn raw_seti(&self, idx: usize, value: impl IntoLua) -> Result<()> {
723        #[cfg(feature = "luau")]
724        self.check_readonly_write()?;
725
726        let lua = self.0.lua.lock();
727        let state = lua.state();
728        unsafe {
729            let _sg = StackGuard::new(state);
730            check_stack(state, 5)?;
731
732            lua.push_ref(&self.0);
733            value.push_into_stack(&lua)?;
734
735            let idx = idx.try_into().unwrap();
736            if lua.unlikely_memory_error() {
737                ffi::lua_rawseti(state, -2, idx);
738            } else {
739                protect_lua!(state, 2, 0, |state| ffi::lua_rawseti(state, -2, idx))?;
740            }
741        }
742        Ok(())
743    }
744
745    #[cfg(feature = "serialize")]
746    pub(crate) fn is_array(&self) -> bool {
747        let lua = self.0.lua.lock();
748        let state = lua.state();
749        unsafe {
750            let _sg = StackGuard::new(state);
751            assert_stack(state, 3);
752
753            lua.push_ref(&self.0);
754            if ffi::lua_getmetatable(state, -1) == 0 {
755                return false;
756            }
757            crate::serde::push_array_metatable(state);
758            ffi::lua_rawequal(state, -1, -2) != 0
759        }
760    }
761
762    #[cfg(feature = "luau")]
763    #[inline(always)]
764    pub(crate) fn check_readonly_write(&self) -> Result<()> {
765        if self.is_readonly() {
766            return Err(Error::runtime("attempt to modify a readonly table"));
767        }
768        Ok(())
769    }
770
771    pub(crate) fn fmt_pretty(
772        &self,
773        fmt: &mut fmt::Formatter,
774        ident: usize,
775        visited: &mut HashSet<*const c_void>,
776    ) -> fmt::Result {
777        visited.insert(self.to_pointer());
778
779        // Collect key/value pairs into a vector so we can sort them
780        let mut pairs = self.pairs::<Value, Value>().flatten().collect::<Vec<_>>();
781        // Sort keys
782        pairs.sort_by(|(a, _), (b, _)| a.cmp(b));
783        if pairs.is_empty() {
784            return write!(fmt, "{{}}");
785        }
786        writeln!(fmt, "{{")?;
787        for (key, value) in pairs {
788            write!(fmt, "{}[", " ".repeat(ident + 2))?;
789            key.fmt_pretty(fmt, false, ident + 2, visited)?;
790            write!(fmt, "] = ")?;
791            value.fmt_pretty(fmt, true, ident + 2, visited)?;
792            writeln!(fmt, ",")?;
793        }
794        write!(fmt, "{}}}", " ".repeat(ident))
795    }
796}
797
798impl fmt::Debug for Table {
799    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
800        if fmt.alternate() {
801            return self.fmt_pretty(fmt, 0, &mut HashSet::new());
802        }
803        fmt.write_fmt(format_args!("Table({:?})", self.0))
804    }
805}
806
807impl PartialEq for Table {
808    fn eq(&self, other: &Self) -> bool {
809        self.0 == other.0
810    }
811}
812
813impl AsRef<Table> for Table {
814    #[inline]
815    fn as_ref(&self) -> &Self {
816        self
817    }
818}
819
820impl<T> PartialEq<[T]> for Table
821where
822    T: IntoLua + Clone,
823{
824    fn eq(&self, other: &[T]) -> bool {
825        let lua = self.0.lua.lock();
826        let state = lua.state();
827        unsafe {
828            let _sg = StackGuard::new(state);
829            assert_stack(state, 4);
830
831            lua.push_ref(&self.0);
832
833            let len = ffi::lua_rawlen(state, -1);
834            for i in 0..len {
835                ffi::lua_rawgeti(state, -1, (i + 1) as _);
836                let val = lua.pop_value();
837                if val == Nil {
838                    return i == other.len();
839                }
840                match other.get(i).map(|v| v.clone().into_lua(lua.lua())) {
841                    Some(Ok(other_val)) if val == other_val => continue,
842                    _ => return false,
843                }
844            }
845        }
846        true
847    }
848}
849
850impl<T> PartialEq<&[T]> for Table
851where
852    T: IntoLua + Clone,
853{
854    #[inline]
855    fn eq(&self, other: &&[T]) -> bool {
856        self == *other
857    }
858}
859
860impl<T, const N: usize> PartialEq<[T; N]> for Table
861where
862    T: IntoLua + Clone,
863{
864    #[inline]
865    fn eq(&self, other: &[T; N]) -> bool {
866        self == &other[..]
867    }
868}
869
870impl ObjectLike for Table {
871    #[inline]
872    fn get<V: FromLua>(&self, key: impl IntoLua) -> Result<V> {
873        self.get(key)
874    }
875
876    #[inline]
877    fn set(&self, key: impl IntoLua, value: impl IntoLua) -> Result<()> {
878        self.set(key, value)
879    }
880
881    #[inline]
882    fn call<R>(&self, args: impl IntoLuaMulti) -> Result<R>
883    where
884        R: FromLuaMulti,
885    {
886        // Convert table to a function and call via pcall that respects the `__call` metamethod.
887        Function(self.0.copy()).call(args)
888    }
889
890    #[cfg(feature = "async")]
891    #[inline]
892    fn call_async<R>(&self, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
893    where
894        R: FromLuaMulti,
895    {
896        Function(self.0.copy()).call_async(args)
897    }
898
899    #[inline]
900    fn call_method<R>(&self, name: &str, args: impl IntoLuaMulti) -> Result<R>
901    where
902        R: FromLuaMulti,
903    {
904        self.call_function(name, (self, args))
905    }
906
907    #[cfg(feature = "async")]
908    fn call_async_method<R>(&self, name: &str, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
909    where
910        R: FromLuaMulti,
911    {
912        self.call_async_function(name, (self, args))
913    }
914
915    #[inline]
916    fn call_function<R: FromLuaMulti>(&self, name: &str, args: impl IntoLuaMulti) -> Result<R> {
917        match self.get(name)? {
918            Value::Function(func) => func.call(args),
919            val => {
920                let msg = format!("attempt to call a {} value (function '{name}')", val.type_name());
921                Err(Error::runtime(msg))
922            }
923        }
924    }
925
926    #[cfg(feature = "async")]
927    #[inline]
928    fn call_async_function<R>(&self, name: &str, args: impl IntoLuaMulti) -> impl Future<Output = Result<R>>
929    where
930        R: FromLuaMulti,
931    {
932        match self.get(name) {
933            Ok(Value::Function(func)) => Either::Left(func.call_async(args)),
934            Ok(val) => {
935                let msg = format!("attempt to call a {} value (function '{name}')", val.type_name());
936                Either::Right(future::ready(Err(Error::RuntimeError(msg))))
937            }
938            Err(err) => Either::Right(future::ready(Err(err))),
939        }
940    }
941
942    #[inline]
943    fn to_string(&self) -> Result<StdString> {
944        Value::Table(Table(self.0.copy())).to_string()
945    }
946}
947
948/// A wrapped [`Table`] with customized serialization behavior.
949#[cfg(feature = "serialize")]
950pub(crate) struct SerializableTable<'a> {
951    table: &'a Table,
952    options: crate::serde::de::Options,
953    visited: Rc<RefCell<FxHashSet<*const c_void>>>,
954}
955
956#[cfg(feature = "serialize")]
957impl Serialize for Table {
958    #[inline]
959    fn serialize<S: Serializer>(&self, serializer: S) -> StdResult<S::Ok, S::Error> {
960        SerializableTable::new(self, Default::default(), Default::default()).serialize(serializer)
961    }
962}
963
964#[cfg(feature = "serialize")]
965impl<'a> SerializableTable<'a> {
966    #[inline]
967    pub(crate) fn new(
968        table: &'a Table,
969        options: crate::serde::de::Options,
970        visited: Rc<RefCell<FxHashSet<*const c_void>>>,
971    ) -> Self {
972        Self {
973            table,
974            options,
975            visited,
976        }
977    }
978}
979
980#[cfg(feature = "serialize")]
981impl<'a> Serialize for SerializableTable<'a> {
982    fn serialize<S>(&self, serializer: S) -> StdResult<S::Ok, S::Error>
983    where
984        S: Serializer,
985    {
986        use crate::serde::de::{check_value_for_skip, MapPairs, RecursionGuard};
987        use crate::value::SerializableValue;
988
989        let convert_result = |res: Result<()>, serialize_err: Option<S::Error>| match res {
990            Ok(v) => Ok(v),
991            Err(Error::SerializeError(_)) if serialize_err.is_some() => Err(serialize_err.unwrap()),
992            Err(Error::SerializeError(msg)) => Err(serde::ser::Error::custom(msg)),
993            Err(err) => Err(serde::ser::Error::custom(err.to_string())),
994        };
995
996        let options = self.options;
997        let visited = &self.visited;
998        let _guard = RecursionGuard::new(self.table, visited);
999
1000        // Array
1001        let len = self.table.raw_len();
1002        if len > 0 || self.table.is_array() {
1003            let mut seq = serializer.serialize_seq(Some(len))?;
1004            let mut serialize_err = None;
1005            let res = self.table.for_each_value::<Value>(|value| {
1006                let skip = check_value_for_skip(&value, self.options, visited)
1007                    .map_err(|err| Error::SerializeError(err.to_string()))?;
1008                if skip {
1009                    // continue iteration
1010                    return Ok(());
1011                }
1012                seq.serialize_element(&SerializableValue::new(&value, options, Some(visited)))
1013                    .map_err(|err| {
1014                        serialize_err = Some(err);
1015                        Error::SerializeError(StdString::new())
1016                    })
1017            });
1018            convert_result(res, serialize_err)?;
1019            return seq.end();
1020        }
1021
1022        // HashMap
1023        let mut map = serializer.serialize_map(None)?;
1024        let mut serialize_err = None;
1025        let mut process_pair = |key, value| {
1026            let skip_key = check_value_for_skip(&key, self.options, visited)
1027                .map_err(|err| Error::SerializeError(err.to_string()))?;
1028            let skip_value = check_value_for_skip(&value, self.options, visited)
1029                .map_err(|err| Error::SerializeError(err.to_string()))?;
1030            if skip_key || skip_value {
1031                // continue iteration
1032                return Ok(());
1033            }
1034            map.serialize_entry(
1035                &SerializableValue::new(&key, options, Some(visited)),
1036                &SerializableValue::new(&value, options, Some(visited)),
1037            )
1038            .map_err(|err| {
1039                serialize_err = Some(err);
1040                Error::SerializeError(StdString::new())
1041            })
1042        };
1043
1044        let res = if !self.options.sort_keys {
1045            // Fast track
1046            self.table.for_each(process_pair)
1047        } else {
1048            MapPairs::new(self.table, self.options.sort_keys)
1049                .map_err(serde::ser::Error::custom)?
1050                .try_for_each(|kv| {
1051                    let (key, value) = kv?;
1052                    process_pair(key, value)
1053                })
1054        };
1055        convert_result(res, serialize_err)?;
1056        map.end()
1057    }
1058}
1059
1060/// An iterator over the pairs of a Lua table.
1061///
1062/// This struct is created by the [`Table::pairs`] method.
1063///
1064/// [`Table::pairs`]: crate::Table::pairs
1065pub struct TablePairs<'a, K, V> {
1066    guard: LuaGuard,
1067    table: &'a Table,
1068    key: Option<Value>,
1069    _phantom: PhantomData<(K, V)>,
1070}
1071
1072impl<'a, K, V> Iterator for TablePairs<'a, K, V>
1073where
1074    K: FromLua,
1075    V: FromLua,
1076{
1077    type Item = Result<(K, V)>;
1078
1079    fn next(&mut self) -> Option<Self::Item> {
1080        if let Some(prev_key) = self.key.take() {
1081            let lua: &RawLua = &self.guard;
1082            let state = lua.state();
1083
1084            let res = (|| unsafe {
1085                let _sg = StackGuard::new(state);
1086                check_stack(state, 5)?;
1087
1088                lua.push_ref(&self.table.0);
1089                lua.push_value(&prev_key)?;
1090
1091                // It must be safe to call `lua_next` unprotected as deleting a key from a table is
1092                // a permitted operation.
1093                // It fails only if the key is not found (never existed) which seems impossible scenario.
1094                if ffi::lua_next(state, -2) != 0 {
1095                    let key = lua.stack_value(-2, None);
1096                    Ok(Some((
1097                        key.clone(),
1098                        K::from_lua(key, lua.lua())?,
1099                        V::from_stack(-1, lua)?,
1100                    )))
1101                } else {
1102                    Ok(None)
1103                }
1104            })();
1105
1106            match res {
1107                Ok(Some((key, ret_key, value))) => {
1108                    self.key = Some(key);
1109                    Some(Ok((ret_key, value)))
1110                }
1111                Ok(None) => None,
1112                Err(e) => Some(Err(e)),
1113            }
1114        } else {
1115            None
1116        }
1117    }
1118}
1119
1120/// An iterator over the sequence part of a Lua table.
1121///
1122/// This struct is created by the [`Table::sequence_values`] method.
1123///
1124/// [`Table::sequence_values`]: crate::Table::sequence_values
1125pub struct TableSequence<'a, V> {
1126    guard: LuaGuard,
1127    table: &'a Table,
1128    index: Integer,
1129    _phantom: PhantomData<V>,
1130}
1131
1132impl<'a, V> Iterator for TableSequence<'a, V>
1133where
1134    V: FromLua,
1135{
1136    type Item = Result<V>;
1137
1138    fn next(&mut self) -> Option<Self::Item> {
1139        let lua: &RawLua = &self.guard;
1140        let state = lua.state();
1141        unsafe {
1142            let _sg = StackGuard::new(state);
1143            if let Err(err) = check_stack(state, 1) {
1144                return Some(Err(err));
1145            }
1146
1147            lua.push_ref(&self.table.0);
1148            match ffi::lua_rawgeti(state, -1, self.index) {
1149                ffi::LUA_TNIL => None,
1150                _ => {
1151                    self.index += 1;
1152                    Some(V::from_stack(-1, lua))
1153                }
1154            }
1155        }
1156    }
1157}
1158
1159#[cfg(test)]
1160mod assertions {
1161    use super::*;
1162
1163    #[cfg(not(feature = "send"))]
1164    static_assertions::assert_not_impl_any!(Table: Send);
1165    #[cfg(feature = "send")]
1166    static_assertions::assert_impl_all!(Table: Send, Sync);
1167}