Repository navigation
Expand file tree
/
Copy pathcustom_loadstring_full.lua
More file actions
6701 lines (6386 loc) · 291 KB
/
Copy pathcustom_loadstring_full.lua
File metadata and controls
6701 lines (6386 loc) · 291 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
--[[
GoogiVM (custom_loadstring_full.lua)
Pure-Lua Lua 5.1 compiler and bytecode interpreter.
Does not use load, loadstring, FFI, debug, io, or os.
API:
local custom_loadstring = dofile("custom_loadstring_full.lua")
local chunk, err = custom_loadstring(source, env, options)
Supported:
- while, repeat/until, numeric for, generic for
- varargs (...), nested functions, closures/upvalues, tail calls
- table constructors (nested, named/keyed/list fields)
- multiple assignment and multiple returns
- method calls/definitions, local function
- all Lua 5.1 operators and host metamethod semantics
- syntax and runtime errors with source-line mapping
Lua 5.1 does not include bitwise operators; expose a library such as
`bit` through `env` if needed.
]]
local str_find = string.find
local str_match = string.match
local str_sub = string.sub
local str_byte = string.byte
local str_char = string.char
local str_gsub = string.gsub
local tconcat = table.concat
local tostring = tostring
local tonumber = tonumber
local type = type
local error = error
local pcall = pcall
local select = select
local setmetatable = setmetatable
local getfenv = getfenv
local setfenv = setfenv
local getmetatable = getmetatable
local next = next
local ipairs = ipairs
local ipairs_iter = ipairs and select(1, ipairs({}))
local unpack = unpack or table.unpack
local function pack(...)
return { n = select("#", ...), ... }
end
local closure_info = setmetatable({}, { __mode = "k" })
-- Default execution policy. All limits can be overridden per chunk through
-- the optional third argument to custom_loadstring.
local DEFAULT_LIMITS = {
maxSourceBytes = 4 * 1024 * 1024,
maxCodeSlots = 8 * 1024 * 1024,
maxInstructions = 50 * 1000 * 1000,
maxCallDepth = 10000,
maxStack = 1000000,
maxArgs = 1000,
maxReturns = 1000,
}
-- Fused ("super") opcodes produced by the post-compile peephole pass.
-- All are >= 52; normal opcodes remain 1..51.
local FOP = {
MOVLOCAL = 52,
MOVLOCAL_NOP = 53,
MOVLOCAL_CELL = 54,
SETLOCAL_K = 55,
SETLOCAL_INDEX = 56,
SETLOCAL_FIELD = 57,
ADD_RR = 58, ADD_RK = 59,
SUB_RR = 60, SUB_RK = 61,
MUL_RR = 62, MUL_RK = 63,
DIV_RR = 64, DIV_RK = 65,
MOD_RR = 66, MOD_RK = 67,
POW_RR = 68, POW_RK = 69,
EQ_RR = 70, EQ_RK = 71,
NE_RR = 72, NE_RK = 73,
LT_RR = 74, LT_RK = 75,
LE_RR = 76, LE_RK = 77,
GT_RR = 78, GT_RK = 79,
GE_RR = 80, GE_RK = 81,
EQ_K = 82, NE_K = 83, LT_K = 84, LE_K = 85, GT_K = 86, GE_K = 87,
INDEX_RR = 88, INDEX_RK = 89, FIELD_R = 90,
SETINDEX_RR = 91, SETINDEX_RK = 92,
SETFIELD_RV = 93, SETFIELD_RK = 94,
-- 95-101 were folded into ASSIGN_RR/RK/INDEX (154-156).
JMPF_LOCAL = 102, JMPNIL_LOCAL = 103,
-- 104-109 and 120-131 were folded into JMPF_CMP_* (151-153).
FORLOOP_MOV = 110, FORLOOP_CELL = 111,
FORLOOP_ADD_RR = 112, FORLOOP_ADD_RK = 113,
FORLOOP_ADD_INDEX = 114,
FORLOOP_SETINDEX_RR = 115, FORLOOP_SETINDEX_RK = 116,
FORLOOP_SUB_RR = 117, FORLOOP_SUB_RK = 118,
JMPF_MOD_EQ_K = 119,
TFOR_2 = 132,
TFOR_1 = 133,
NEWTABLE_LOCAL = 134,
SETLIST_R = 135,
SETLIST_K = 136,
SETLIST_ADD_RK = 137,
NEWTABLE_FIELD_R = 138,
NEWTABLE_FIELD_K = 139,
NEWTABLE_SETINDEX_R = 140,
NEWTABLE_LIST3_ADD_RK = 141,
JMPF_MOD_RR_EQ_K = 142,
ADD_ASSIGN_BINOP_RR = 143,
SETLOCAL_BOOL = 144,
FORLOOP_MOD_RR_EQ_K = 145,
FORLOOP_NEWTABLE_SETINDEX_R = 146,
TESTF_CMP_RR = 147, TESTF_CMP_RK = 148,
TESTT_CMP_RR = 149, TESTT_CMP_RK = 150,
JMPF_CMP_RR = 151, JMPF_CMP_RK = 152, JMPF_CMP_K = 153,
-- Compound assignment collapsed to one opcode per shape; the arithmetic
-- operator travels in the dead slot at C[pc + 4] as the base opcode
-- number (ADD=13 .. CONCAT=19).
ASSIGN_RR = 154, ASSIGN_RK = 155, ASSIGN_INDEX = 156,
-- `a, b = b, a`
SWAP = 157,
-- `x = (x + y) % k`, e.g. s = (s + i) % 7
ASSIGN_MOD_ADD_RK = 158,
-- `t[x % k] = v`
SETINDEX_MOD_RK = 159,
-- `local cell = { v = x }; local f = function() ... end`
CLOSURE_CELL = 160,
-- `t[cell.v] = function() ... end` (closure over a boxed loop variable)
SETINDEX_CLOSURE_CELL = 161,
-- `for ... do s = s <op> operand end` for ops other than + and -.
-- The arithmetic kind is stored in the dead slot C[body + 16].
FORLOOP_ASSIGN_RR = 162, FORLOOP_ASSIGN_RK = 163,
-- Left-to-right arithmetic chains: `k <op> a`, `<stack> <op> a`, `<stack> <op> k`.
-- The arithmetic kind (13..19) is stored in the dead slot C[pc + 4].
PUSH_OP_RK = 164, PUSH_OP_RS = 165, PUSH_OP_KS = 166,
-- `dest = a <op> b` where the destination is not the left operand.
-- The arithmetic kind (13..19) is stored in the dead slot C[pc + 4].
ASSIGN_TO_RR = 167, ASSIGN_TO_RK = 168,
JMPT_CMP_RR_LT = 169, JMPT_CMP_RR_LE = 170,
JMPT_CMP_RR_GT = 171, JMPT_CMP_RR_GE = 172,
JMPT_CMP_RR_EQ = 173, JMPT_CMP_RR_NE = 174,
JMPT_CMP_RK_LT = 175, JMPT_CMP_RK_LE = 176,
JMPT_CMP_RK_GT = 177, JMPT_CMP_RK_GE = 178,
JMPT_CMP_RK_EQ = 179, JMPT_CMP_RK_NE = 180,
CALL_U_RR2 = 181, CALL_U_RR1 = 182,
CALL_R_RR2 = 183, CALL_R_RR1 = 184,
-- `{v, v + k1, v + k2}` built directly into an existing local.
NEWTABLE_LIST3_RK = 185,
-- `t[i], t[j] = t[j], t[i]`.
SWAP_INDEX = 186,
ADD_RR_ADD_K = 187, MUL_DIV_K = 188,
ADD_R_ADD_K = 189, DIV_RET = 190,
-- `env.name.field` pushed as one value (for `math.sqrt`, `string.sub`, ...).
GETGLOBAL_FIELD = 191,
-- `PUSH_OP_RS` immediately followed by `ADD`.
PUSH_OP_RS_ADD = 192,
-- Generic form: operands are encoded as +reg or -upvalue.
SETINDEX_MUL3 = 193,
-- `t[k] = t[k] +/- a * t[k2]`; a is +reg/-upvalue, k2 is +reg/-const.
SETINDEX_MUL2 = 194,
-- Numeric for whose body is `t[i] = true` (sieve).
FORLOOP_SETTRUE = 195,
-- dest = k * a * b + c (mandelbrot inner loop).
ASSIGN_MUL3_ADD = 196,
-- `t[a + k]` and `dest = c + b * a` on the expression stack.
INDEX_ADD_RK = 198, MUL_ADD_SETLOCAL = 199,
-- Indexed comparison followed by JMPF: `t[k] <op> v` / `t[k] <op> k2`.
INDEX_CMP_JMPF = 200,
-- `dest = (a - b) + c` (mandelbrot's zr update).
ASSIGN_SUB_ADD = 201,
-- `(a + b) <op> k` followed by JMPF, and `t[a][b]`.
ADD_JMPF_K = 202, INDEX_INDEX_RR = 203,
-- `dest = c + b * a` immediately followed by the numeric-for back-edge.
MUL_ADD_SETLOCAL_FORLOOP = 204,
-- `v[j + k]` pushed immediately before a two-argument upvalue call.
INDEX_ADD_RK_CALL_U_RR2 = 205,
-- `acc = acc + v * (a / b)` at the end of a numeric for.
DIV_MUL_ADD_SETLOCAL_FORLOOP = 206,
-- `k / x` with the constant folded out of the inlined stack.
DIV_K = 207,
DIV_K_MUL_ADD_SETLOCAL_FORLOOP = 208,
}
local run_closure
local active_trace = nil
-- LuaJIT 2.1 can intermittently miscompile the base comparison path inside
-- the large VM function. Keeping comparisons in a tiny, non-JIT function is
-- cheap compared to making the entire interpreter non-JIT.
local function compare_op(op, a, b)
if op == 23 then
return a == b
elseif op == 24 then
return a ~= b
elseif op == 25 then
return a < b
elseif op == 26 then
return a <= b
elseif op == 27 then
return a > b
else
return a >= b
end
end
if type(jit) == "table" and jit.off then
jit.off(compare_op)
end
-- Ordered comparisons on two numbers are the overwhelmingly common case inside
-- hot loops. Doing them inline keeps those loops traceable by LuaJIT instead of
-- bouncing through the jit-disabled compare_op on every iteration. Anything
-- else (strings, metamethods) still goes through compare_op unchanged.
local function cmp_lt(a, b)
if type(a) == "number" and type(b) == "number" then
return a < b
end
return compare_op(25, a, b)
end
local function cmp_le(a, b)
if type(a) == "number" and type(b) == "number" then
return a <= b
end
return compare_op(26, a, b)
end
local function cmp_gt(a, b)
if type(a) == "number" and type(b) == "number" then
return a > b
end
return compare_op(27, a, b)
end
local function cmp_ge(a, b)
if type(a) == "number" and type(b) == "number" then
return a >= b
end
return compare_op(28, a, b)
end
-- Generic form used by the fused compare+test opcodes, which carry the
-- comparison opcode as a separate operand.
local function cmp_value(op, a, b)
if op == 23 then
return a == b
elseif op == 24 then
return a ~= b
elseif op == 25 then
return cmp_lt(a, b)
elseif op == 26 then
return cmp_le(a, b)
elseif op == 27 then
return cmp_gt(a, b)
else
return cmp_ge(a, b)
end
end
-- Messages already carrying a sandbox location are prefixed with this byte so
-- nested handlers do not re-prefix them. It is stripped at every boundary the
-- guest or the host can observe.
local LOCATION_MARK = "\1"
local function sandbox_message(err)
if type(err) ~= "string" then
return err
end
if str_sub(err, 1, 1) == LOCATION_MARK then
return str_sub(err, 2)
end
-- Drop a host location prefix ("file.lua:12: " or "C:\...\vm.lua:12: ")
-- without inventing a sandbox location.
local body = str_match(err, "^.-:%d+: ([^\n]*)")
return body or err
end
local last_native_fn = nil
local native_name_cache = setmetatable({}, { __mode = "k" })
local function native_name_of(fn, env)
if type(fn) ~= "function" or type(env) ~= "table" then
return nil
end
local name = native_name_cache[fn]
if name then
return name
end
for k, v in pairs(env) do
if v == fn then
if type(k) == "string" then
name = k
break
end
elseif type(v) == "table" then
for k2, v2 in pairs(v) do
if v2 == fn and type(k2) == "string" then
name = k2
break
end
end
if name then
break
end
end
end
if name then
native_name_cache[fn] = name
end
return name
end
local function append_trace(out, trace)
local frames = trace.frames
local fi = trace.fi
if not frames or not fi then
return out
end
local function frame_line(fr)
local fl = fr.line
if not fl then
local fpc = fr.pc or 1
fl = fr.p.lines and fr.p.lines[fpc]
end
return fl
end
local function inline_site(fp, pc)
local sites = fp.inline_sites
if sites and pc then
for i = #sites, 1, -1 do
local site = sites[i]
if pc >= site.start and pc <= site.finish then
return site
end
end
end
return nil
end
out = out .. "\nstack traceback:"
-- `error` is a native function, so the native LuaJIT shape starts with
-- a C frame. Other runtime errors start at the innermost Lua frame.
if last_native_fn == error then
out = out .. "\n\t[C]: in function 'error'"
end
for i = fi, 1, -1 do
local fr = frames[i]
if fr then
local fl
if i == fi and trace[1] then
fl = fr.p.lines and fr.p.lines[trace[1]]
end
fl = fl or frame_line(fr)
if fl then
local fp = fr.p
local src = fp.sourceName or "<string>"
local site
if i == fi and trace[1] then
site = inline_site(fp, trace[1])
end
if site then
local inline_name = site.name
if inline_name then
out = out .. "\n\t" .. src .. ":" .. fl ..
": in function '" .. inline_name .. "'"
else
out = out .. "\n\t" .. src .. ":" .. fl ..
": in function <" .. src .. ":" .. fl .. ">"
end
fl = site.call_line or fl
end
local label
if i == 1 then
label = "in main chunk"
elseif fp.name then
label = "in function '" .. fp.name .. "'"
else
label = "in function <" .. src .. ":" .. fl .. ">"
end
out = out .. "\n\t" .. src .. ":" .. fl .. ": " .. label
end
end
end
return out
end
local function format_runtime_error(err, trace, with_trace)
if type(err) ~= "string" then
return err
end
if str_sub(err, 1, 1) == LOCATION_MARK then
local out = str_sub(err, 2)
if with_trace and trace and trace.cur then
local head = str_match(out, "^([^\n]*):%d+: ")
out = append_trace(out, trace, head)
end
return out
end
if not trace or not trace.cur then
return err
end
local p = trace.cur.p
local pc = trace[1] or 1
local line = p.lines and p.lines[pc]
if not line then
return err
end
-- Host errors start with "file.lua:NN: " (possibly a Windows path such as
-- "C:\mods\vm.lua:1771: ..."). Replace that with the interpreted source
-- location so host paths never leak into the sandbox.
local msg = str_match(err, "^.-:%d+: ([^\n]*)") or err
-- Host argument errors name the dispatcher local `f` (or `?` when the
-- function was called through pcall); restore the real name if known.
if last_native_fn and
(str_find(msg, "to 'f'", 1, true) or str_find(msg, "to '?'", 1, true)) then
local name = native_name_of(last_native_fn, trace.cur.env)
if name then
msg = str_gsub(msg, "to 'f'", "to '" .. name .. "'", 1)
msg = str_gsub(msg, "to '%?'", "to '" .. name .. "'", 1)
end
end
local out = (p.sourceName or "<string>") .. ":" .. line .. ": " .. msg
if with_trace ~= false then
out = append_trace(out, trace, (p.sourceName or "<string>") .. ":" .. line)
end
return out
end
local function sandbox_error(err, trace, with_trace)
local formatted = format_runtime_error(err, trace, with_trace)
if formatted ~= err then
return formatted
end
return sandbox_message(err)
end
-- Forward declarations: defined after get_closure_env/set_closure_env below.
local sandbox_getfenv, sandbox_setfenv
-- Kept outside run_closure so the dispatch loop stays small enough for the
-- LuaJIT trace compiler.
local function protected_call(is_xpcall, target, handler, tr, outer_trace,
S, fpos, top, caller_env, frames, fi)
local target_info = closure_info[target]
local te = target_info and tr or outer_trace
local rs
-- getfenv/setfenv must never reach the host implementation: the host would
-- happily return a native function's environment, which is the host _G.
local guarded, new_e
if getfenv and target == getfenv then
guarded = function(a)
return sandbox_getfenv(a, caller_env, frames, fi)
end
elseif setfenv and target == setfenv then
guarded = function(a, b)
local r
r, new_e = sandbox_setfenv(a, b, frames, fi)
return r
end
end
if guarded then
-- pcall(target, ...) stores args from fpos+2; xpcall(target, handler, ...)
-- shifts them by the handler slot.
local first = is_xpcall and (fpos + 3) or (fpos + 2)
local nargs = top - first + 1
if is_xpcall then
local function finish(err)
local msg = sandbox_error(err, te, false)
if handler == nil then
return msg
end
if closure_info[handler] then
return run_closure(handler, {}, msg)
end
return handler(msg)
end
if nargs < 1 then
rs = pack(xpcall(guarded, finish))
elseif nargs == 1 then
rs = pack(xpcall(guarded, finish, S[first]))
else
rs = pack(xpcall(guarded, finish, S[first], S[first + 1]))
end
else
if nargs < 1 then
rs = pack(pcall(guarded))
elseif nargs == 1 then
rs = pack(pcall(guarded, S[first]))
else
rs = pack(pcall(guarded, S[first], S[first + 1]))
end
if not rs[1] then
rs[2] = sandbox_error(rs[2], te, false)
end
end
rs.new_e = new_e
return rs
end
if not is_xpcall then
if target_info then
rs = pack(pcall(run_closure, target, tr, unpack(S, fpos + 2, top)))
else
last_native_fn = target
rs = pack(pcall(target, unpack(S, fpos + 2, top)))
end
if not rs[1] then
rs[2] = sandbox_error(rs[2], te, false)
end
return rs
end
local args = { n = 0 }
local n = 0
for j = fpos + 3, top do
n = n + 1
args[n] = S[j]
end
args.n = n
local function finish(err)
local msg = sandbox_error(err, te, false)
if handler == nil then
return msg
end
if closure_info[handler] then
return run_closure(handler, {}, msg)
end
return handler(msg)
end
if target_info then
rs = pack(xpcall(function()
return run_closure(target, tr, unpack(args, 1, n))
end, finish))
else
last_native_fn = target
rs = pack(xpcall(function()
return target(unpack(args, 1, n))
end, finish))
end
return rs
end
local function make_closure(proto, upvals, fenv, set_native_env)
local info
local function closure(...)
if info[4] then
return run_closure(closure, nil, ...)
end
local trace = {}
local result = pack(pcall(run_closure, closure, trace, ...))
if result[1] then
return unpack(result, 2, result.n)
end
error(sandbox_error(result[2], trace, true), 0)
end
local env = fenv or proto.env
local native_env = false
-- Only the root chunk needs a native Lua fenv for host getfenv/setfenv.
-- Nested closures resolve getfenv/setfenv through closure_info instead.
if set_native_env and setfenv then
native_env = pcall(setfenv, closure, env)
end
info = upvals
info[1] = proto
info[2] = env
info[3] = native_env
info[4] = not set_native_env
closure_info[closure] = info
return closure
end
local function get_closure_env(fn)
local info = closure_info[fn]
if not info then
return nil
end
if info[3] and getfenv then
return getfenv(fn)
end
return info[2]
end
local function set_closure_env(fn, newenv)
local info = closure_info[fn]
if not info then
return false
end
info[2] = newenv
if info[3] and setfenv then
info[3] = pcall(setfenv, fn, newenv)
end
return true
end
-- Sandbox-safe getfenv/setfenv. Both the CALLD special case and guest
-- pcall/xpcall route through these, so a native function environment (the
-- host _G) can never be reached from interpreted code.
sandbox_getfenv = function(arg, caller_env, frames, fi)
if arg == nil then
arg = 1
end
if type(arg) == "number" then
if arg == 0 then
return caller_env
end
if arg < 0 then
error("bad argument #1 to 'getfenv' (invalid level)", 0)
end
local idx = fi - arg + 1
if idx < 1 then
error("bad argument #1 to 'getfenv' (invalid level)", 0)
end
return frames[idx].env
elseif type(arg) == "function" then
if closure_info[arg] then
return get_closure_env(arg)
end
error("getfenv: native function environments are disabled", 0)
end
error("bad argument #1 to 'getfenv' (number or function expected)", 0)
end
sandbox_setfenv = function(target, newenv, frames, fi)
if type(newenv) ~= "table" then
error("bad argument #2 to 'setfenv' (table expected)", 0)
end
if type(target) == "number" then
if target <= 0 then
error("cannot change environment of thread", 0)
end
local idx = fi - target + 1
if idx < 1 then
error("invalid level", 0)
end
local fr = frames[idx]
set_closure_env(fr.cl, newenv)
local new_e
for j = 1, fi do
if frames[j].cl == fr.cl then
frames[j].env = newenv
if j == fi then
new_e = newenv
end
end
end
return fr.cl, new_e
elseif type(target) == "function" then
if closure_info[target] then
set_closure_env(target, newenv)
local new_e
for j = 1, fi do
if frames[j].cl == target then
frames[j].env = newenv
if j == fi then
new_e = newenv
end
end
end
return target, new_e
end
error("setfenv: native function environments are disabled", 0)
end
error("bad argument #1 to 'setfenv' (number or function expected)", 0)
end
-- The `F_*` opcode aliases are resolved to numeric literals by retree2.lua
-- when the dispatch tree is generated. They are intentionally not written to
-- `_G`: the linear source is an input to the generator, not a runtime module.
run_closure = function(fn, trace, ...)
trace = trace or {}
active_trace = trace
local CELL, NOP = 8, 50
local info = closure_info[fn]
local p = info[1]
local limits = info[1].limits or {}
local maxInstructions = limits.maxInstructions
local instructionBudget = maxInstructions
if instructionBudget == math.huge then instructionBudget = nil end
local args = pack(...)
local nargs = args.n
if limits.maxArgs and nargs > limits.maxArgs then
error("too many arguments", 0)
end
local S = {}
local top = 0
local frames = {}
local fi = 1
local free_frames = {}
local free_frame_count = 0
local base = 1
local maxreg = p.maxreg
local va = nil
if p.isvararg then
local nv = nargs - p.numparams
if nv > 0 then
va = { n = nv }
for i = 1, nv do
va[i] = args[p.numparams + i]
end
end
end
for i = 1, p.numparams do
S[i] = args[i]
end
for i = p.numparams + 1, maxreg do
S[i] = nil
end
top = maxreg
if limits.maxStack and top > limits.maxStack then
error("stack limit exceeded", 0)
end
local cenv = info[2]
if info[3] and getfenv then
cenv = getfenv(fn)
end
local cur = {
p = p, ups = info, base = base, pc = 1,
ret_sp = 0, wanted = -1, va = va, env = cenv, cl = fn,
line = nil,
}
frames[1] = cur
trace.p = p
trace.cur = cur
trace.frames = frames
trace.fi = fi
trace[1] = 1
local C = p.code
local K = p.consts
local U = info
local e = cenv
local pc = p.entry or 1
local has_jit = type(jit) == "table" and (jit.status == nil or jit.status() == true)
while true do
if instructionBudget then
instructionBudget = instructionBudget - 1
if instructionBudget == 0 then
error("instruction limit exceeded", 0)
end
end
trace[1] = pc
local op = C[pc]
if op >= 52 then
-- Fast-path ordering for the hottest fused opcodes.
-- Balanced dispatch tree (generated): ~5 compares instead of ~90.
if op < 140 then
if op < 83 then
if op < 67 then
if op < 59 then
if op < 55 then
if op == 52 then
S[base + C[pc + 1] - 1] = S[base + C[pc + 2] - 1]
pc = pc + 8
elseif op == 53 then
S[base + C[pc + 1] - 1] = S[base + C[pc + 2] - 1]
pc = pc + 12
elseif op == 54 then
S[base + C[pc + 1] - 1] =
{ v = S[base + C[pc + 2] - 1] }
pc = pc + 12
else
error("bad fused bytecode", 0)
end
else
if op == 55 then
S[base + C[pc + 1] - 1] = K[C[pc + 2]]
pc = pc + 8
elseif op == 56 then
local dest = base + C[pc + 1] - 1
local t = S[base + C[pc + 2] - 1]
local k = S[base + C[pc + 3] - 1]
S[dest] = t[k]
pc = pc + 16
elseif op == 57 then
local dest = base + C[pc + 1] - 1
local t = S[base + C[pc + 2] - 1]
S[dest] = t[K[C[pc + 3]]]
pc = pc + 12
elseif op == 58 then
top = top + 1
S[top] = S[base + C[pc + 1] - 1] + S[base + C[pc + 2] - 1]
pc = pc + 12
else
error("bad fused bytecode", 0)
end
end
else
if op < 62 then
if op == 59 then
top = top + 1
S[top] = S[base + C[pc + 1] - 1] + K[C[pc + 2]]
pc = pc + 12
elseif op == 60 then
top = top + 1
S[top] = S[base + C[pc + 1] - 1] - S[base + C[pc + 2] - 1]
pc = pc + 12
elseif op == 61 then
top = top + 1
S[top] = S[base + C[pc + 1] - 1] - K[C[pc + 2]]
pc = pc + 12
else
error("bad fused bytecode", 0)
end
else
if op < 64 then
if op == 62 then
top = top + 1
S[top] = S[base + C[pc + 1] - 1] * S[base + C[pc + 2] - 1]
pc = pc + 12
elseif op == 63 then
top = top + 1
S[top] = S[base + C[pc + 1] - 1] * K[C[pc + 2]]
pc = pc + 12
else
error("bad fused bytecode", 0)
end
else
if op == 64 then
top = top + 1
S[top] = S[base + C[pc + 1] - 1] / S[base + C[pc + 2] - 1]
pc = pc + 12
elseif op == 65 then
top = top + 1
S[top] = S[base + C[pc + 1] - 1] / K[C[pc + 2]]
pc = pc + 12
elseif op == 66 then
top = top + 1
S[top] = S[base + C[pc + 1] - 1] % S[base + C[pc + 2] - 1]
pc = pc + 12
else
error("bad fused bytecode", 0)
end
end
end
end
else
if op < 74 then
if op < 70 then
if op == 67 then
top = top + 1
S[top] = S[base + C[pc + 1] - 1] % K[C[pc + 2]]
pc = pc + 12
elseif op == 68 then
top = top + 1
S[top] = S[base + C[pc + 1] - 1] ^ S[base + C[pc + 2] - 1]
pc = pc + 12
elseif op == 69 then
top = top + 1
S[top] = S[base + C[pc + 1] - 1] ^ K[C[pc + 2]]
pc = pc + 12
else
error("bad fused bytecode", 0)
end
else
if op == 70 then
top = top + 1
S[top] = (S[base + C[pc + 1] - 1] == S[base + C[pc + 2] - 1])
pc = pc + 12
elseif op == 71 then
top = top + 1
S[top] = (S[base + C[pc + 1] - 1] == K[C[pc + 2]])
pc = pc + 12
elseif op == 72 then
top = top + 1
S[top] = (S[base + C[pc + 1] - 1] ~= S[base + C[pc + 2] - 1])
pc = pc + 12
elseif op == 73 then
top = top + 1
S[top] = (S[base + C[pc + 1] - 1] ~= K[C[pc + 2]])
pc = pc + 12
else
error("bad fused bytecode", 0)
end
end
else
if op < 78 then
if op == 74 then
top = top + 1
S[top] = (S[base + C[pc + 1] - 1] < S[base + C[pc + 2] - 1])
pc = pc + 12
elseif op == 75 then
top = top + 1
S[top] = (S[base + C[pc + 1] - 1] < K[C[pc + 2]])
pc = pc + 12
elseif op == 76 then
top = top + 1
S[top] = (S[base + C[pc + 1] - 1] <= S[base + C[pc + 2] - 1])
pc = pc + 12
elseif op == 77 then
top = top + 1
S[top] = (S[base + C[pc + 1] - 1] <= K[C[pc + 2]])
pc = pc + 12
else
error("bad fused bytecode", 0)
end
else
if op < 80 then
if op == 78 then
top = top + 1
S[top] = (S[base + C[pc + 1] - 1] > S[base + C[pc + 2] - 1])
pc = pc + 12
elseif op == 79 then
top = top + 1
S[top] = (S[base + C[pc + 1] - 1] > K[C[pc + 2]])
pc = pc + 12
else
error("bad fused bytecode", 0)
end
else
if op == 80 then
top = top + 1
S[top] = (S[base + C[pc + 1] - 1] >= S[base + C[pc + 2] - 1])
pc = pc + 12
elseif op == 81 then
top = top + 1
S[top] = (S[base + C[pc + 1] - 1] >= K[C[pc + 2]])
pc = pc + 12
elseif op == 82 then
S[top] = (S[top] == K[C[pc + 1]])
pc = pc + 8
else
error("bad fused bytecode", 0)
end
end
end
end
end
else
if op < 111 then
if op < 90 then
if op < 86 then
if op == 83 then
S[top] = (S[top] ~= K[C[pc + 1]])
pc = pc + 8
elseif op == 84 then
S[top] = (S[top] < K[C[pc + 1]])
pc = pc + 8
elseif op == 85 then
S[top] = (S[top] <= K[C[pc + 1]])
pc = pc + 8
else
error("bad fused bytecode", 0)
end
else
if op == 86 then
S[top] = (S[top] > K[C[pc + 1]])
pc = pc + 8
elseif op == 87 then
S[top] = (S[top] >= K[C[pc + 1]])
pc = pc + 8
elseif op == 88 then
local t = S[base + C[pc + 1] - 1]
local k = S[base + C[pc + 2] - 1]