[{"data":1,"prerenderedAt":3311},["ShallowReactive",2],{"page-\u002Flua\u002F01-getting-started":3},{"id":4,"title":5,"body":6,"description":3304,"extension":3305,"meta":3306,"navigation":98,"path":3307,"seo":3308,"stem":3309,"__hash__":3310},"content\u002Flua\u002F01-getting-started.md","Lua 01 — Runtime Architecture, Embedding & Execution Model",{"type":7,"value":8,"toc":3283},"minimark",[9,14,27,32,111,139,143,151,353,539,547,677,685,703,969,973,1255,1377,1385,1808,1812,2247,2254,2496,2503,2551,2602,2606,2616,2638,2655,2688,2697,2774,2778,2798,2853,2874,2882,2978,2991,3025,3035,3039,3042,3125,3174,3178,3233,3279],[10,11,13],"h1",{"id":12},"_01-runtime-architecture-embedding-execution-model","01 — Runtime Architecture, Embedding & Execution Model",[15,16,17,18,22,23,26],"p",{},"Lua is a register-based VM that compiles source to bytecode, then executes on a stack-machine. The entire runtime is a C library (",[19,20,21],"code",{},"liblua",") — the ",[19,24,25],{},"lua"," executable is a thin 200-line wrapper. Understanding the compilation→execution boundary, the registry, and how chunks are loaded is the difference between \"scripting\" and engineering with Lua.",[28,29,31],"h2",{"id":30},"installation","Installation",[33,34,36],"code-wrapper",{"language":35},"bash",[37,38,42],"pre",{"className":39,"code":40,"language":35,"meta":41,"style":41},"language-bash shiki shiki-themes github-light github-dark","brew install lua         # macOS — Lua 5.4\nsudo apt install lua5.4  # Debian\u002FUbuntu\nlua -v                   # Lua 5.4.6  Copyright (C) 1994-2023 Lua.org, PUC-Rio\n\nluajit -v               # LuaJIT 2.1 — drop-in, JIT-compiled, 10-100x faster\n","",[19,43,44,64,81,93,100],{"__ignoreMap":41},[45,46,49,53,57,60],"span",{"class":47,"line":48},"line",1,[45,50,52],{"class":51},"sIsaT","brew",[45,54,56],{"class":55},"sJ6F3"," install",[45,58,59],{"class":55}," lua",[45,61,63],{"class":62},"sdCPZ","         # macOS — Lua 5.4\n",[45,65,67,70,73,75,78],{"class":47,"line":66},2,[45,68,69],{"class":51},"sudo",[45,71,72],{"class":55}," apt",[45,74,56],{"class":55},[45,76,77],{"class":55}," lua5.4",[45,79,80],{"class":62},"  # Debian\u002FUbuntu\n",[45,82,84,86,90],{"class":47,"line":83},3,[45,85,25],{"class":51},[45,87,89],{"class":88},"snvgF"," -v",[45,91,92],{"class":62},"                   # Lua 5.4.6  Copyright (C) 1994-2023 Lua.org, PUC-Rio\n",[45,94,96],{"class":47,"line":95},4,[45,97,99],{"emptyLinePlaceholder":98},true,"\n",[45,101,103,106,108],{"class":47,"line":102},5,[45,104,105],{"class":51},"luajit",[45,107,89],{"class":88},[45,109,110],{"class":62},"               # LuaJIT 2.1 — drop-in, JIT-compiled, 10-100x faster\n",[33,112,113],{"language":35},[37,114,116],{"className":39,"code":115,"language":35,"meta":41,"style":41},"luarocks --version      # package manager: luarocks install lua-cjson\nluacheck --version      # static analyzer: catches globals, shadowing, dead code\n",[19,117,118,129],{"__ignoreMap":41},[45,119,120,123,126],{"class":47,"line":48},[45,121,122],{"class":51},"luarocks",[45,124,125],{"class":88}," --version",[45,127,128],{"class":62},"      # package manager: luarocks install lua-cjson\n",[45,130,131,134,136],{"class":47,"line":66},[45,132,133],{"class":51},"luacheck",[45,135,125],{"class":88},[45,137,138],{"class":62},"      # static analyzer: catches globals, shadowing, dead code\n",[28,140,142],{"id":141},"chunk-compilation-execution","Chunk Compilation & Execution",[15,144,145,146,150],{},"A ",[147,148,149],"strong",{},"chunk"," is the unit of compilation. Lua compiles the entire chunk to bytecode before executing any statement — syntax errors abort before any side effects.",[33,152,154],{"language":25,"filename":153},"chunk_demo.lua",[37,155,158],{"className":156,"code":157,"language":25,"meta":41,"style":41},"language-lua shiki shiki-themes github-light github-dark","-- loadstring compiles source → bytecode; returns a function (the \"main\" of the chunk)\n-- The returned closure captures the global environment at compile time.\nlocal chunk = load([[\n  local x = 10          -- local: stored in the closure's upvalue slot\n  y = 20                -- global: writes to _G via the chunk's environment\n  return x + 5\n]])\n\n-- load() does NOT execute; it returns a function. Call it to run.\nlocal ok, result = pcall(chunk)   -- pcall: protected call, catches runtime errors\nprint(ok, result)                 -- true   15\nprint(y)                          -- 20     (side effect: global created)\n\n-- load() with a custom environment (sandbox boundary):\nlocal env = { print = print, math = math }   -- whitelist only safe globals\nlocal sandbox = load(\"print(math.sqrt(16))\", \"sandbox\", \"t\", env)\nsandbox()                        -- 4.0    — cannot access io, os, debug, _G\n",[19,159,160,165,170,192,197,202,208,217,222,228,247,259,270,275,281,310,341],{"__ignoreMap":41},[45,161,162],{"class":47,"line":48},[45,163,164],{"class":62},"-- loadstring compiles source → bytecode; returns a function (the \"main\" of the chunk)\n",[45,166,167],{"class":47,"line":66},[45,168,169],{"class":62},"-- The returned closure captures the global environment at compile time.\n",[45,171,172,176,180,183,186,189],{"class":47,"line":83},[45,173,175],{"class":174},"svdQ7","local",[45,177,179],{"class":178},"ssxIu"," chunk ",[45,181,182],{"class":174},"=",[45,184,185],{"class":88}," load",[45,187,188],{"class":178},"(",[45,190,191],{"class":55},"[[\n",[45,193,194],{"class":47,"line":95},[45,195,196],{"class":55},"  local x = 10          -- local: stored in the closure's upvalue slot\n",[45,198,199],{"class":47,"line":102},[45,200,201],{"class":55},"  y = 20                -- global: writes to _G via the chunk's environment\n",[45,203,205],{"class":47,"line":204},6,[45,206,207],{"class":55},"  return x + 5\n",[45,209,211,214],{"class":47,"line":210},7,[45,212,213],{"class":55},"]]",[45,215,216],{"class":178},")\n",[45,218,220],{"class":47,"line":219},8,[45,221,99],{"emptyLinePlaceholder":98},[45,223,225],{"class":47,"line":224},9,[45,226,227],{"class":62},"-- load() does NOT execute; it returns a function. Call it to run.\n",[45,229,231,233,236,238,241,244],{"class":47,"line":230},10,[45,232,175],{"class":174},[45,234,235],{"class":178}," ok, result ",[45,237,182],{"class":174},[45,239,240],{"class":88}," pcall",[45,242,243],{"class":178},"(chunk)   ",[45,245,246],{"class":62},"-- pcall: protected call, catches runtime errors\n",[45,248,250,253,256],{"class":47,"line":249},11,[45,251,252],{"class":88},"print",[45,254,255],{"class":178},"(ok, result)                 ",[45,257,258],{"class":62},"-- true   15\n",[45,260,262,264,267],{"class":47,"line":261},12,[45,263,252],{"class":88},[45,265,266],{"class":178},"(y)                          ",[45,268,269],{"class":62},"-- 20     (side effect: global created)\n",[45,271,273],{"class":47,"line":272},13,[45,274,99],{"emptyLinePlaceholder":98},[45,276,278],{"class":47,"line":277},14,[45,279,280],{"class":62},"-- load() with a custom environment (sandbox boundary):\n",[45,282,284,286,289,291,294,296,299,302,304,307],{"class":47,"line":283},15,[45,285,175],{"class":174},[45,287,288],{"class":178}," env ",[45,290,182],{"class":174},[45,292,293],{"class":178}," { print ",[45,295,182],{"class":174},[45,297,298],{"class":88}," print",[45,300,301],{"class":178},", math ",[45,303,182],{"class":174},[45,305,306],{"class":178}," math }   ",[45,308,309],{"class":62},"-- whitelist only safe globals\n",[45,311,313,315,318,320,322,324,327,330,333,335,338],{"class":47,"line":312},16,[45,314,175],{"class":174},[45,316,317],{"class":178}," sandbox ",[45,319,182],{"class":174},[45,321,185],{"class":88},[45,323,188],{"class":178},[45,325,326],{"class":55},"\"print(math.sqrt(16))\"",[45,328,329],{"class":178},", ",[45,331,332],{"class":55},"\"sandbox\"",[45,334,329],{"class":178},[45,336,337],{"class":55},"\"t\"",[45,339,340],{"class":178},", env)\n",[45,342,344,347,350],{"class":47,"line":343},17,[45,345,346],{"class":88},"sandbox",[45,348,349],{"class":178},"()                        ",[45,351,352],{"class":62},"-- 4.0    — cannot access io, os, debug, _G\n",[33,354,355],{"language":25},[37,356,358],{"className":156,"code":357,"language":25,"meta":41,"style":41},"-- loadfile: compile a file without executing. nil + error message on failure.\nlocal fn, err = loadfile(\"plugin.lua\")\nif not fn then\n  error(\"Failed to compile plugin: \" .. err)   -- err contains line:col from parser\nend\n-- fn is the chunk's main function; call it when ready (deferred execution)\nfn()\n\n-- dofile: compile + execute immediately. No error handling — crashes on failure.\ndofile(\"config.lua\")    -- returns the chunk's value (usually discarded)\n\n-- require: compile + execute + cache in package.loaded[name]\n-- Returns cached value on subsequent calls; never re-executes unless cache cleared.\nlocal mod = require(\"mymod\")         -- searches package.path\npackage.loaded[\"mymod\"] = nil       -- evict from cache\nlocal mod2 = require(\"mymod\")       -- re-executes the chunk\n",[19,359,360,365,384,398,417,422,427,435,439,444,460,464,469,474,497,518],{"__ignoreMap":41},[45,361,362],{"class":47,"line":48},[45,363,364],{"class":62},"-- loadfile: compile a file without executing. nil + error message on failure.\n",[45,366,367,369,372,374,377,379,382],{"class":47,"line":66},[45,368,175],{"class":174},[45,370,371],{"class":178}," fn, err ",[45,373,182],{"class":174},[45,375,376],{"class":88}," loadfile",[45,378,188],{"class":178},[45,380,381],{"class":55},"\"plugin.lua\"",[45,383,216],{"class":178},[45,385,386,389,392,395],{"class":47,"line":83},[45,387,388],{"class":174},"if",[45,390,391],{"class":174}," not",[45,393,394],{"class":178}," fn ",[45,396,397],{"class":174},"then\n",[45,399,400,403,405,408,411,414],{"class":47,"line":95},[45,401,402],{"class":88},"  error",[45,404,188],{"class":178},[45,406,407],{"class":55},"\"Failed to compile plugin: \" ",[45,409,410],{"class":174},"..",[45,412,413],{"class":178}," err)   ",[45,415,416],{"class":62},"-- err contains line:col from parser\n",[45,418,419],{"class":47,"line":102},[45,420,421],{"class":174},"end\n",[45,423,424],{"class":47,"line":204},[45,425,426],{"class":62},"-- fn is the chunk's main function; call it when ready (deferred execution)\n",[45,428,429,432],{"class":47,"line":210},[45,430,431],{"class":88},"fn",[45,433,434],{"class":178},"()\n",[45,436,437],{"class":47,"line":219},[45,438,99],{"emptyLinePlaceholder":98},[45,440,441],{"class":47,"line":224},[45,442,443],{"class":62},"-- dofile: compile + execute immediately. No error handling — crashes on failure.\n",[45,445,446,449,451,454,457],{"class":47,"line":230},[45,447,448],{"class":88},"dofile",[45,450,188],{"class":178},[45,452,453],{"class":55},"\"config.lua\"",[45,455,456],{"class":178},")    ",[45,458,459],{"class":62},"-- returns the chunk's value (usually discarded)\n",[45,461,462],{"class":47,"line":249},[45,463,99],{"emptyLinePlaceholder":98},[45,465,466],{"class":47,"line":261},[45,467,468],{"class":62},"-- require: compile + execute + cache in package.loaded[name]\n",[45,470,471],{"class":47,"line":272},[45,472,473],{"class":62},"-- Returns cached value on subsequent calls; never re-executes unless cache cleared.\n",[45,475,476,478,481,483,486,488,491,494],{"class":47,"line":277},[45,477,175],{"class":174},[45,479,480],{"class":178}," mod ",[45,482,182],{"class":174},[45,484,485],{"class":88}," require",[45,487,188],{"class":178},[45,489,490],{"class":55},"\"mymod\"",[45,492,493],{"class":178},")         ",[45,495,496],{"class":62},"-- searches package.path\n",[45,498,499,502,505,507,510,512,515],{"class":47,"line":283},[45,500,501],{"class":88},"package.loaded",[45,503,504],{"class":178},"[",[45,506,490],{"class":55},[45,508,509],{"class":178},"] ",[45,511,182],{"class":174},[45,513,514],{"class":88}," nil",[45,516,517],{"class":62},"       -- evict from cache\n",[45,519,520,522,525,527,529,531,533,536],{"class":47,"line":312},[45,521,175],{"class":174},[45,523,524],{"class":178}," mod2 ",[45,526,182],{"class":174},[45,528,485],{"class":88},[45,530,188],{"class":178},[45,532,490],{"class":55},[45,534,535],{"class":178},")       ",[45,537,538],{"class":62},"-- re-executes the chunk\n",[540,541,543,544,546],"h3",{"id":542},"anti-pattern-dofile-in-production","Anti-pattern: ",[19,545,448],{}," in production",[33,548,549],{"language":25},[37,550,552],{"className":156,"code":551,"language":25,"meta":41,"style":41},"-- BAD: dofile has no error handling. A syntax error or runtime error crashes\n-- the entire host process. No way to catch, no way to log.\ndofile(user_script)         -- if user_script has a typo → uncaught error → process dies\n\n-- GOOD: loadfile + pcall gives compile-time and run-time isolation\nlocal fn, compile_err = loadfile(user_script)\nif not fn then\n  log_error(\"Compile failed: \" .. compile_err)\n  return\nend\nlocal ok, runtime_err = pcall(fn)\nif not ok then\n  log_error(\"Runtime: \" .. runtime_err)   -- full error, process survives\nend\n",[19,553,554,559,564,574,578,583,597,607,622,627,631,645,656,673],{"__ignoreMap":41},[45,555,556],{"class":47,"line":48},[45,557,558],{"class":62},"-- BAD: dofile has no error handling. A syntax error or runtime error crashes\n",[45,560,561],{"class":47,"line":66},[45,562,563],{"class":62},"-- the entire host process. No way to catch, no way to log.\n",[45,565,566,568,571],{"class":47,"line":83},[45,567,448],{"class":88},[45,569,570],{"class":178},"(user_script)         ",[45,572,573],{"class":62},"-- if user_script has a typo → uncaught error → process dies\n",[45,575,576],{"class":47,"line":95},[45,577,99],{"emptyLinePlaceholder":98},[45,579,580],{"class":47,"line":102},[45,581,582],{"class":62},"-- GOOD: loadfile + pcall gives compile-time and run-time isolation\n",[45,584,585,587,590,592,594],{"class":47,"line":204},[45,586,175],{"class":174},[45,588,589],{"class":178}," fn, compile_err ",[45,591,182],{"class":174},[45,593,376],{"class":88},[45,595,596],{"class":178},"(user_script)\n",[45,598,599,601,603,605],{"class":47,"line":210},[45,600,388],{"class":174},[45,602,391],{"class":174},[45,604,394],{"class":178},[45,606,397],{"class":174},[45,608,609,612,614,617,619],{"class":47,"line":219},[45,610,611],{"class":88},"  log_error",[45,613,188],{"class":178},[45,615,616],{"class":55},"\"Compile failed: \" ",[45,618,410],{"class":174},[45,620,621],{"class":178}," compile_err)\n",[45,623,624],{"class":47,"line":224},[45,625,626],{"class":174},"  return\n",[45,628,629],{"class":47,"line":230},[45,630,421],{"class":174},[45,632,633,635,638,640,642],{"class":47,"line":249},[45,634,175],{"class":174},[45,636,637],{"class":178}," ok, runtime_err ",[45,639,182],{"class":174},[45,641,240],{"class":88},[45,643,644],{"class":178},"(fn)\n",[45,646,647,649,651,654],{"class":47,"line":261},[45,648,388],{"class":174},[45,650,391],{"class":174},[45,652,653],{"class":178}," ok ",[45,655,397],{"class":174},[45,657,658,660,662,665,667,670],{"class":47,"line":272},[45,659,611],{"class":88},[45,661,188],{"class":178},[45,663,664],{"class":55},"\"Runtime: \" ",[45,666,410],{"class":174},[45,668,669],{"class":178}," runtime_err)   ",[45,671,672],{"class":62},"-- full error, process survives\n",[45,674,675],{"class":47,"line":277},[45,676,421],{"class":174},[28,678,680,681,684],{"id":679},"the-registry-global-table-_g","The Registry & Global Table (",[19,682,683],{},"_G",")",[15,686,687,688,691,692,695,696,698,699,702],{},"Every Lua state has a ",[147,689,690],{},"registry"," — a hidden table accessible from C via ",[19,693,694],{},"LUA_REGISTRYINDEX",". The global table ",[19,697,683],{}," is stored in the registry under the key ",[19,700,701],{},"LUA_RIDX_GLOBALS",".",[33,704,705],{"language":25},[37,706,708],{"className":156,"code":707,"language":25,"meta":41,"style":41},"-- _G is just a regular table. All globals are entries in it.\nx = 42\nprint(_G.x)                    -- 42\nprint(rawget(_G, \"x\"))          -- 42  (rawget bypasses __index metamethod)\n\n-- _G is itself accessible from _G (circular reference)\nprint(_G._G._G == _G)           -- true\n\n-- Enumerate all globals (note: includes standard library tables)\nfor name in pairs(_G) do\n  print(name)                   -- print, string, table, math, io, os, _G, ...\nend\n\n-- Production: lock down globals to catch typos at runtime\nsetmetatable(_G, {\n  __index = function(t, k)\n    error(\"attempt to read undefined global: \" .. k, 2)\n  end,\n  __newindex = function(t, k, v)\n    error(\"attempt to create global: \" .. k .. \" (use local)\", 2)\n  end,\n})\n",[19,709,710,715,725,744,768,772,777,805,809,814,838,849,853,857,862,874,888,908,917,930,956,963],{"__ignoreMap":41},[45,711,712],{"class":47,"line":48},[45,713,714],{"class":62},"-- _G is just a regular table. All globals are entries in it.\n",[45,716,717,720,722],{"class":47,"line":66},[45,718,719],{"class":178},"x ",[45,721,182],{"class":174},[45,723,724],{"class":88}," 42\n",[45,726,727,729,731,733,735,738,741],{"class":47,"line":83},[45,728,252],{"class":88},[45,730,188],{"class":178},[45,732,683],{"class":88},[45,734,702],{"class":178},[45,736,737],{"class":51},"x",[45,739,740],{"class":178},")                    ",[45,742,743],{"class":62},"-- 42\n",[45,745,746,748,750,753,755,757,759,762,765],{"class":47,"line":95},[45,747,252],{"class":88},[45,749,188],{"class":178},[45,751,752],{"class":88},"rawget",[45,754,188],{"class":178},[45,756,683],{"class":88},[45,758,329],{"class":178},[45,760,761],{"class":55},"\"x\"",[45,763,764],{"class":178},"))          ",[45,766,767],{"class":62},"-- 42  (rawget bypasses __index metamethod)\n",[45,769,770],{"class":47,"line":102},[45,771,99],{"emptyLinePlaceholder":98},[45,773,774],{"class":47,"line":204},[45,775,776],{"class":62},"-- _G is itself accessible from _G (circular reference)\n",[45,778,779,781,783,785,787,789,791,793,796,799,802],{"class":47,"line":210},[45,780,252],{"class":88},[45,782,188],{"class":178},[45,784,683],{"class":88},[45,786,702],{"class":178},[45,788,683],{"class":51},[45,790,702],{"class":178},[45,792,683],{"class":51},[45,794,795],{"class":174}," ==",[45,797,798],{"class":88}," _G",[45,800,801],{"class":178},")           ",[45,803,804],{"class":62},"-- true\n",[45,806,807],{"class":47,"line":219},[45,808,99],{"emptyLinePlaceholder":98},[45,810,811],{"class":47,"line":224},[45,812,813],{"class":62},"-- Enumerate all globals (note: includes standard library tables)\n",[45,815,816,819,822,825,828,830,832,835],{"class":47,"line":230},[45,817,818],{"class":174},"for",[45,820,821],{"class":178}," name ",[45,823,824],{"class":174},"in",[45,826,827],{"class":88}," pairs",[45,829,188],{"class":178},[45,831,683],{"class":88},[45,833,834],{"class":178},") ",[45,836,837],{"class":174},"do\n",[45,839,840,843,846],{"class":47,"line":249},[45,841,842],{"class":88},"  print",[45,844,845],{"class":178},"(name)                   ",[45,847,848],{"class":62},"-- print, string, table, math, io, os, _G, ...\n",[45,850,851],{"class":47,"line":261},[45,852,421],{"class":174},[45,854,855],{"class":47,"line":272},[45,856,99],{"emptyLinePlaceholder":98},[45,858,859],{"class":47,"line":277},[45,860,861],{"class":62},"-- Production: lock down globals to catch typos at runtime\n",[45,863,864,867,869,871],{"class":47,"line":283},[45,865,866],{"class":88},"setmetatable",[45,868,188],{"class":178},[45,870,683],{"class":88},[45,872,873],{"class":178},", {\n",[45,875,876,879,882,885],{"class":47,"line":312},[45,877,878],{"class":51},"  __index",[45,880,881],{"class":174}," =",[45,883,884],{"class":174}," function",[45,886,887],{"class":178},"(t, k)\n",[45,889,890,893,895,898,900,903,906],{"class":47,"line":343},[45,891,892],{"class":88},"    error",[45,894,188],{"class":178},[45,896,897],{"class":55},"\"attempt to read undefined global: \" ",[45,899,410],{"class":174},[45,901,902],{"class":178}," k, ",[45,904,905],{"class":88},"2",[45,907,216],{"class":178},[45,909,911,914],{"class":47,"line":910},18,[45,912,913],{"class":174},"  end",[45,915,916],{"class":178},",\n",[45,918,920,923,925,927],{"class":47,"line":919},19,[45,921,922],{"class":51},"  __newindex",[45,924,881],{"class":174},[45,926,884],{"class":174},[45,928,929],{"class":178},"(t, k, v)\n",[45,931,933,935,937,940,942,945,947,950,952,954],{"class":47,"line":932},20,[45,934,892],{"class":88},[45,936,188],{"class":178},[45,938,939],{"class":55},"\"attempt to create global: \" ",[45,941,410],{"class":174},[45,943,944],{"class":178}," k ",[45,946,410],{"class":174},[45,948,949],{"class":55}," \" (use local)\"",[45,951,329],{"class":178},[45,953,905],{"class":88},[45,955,216],{"class":178},[45,957,959,961],{"class":47,"line":958},21,[45,960,913],{"class":174},[45,962,916],{"class":178},[45,964,966],{"class":47,"line":965},22,[45,967,968],{"class":178},"})\n",[28,970,972],{"id":971},"standard-library-map","Standard Library Map",[974,975,976,992],"table",{},[977,978,979],"thead",{},[980,981,982,986,989],"tr",{},[983,984,985],"th",{},"Library",[983,987,988],{},"Key Functions",[983,990,991],{},"When to Use",[993,994,995,1026,1058,1088,1118,1148,1175,1202,1228],"tbody",{},[980,996,997,1003,1023],{},[998,999,1000],"td",{},[19,1001,1002],{},"string",[998,1004,1005,329,1008,329,1011,329,1014,329,1017,329,1020],{},[19,1006,1007],{},"match",[19,1009,1010],{},"gsub",[19,1012,1013],{},"gmatch",[19,1015,1016],{},"format",[19,1018,1019],{},"byte",[19,1021,1022],{},"char",[998,1024,1025],{},"Pattern matching, formatting",[980,1027,1028,1032,1055],{},[998,1029,1030],{},[19,1031,974],{},[998,1033,1034,329,1037,329,1040,329,1043,329,1046,329,1049,329,1052],{},[19,1035,1036],{},"insert",[19,1038,1039],{},"remove",[19,1041,1042],{},"concat",[19,1044,1045],{},"sort",[19,1047,1048],{},"move",[19,1050,1051],{},"pack",[19,1053,1054],{},"unpack",[998,1056,1057],{},"Array\u002Fdict manipulation",[980,1059,1060,1065,1085],{},[998,1061,1062],{},[19,1063,1064],{},"math",[998,1066,1067,329,1070,329,1073,329,1076,329,1079,329,1082],{},[19,1068,1069],{},"random",[19,1071,1072],{},"randomseed",[19,1074,1075],{},"floor",[19,1077,1078],{},"ceil",[19,1080,1081],{},"huge",[19,1083,1084],{},"maxinteger",[998,1086,1087],{},"Math operations",[980,1089,1090,1095,1115],{},[998,1091,1092],{},[19,1093,1094],{},"io",[998,1096,1097,329,1100,329,1103,329,1106,329,1109,329,1112],{},[19,1098,1099],{},"open",[19,1101,1102],{},"read",[19,1104,1105],{},"write",[19,1107,1108],{},"lines",[19,1110,1111],{},"popen",[19,1113,1114],{},"stderr",[998,1116,1117],{},"File I\u002FO",[980,1119,1120,1125,1145],{},[998,1121,1122],{},[19,1123,1124],{},"os",[998,1126,1127,329,1130,329,1133,329,1136,329,1139,329,1142],{},[19,1128,1129],{},"time",[19,1131,1132],{},"date",[19,1134,1135],{},"clock",[19,1137,1138],{},"exit",[19,1140,1141],{},"getenv",[19,1143,1144],{},"execute",[998,1146,1147],{},"OS interface",[980,1149,1150,1155,1172],{},[998,1151,1152],{},[19,1153,1154],{},"debug",[998,1156,1157,329,1160,329,1163,329,1166,329,1169],{},[19,1158,1159],{},"traceback",[19,1161,1162],{},"getinfo",[19,1164,1165],{},"getlocal",[19,1167,1168],{},"setlocal",[19,1170,1171],{},"sethook",[998,1173,1174],{},"Introspection, profiling",[980,1176,1177,1182,1199],{},[998,1178,1179],{},[19,1180,1181],{},"coroutine",[998,1183,1184,329,1187,329,1190,329,1193,329,1196],{},[19,1185,1186],{},"create",[19,1188,1189],{},"resume",[19,1191,1192],{},"yield",[19,1194,1195],{},"status",[19,1197,1198],{},"wrap",[998,1200,1201],{},"Cooperative multitasking",[980,1203,1204,1209,1225],{},[998,1205,1206],{},[19,1207,1208],{},"utf8",[998,1210,1211,329,1214,329,1217,329,1220,329,1222],{},[19,1212,1213],{},"codes",[19,1215,1216],{},"codepoint",[19,1218,1219],{},"len",[19,1221,1022],{},[19,1223,1224],{},"offset",[998,1226,1227],{},"UTF-8 (Lua 5.3+)",[980,1229,1230,1235,1252],{},[998,1231,1232],{},[19,1233,1234],{},"package",[998,1236,1237,329,1240,329,1243,329,1246,329,1249],{},[19,1238,1239],{},"loaded",[19,1241,1242],{},"path",[19,1244,1245],{},"cpath",[19,1247,1248],{},"searchpath",[19,1250,1251],{},"preload",[998,1253,1254],{},"Module system",[33,1256,1257],{"language":25},[37,1258,1260],{"className":156,"code":1259,"language":25,"meta":41,"style":41},"-- Localize hot-path standard library functions: global lookup → local upvalue\n-- This is the single most impactful micro-optimization in Lua.\nlocal sformat = string.format    -- 3x faster than string.format on hot paths\nlocal tinsert  = table.insert    -- avoids _G.table.insert chain per call\nlocal mrandom = math.random\n\nlocal function generate_uuid()\n  return sformat(\"%04x%04x\", mrandom(0, 0xFFFF), mrandom(0, 0xFFFF))\nend\n",[19,1261,1262,1267,1272,1287,1302,1314,1318,1329,1373],{"__ignoreMap":41},[45,1263,1264],{"class":47,"line":48},[45,1265,1266],{"class":62},"-- Localize hot-path standard library functions: global lookup → local upvalue\n",[45,1268,1269],{"class":47,"line":66},[45,1270,1271],{"class":62},"-- This is the single most impactful micro-optimization in Lua.\n",[45,1273,1274,1276,1279,1281,1284],{"class":47,"line":83},[45,1275,175],{"class":174},[45,1277,1278],{"class":178}," sformat ",[45,1280,182],{"class":174},[45,1282,1283],{"class":88}," string.format",[45,1285,1286],{"class":62},"    -- 3x faster than string.format on hot paths\n",[45,1288,1289,1291,1294,1296,1299],{"class":47,"line":95},[45,1290,175],{"class":174},[45,1292,1293],{"class":178}," tinsert  ",[45,1295,182],{"class":174},[45,1297,1298],{"class":88}," table.insert",[45,1300,1301],{"class":62},"    -- avoids _G.table.insert chain per call\n",[45,1303,1304,1306,1309,1311],{"class":47,"line":102},[45,1305,175],{"class":174},[45,1307,1308],{"class":178}," mrandom ",[45,1310,182],{"class":174},[45,1312,1313],{"class":88}," math.random\n",[45,1315,1316],{"class":47,"line":204},[45,1317,99],{"emptyLinePlaceholder":98},[45,1319,1320,1322,1324,1327],{"class":47,"line":210},[45,1321,175],{"class":174},[45,1323,884],{"class":174},[45,1325,1326],{"class":51}," generate_uuid",[45,1328,434],{"class":178},[45,1330,1331,1334,1337,1339,1342,1344,1347,1349,1352,1354,1357,1360,1362,1364,1366,1368,1370],{"class":47,"line":219},[45,1332,1333],{"class":174},"  return",[45,1335,1336],{"class":88}," sformat",[45,1338,188],{"class":178},[45,1340,1341],{"class":55},"\"%04x%04x\"",[45,1343,329],{"class":178},[45,1345,1346],{"class":88},"mrandom",[45,1348,188],{"class":178},[45,1350,1351],{"class":88},"0",[45,1353,329],{"class":178},[45,1355,1356],{"class":88},"0xFFFF",[45,1358,1359],{"class":178},"), ",[45,1361,1346],{"class":88},[45,1363,188],{"class":178},[45,1365,1351],{"class":88},[45,1367,329],{"class":178},[45,1369,1356],{"class":88},[45,1371,1372],{"class":178},"))\n",[45,1374,1375],{"class":47,"line":224},[45,1376,421],{"class":174},[28,1378,1380,1381,1384],{"id":1379},"the-arg-table-command-line-interface","The ",[19,1382,1383],{},"arg"," Table & Command-Line Interface",[33,1386,1388],{"language":25,"filename":1387},"cli.lua",[37,1389,1391],{"className":156,"code":1390,"language":25,"meta":41,"style":41},"-- arg[0] = script name, arg[1..n] = command-line args, arg[-n..-1] = interpreter args\nprint(arg[0])          -- \"cli.lua\" (script path)\nprint(arg[-1])        -- \"lua\" (interpreter name, when invoked as `lua cli.lua`)\n\n-- Production argument parser: no external deps, handles flags + values\nlocal function parse_args(args)\n  local opts, positional = {}, {}\n  local i = 1\n  while i \u003C= #args do\n    local arg = args[i]\n    if arg:sub(1, 2) == \"--\" then\n      local name, value = arg:match(\"^%-%-([%w_-]+)=?(.*)$\")\n      if value == \"\" and args[i + 1] and not args[i + 1]:match(\"^%-\") then\n        value = args[i + 1]; i = i + 1     -- --flag value\n      else\n        value = value == \"\" and true or value  -- --flag (boolean) or --flag=val\n      end\n      opts[name] = value\n    else\n      positional[#positional + 1] = arg\n    end\n    i = i + 1\n  end\n  return opts, positional\nend\n\nlocal opts, args_rest = parse_args(arg)\nprint(opts.verbose, opts.output, args_rest[1])\n-- $ lua cli.lua --verbose --output=result.txt input.txt\n-- true    result.txt    input.txt\n",[19,1392,1393,1398,1413,1431,1435,1440,1452,1465,1477,1495,1508,1540,1563,1614,1641,1646,1672,1677,1687,1692,1714,1719,1732,1738,1746,1751,1756,1771,1796,1802],{"__ignoreMap":41},[45,1394,1395],{"class":47,"line":48},[45,1396,1397],{"class":62},"-- arg[0] = script name, arg[1..n] = command-line args, arg[-n..-1] = interpreter args\n",[45,1399,1400,1402,1405,1407,1410],{"class":47,"line":66},[45,1401,252],{"class":88},[45,1403,1404],{"class":178},"(arg[",[45,1406,1351],{"class":88},[45,1408,1409],{"class":178},"])          ",[45,1411,1412],{"class":62},"-- \"cli.lua\" (script path)\n",[45,1414,1415,1417,1419,1422,1425,1428],{"class":47,"line":83},[45,1416,252],{"class":88},[45,1418,1404],{"class":178},[45,1420,1421],{"class":174},"-",[45,1423,1424],{"class":88},"1",[45,1426,1427],{"class":178},"])        ",[45,1429,1430],{"class":62},"-- \"lua\" (interpreter name, when invoked as `lua cli.lua`)\n",[45,1432,1433],{"class":47,"line":95},[45,1434,99],{"emptyLinePlaceholder":98},[45,1436,1437],{"class":47,"line":102},[45,1438,1439],{"class":62},"-- Production argument parser: no external deps, handles flags + values\n",[45,1441,1442,1444,1446,1449],{"class":47,"line":204},[45,1443,175],{"class":174},[45,1445,884],{"class":174},[45,1447,1448],{"class":51}," parse_args",[45,1450,1451],{"class":178},"(args)\n",[45,1453,1454,1457,1460,1462],{"class":47,"line":210},[45,1455,1456],{"class":174},"  local",[45,1458,1459],{"class":178}," opts, positional ",[45,1461,182],{"class":174},[45,1463,1464],{"class":178}," {}, {}\n",[45,1466,1467,1469,1472,1474],{"class":47,"line":219},[45,1468,1456],{"class":174},[45,1470,1471],{"class":178}," i ",[45,1473,182],{"class":174},[45,1475,1476],{"class":88}," 1\n",[45,1478,1479,1482,1484,1487,1490,1493],{"class":47,"line":224},[45,1480,1481],{"class":174},"  while",[45,1483,1471],{"class":178},[45,1485,1486],{"class":174},"\u003C=",[45,1488,1489],{"class":174}," #",[45,1491,1492],{"class":178},"args ",[45,1494,837],{"class":174},[45,1496,1497,1500,1503,1505],{"class":47,"line":230},[45,1498,1499],{"class":174},"    local",[45,1501,1502],{"class":178}," arg ",[45,1504,182],{"class":174},[45,1506,1507],{"class":178}," args[i]\n",[45,1509,1510,1513,1516,1519,1522,1524,1526,1528,1530,1532,1535,1538],{"class":47,"line":249},[45,1511,1512],{"class":174},"    if",[45,1514,1515],{"class":51}," arg",[45,1517,1518],{"class":178},":",[45,1520,1521],{"class":88},"sub",[45,1523,188],{"class":178},[45,1525,1424],{"class":88},[45,1527,329],{"class":178},[45,1529,905],{"class":88},[45,1531,834],{"class":178},[45,1533,1534],{"class":174},"==",[45,1536,1537],{"class":55}," \"--\" ",[45,1539,397],{"class":174},[45,1541,1542,1545,1548,1550,1552,1554,1556,1558,1561],{"class":47,"line":261},[45,1543,1544],{"class":174},"      local",[45,1546,1547],{"class":178}," name, value ",[45,1549,182],{"class":174},[45,1551,1515],{"class":51},[45,1553,1518],{"class":178},[45,1555,1007],{"class":88},[45,1557,188],{"class":178},[45,1559,1560],{"class":55},"\"^%-%-([%w_-]+)=?(.*)$\"",[45,1562,216],{"class":178},[45,1564,1565,1568,1571,1573,1576,1579,1582,1585,1588,1590,1592,1594,1596,1598,1600,1603,1605,1607,1610,1612],{"class":47,"line":272},[45,1566,1567],{"class":174},"      if",[45,1569,1570],{"class":178}," value ",[45,1572,1534],{"class":174},[45,1574,1575],{"class":55}," \"\" ",[45,1577,1578],{"class":174},"and",[45,1580,1581],{"class":178}," args[i ",[45,1583,1584],{"class":174},"+",[45,1586,1587],{"class":88}," 1",[45,1589,509],{"class":178},[45,1591,1578],{"class":174},[45,1593,391],{"class":174},[45,1595,1581],{"class":178},[45,1597,1584],{"class":174},[45,1599,1587],{"class":88},[45,1601,1602],{"class":178},"]:",[45,1604,1007],{"class":88},[45,1606,188],{"class":178},[45,1608,1609],{"class":55},"\"^%-\"",[45,1611,834],{"class":178},[45,1613,397],{"class":174},[45,1615,1616,1619,1621,1623,1625,1627,1630,1632,1634,1636,1638],{"class":47,"line":277},[45,1617,1618],{"class":178},"        value ",[45,1620,182],{"class":174},[45,1622,1581],{"class":178},[45,1624,1584],{"class":174},[45,1626,1587],{"class":88},[45,1628,1629],{"class":178},"]; i ",[45,1631,182],{"class":174},[45,1633,1471],{"class":178},[45,1635,1584],{"class":174},[45,1637,1587],{"class":88},[45,1639,1640],{"class":62},"     -- --flag value\n",[45,1642,1643],{"class":47,"line":283},[45,1644,1645],{"class":174},"      else\n",[45,1647,1648,1650,1652,1654,1656,1658,1660,1663,1666,1669],{"class":47,"line":312},[45,1649,1618],{"class":178},[45,1651,182],{"class":174},[45,1653,1570],{"class":178},[45,1655,1534],{"class":174},[45,1657,1575],{"class":55},[45,1659,1578],{"class":174},[45,1661,1662],{"class":88}," true",[45,1664,1665],{"class":174}," or",[45,1667,1668],{"class":178}," value  ",[45,1670,1671],{"class":62},"-- --flag (boolean) or --flag=val\n",[45,1673,1674],{"class":47,"line":343},[45,1675,1676],{"class":174},"      end\n",[45,1678,1679,1682,1684],{"class":47,"line":910},[45,1680,1681],{"class":178},"      opts[name] ",[45,1683,182],{"class":174},[45,1685,1686],{"class":178}," value\n",[45,1688,1689],{"class":47,"line":919},[45,1690,1691],{"class":174},"    else\n",[45,1693,1694,1697,1700,1703,1705,1707,1709,1711],{"class":47,"line":932},[45,1695,1696],{"class":178},"      positional[",[45,1698,1699],{"class":174},"#",[45,1701,1702],{"class":178},"positional ",[45,1704,1584],{"class":174},[45,1706,1587],{"class":88},[45,1708,509],{"class":178},[45,1710,182],{"class":174},[45,1712,1713],{"class":178}," arg\n",[45,1715,1716],{"class":47,"line":958},[45,1717,1718],{"class":174},"    end\n",[45,1720,1721,1724,1726,1728,1730],{"class":47,"line":965},[45,1722,1723],{"class":178},"    i ",[45,1725,182],{"class":174},[45,1727,1471],{"class":178},[45,1729,1584],{"class":174},[45,1731,1476],{"class":88},[45,1733,1735],{"class":47,"line":1734},23,[45,1736,1737],{"class":174},"  end\n",[45,1739,1741,1743],{"class":47,"line":1740},24,[45,1742,1333],{"class":174},[45,1744,1745],{"class":178}," opts, positional\n",[45,1747,1749],{"class":47,"line":1748},25,[45,1750,421],{"class":174},[45,1752,1754],{"class":47,"line":1753},26,[45,1755,99],{"emptyLinePlaceholder":98},[45,1757,1759,1761,1764,1766,1768],{"class":47,"line":1758},27,[45,1760,175],{"class":174},[45,1762,1763],{"class":178}," opts, args_rest ",[45,1765,182],{"class":174},[45,1767,1448],{"class":88},[45,1769,1770],{"class":178},"(arg)\n",[45,1772,1774,1776,1779,1782,1785,1788,1791,1793],{"class":47,"line":1773},28,[45,1775,252],{"class":88},[45,1777,1778],{"class":178},"(opts.",[45,1780,1781],{"class":51},"verbose",[45,1783,1784],{"class":178},", opts.",[45,1786,1787],{"class":51},"output",[45,1789,1790],{"class":178},", args_rest[",[45,1792,1424],{"class":88},[45,1794,1795],{"class":178},"])\n",[45,1797,1799],{"class":47,"line":1798},29,[45,1800,1801],{"class":62},"-- $ lua cli.lua --verbose --output=result.txt input.txt\n",[45,1803,1805],{"class":47,"line":1804},30,[45,1806,1807],{"class":62},"-- true    result.txt    input.txt\n",[28,1809,1811],{"id":1810},"embedding-boundary-lua-state-as-a-sandbox","Embedding Boundary: Lua State as a Sandbox",[33,1813,1814],{"language":25},[37,1815,1817],{"className":156,"code":1816,"language":25,"meta":41,"style":41},"-- load() with a custom environment creates a sandboxed execution context.\n-- The chunk can ONLY access what's in the env table. This is how game engines,\n-- Neovim, and Redis isolate untrusted scripts.\n\nlocal function create_sandbox()\n  local env = {}\n  -- Whitelist safe standard library functions\n  env.print    = print\n  env.pairs    = pairs\n  env.ipairs   = ipairs\n  env.tostring = tostring\n  env.tonumber = tonumber\n  env.type     = type\n  env.error    = error\n  env.assert   = assert\n  env.select   = select\n  env.unpack   = table.unpack\n  env.string   = { format = string.format, sub = string.sub, len = string.len }\n  env.math     = { floor = math.floor, ceil = math.ceil, random = math.random }\n  env.table    = { insert = table.insert, remove = table.remove, concat = table.concat }\n  -- NO io, os, debug, require, loadfile, dofile, load — cannot escape\n  return env\nend\n\nlocal function run_untrusted(code_str)\n  local fn, err = load(code_str, \"user_code\", \"t\", create_sandbox())\n  if not fn then return false, \"compile: \" .. err end\n  local ok, result = pcall(fn)\n  if not ok then return false, \"runtime: \" .. result end\n  return true, result\nend\n\n-- Even if the user code tries:  os.execute(\"rm -rf \u002F\")\n-- → attempt to index nil value (global 'os')  → caught, sandbox holds\n",[19,1818,1819,1824,1829,1834,1838,1849,1860,1865,1878,1890,1903,1915,1927,1940,1952,1964,1976,1987,2021,2055,2088,2093,2100,2104,2108,2120,2148,2178,2190,2216,2225,2230,2235,2241],{"__ignoreMap":41},[45,1820,1821],{"class":47,"line":48},[45,1822,1823],{"class":62},"-- load() with a custom environment creates a sandboxed execution context.\n",[45,1825,1826],{"class":47,"line":66},[45,1827,1828],{"class":62},"-- The chunk can ONLY access what's in the env table. This is how game engines,\n",[45,1830,1831],{"class":47,"line":83},[45,1832,1833],{"class":62},"-- Neovim, and Redis isolate untrusted scripts.\n",[45,1835,1836],{"class":47,"line":95},[45,1837,99],{"emptyLinePlaceholder":98},[45,1839,1840,1842,1844,1847],{"class":47,"line":102},[45,1841,175],{"class":174},[45,1843,884],{"class":174},[45,1845,1846],{"class":51}," create_sandbox",[45,1848,434],{"class":178},[45,1850,1851,1853,1855,1857],{"class":47,"line":204},[45,1852,1456],{"class":174},[45,1854,288],{"class":178},[45,1856,182],{"class":174},[45,1858,1859],{"class":178}," {}\n",[45,1861,1862],{"class":47,"line":210},[45,1863,1864],{"class":62},"  -- Whitelist safe standard library functions\n",[45,1866,1867,1870,1872,1875],{"class":47,"line":219},[45,1868,1869],{"class":178},"  env.",[45,1871,252],{"class":51},[45,1873,1874],{"class":174},"    =",[45,1876,1877],{"class":88}," print\n",[45,1879,1880,1882,1885,1887],{"class":47,"line":224},[45,1881,1869],{"class":178},[45,1883,1884],{"class":51},"pairs",[45,1886,1874],{"class":174},[45,1888,1889],{"class":88}," pairs\n",[45,1891,1892,1894,1897,1900],{"class":47,"line":230},[45,1893,1869],{"class":178},[45,1895,1896],{"class":51},"ipairs",[45,1898,1899],{"class":174},"   =",[45,1901,1902],{"class":88}," ipairs\n",[45,1904,1905,1907,1910,1912],{"class":47,"line":249},[45,1906,1869],{"class":178},[45,1908,1909],{"class":51},"tostring",[45,1911,881],{"class":174},[45,1913,1914],{"class":88}," tostring\n",[45,1916,1917,1919,1922,1924],{"class":47,"line":261},[45,1918,1869],{"class":178},[45,1920,1921],{"class":51},"tonumber",[45,1923,881],{"class":174},[45,1925,1926],{"class":88}," tonumber\n",[45,1928,1929,1931,1934,1937],{"class":47,"line":272},[45,1930,1869],{"class":178},[45,1932,1933],{"class":51},"type",[45,1935,1936],{"class":174},"     =",[45,1938,1939],{"class":88}," type\n",[45,1941,1942,1944,1947,1949],{"class":47,"line":277},[45,1943,1869],{"class":178},[45,1945,1946],{"class":51},"error",[45,1948,1874],{"class":174},[45,1950,1951],{"class":88}," error\n",[45,1953,1954,1956,1959,1961],{"class":47,"line":283},[45,1955,1869],{"class":178},[45,1957,1958],{"class":51},"assert",[45,1960,1899],{"class":174},[45,1962,1963],{"class":88}," assert\n",[45,1965,1966,1968,1971,1973],{"class":47,"line":312},[45,1967,1869],{"class":178},[45,1969,1970],{"class":51},"select",[45,1972,1899],{"class":174},[45,1974,1975],{"class":88}," select\n",[45,1977,1978,1980,1982,1984],{"class":47,"line":343},[45,1979,1869],{"class":178},[45,1981,1054],{"class":51},[45,1983,1899],{"class":174},[45,1985,1986],{"class":88}," table.unpack\n",[45,1988,1989,1991,1993,1995,1998,2000,2002,2005,2007,2010,2013,2015,2018],{"class":47,"line":910},[45,1990,1869],{"class":178},[45,1992,1002],{"class":51},[45,1994,1899],{"class":174},[45,1996,1997],{"class":178}," { format ",[45,1999,182],{"class":174},[45,2001,1283],{"class":88},[45,2003,2004],{"class":178},", sub ",[45,2006,182],{"class":174},[45,2008,2009],{"class":88}," string.sub",[45,2011,2012],{"class":178},", len ",[45,2014,182],{"class":174},[45,2016,2017],{"class":88}," string.len",[45,2019,2020],{"class":178}," }\n",[45,2022,2023,2025,2027,2029,2032,2034,2037,2040,2042,2045,2048,2050,2053],{"class":47,"line":919},[45,2024,1869],{"class":178},[45,2026,1064],{"class":51},[45,2028,1936],{"class":174},[45,2030,2031],{"class":178}," { floor ",[45,2033,182],{"class":174},[45,2035,2036],{"class":88}," math.floor",[45,2038,2039],{"class":178},", ceil ",[45,2041,182],{"class":174},[45,2043,2044],{"class":88}," math.ceil",[45,2046,2047],{"class":178},", random ",[45,2049,182],{"class":174},[45,2051,2052],{"class":88}," math.random",[45,2054,2020],{"class":178},[45,2056,2057,2059,2061,2063,2066,2068,2070,2073,2075,2078,2081,2083,2086],{"class":47,"line":932},[45,2058,1869],{"class":178},[45,2060,974],{"class":51},[45,2062,1874],{"class":174},[45,2064,2065],{"class":178}," { insert ",[45,2067,182],{"class":174},[45,2069,1298],{"class":88},[45,2071,2072],{"class":178},", remove ",[45,2074,182],{"class":174},[45,2076,2077],{"class":88}," table.remove",[45,2079,2080],{"class":178},", concat ",[45,2082,182],{"class":174},[45,2084,2085],{"class":88}," table.concat",[45,2087,2020],{"class":178},[45,2089,2090],{"class":47,"line":958},[45,2091,2092],{"class":62},"  -- NO io, os, debug, require, loadfile, dofile, load — cannot escape\n",[45,2094,2095,2097],{"class":47,"line":965},[45,2096,1333],{"class":174},[45,2098,2099],{"class":178}," env\n",[45,2101,2102],{"class":47,"line":1734},[45,2103,421],{"class":174},[45,2105,2106],{"class":47,"line":1740},[45,2107,99],{"emptyLinePlaceholder":98},[45,2109,2110,2112,2114,2117],{"class":47,"line":1748},[45,2111,175],{"class":174},[45,2113,884],{"class":174},[45,2115,2116],{"class":51}," run_untrusted",[45,2118,2119],{"class":178},"(code_str)\n",[45,2121,2122,2124,2126,2128,2130,2133,2136,2138,2140,2142,2145],{"class":47,"line":1753},[45,2123,1456],{"class":174},[45,2125,371],{"class":178},[45,2127,182],{"class":174},[45,2129,185],{"class":88},[45,2131,2132],{"class":178},"(code_str, ",[45,2134,2135],{"class":55},"\"user_code\"",[45,2137,329],{"class":178},[45,2139,337],{"class":55},[45,2141,329],{"class":178},[45,2143,2144],{"class":88},"create_sandbox",[45,2146,2147],{"class":178},"())\n",[45,2149,2150,2153,2155,2157,2160,2163,2166,2168,2171,2173,2176],{"class":47,"line":1758},[45,2151,2152],{"class":174},"  if",[45,2154,391],{"class":174},[45,2156,394],{"class":178},[45,2158,2159],{"class":174},"then",[45,2161,2162],{"class":174}," return",[45,2164,2165],{"class":88}," false",[45,2167,329],{"class":178},[45,2169,2170],{"class":55},"\"compile: \" ",[45,2172,410],{"class":174},[45,2174,2175],{"class":178}," err ",[45,2177,421],{"class":174},[45,2179,2180,2182,2184,2186,2188],{"class":47,"line":1773},[45,2181,1456],{"class":174},[45,2183,235],{"class":178},[45,2185,182],{"class":174},[45,2187,240],{"class":88},[45,2189,644],{"class":178},[45,2191,2192,2194,2196,2198,2200,2202,2204,2206,2209,2211,2214],{"class":47,"line":1798},[45,2193,2152],{"class":174},[45,2195,391],{"class":174},[45,2197,653],{"class":178},[45,2199,2159],{"class":174},[45,2201,2162],{"class":174},[45,2203,2165],{"class":88},[45,2205,329],{"class":178},[45,2207,2208],{"class":55},"\"runtime: \" ",[45,2210,410],{"class":174},[45,2212,2213],{"class":178}," result ",[45,2215,421],{"class":174},[45,2217,2218,2220,2222],{"class":47,"line":1804},[45,2219,1333],{"class":174},[45,2221,1662],{"class":88},[45,2223,2224],{"class":178},", result\n",[45,2226,2228],{"class":47,"line":2227},31,[45,2229,421],{"class":174},[45,2231,2233],{"class":47,"line":2232},32,[45,2234,99],{"emptyLinePlaceholder":98},[45,2236,2238],{"class":47,"line":2237},33,[45,2239,2240],{"class":62},"-- Even if the user code tries:  os.execute(\"rm -rf \u002F\")\n",[45,2242,2244],{"class":47,"line":2243},34,[45,2245,2246],{"class":62},"-- → attempt to index nil value (global 'os')  → caught, sandbox holds\n",[28,2248,2250,2251,2253],{"id":2249},"hot-reload-via-packageloaded-eviction","Hot-Reload via ",[19,2252,501],{}," Eviction",[33,2255,2256],{"language":25},[37,2257,2259],{"className":156,"code":2258,"language":25,"meta":41,"style":41},"-- Production hot-reload: evict a module from cache, re-require, preserve state\nlocal function reload_module(name, state_preserver)\n  local old = package.loaded[name]\n  local preserved_state = state_preserver and state_preserver(old) or {}\n  package.loaded[name] = nil          -- evict: next require re-executes the chunk\n  local new = require(name)           -- fresh module table\n  if state_preserver then\n    state_preserver(new, true, preserved_state)  -- restore state into new module\n  end\n  return new, old\nend\n\n-- Usage: reload a game system without restarting the process\nlocal GameAI = require(\"game.ai\")\n-- ... modify game\u002Fai.lua on disk ...\nGameAI = reload_module(\"game.ai\", function(mod, is_restore, state)\n  if not is_restore then return { current_target = mod.current_target } end\n  mod.current_target = state.current_target  -- restore into fresh module\nend)\n",[19,2260,2261,2266,2278,2293,2318,2333,2350,2358,2375,2379,2386,2390,2394,2399,2417,2422,2443,2472,2489],{"__ignoreMap":41},[45,2262,2263],{"class":47,"line":48},[45,2264,2265],{"class":62},"-- Production hot-reload: evict a module from cache, re-require, preserve state\n",[45,2267,2268,2270,2272,2275],{"class":47,"line":66},[45,2269,175],{"class":174},[45,2271,884],{"class":174},[45,2273,2274],{"class":51}," reload_module",[45,2276,2277],{"class":178},"(name, state_preserver)\n",[45,2279,2280,2282,2285,2287,2290],{"class":47,"line":83},[45,2281,1456],{"class":174},[45,2283,2284],{"class":178}," old ",[45,2286,182],{"class":174},[45,2288,2289],{"class":88}," package.loaded",[45,2291,2292],{"class":178},"[name]\n",[45,2294,2295,2297,2300,2302,2305,2307,2310,2313,2316],{"class":47,"line":95},[45,2296,1456],{"class":174},[45,2298,2299],{"class":178}," preserved_state ",[45,2301,182],{"class":174},[45,2303,2304],{"class":178}," state_preserver ",[45,2306,1578],{"class":174},[45,2308,2309],{"class":88}," state_preserver",[45,2311,2312],{"class":178},"(old) ",[45,2314,2315],{"class":174},"or",[45,2317,1859],{"class":178},[45,2319,2320,2323,2326,2328,2330],{"class":47,"line":102},[45,2321,2322],{"class":88},"  package.loaded",[45,2324,2325],{"class":178},"[name] ",[45,2327,182],{"class":174},[45,2329,514],{"class":88},[45,2331,2332],{"class":62},"          -- evict: next require re-executes the chunk\n",[45,2334,2335,2337,2340,2342,2344,2347],{"class":47,"line":204},[45,2336,1456],{"class":174},[45,2338,2339],{"class":178}," new ",[45,2341,182],{"class":174},[45,2343,485],{"class":88},[45,2345,2346],{"class":178},"(name)           ",[45,2348,2349],{"class":62},"-- fresh module table\n",[45,2351,2352,2354,2356],{"class":47,"line":210},[45,2353,2152],{"class":174},[45,2355,2304],{"class":178},[45,2357,397],{"class":174},[45,2359,2360,2363,2366,2369,2372],{"class":47,"line":219},[45,2361,2362],{"class":88},"    state_preserver",[45,2364,2365],{"class":178},"(new, ",[45,2367,2368],{"class":88},"true",[45,2370,2371],{"class":178},", preserved_state)  ",[45,2373,2374],{"class":62},"-- restore state into new module\n",[45,2376,2377],{"class":47,"line":224},[45,2378,1737],{"class":174},[45,2380,2381,2383],{"class":47,"line":230},[45,2382,1333],{"class":174},[45,2384,2385],{"class":178}," new, old\n",[45,2387,2388],{"class":47,"line":249},[45,2389,421],{"class":174},[45,2391,2392],{"class":47,"line":261},[45,2393,99],{"emptyLinePlaceholder":98},[45,2395,2396],{"class":47,"line":272},[45,2397,2398],{"class":62},"-- Usage: reload a game system without restarting the process\n",[45,2400,2401,2403,2406,2408,2410,2412,2415],{"class":47,"line":277},[45,2402,175],{"class":174},[45,2404,2405],{"class":178}," GameAI ",[45,2407,182],{"class":174},[45,2409,485],{"class":88},[45,2411,188],{"class":178},[45,2413,2414],{"class":55},"\"game.ai\"",[45,2416,216],{"class":178},[45,2418,2419],{"class":47,"line":283},[45,2420,2421],{"class":62},"-- ... modify game\u002Fai.lua on disk ...\n",[45,2423,2424,2427,2429,2431,2433,2435,2437,2440],{"class":47,"line":312},[45,2425,2426],{"class":178},"GameAI ",[45,2428,182],{"class":174},[45,2430,2274],{"class":88},[45,2432,188],{"class":178},[45,2434,2414],{"class":55},[45,2436,329],{"class":178},[45,2438,2439],{"class":174},"function",[45,2441,2442],{"class":178},"(mod, is_restore, state)\n",[45,2444,2445,2447,2449,2452,2454,2456,2459,2461,2464,2467,2470],{"class":47,"line":343},[45,2446,2152],{"class":174},[45,2448,391],{"class":174},[45,2450,2451],{"class":178}," is_restore ",[45,2453,2159],{"class":174},[45,2455,2162],{"class":174},[45,2457,2458],{"class":178}," { current_target ",[45,2460,182],{"class":174},[45,2462,2463],{"class":178}," mod.",[45,2465,2466],{"class":51},"current_target",[45,2468,2469],{"class":178}," } ",[45,2471,421],{"class":174},[45,2473,2474,2477,2479,2481,2484,2486],{"class":47,"line":910},[45,2475,2476],{"class":178},"  mod.",[45,2478,2466],{"class":51},[45,2480,881],{"class":174},[45,2482,2483],{"class":178}," state.",[45,2485,2466],{"class":51},[45,2487,2488],{"class":62},"  -- restore into fresh module\n",[45,2490,2491,2494],{"class":47,"line":919},[45,2492,2493],{"class":174},"end",[45,2495,216],{"class":178},[28,2497,2499,2502],{"id":2498},"luac-bytecode-compiler-inspector",[19,2500,2501],{},"luac"," — Bytecode Compiler & Inspector",[33,2504,2505],{"language":35},[37,2506,2508],{"className":39,"code":2507,"language":35,"meta":41,"style":41},"luac -o compiled.out script.lua    # precompile to bytecode (faster startup)\nluac -l script.lua                  # disassemble: list bytecode instructions\nluac -l -l script.lua               # full disassembly with constants & upvalues\n",[19,2509,2510,2526,2538],{"__ignoreMap":41},[45,2511,2512,2514,2517,2520,2523],{"class":47,"line":48},[45,2513,2501],{"class":51},[45,2515,2516],{"class":88}," -o",[45,2518,2519],{"class":55}," compiled.out",[45,2521,2522],{"class":55}," script.lua",[45,2524,2525],{"class":62},"    # precompile to bytecode (faster startup)\n",[45,2527,2528,2530,2533,2535],{"class":47,"line":66},[45,2529,2501],{"class":51},[45,2531,2532],{"class":88}," -l",[45,2534,2522],{"class":55},[45,2536,2537],{"class":62},"                  # disassemble: list bytecode instructions\n",[45,2539,2540,2542,2544,2546,2548],{"class":47,"line":83},[45,2541,2501],{"class":51},[45,2543,2532],{"class":88},[45,2545,2532],{"class":88},[45,2547,2522],{"class":55},[45,2549,2550],{"class":62},"               # full disassembly with constants & upvalues\n",[33,2552,2553],{"language":25},[37,2554,2556],{"className":156,"code":2555,"language":25,"meta":41,"style":41},"-- Precompiled chunks load faster (skip parse step) but are NOT portable across\n-- Lua versions or architectures. Distribute source for portability; precompile\n-- for startup-critical embedded deployments.\nlocal f = loadfile(\"compiled.out\")  -- loadstring\u002Floadfile accept bytecode too\nf()\n",[19,2557,2558,2563,2568,2573,2595],{"__ignoreMap":41},[45,2559,2560],{"class":47,"line":48},[45,2561,2562],{"class":62},"-- Precompiled chunks load faster (skip parse step) but are NOT portable across\n",[45,2564,2565],{"class":47,"line":66},[45,2566,2567],{"class":62},"-- Lua versions or architectures. Distribute source for portability; precompile\n",[45,2569,2570],{"class":47,"line":83},[45,2571,2572],{"class":62},"-- for startup-critical embedded deployments.\n",[45,2574,2575,2577,2580,2582,2584,2586,2589,2592],{"class":47,"line":95},[45,2576,175],{"class":174},[45,2578,2579],{"class":178}," f ",[45,2581,182],{"class":174},[45,2583,376],{"class":88},[45,2585,188],{"class":178},[45,2587,2588],{"class":55},"\"compiled.out\"",[45,2590,2591],{"class":178},")  ",[45,2593,2594],{"class":62},"-- loadstring\u002Floadfile accept bytecode too\n",[45,2596,2597,2600],{"class":47,"line":102},[45,2598,2599],{"class":88},"f",[45,2601,434],{"class":178},[28,2603,2605],{"id":2604},"tips-tricks","💡 Tips & Tricks",[15,2607,2608,2611,2612,2615],{},[147,2609,2610],{},"Localize standard library functions in hot paths",": ",[19,2613,2614],{},"local sformat = string.format"," turns a global table lookup into a fast upvalue reference. Measurable on any profile.",[33,2617,2618],{"language":25},[37,2619,2621],{"className":156,"code":2620,"language":25,"meta":41,"style":41},"-- In a tight loop doing 10M string formats:\n-- Global:     string.format(...)   →  _G → string → format  (2 hash lookups)\n-- Localized:  local sfmt = string.format; sfmt(...)  → upvalue (direct pointer)\n",[19,2622,2623,2628,2633],{"__ignoreMap":41},[45,2624,2625],{"class":47,"line":48},[45,2626,2627],{"class":62},"-- In a tight loop doing 10M string formats:\n",[45,2629,2630],{"class":47,"line":66},[45,2631,2632],{"class":62},"-- Global:     string.format(...)   →  _G → string → format  (2 hash lookups)\n",[45,2634,2635],{"class":47,"line":83},[45,2636,2637],{"class":62},"-- Localized:  local sfmt = string.format; sfmt(...)  → upvalue (direct pointer)\n",[15,2639,2640,2611,2651,2654],{},[147,2641,2642,2643,2646,2647,2650],{},"Use ",[19,2644,2645],{},"load()"," with ",[19,2648,2649],{},"mode"," parameter to prevent bytecode injection",[19,2652,2653],{},"load(src, name, \"t\")"," only accepts text, never bytecode — defends against precompiled malicious chunks.",[33,2656,2657],{"language":25},[37,2658,2660],{"className":156,"code":2659,"language":25,"meta":41,"style":41},"local fn = load(user_input, \"user\", \"t\")   -- \"t\" = text only, \"b\" = bytecode, \"bt\" = both\n",[19,2661,2662],{"__ignoreMap":41},[45,2663,2664,2666,2668,2670,2672,2675,2678,2680,2682,2685],{"class":47,"line":48},[45,2665,175],{"class":174},[45,2667,394],{"class":178},[45,2669,182],{"class":174},[45,2671,185],{"class":88},[45,2673,2674],{"class":178},"(user_input, ",[45,2676,2677],{"class":55},"\"user\"",[45,2679,329],{"class":178},[45,2681,337],{"class":55},[45,2683,2684],{"class":178},")   ",[45,2686,2687],{"class":62},"-- \"t\" = text only, \"b\" = bytecode, \"bt\" = both\n",[15,2689,2690,2696],{},[147,2691,2692,2695],{},[19,2693,2694],{},"debug.getinfo(1, \"Sl\")"," gives source + line for error reporting",": Build structured error objects with location info.",[33,2698,2699],{"language":25},[37,2700,2702],{"className":156,"code":2701,"language":25,"meta":41,"style":41},"local function err_at(level, msg)\n  local info = debug.getinfo(level + 1, \"Sl\")  -- +1 to skip this function\n  return string.format(\"%s:%d: %s\", info.source, info.currentline, msg)\nend\n",[19,2703,2704,2716,2745,2770],{"__ignoreMap":41},[45,2705,2706,2708,2710,2713],{"class":47,"line":48},[45,2707,175],{"class":174},[45,2709,884],{"class":174},[45,2711,2712],{"class":51}," err_at",[45,2714,2715],{"class":178},"(level, msg)\n",[45,2717,2718,2720,2723,2725,2728,2731,2733,2735,2737,2740,2742],{"class":47,"line":66},[45,2719,1456],{"class":174},[45,2721,2722],{"class":178}," info ",[45,2724,182],{"class":174},[45,2726,2727],{"class":88}," debug.getinfo",[45,2729,2730],{"class":178},"(level ",[45,2732,1584],{"class":174},[45,2734,1587],{"class":88},[45,2736,329],{"class":178},[45,2738,2739],{"class":55},"\"Sl\"",[45,2741,2591],{"class":178},[45,2743,2744],{"class":62},"-- +1 to skip this function\n",[45,2746,2747,2749,2751,2753,2756,2759,2762,2764,2767],{"class":47,"line":83},[45,2748,1333],{"class":174},[45,2750,1283],{"class":88},[45,2752,188],{"class":178},[45,2754,2755],{"class":55},"\"%s:%d: %s\"",[45,2757,2758],{"class":178},", info.",[45,2760,2761],{"class":51},"source",[45,2763,2758],{"class":178},[45,2765,2766],{"class":51},"currentline",[45,2768,2769],{"class":178},", msg)\n",[45,2771,2772],{"class":47,"line":95},[45,2773,421],{"class":174},[28,2775,2777],{"id":2776},"️-edge-cases-gotchas","⚠️ Edge Cases & Gotchas",[15,2779,2780,2789,2790,2792,2793,2795,2796,702],{},[147,2781,2782,2784,2785,2788],{},[19,2783,2645],{}," inherits ",[19,2786,2787],{},"_ENV"," at compile time, not call time",": Changing ",[19,2791,2787],{}," after ",[19,2794,2645],{}," has no effect. Pass the environment as the 4th arg to ",[19,2797,2645],{},[33,2799,2800],{"language":25},[37,2801,2803],{"className":156,"code":2802,"language":25,"meta":41,"style":41},"local fn = load(\"x = 1\")    -- compiles with _ENV = _G\n_ENV = {}                    -- too late — fn already bound to _G\nfn()\nprint(x)                     -- 1 (wrote to _G, not the new _ENV)\n",[19,2804,2805,2825,2837,2843],{"__ignoreMap":41},[45,2806,2807,2809,2811,2813,2815,2817,2820,2822],{"class":47,"line":48},[45,2808,175],{"class":174},[45,2810,394],{"class":178},[45,2812,182],{"class":174},[45,2814,185],{"class":88},[45,2816,188],{"class":178},[45,2818,2819],{"class":55},"\"x = 1\"",[45,2821,456],{"class":178},[45,2823,2824],{"class":62},"-- compiles with _ENV = _G\n",[45,2826,2827,2829,2831,2834],{"class":47,"line":66},[45,2828,2787],{"class":88},[45,2830,881],{"class":174},[45,2832,2833],{"class":178}," {}                    ",[45,2835,2836],{"class":62},"-- too late — fn already bound to _G\n",[45,2838,2839,2841],{"class":47,"line":83},[45,2840,431],{"class":88},[45,2842,434],{"class":178},[45,2844,2845,2847,2850],{"class":47,"line":95},[45,2846,252],{"class":88},[45,2848,2849],{"class":178},"(x)                     ",[45,2851,2852],{"class":62},"-- 1 (wrote to _G, not the new _ENV)\n",[15,2854,2855,2863,2864,2867,2868,2646,2870,2873],{},[147,2856,2857,2859,2860],{},[19,2858,448],{}," does not search ",[19,2861,2862],{},"package.path",": It takes a literal file path. ",[19,2865,2866],{},"require"," searches ",[19,2869,2862],{},[19,2871,2872],{},"?"," substitution. Don't mix them.",[15,2875,2876,2881],{},[147,2877,2878,2880],{},[19,2879,2866],{}," caches the return value, not the side effects",": If a module has side effects (registering callbacks) and you evict + re-require, side effects fire again — can cause double-registration bugs.",[33,2883,2884],{"language":25},[37,2885,2887],{"className":156,"code":2886,"language":25,"meta":41,"style":41},"-- module_with_side_effects.lua\nlocal M = {}\nhooks[\"on_tick\"] = function() ... end   -- side effect: modifies global hooks\nreturn M\n\n-- Evicting and re-requiring runs the hook registration AGAIN:\npackage.loaded[\"module_with_side_effects\"] = nil\nrequire(\"module_with_side_effects\")      -- hooks[\"on_tick\"] now registered twice\n",[19,2888,2889,2894,2905,2931,2939,2943,2948,2964],{"__ignoreMap":41},[45,2890,2891],{"class":47,"line":48},[45,2892,2893],{"class":62},"-- module_with_side_effects.lua\n",[45,2895,2896,2898,2901,2903],{"class":47,"line":66},[45,2897,175],{"class":174},[45,2899,2900],{"class":178}," M ",[45,2902,182],{"class":174},[45,2904,1859],{"class":178},[45,2906,2907,2910,2913,2915,2917,2919,2922,2925,2928],{"class":47,"line":83},[45,2908,2909],{"class":178},"hooks[",[45,2911,2912],{"class":55},"\"on_tick\"",[45,2914,509],{"class":178},[45,2916,182],{"class":174},[45,2918,884],{"class":174},[45,2920,2921],{"class":178},"() ",[45,2923,2924],{"class":88},"...",[45,2926,2927],{"class":174}," end",[45,2929,2930],{"class":62},"   -- side effect: modifies global hooks\n",[45,2932,2933,2936],{"class":47,"line":95},[45,2934,2935],{"class":174},"return",[45,2937,2938],{"class":178}," M\n",[45,2940,2941],{"class":47,"line":102},[45,2942,99],{"emptyLinePlaceholder":98},[45,2944,2945],{"class":47,"line":204},[45,2946,2947],{"class":62},"-- Evicting and re-requiring runs the hook registration AGAIN:\n",[45,2949,2950,2952,2954,2957,2959,2961],{"class":47,"line":210},[45,2951,501],{"class":88},[45,2953,504],{"class":178},[45,2955,2956],{"class":55},"\"module_with_side_effects\"",[45,2958,509],{"class":178},[45,2960,182],{"class":174},[45,2962,2963],{"class":88}," nil\n",[45,2965,2966,2968,2970,2972,2975],{"class":47,"line":219},[45,2967,2866],{"class":88},[45,2969,188],{"class":178},[45,2971,2956],{"class":55},[45,2973,2974],{"class":178},")      ",[45,2976,2977],{"class":62},"-- hooks[\"on_tick\"] now registered twice\n",[15,2979,2980,2990],{},[147,2981,2982,2985,2986,2989],{},[19,2983,2984],{},"loadfile"," returns ",[19,2987,2988],{},"nil"," for both missing file and syntax errors",": Always check the second return value.",[33,2992,2993],{"language":25},[37,2994,2996],{"className":156,"code":2995,"language":25,"meta":41,"style":41},"local fn, err = loadfile(\"missing.lua\")\n-- fn == nil, err == \"missing.lua: No such file or directory.\"\n-- Don't just check fn — the error message tells you if it's missing vs malformed\n",[19,2997,2998,3015,3020],{"__ignoreMap":41},[45,2999,3000,3002,3004,3006,3008,3010,3013],{"class":47,"line":48},[45,3001,175],{"class":174},[45,3003,371],{"class":178},[45,3005,182],{"class":174},[45,3007,376],{"class":88},[45,3009,188],{"class":178},[45,3011,3012],{"class":55},"\"missing.lua\"",[45,3014,216],{"class":178},[45,3016,3017],{"class":47,"line":66},[45,3018,3019],{"class":62},"-- fn == nil, err == \"missing.lua: No such file or directory.\"\n",[45,3021,3022],{"class":47,"line":83},[45,3023,3024],{"class":62},"-- Don't just check fn — the error message tells you if it's missing vs malformed\n",[15,3026,3027,3030,3031,3034],{},[147,3028,3029],{},"Bytecode is not stable across versions",": A ",[19,3032,3033],{},".out"," compiled with Lua 5.3 won't load in 5.4. Always ship source unless you control the exact runtime.",[28,3036,3038],{"id":3037},"spot-the-bug","🧠 Spot the Bug",[15,3040,3041],{},"What does this print?",[33,3043,3044],{"language":25},[37,3045,3047],{"className":156,"code":3046,"language":25,"meta":41,"style":41},"local env = { x = 100 }\nlocal fn = load(\"return x + 1\", \"test\", \"t\", env)\nprint(fn())\nenv.x = 200\nprint(fn())\n",[19,3048,3049,3067,3093,3103,3115],{"__ignoreMap":41},[45,3050,3051,3053,3055,3057,3060,3062,3065],{"class":47,"line":48},[45,3052,175],{"class":174},[45,3054,288],{"class":178},[45,3056,182],{"class":174},[45,3058,3059],{"class":178}," { x ",[45,3061,182],{"class":174},[45,3063,3064],{"class":88}," 100",[45,3066,2020],{"class":178},[45,3068,3069,3071,3073,3075,3077,3079,3082,3084,3087,3089,3091],{"class":47,"line":66},[45,3070,175],{"class":174},[45,3072,394],{"class":178},[45,3074,182],{"class":174},[45,3076,185],{"class":88},[45,3078,188],{"class":178},[45,3080,3081],{"class":55},"\"return x + 1\"",[45,3083,329],{"class":178},[45,3085,3086],{"class":55},"\"test\"",[45,3088,329],{"class":178},[45,3090,337],{"class":55},[45,3092,340],{"class":178},[45,3094,3095,3097,3099,3101],{"class":47,"line":83},[45,3096,252],{"class":88},[45,3098,188],{"class":178},[45,3100,431],{"class":88},[45,3102,2147],{"class":178},[45,3104,3105,3108,3110,3112],{"class":47,"line":95},[45,3106,3107],{"class":178},"env.",[45,3109,737],{"class":51},[45,3111,881],{"class":174},[45,3113,3114],{"class":88}," 200\n",[45,3116,3117,3119,3121,3123],{"class":47,"line":102},[45,3118,252],{"class":88},[45,3120,188],{"class":178},[45,3122,431],{"class":88},[45,3124,2147],{"class":178},[3126,3127,3128,3132,3142,3168],"details",{},[3129,3130,3131],"summary",{},"Answer",[15,3133,3134,3135,3138,3139,702],{},"Prints ",[19,3136,3137],{},"101"," then ",[19,3140,3141],{},"201",[15,3143,3144,3146,3147,3150,3151,3153,3154,3157,3158,3161,3162,3164,3165,3167],{},[19,3145,2645],{}," compiles with the provided ",[19,3148,3149],{},"env"," table as the chunk's ",[19,3152,2787],{},". The closure captures a reference to the ",[147,3155,3156],{},"same table"," — it doesn't snapshot values. So when ",[19,3159,3160],{},"env.x"," changes, the next call sees the updated value. This is because ",[19,3163,737],{}," in the chunk is a global lookup into ",[19,3166,2787],{}," (the env table), evaluated at call time, not compile time.",[15,3169,3170,3171,3173],{},"If you wanted a snapshot, you'd need to pass ",[19,3172,737],{}," as a parameter or upvalue, not via the environment table.",[28,3175,3177],{"id":3176},"spot-the-bug-ii","🔍 Spot the Bug II",[33,3179,3180],{"language":25},[37,3181,3183],{"className":156,"code":3182,"language":25,"meta":41,"style":41},"local sandbox = { print = print }\nlocal fn = load(\"print('hello'); os.exit(1)\", \"test\", \"t\", sandbox)\nfn()\n",[19,3184,3185,3201,3227],{"__ignoreMap":41},[45,3186,3187,3189,3191,3193,3195,3197,3199],{"class":47,"line":48},[45,3188,175],{"class":174},[45,3190,317],{"class":178},[45,3192,182],{"class":174},[45,3194,293],{"class":178},[45,3196,182],{"class":174},[45,3198,298],{"class":88},[45,3200,2020],{"class":178},[45,3202,3203,3205,3207,3209,3211,3213,3216,3218,3220,3222,3224],{"class":47,"line":66},[45,3204,175],{"class":174},[45,3206,394],{"class":178},[45,3208,182],{"class":174},[45,3210,185],{"class":88},[45,3212,188],{"class":178},[45,3214,3215],{"class":55},"\"print('hello'); os.exit(1)\"",[45,3217,329],{"class":178},[45,3219,3086],{"class":55},[45,3221,329],{"class":178},[45,3223,337],{"class":55},[45,3225,3226],{"class":178},", sandbox)\n",[45,3228,3229,3231],{"class":47,"line":83},[45,3230,431],{"class":88},[45,3232,434],{"class":178},[3126,3234,3235,3237,3243],{},[3129,3236,3131],{},[15,3238,3239,3240,702],{},"It errors with: ",[19,3241,3242],{},"attempt to index a nil value (global 'os')",[15,3244,3245,3246,3248,3249,3252,3253,3255,3256,3258,3259,3262,3263,3266,3267,3270,3271,3274,3275,3278],{},"The sandbox env table only has ",[19,3247,252],{},". When the chunk tries ",[19,3250,3251],{},"os.exit(1)",", Lua looks up ",[19,3254,1124],{}," in the env table, finds ",[19,3257,2988],{},", then tries to index it (",[19,3260,3261],{},"nil.exit",") — which throws. The sandbox ",[147,3264,3265],{},"holds"," — ",[19,3268,3269],{},"os.exit"," never runs. This is exactly the security property you want. But if you had accidentally put ",[19,3272,3273],{},"os = os"," in the env, the script could call ",[19,3276,3277],{},"os.execute(\"rm -rf \u002F\")",". Audit every key you whitelist.",[3280,3281,3282],"style",{},"html pre.shiki code .sIsaT, html code.shiki .sIsaT{--shiki-default:#6F42C1;--shiki-github-dark:#B392F0}html pre.shiki code .sJ6F3, html code.shiki .sJ6F3{--shiki-default:#032F62;--shiki-github-dark:#9ECBFF}html pre.shiki code .sdCPZ, html code.shiki .sdCPZ{--shiki-default:#6A737D;--shiki-github-dark:#6A737D}html pre.shiki code .snvgF, html code.shiki .snvgF{--shiki-default:#005CC5;--shiki-github-dark:#79B8FF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html.github-dark .shiki span {color: var(--shiki-github-dark);background: var(--shiki-github-dark-bg);font-style: var(--shiki-github-dark-font-style);font-weight: var(--shiki-github-dark-font-weight);text-decoration: var(--shiki-github-dark-text-decoration);}html pre.shiki code .svdQ7, html code.shiki .svdQ7{--shiki-default:#D73A49;--shiki-github-dark:#F97583}html pre.shiki code .ssxIu, html code.shiki .ssxIu{--shiki-default:#24292E;--shiki-github-dark:#E1E4E8}",{"title":41,"searchDepth":66,"depth":66,"links":3284},[3285,3286,3290,3292,3293,3295,3296,3298,3300,3301,3302,3303],{"id":30,"depth":66,"text":31},{"id":141,"depth":66,"text":142,"children":3287},[3288],{"id":542,"depth":83,"text":3289},"Anti-pattern: dofile in production",{"id":679,"depth":66,"text":3291},"The Registry & Global Table (_G)",{"id":971,"depth":66,"text":972},{"id":1379,"depth":66,"text":3294},"The arg Table & Command-Line Interface",{"id":1810,"depth":66,"text":1811},{"id":2249,"depth":66,"text":3297},"Hot-Reload via package.loaded Eviction",{"id":2498,"depth":66,"text":3299},"luac — Bytecode Compiler & Inspector",{"id":2604,"depth":66,"text":2605},{"id":2776,"depth":66,"text":2777},{"id":3037,"depth":66,"text":3038},{"id":3176,"depth":66,"text":3177},"Deep-dive into Lua's VM, chunk compilation, registry, embedding boundary, and the require\u002Fdofile\u002Floadfile execution pipeline. Production-grade patterns for sandboxing, hot-reload, and C interop.","md",{},"\u002Flua\u002F01-getting-started",{"title":5,"description":3304},"lua\u002F01-getting-started","b19_4sBPLppgE66T9eEWTsHS9uhepV6m6OGy2AWElQM",1789924650253]