luau-src 0.3.0

Luau source code bindings
Documentation
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
-- This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details
print("testing language/library basics")

function concat(head, ...)
    if select('#', ...) == 0 then
        return tostring(head)
    else
        return tostring(head) .. "," .. concat(...)
    end
end

-- constants
assert(tostring(1) == "1")
assert(tostring(-1) == "-1")
assert(tostring(1.125) == "1.125")
assert(tostring(true) == "true")
assert(tostring(nil) == "nil")

-- empty return
assert(select('#', (function() end)()) == 0)
assert(select('#', (function() return end)()) == 0)

-- locals
assert((function() local a = 1 return a end)() == 1)
assert((function() local a, b, c = 1, 2, 3 return c end)() == 3)
assert((function() local a, b, c = 1, 2 return c end)() == nil)
assert((function() local a = 1, 2 return a end)() == 1)

-- function calls
local function foo(a, b) return b end
assert(foo(1) == nil)
assert(foo(1, 2) == 2)
assert(foo(1, 2, 3) == 2)

-- pcall
assert(concat(pcall(function () end)) ==  "true")
assert(concat(pcall(function () return nil end)) ==  "true,nil")
assert(concat(pcall(function () return 1,2,3 end)) ==  "true,1,2,3")
assert(concat(pcall(function () error("oops") end)) ==  "false,basic.lua:39: oops")

-- assignments
assert((function() local a = 1 a = 2 return a end)() == 2)
assert((function() a = 1 a = 2 return a end)() == 2)
assert((function() local a = 1 a, b = 1 return a end)() == 1)
assert((function() local a = 1 a, b = 1 return b end)() == nil)
assert((function() local a = 1 b = 2 a, b = b, a return a end)() == 2)
assert((function() local a = 1 b = 2 a, b = b, a return b end)() == 1)
assert((function() _G.foo = 1 return _G['foo'] end)() == 1)
assert((function() _G['bar'] = 1 return _G.bar end)() == 1)
assert((function() local a = 1 (function () a = 2 end)() return a end)() == 2)

-- upvalues
assert((function() local a = 1 function foo() return a end return foo() end)() == 1)

-- check upvalue propagation - foo must have numupvalues=1
assert((function() local a = 1 function foo() return function() return a end end return foo()() end)() == 1)

-- check that function args are properly closed over
assert((function() function foo(a) return function () return a end end return foo(1)() end)() == 1)

-- this checks local aliasing - b & a should share the same local slot, but the capture must return 1 instead of 2
assert((function() function foo() local f do local a = 1 f = function () return a end end local b = 2 return f end return foo()() end)() == 1)

-- this checks local mutability - we capture a ref to 1 but must return 2
assert((function() function foo() local a = 1 local function f() return a end a = 2 return f end return foo()() end)() == 2)

-- this checks upval mutability - we change the value from a context where it's upval
assert((function() function foo() local a = 1 (function () a = 2 end)() return a end return foo() end)() == 2)

-- check self capture: does self go into any upvalues?
assert((function() local t = {f=5} function t:get() return (function() return self.f end)() end return t:get() end)() == 5)

-- check self capture & close: is self copied to upval?
assert((function() function foo() local t = {f=5} function t:get() return function() return self.f end end return t:get() end return foo()() end)() == 5)

-- if
assert((function() local a = 1 if a then a = 2 end return a end)() == 2)
assert((function() local a if a then a = 2 end return a end)() == nil)

assert((function() local a = 0 if a then a = 1 else a = 2 end return a end)() == 1)
assert((function() local a if a then a = 1 else a = 2 end return a end)() == 2)

-- binary ops
assert((function() local a = 1 a = a + 2 return a end)() == 3)
assert((function() local a = 1 a = a - 2 return a end)() == -1)
assert((function() local a = 1 a = a * 2 return a end)() == 2)
assert((function() local a = 1 a = a / 2 return a end)() == 0.5)
assert((function() local a = 5 a = a % 2 return a end)() == 1)
assert((function() local a = 3 a = a ^ 2 return a end)() == 9)

assert((function() local a = '1' a = a .. '2' return a end)() == "12")
assert((function() local a = '1' a = a .. '2' .. '3' return a end)() == "123")

assert(concat(pcall(function() return '1' .. nil .. '2' end)):match("^false,.*attempt to concatenate nil with string"))

assert((function() local a = 1 a = a == 2 return a end)() == false)
assert((function() local a = 1 a = a ~= 2 return a end)() == true)
assert((function() local a = 1 a = a < 2 return a end)() == true)
assert((function() local a = 1 a = a <= 2 return a end)() == true)
assert((function() local a = 1 a = a > 2 return a end)() == false)
assert((function() local a = 1 a = a >= 2 return a end)() == false)

assert((function() local a = 1 a = a and 2 return a end)() == 2)
assert((function() local a = nil a = a and 2 return a end)() == nil)
assert((function() local a = 1 a = a or 2 return a end)() == 1)
assert((function() local a = nil a = a or 2 return a end)() == 2)

