From 07671cbfd7654bc357c30fab9ba46ded9faa101c Mon Sep 17 00:00:00 2001 From: Dave Lucia Date: Tue, 10 Feb 2026 20:00:12 -0800 Subject: [PATCH 1/2] feat: Add _G global table Implements _G as a table containing references to global functions. Features: - _G exists and is a table - _G contains standard library functions - _G._G references itself Limitations (marked as skipped tests): - Not a live reference (changes don't sync with globals) - Full live reference would require metamethods or VM changes Tests: 3 passing, 2 skipped All existing tests pass (1089 tests, 0 failures) --- lib/lua/vm/stdlib.ex | 20 +++++++++++++++++++ test/lua_test.exs | 46 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/lib/lua/vm/stdlib.ex b/lib/lua/vm/stdlib.ex index a322df5..15ac7f2 100644 --- a/lib/lua/vm/stdlib.ex +++ b/lib/lua/vm/stdlib.ex @@ -37,6 +37,26 @@ defmodule Lua.VM.Stdlib do |> Lua.VM.Stdlib.String.install() |> Lua.VM.Stdlib.Math.install() |> Lua.VM.Stdlib.Table.install() + |> install_global_g() + end + + # Install _G global table - a table that references the global environment + defp install_global_g(state) do + # Create a table containing all current globals + # Copy all globals into the _G table + g_data = state.globals + + {{:tref, _id} = g_ref, state} = State.alloc_table(state, g_data) + + # Set _G to point to this table + state = State.set_global(state, "_G", g_ref) + + # Also add _G to itself so _G._G == _G + state = State.update_table(state, g_ref, fn table -> + %{table | data: Map.put(table.data, "_G", g_ref)} + end) + + state end # type(v) — returns the type of v as a string diff --git a/test/lua_test.exs b/test/lua_test.exs index 2a29e9c..9cbec95 100644 --- a/test/lua_test.exs +++ b/test/lua_test.exs @@ -1397,6 +1397,52 @@ defmodule LuaTest do end end + describe "_G global table" do + setup do + %{lua: Lua.new(sandboxed: [])} + end + + test "_G references the global environment", %{lua: lua} do + # _G should be a table that contains itself + assert {[true], _} = Lua.eval!(lua, "return _G ~= nil") + assert {[true], _} = Lua.eval!(lua, "return type(_G) == 'table'") + end + + test "_G contains global functions", %{lua: lua} do + # Standard functions should be accessible via _G + assert {[true], _} = Lua.eval!(lua, "return _G.print == print") + assert {[true], _} = Lua.eval!(lua, "return _G.type == type") + assert {[true], _} = Lua.eval!(lua, "return _G.tostring == tostring") + end + + test "_G contains itself", %{lua: lua} do + # _G._G should reference _G + assert {[true], _} = Lua.eval!(lua, "return _G._G == _G") + end + + @tag :skip + test "can set globals via _G", %{lua: lua} do + # Requires _G to be a live reference with __index/__newindex metamethods + code = """ + _G.myvar = 42 + return myvar + """ + + assert {[42], _} = Lua.eval!(lua, code) + end + + @tag :skip + test "can read globals via _G", %{lua: lua} do + # Requires _G to be a live reference with __index metamethods + code = """ + myvar = 123 + return _G.myvar + """ + + assert {[123], _} = Lua.eval!(lua, code) + end + end + defp test_file(name) do Path.join(["test", "fixtures", name]) end From 731e23519844a45611cd66a6b69b1e5b0443f865 Mon Sep 17 00:00:00 2001 From: Dave Lucia Date: Tue, 10 Feb 2026 20:23:06 -0800 Subject: [PATCH 2/2] format: Fix formatting in stdlib.ex --- lib/lua/vm/stdlib.ex | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/lib/lua/vm/stdlib.ex b/lib/lua/vm/stdlib.ex index 15ac7f2..5d9e663 100644 --- a/lib/lua/vm/stdlib.ex +++ b/lib/lua/vm/stdlib.ex @@ -52,9 +52,10 @@ defmodule Lua.VM.Stdlib do state = State.set_global(state, "_G", g_ref) # Also add _G to itself so _G._G == _G - state = State.update_table(state, g_ref, fn table -> - %{table | data: Map.put(table.data, "_G", g_ref)} - end) + state = + State.update_table(state, g_ref, fn table -> + %{table | data: Map.put(table.data, "_G", g_ref)} + end) state end