-- binary arithmetics coerces strings to numbers (sadly)
assert(1 + "2" == 3)
assert(2 * "0xa" == 20)

-- unary ops
assert((function() local a = true a = not a return a end)() == false)
assert((function() local a = false a = not a return a end)() == true)
assert((function() local a = nil a = not a return a end)() == true)

assert((function() return #_G end)() == 0)
assert((function() return #{1,2} end)() == 2)
assert((function() return #'g' end)() == 1)

assert((function() local a = 1 a = -a return a end)() == -1)

-- while/repeat
assert((function() local a = 10 local b = 1 while a > 1 do b = b * 2 a = a - 1 end return b end)() == 512)
assert((function() local a = 10 local b = 1 repeat b = b * 2 a = a - 1 until a == 1 return b end)() == 512)

assert((function() local a = 10 local b = 1 while true do b = b * 2 a = a - 1 if a == 1 then break end end return b end)() == 512)
assert((function() local a = 10 local b = 1 while true do b = b * 2 a = a - 1 if a == 1 then break else end end return b end)() == 512)
assert((function() local a = 10 local b = 1 repeat b = b * 2 a = a - 1 if a == 1 then break end until false return b end)() == 512)
assert((function() local a = 10 local b = 1 repeat b = b * 2 a = a - 1 if a == 1 then break else end until false return b end)() == 512)

-- this makes sure a - 4 doesn't clobber a (which would happen if the lifetime of locals inside the repeat..until block is contained within
-- the block and ends before the condition is evaluated
assert((function() repeat local a = 5 until a - 4 < 0 or a - 4 >= 0 end)() == nil)

-- numeric for
-- basic tests with positive/negative step sizes
assert((function() local a = 1 for b=1,9 do a = a * 2 end return a end)() == 512)
assert((function() local a = 1 for b=1,9,2 do a = a * 2 end return a end)() == 32)
assert((function() local a = 1 for b=1,9,-2 do a = a * 2 end return a end)() == 1)
assert((function() local a = 1 for b=9,1,-2 do a = a * 2 end return a end)() == 32)

-- make sure break works
assert((function() local a = 1 for b=1,9 do a = a * 2 if a == 128 then break end end return a end)() == 128)
assert((function() local a = 1 for b=1,9 do a = a * 2 if a == 128 then break else end end return a end)() == 128)

-- make sure internal index is protected against modification
assert((function() local a = 1 for b=9,1,-2 do a = a * 2 b = nil end return a end)() == 32)

-- generic for
-- ipairs
assert((function() local a = '' for k in ipairs({5, 6, 7}) do a = a .. k end return a end)() == "123")
assert((function() local a = '' for k,v in ipairs({5, 6, 7}) do a = a .. k end return a end)() == "123")
assert((function() local a = '' for k,v in ipairs({5, 6, 7}) do a = a .. v end return a end)() == "567")

-- ipairs with gaps
assert((function() local a = '' for k in ipairs({5, 6, 7, nil, 8}) do a = a .. k end return a end)() == "123")
assert((function() local a = '' for k,v in ipairs({5, 6, 7, nil, 8}) do a = a .. k end return a end)() == "123")
assert((function() local a = '' for k,v in ipairs({5, 6, 7, nil, 8}) do a = a .. v end return a end)() == "567")

-- manual ipairs/inext
local inext = ipairs({5,6,7})
assert(concat(inext({5,6,7}, 2)) == "3,7")

-- pairs on array
assert((function() local a = '' for k in pairs({5, 6, 7}) do a = a .. k end return a end)() == "123")
assert((function() local a = '' for k,v in pairs({5, 6, 7}) do a = a .. k end return a end)() == "123")
assert((function() local a = '' for k,v in pairs({5, 6, 7}) do a = a .. v end return a end)() == "567")

-- pairs on array with gaps
assert((function() local a = '' for k in pairs({5, 6, 7, nil, 8}) do a = a .. k end return a end)() == "1235")
assert((function() local a = '' for k,v in pairs({5, 6, 7, nil, 8}) do a = a .. k end return a end)() == "1235")
assert((function() local a = '' for k,v in pairs({5, 6, 7, nil, 8}) do a = a .. v end return a end)() == "5678")

-- pairs on table
assert((function() local a = {} for k in pairs({a=1, b=2, c=3}) do a[k] = 1 end return a.a + a.b + a.c end)() == 3)
assert((function() local a = {} for k,v in pairs({a=1, b=2, c=3}) do a[k] = 1 end return a.a + a.b + a.c end)() == 3)
assert((function() local a = {} for k,v in pairs({a=1, b=2, c=3}) do a[k] = v end return a.a + a.b + a.c end)() == 6)

-- pairs on mixed array/table + gaps in the array portion
-- note that a,b,c results in a,c,b during traversal since index is based on hash & size
assert((function() local a = {} for k,v in pairs({1, 2, 3, a=5, b=6, c=7}) do a[#a+1] = v end return table.concat(a, ',') end)() == "1,2,3,5,7,6")
assert((function() local a = {} for k,v in pairs({1, 2, 3, nil, 4, a=5, b=6, c=7}) do a[#a+1] = v end return table.concat(a, ',') end)() == "1,2,3,4,5,7,6")

-- pairs manually
assert((function() local a = '' for k in next,{5, 6, 7} do a = a .. k end return a end)() == "123")
assert((function() local a = '' for k,v in next,{5, 6, 7} do a = a .. k end return a end)() == "123")
assert((function() local a = '' for k,v in next,{5, 6, 7} do a = a .. v end return a end)() == "567")
assert((function() local a = {} for k in next,{a=1, b=2, c=3} do a[k] = 1 end return a.a + a.b + a.c end)() == 3)
assert((function() local a = {} for k,v in next,{a=1, b=2, c=3} do a[k] = 1 end return a.a + a.b + a.c end)() == 3)
assert((function() local a = {} for k,v in next,{a=1, b=2, c=3} do a[k] = v end return a.a + a.b + a.c end)() == 6)

-- too many vars
assert((function() local a = '' for k,v,p in pairs({a=1, b=2, c=3}) do a = a .. tostring(p) end return a end)() == "nilnilnil")

-- make sure break works
assert((function() local a = 1 for _ in pairs({1,2,3}) do a = a * 2 if a == 4 then break end end return a end)() == 4)
assert((function() local a = 1 for _ in pairs({1,2,3}) do a = a * 2 if a == 4 then break else end end return a end)() == 4)

-- make sure internal index is protected against modification
assert((function() local a = 1 for b in pairs({1,2,3}) do a = a * 2 b = nil end return a end)() == 8)

-- make sure custom iterators work! example is from PIL 7.1
function list_iter(t)
    local i = 0
    local n = table.getn(t)
    return function()
        i = i + 1
        if i <= n then return t[i] end
    end
end

assert((function() local a = '' for e in list_iter({4,2,1}) do a = a .. e end return a end)() == "421")

-- make sure multret works in context of pairs() - this is a very painful to handle combination due to complex internal details
assert((function() local function f() return {5,6,7},8,9,0 end local a = '' for k,v in ipairs(f()) do a = a .. v end return a end)() == "567")

-- table literals
-- basic tests
assert((function() local t = {} return #t end)() == 0)

assert((function() local t = {1, 2} return #t end)() == 2)
assert((function() local t = {1, 2} return t[1] + t[2] end)() == 3)

assert((function() local t = {data = 4} return t.data end)() == 4)
assert((function() local t = {[1+2] = 4} return t[3] end)() == 4)

assert((function() local t = {data = 4, [1+2] = 5} return t.data + t[3] end)() == 9)

assert((function() local t = {[1] = 1, [2] = 2} return t[1] + t[2] end)() == 3)

-- since table ctor is chunked in groups of 16, we should be careful with edge cases around that
assert((function() return table.concat({}, ',') end)() == "")
assert((function() return table.concat({1}, ',') end)() == "1")
assert((function() return table.concat({1,2}, ',') end)() == "1,2")
assert((function() return table.concat({1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, ',') end)() == 
    "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15")
assert((function() return table.concat({1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}, ',') end)() == "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16")
assert((function() return table.concat({1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17}, ',') end)() == "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17")

-- some scripts rely on exact table traversal order; while it's evil to do so, let's check that it works
assert((function()
    local kSelectedBiomes = {
        ['Mountains'] = true,
        ['Canyons'] = true,
        ['Dunes'] = true,
        ['Arctic'] = true,
        ['Lavaflow'] = true,
        ['Hills'] = true,
        ['Plains'] = true,
        ['Marsh'] = true,
        ['Water'] = true,
    }
    local result = ""
    for k in pairs(kSelectedBiomes) do result = result .. k end
    return result
end)() == "ArcticDunesCanyonsWaterMountainsHillsLavaflowPlainsMarsh")

-- multiple returns
-- local=
assert((function() function foo() return 2, 3, 4 end local a, b, c = foo() return ''..a..b..c end)() == "234")
assert((function() function foo() return 2, 3, 4 end local a, b, c = 1, foo() return ''..a..b..c end)() == "123")
assert((function() function foo() return 2 end local a, b, c = 1, foo() return ''..a..b..tostring(c) end)() == "12nil")

-- assignments
assert((function() function foo() return 2, 3 end a, b, c, d = 1, foo() return ''..a..b..c..tostring(d) end)() == "123nil")
assert((function() function foo() return 2, 3 end local a, b, c, d a, b, c, d = 1, foo() return ''..a..b..c..tostring(d) end)() == "123nil")

-- varargs
-- local=
assert((function() function foo(...) local a, b, c = ... return a + b + c end return foo(1, 2, 3) end)() == 6)
assert((function() function foo(x, ...) local a, b, c = ... return a + b + c end return foo(1, 2, 3, 4) end)() == 9)

-- assignments
assert((function() function foo(...) a, b, c = ... return a + b + c end return foo(1, 2, 3) end)() == 6)
assert((function() function foo(x, ...) a, b, c = ... return a + b + c end return foo(1, 2, 3, 4) end)() == 9)

-- extra nils
assert((function() function foo(...) local a, b, c = ... return tostring(a) .. tostring(b) .. tostring(c) end return foo(1, 2) end)() == "12nil")

-- varargs + multiple returns
-- return
assert((function() function foo(...) return ... end return concat(foo(1, 2, 3)) end)() == "1,2,3")
assert((function() function foo(...) return ... end return foo() end)() == nil)
assert((function() function foo(a, ...) return a + 10, ... end return concat(foo(1, 2, 3)) end)() == "11,2,3")

-- call
assert((function() function foo(...) return ... end function bar(...) return foo(...) end return concat(bar(1, 2, 3)) end)() == "1,2,3")
assert((function() function foo(...) return ... end function bar(...) return foo(...) end return bar() end)() == nil)
assert((function() function foo(a, ...) return a + 10, ... end function bar(a, ...) return foo(a * 2, ...) end return concat(bar(1, 2, 3)) end)() == "12,2,3")

-- manual pack
assert((function() function pack(first, ...) if not first then return {} end local t = pack(...) table.insert(t, 1, first) return t end function foo(...) return pack(...) end return #foo(0, 1, 2) end)() == 3)

-- multret + table literals
-- basic tests
assert((function() function foo(...) return { ... } end return #(foo()) end)() == 0)
assert((function() function foo(...) return { ... } end return #(foo(1, 2, 3)) end)() == 3)
assert((function() function foo() return 1, 2, 3 end return #({foo()}) end)() == 3)

-- since table ctor is chunked in groups of 16, we should be careful with edge cases around that
assert((function() function foo() return 1, 2, 3 end return table.concat({foo()}, ',') end)() == "1,2,3")
assert((function() function foo() return 1, 2, 3 end return table.concat({1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, foo()}, ',') end)() == "1,2,3,4,5,6,7,8,9,10,11,12,13,14,1,2,3")
assert((function() function foo() return 1, 2, 3 end return table.concat({1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, foo()}, ',') end)() == "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,1,2,3")
assert((function() function foo() return 1, 2, 3 end return table.concat({1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, foo()}, ',') end)() == "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,1,2,3")
assert((function() function foo() return 1, 2, 3 end return table.concat({1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, foo()}, ',') end)() == "1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,1,2,3")

-- table access
assert((function() local t = {6, 9, 7} return t[2] end)() == 9)
assert((function() local t = {6, 9, 7} return t[0] end)() == nil)
assert((function() local t = {6, 9, 7} return t[4] end)() == nil)
assert((function() local t = {6, 9, 7} return t[4.5] end)() == nil)
assert((function() local t = {6, 9, 7, [4.5]=11} return t[4.5] end)() == 11)
assert((function() local t = {6, 9, 7, a=11} return t['a'] end)() == 11)
assert((function() local t = {6, 9, 7} setmetatable(t, { __index = function(t,i) return i * 10 end }) return concat(t[2],t[5]) end)() == "9,50")

assert((function() local t = {6, 9, 7} t[2] = 10 return t[2] end)() == 10)
assert((function() local t = {6, 9, 7} t[0] = 5 return t[0] end)() == 5)
assert((function() local t = {6, 9, 7} t[4] = 10 return t[4] end)() == 10)
assert((function() local t = {6, 9, 7} t[4.5] = 10 return t[4.5] end)() == 10)
assert((function() local t = {6, 9, 7} t['a'] = 11 return t['a'] end)() == 11)
assert((function() local t = {6, 9, 7} setmetatable(t, { __newindex = function(t,i,v) rawset(t, i * 10, v) end }) t[1] = 17 t[5] = 1 return concat(t[1],t[5],t[50]) end)() == "17,nil,1")

-- and/or
-- rhs is a constant
assert((function() local a = 1 a = a and 2 return a end)() == 2)
assert((function() local a = nil a = a and 2 return a end)() == nil)
assert((function() local a = 1 a = a or 2 return a end)() == 1)
assert((function() local a = nil a = a or 2 return a end)() == 2)

-- rhs is a local
assert((function() local a = 1 local b = 2 a = a and b return a end)() == 2)
assert((function() local a = nil local b = 2 a = a and b return a end)() == nil)
assert((function() local a = 1 local b = 2 a = a or b return a end)() == 1)
assert((function() local a = nil local b = 2 a = a or b return a end)() == 2)

-- rhs is a global (prevents optimizations)
assert((function() local a = 1 b = 2 a = a and b return a end)() == 2)
assert((function() local a = nil b = 2 a = a and b return a end)() == nil)
assert((function() local a = 1 b = 2 a = a or b return a end)() == 1)
assert((function() local a = nil b = 2 a = a or b return a end)() == 2)

-- table access: method calls + fake oop via mt
assert((function()
    local Class = {}
    Class.__index = Class

    function Class.new()
        local self = {}
        setmetatable(self, Class)

        self.field = 5

        return self
    end

    function Class:GetField()
        return self.field
    end

    local object = Class.new()
    return object:GetField()
end)() == 5)

-- table access: evil indexer
assert((function()
    local a = {5}
    local b = {6}
    local mt = { __index = function() return b[1] end }
    setmetatable(a, mt)
    b = a.hi
    return b
end)() == 6)

-- table access:  fast-path tests for array lookup
-- in-bounds array lookup shouldn't call into Lua, but if the element isn't there we'll still call the metatable
assert((function() local a = {9, [1.5] = 7} return a[1], a[2], a[1.5] end)() == 9,nil,7)
assert((function() local a = {9, [1.5] = 7} setmetatable(a, { __index = function() return 5 end }) return concat(a[1],a[2],a[1.5]) end)() == "9,5,7")
assert((function() local a = {9, nil, 11} setmetatable(a, { __index = function() return 5 end }) return concat(a[1],a[2],a[3],a[4]) end)() == "9,5,11,5")

-- namecall for userdata: technically not officially supported but hard to test in a different way!
-- warning: this test may break at any time as we may decide that we'll only use userdata-namecall on tagged user data objects
assert((function()
    local obj = newproxy(true)
    getmetatable(obj).__namecall = function(self, arg) return 42 + arg end
    return obj:Foo(10)
end)() == 52)
assert((function()
    local obj = newproxy(true)
    local t = {}
    setmetatable(t, { __call = function(self1, self2, arg) return 42 + arg end })
    getmetatable(obj).__namecall = t
    return obj:Foo(10)
end)() == 52)

-- namecall for oop to test fast paths
assert((function()
    local Class = {}
    Class.__index = Class

    function Class:new(klass, v) -- note, this isn't necessarily common but it exercises additional namecall paths
        local self = {value = v}
        setmetatable(self, Class)
        return self
    end

    function Class:get()
        return self.value
    end

    function Class:set(v)
        self.value = v
    end

    local n = Class:new(32)
    n:set(42)
    return n:get()
end)() == 42)

-- comparison
-- basic types
assert((function() a = nil return concat(a == nil, a ~= nil) end)() == "true,false")
assert((function() a = nil return concat(a == 1, a ~= 1) end)() == "false,true")
assert((function() a = 1 return concat(a == 1, a ~= 1) end)() == "true,false")
assert((function() a = 1 return concat(a == 2, a ~= 2) end)() == "false,true")
assert((function() a = true return concat(a == true, a ~= true) end)() == "true,false")
assert((function() a = true return concat(a == false, a ~= false) end)() == "false,true")
assert((function() a = 'a' return concat(a == 'a', a ~= 'a') end)() == "true,false")
assert((function() a = 'a' return concat(a == 'b', a ~= 'b') end)() == "false,true")

-- tables, reference equality (no mt)
assert((function() a = {} return concat(a == a, a ~= a) end)() == "true,false")
assert((function() a = {} b = {} return concat(a == b, a ~= b) end)() == "false,true")

-- tables, reference equality (mt without __eq)
assert((function() a = {} setmetatable(a, {}) return concat(a == a, a ~= a) end)() == "true,false")
assert((function() a = {} b = {} mt = {} setmetatable(a, mt) setmetatable(b, mt) return concat(a == b, a ~= b) end)() == "false,true")

-- tables, __eq with same mt/different mt but same function/different function
assert((function() a = {} b = {} mt = { __eq = function(l, r) return #l == #r end } setmetatable(a, mt) setmetatable(b, mt) return concat(a == b, a ~= b) end)() == "true,false")
assert((function() a = {} b = {} function eq(l, r) return #l == #r end setmetatable(a, {__eq = eq}) setmetatable(b, {__eq = eq}) return concat(a == b, a ~= b) end)() == "true,false")
assert((function() a = {} b = {} setmetatable(a, {__eq = function(l, r) return #l == #r end}) setmetatable(b, {__eq = function(l, r) return #l == #r end}) return concat(a == b, a ~= b) end)() == "false,true")

-- userdata, reference equality (no mt or mt.__eq)
assert((function() a = newproxy() return concat(a == newproxy(),a ~= newproxy()) end)() == "false,true")
assert((function() a = newproxy(true) return concat(a == newproxy(true),a ~= newproxy(true)) end)() == "false,true")

-- rawequal
assert(rawequal(true, 5) == false)
assert(rawequal(nil, nil) == true)
assert(rawequal(true, false) == false)
assert(rawequal(true, true) == true)
assert(rawequal(0, -0) == true)
assert(rawequal(1, 2) == false)
assert(rawequal("a", "a") == true)
assert(rawequal("a", "b") == false)
assert((function() a = {} b = {} mt = { __eq = function(l, r) return #l == #r end } setmetatable(a, mt) setmetatable(b, mt) return concat(a == b, rawequal(a, b)) end)() == "true,false")

-- metatable ops
local function vec3t(x, y, z)
    return setmetatable({ x=x, y=y, z=z}, {
        __add = function(l, r) return vec3t(l.x + r.x, l.y + r.y, l.z + r.z) end,
        __sub = function(l, r) return vec3t(l.x - r.x, l.y - r.y, l.z - r.z) end,
        __mul = function(l, r) return type(r) == "number" and vec3t(l.x * r, l.y * r, l.z * r) or vec3t(l.x * r.x, l.y * r.y, l.z * r.z) end,
        __div = function(l, r) return type(r) == "number" and vec3t(l.x / r, l.y / r, l.z / r) or vec3t(l.x / r.x, l.y / r.y, l.z / r.z) end,
        __unm = function(v) return vec3t(-v.x, -v.y, -v.z) end,
        __tostring = function(v) return string.format("%g, %g, %g", v.x, v.y, v.z) end
    })
end

-- reg vs reg
assert((function() return tostring(vec3t(1,2,3) + vec3t(4,5,6)) end)() == "5, 7, 9")
assert((function() return tostring(vec3t(1,2,3) - vec3t(4,5,6)) end)() == "-3, -3, -3")
assert((function() return tostring(vec3t(1,2,3) * vec3t(4,5,6)) end)() == "4, 10, 18")
assert((function() return tostring(vec3t(1,2,3) / vec3t(2,4,8)) end)() == "0.5, 0.5, 0.375")

-- reg vs constant
assert((function() return tostring(vec3t(1,2,3) * 2) end)() == "2, 4, 6")
assert((function() return tostring(vec3t(1,2,3) / 2) end)() == "0.5, 1, 1.5")

-- unary
assert((function() return tostring(-vec3t(1,2,3)) end)() == "-1, -2, -3")

-- string comparison
assert((function() function cmp(a,b) return a<b,a<=b,a>b,a>=b end return concat(cmp('a', 'b')) end)() == "true,true,false,false")
assert((function() function cmp(a,b) return a<b,a<=b,a>b,a>=b end return concat(cmp('a', 'a')) end)() == "false,true,false,true")
assert((function() function cmp(a,b) return a<b,a<=b,a>b,a>=b end return concat(cmp('a', '')) end)() == "false,false,true,true")
assert((function() function cmp(a,b) return a<b,a<=b,a>b,a>=b end return concat(cmp('', '\\0')) end)() == "true,true,false,false")
assert((function() function cmp(a,b) return a<b,a<=b,a>b,a>=b end return concat(cmp('abc', 'abd')) end)() == "true,true,false,false")
assert((function() function cmp(a,b) return a<b,a<=b,a>b,a>=b end return concat(cmp('ab\\0c', 'ab\\0d')) end)() == "true,true,false,false")
assert((function() function cmp(a,b) return a<b,a<=b,a>b,a>=b end return concat(cmp('ab\\0c', 'ab\\0')) end)() == "false,false,true,true")

-- array access
assert((function() local a = {4,5,6} return a[3] end)() == 6)
assert((function() local a = {4,5,nil,6} return a[3] end)() == nil)
assert((function() local a = {4,5,nil,6} setmetatable(a, { __index = function() return 42 end }) return a[4] end)() == 6)
assert((function() local a = {4,5,nil,6} setmetatable(a, { __index = function() return 42 end }) return a[3] end)() == 42)
assert((function() local a = {4,5,6} a[3] = 8 return a[3] end)() == 8)
assert((function() local a = {4,5,nil,6} a[3] = 8 return a[3] end)() == 8)
assert((function() local a = {4,5,nil,6} setmetatable(a, { __newindex = function(t,i) rawset(t,i,42) end }) a[4] = 0 return a[4] end)() == 0)
assert((function() local a = {4,5,nil,6} setmetatable(a, { __newindex = function(t,i) rawset(t,i,42) end }) a[3] = 0 return a[3] end)() == 42)

-- array index for literal
assert((function() local a = {4, 5, nil, 6} return concat(a[1], a[3], a[4], a[100]) end)() == "4,nil,6,nil")
assert((function() local a = {4, 5, nil, 6} a[1] = 42 a[3] = 0 a[100] = 75 return concat(a[1], a[3], a[75], a[100]) end)() == "42,0,nil,75")

-- load error
assert((function() return concat(loadstring('hello world')) end)() == "nil,[string \"hello world\"]:1: Incomplete statement: expected assignment or a function call")

-- many arguments & locals
function f(p1, p2, p3, p4, p5, p6, p7, p8, p9, p10,
    p11, p12, p13, p14, p15, p16, p17, p18, p19, p20,
    p21, p22, p23, p24, p25, p26, p27, p28, p29, p30,
    p31, p32, p33, p34, p35, p36, p37, p38, p39, p40,
    p41, p42, p43, p44, p45, p46, p48, p49, p50, ...)
    local a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a12, a13, a14
end

assert(f() == nil)

-- upvalues & loops (validates timely closing)
assert((function()
    local res = {}

    for i=1,5 do
        res[#res+1] = (function() return i end)
    end

    local sum = 0
    for i,f in pairs(res) do sum = sum + f() end

    return sum
end)() == 15)

assert((function()
    local res = {}

    for i in ipairs({1,2,3,4,5}) do
        res[#res+1] =(function() return i end)
    end

    local sum = 0
    for i,f in pairs(res) do sum = sum + f() end

    return sum
end)() == 15)

assert((function()
    local res = {}

    local i = 0
    while i <= 5 do
        local j = i
        res[#res+1] = (function() return j end)
        i = i + 1
    end

    local sum = 0
    for i,f in pairs(res) do sum = sum + f() end

    return sum
end)() == 15)

assert((function()
    local res = {}

    local i = 0
    repeat
        local j = i
        res[#res+1] = (function() return j end)
        i = i + 1
    until i > 5

    local sum = 0
    for i,f in pairs(res) do sum = sum + f() end

    return sum
end)() == 15)

-- upvalues & loops & break!
assert((function()
    local res = {}

    for i=1,10 do
        res[#res+1] = (function() return i end)
        if i == 5 then
            break
        end
    end

    local sum = 0
    for i,f in pairs(res) do sum = sum + f() end

    return sum
end)() == 15)

assert((function()
    local res = {}

    for i in ipairs({1,2,3,4,5,6,7,8,9,10}) do
        res[#res+1] =(function() return i end)
        if i == 5 then
            break
        end
    end

    local sum = 0
    for i,f in pairs(res) do sum = sum + f() end

    return sum
end)() == 15)

assert((function()
    local res = {}

    local i = 0
    while i < 10 do
        local j = i
        res[#res+1] = (function() return j end)
        if i == 5 then
            break
        end
        i = i + 1
    end

    local sum = 0
    for i,f in pairs(res) do sum = sum + f() end

    return sum
end)() == 15)

assert((function()
    local res = {}

    local i = 0
    repeat
        local j = i
        res[#res+1] = (function() return j end)
        if i == 5 then
            break
        end
        i = i + 1
    until i >= 10

    local sum = 0
    for i,f in pairs(res) do sum = sum + f() end

    return sum
end)() == 15)

-- the reason why this test is interesting is that the table created here has arraysize=0 and a single hash element with key = 1.0
-- ipairs must iterate through that
assert((function()
    local arr = { [1] = 42 }
    local sum = 0
    for i,v in ipairs(arr) do
    sum = sum + v
    end
    return sum
end)() == 42)

-- the reason why this test is interesting is it ensures we do correct mutability analysis for locals
local function chainTest(n)
     local first = nil
     local last = nil

     -- Build chain of n equality constraints
     for i = 0, n do
            local name = "v" .. i;
            if i == 0 then first = name end
            if i == n then last = name end
     end

     return concat(first, last)
end

assert(chainTest(100) == "v0,v100")

-- this validates import fallbacks
assert(pcall(function() return idontexist.a end) == false)

-- make sure that NaN is preserved by the bytecode compiler
local realnan = tostring(math.abs(0)/math.abs(0))
assert(tostring(0/0*0) == realnan)
assert(tostring((-1)^(1/2)) == realnan)

-- make sure that negative zero is preserved by bytecode compiler
assert(tostring(0) == "0")
assert(tostring(-0) == "-0")

-- test newline handling in long strings
assert((function() 
    local s1 = [[
]]
    local s2 = [[

]]
     local s3 = [[
foo
bar]]
     local s4 = [[
foo
bar
]]
    return concat(s1,s2,s3,s4)
end)() == ",\n,foo\nbar,foo\nbar\n")

-- fastcall
-- positive tests for all simple examples; note that in this case the call is a multret call (nresults=LUA_MULTRET)
assert((function() return math.abs(-5) end)() == 5)
assert((function() local abs = math.abs return abs(-5) end)() == 5)
assert((function() local abs = math.abs function foo() return abs(-5) end return foo() end)() == 5)

-- vararg testing - in this case nparams = LUA_MULTRET and it gets adjusted before execution
assert((function() function foo(...) return math.abs(...) end return foo(-5) end)() == 5)
assert((function() function foo(...) local abs = math.abs return abs(...) end return foo(-5) end)() == 5)
assert((function() local abs = math.abs function foo(...) return abs(...) end return foo(-5) end)() == 5)

-- NOTE: getfenv breaks fastcalls for the remainder of the source! hence why this is delayed until the end
function testgetfenv()
    getfenv()
    -- getfenv breaks fastcalls (we assume we can't rely on knowing the semantics), but behavior shouldn't change
    assert((function() return math.abs(-5) end)() == 5)
    assert((function() local abs = math.abs return abs(-5) end)() == 5)
    assert((function() local abs = math.abs function foo() return abs(-5) end return foo() end)() == 5)

    -- ... unless you actually reassign the function :D
    getfenv().math = { abs = function(n) return n*n end }
    assert((function() return math.abs(-5) end)() == 25)
    assert((function() local abs = math.abs return abs(-5) end)() == 25)
    assert((function() local abs = math.abs function foo() return abs(-5) end return foo() end)() == 25)
end

-- you need to have enough arguments and arguments of the right type; if you don't, we'll fallback to the regular code. This checks coercions
-- first to make sure all fallback paths work
assert((function() return math.abs('-5') end)() == 5)
assert((function() local abs = math.abs return abs('-5') end)() == 5)
assert((function() local abs = math.abs function foo() return abs('-5') end return foo() end)() == 5)

-- if you don't have enough arguments or types are wrong, we fall back to the regular execution; this checks that the error generated is actually correct
assert(concat(pcall(function() return math.abs() end)):match("missing argument #1 to 'abs'"))
assert(concat(pcall(function() return math.abs(nil) end)):match("invalid argument #1 to 'abs'"))
assert(concat(pcall(function() return math.abs({}) end)):match("invalid argument #1 to 'abs'"))

-- very large unpack
assert(select('#', table.unpack({1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1})) == 263)

-- basic continue in for/while/repeat loops
assert((function() local a = 1 for i=1,8 do a = a + 1 if a < 5 then continue end a = a * 2 end return a end)() == 190)
assert((function() local a = 1 while a < 100 do a = a + 1 if a < 5 then continue end a = a * 2 end return a end)() == 190)
assert((function() local a = 1 repeat a = a + 1 if a < 5 then continue end a = a * 2 until a > 100 return a end)() == 190)

-- upvalues, loops, continue
assert((function()
    local res = {}

    for i=1,10 do
        res[#res+1] = (function() return i end)
        if i == 5 then
            continue
        end
        i = i * 2
    end

    local sum = 0
    for i,f in pairs(res) do sum = sum + f() end

    return sum
end)() == 105)

assert((function()
    local res = {}

    for i in ipairs({1,2,3,4,5,6,7,8,9,10}) do
        res[#res+1] =(function() return i end)
        if i == 5 then
            continue
        end
        i = i * 2
    end

    local sum = 0
    for i,f in pairs(res) do sum = sum + f() end

    return sum
end)() == 105)

assert((function()
    local res = {}

    local i = 1
    while i <= 10 do
        local j = i
        res[#res+1] = (function() return j end)
        if i == 5 then
            i = i + 1
            continue
        end
        i = i + 1
        j = j * 2
    end

    local sum = 0
    for i,f in pairs(res) do sum = sum + f() end

    return sum
end)() == 105)

assert((function()
    local res = {}

    local i = 1
    repeat
        local j = i
        res[#res+1] = (function() return j end)
        if i == 5 then
            i = i + 1
            continue
        end
        i = i + 1
        j = j * 2
    until i > 10

    local sum = 0
    for i,f in pairs(res) do sum = sum + f() end

    return sum
end)() == 105)

-- upvalues: recursive capture
assert((function() local function fact(n) return n < 1 and 1 or n * fact(n-1) end return fact(5) end)() == 120)

-- basic compound assignment
assert((function()
    local a = 1
    b = 2
    local c = { value = 3 }
    local d = { 4 }
    local e = 3
    local f = 2

    a += 5
    b -= a
    c.value *= 3
    d[1] /= b
    e %= 2
    f ^= 4

    return concat(a,b,c.value,d[1],e,f)
end)() == "6,-4,9,-1,1,16")

-- compound concat
assert((function()
    local a = 'a'

    a ..= 'b'
    a ..= 'c' .. 'd'
    a ..= 'e' .. 'f' .. a

    return a
end)() == "abcdefabcd")

-- compound assignment with side effects validates lhs is evaluated once
assert((function()
    local res = { 1, 2, 3 }
    local count = 0

    res[(function() count += 1 return count end)()] += 5
    res[(function() count += 1 return count end)()] += 6
    res[(function() count += 1 return count end)()] += 7

    return table.concat(res, ',')
end)() == "6,8,10")

-- typeof == type in absence of custom userdata
assert(concat(typeof(5), typeof(nil), typeof({}), typeof(newproxy())) == "number,nil,table,userdata")

testgetfenv() -- DONT MOVE THIS LINE

return 'OK'