diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml index 72a0c9ffe..f997935f5 100644 --- a/.github/workflows/benchmark.yml +++ b/.github/workflows/benchmark.yml @@ -61,28 +61,28 @@ jobs: filter() { awk '/.*I\s+refs:\s+[0-9,]+/ {gsub(",", "", $4); X=$4} END {print "SUCCESS: '$1' : " X/1e7 "ms +/- 0% on luau-analyze"}' } - valgrind --tool=callgrind ./luau-analyze --mode=nonstrict bench/other/LuauPolyfillMap.lua 2>&1 | filter map-nonstrict | tee -a analyze-output.txt - valgrind --tool=callgrind ./luau-analyze --mode=strict bench/other/LuauPolyfillMap.lua 2>&1 | filter map-strict | tee -a analyze-output.txt - valgrind --tool=callgrind ./luau-analyze --mode=strict --fflags=LuauSolverV2 bench/other/LuauPolyfillMap.lua 2>&1 | filter map-dcr | tee -a analyze-output.txt - valgrind --tool=callgrind ./luau-analyze --mode=nonstrict bench/other/regex.lua 2>&1 | filter regex-nonstrict | tee -a analyze-output.txt - valgrind --tool=callgrind ./luau-analyze --mode=strict bench/other/regex.lua 2>&1 | filter regex-strict | tee -a analyze-output.txt - valgrind --tool=callgrind ./luau-analyze --mode=strict --fflags=LuauSolverV2 bench/other/regex.lua 2>&1 | filter regex-dcr | tee -a analyze-output.txt + valgrind --tool=callgrind ./luau-analyze --mode=nonstrict bench/other/LuauPolyfillMap.luau 2>&1 | filter map-nonstrict | tee -a analyze-output.txt + valgrind --tool=callgrind ./luau-analyze --mode=strict bench/other/LuauPolyfillMap.luau 2>&1 | filter map-strict | tee -a analyze-output.txt + valgrind --tool=callgrind ./luau-analyze --mode=strict --fflags=LuauSolverV2 bench/other/LuauPolyfillMap.luau 2>&1 | filter map-dcr | tee -a analyze-output.txt + valgrind --tool=callgrind ./luau-analyze --mode=nonstrict bench/other/regex.luau 2>&1 | filter regex-nonstrict | tee -a analyze-output.txt + valgrind --tool=callgrind ./luau-analyze --mode=strict bench/other/regex.luau 2>&1 | filter regex-strict | tee -a analyze-output.txt + valgrind --tool=callgrind ./luau-analyze --mode=strict --fflags=LuauSolverV2 bench/other/regex.luau 2>&1 | filter regex-dcr | tee -a analyze-output.txt - name: Run benchmark (compile) run: | filter() { awk '/.*I\s+refs:\s+[0-9,]+/ {gsub(",", "", $4); X=$4} END {print "SUCCESS: '$1' : " X/1e7 "ms +/- 0% on luau-compile"}' } - valgrind --tool=callgrind ./luau-compile --null -O0 bench/other/LuauPolyfillMap.lua 2>&1 | filter map-O0 | tee -a compile-output.txt - valgrind --tool=callgrind ./luau-compile --null -O1 bench/other/LuauPolyfillMap.lua 2>&1 | filter map-O1 | tee -a compile-output.txt - valgrind --tool=callgrind ./luau-compile --null -O2 bench/other/LuauPolyfillMap.lua 2>&1 | filter map-O2 | tee -a compile-output.txt - valgrind --tool=callgrind ./luau-compile --codegennull -O2 bench/other/LuauPolyfillMap.lua 2>&1 | filter map-O2-codegen | tee -a compile-output.txt - valgrind --tool=callgrind ./luau-compile --codegennull -O2 -t1 bench/other/LuauPolyfillMap.lua 2>&1 | filter map-O2-t1-codegen | tee -a compile-output.txt - valgrind --tool=callgrind ./luau-compile --null -O0 bench/other/regex.lua 2>&1 | filter regex-O0 | tee -a compile-output.txt - valgrind --tool=callgrind ./luau-compile --null -O1 bench/other/regex.lua 2>&1 | filter regex-O1 | tee -a compile-output.txt - valgrind --tool=callgrind ./luau-compile --null -O2 bench/other/regex.lua 2>&1 | filter regex-O2 | tee -a compile-output.txt - valgrind --tool=callgrind ./luau-compile --codegennull -O2 bench/other/regex.lua 2>&1 | filter regex-O2-codegen | tee -a compile-output.txt - valgrind --tool=callgrind ./luau-compile --codegennull -O2 -t1 bench/other/regex.lua 2>&1 | filter regex-O2-t1-codegen | tee -a compile-output.txt + valgrind --tool=callgrind ./luau-compile --null -O0 bench/other/LuauPolyfillMap.luau 2>&1 | filter map-O0 | tee -a compile-output.txt + valgrind --tool=callgrind ./luau-compile --null -O1 bench/other/LuauPolyfillMap.luau 2>&1 | filter map-O1 | tee -a compile-output.txt + valgrind --tool=callgrind ./luau-compile --null -O2 bench/other/LuauPolyfillMap.luau 2>&1 | filter map-O2 | tee -a compile-output.txt + valgrind --tool=callgrind ./luau-compile --codegennull -O2 bench/other/LuauPolyfillMap.luau 2>&1 | filter map-O2-codegen | tee -a compile-output.txt + valgrind --tool=callgrind ./luau-compile --codegennull -O2 -t1 bench/other/LuauPolyfillMap.luau 2>&1 | filter map-O2-t1-codegen | tee -a compile-output.txt + valgrind --tool=callgrind ./luau-compile --null -O0 bench/other/regex.luau 2>&1 | filter regex-O0 | tee -a compile-output.txt + valgrind --tool=callgrind ./luau-compile --null -O1 bench/other/regex.luau 2>&1 | filter regex-O1 | tee -a compile-output.txt + valgrind --tool=callgrind ./luau-compile --null -O2 bench/other/regex.luau 2>&1 | filter regex-O2 | tee -a compile-output.txt + valgrind --tool=callgrind ./luau-compile --codegennull -O2 bench/other/regex.luau 2>&1 | filter regex-O2-codegen | tee -a compile-output.txt + valgrind --tool=callgrind ./luau-compile --codegennull -O2 -t1 bench/other/regex.luau 2>&1 | filter regex-O2-t1-codegen | tee -a compile-output.txt - name: Checkout benchmark results uses: actions/checkout@v3 diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7a2b5f105..acc5d58d6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -46,9 +46,9 @@ jobs: - name: make cli run: | make -j2 config=sanitize werror=1 luau luau-analyze luau-compile # match config with tests to improve build time - ./luau tests/conformance/assert.lua - ./luau-analyze tests/conformance/assert.lua - ./luau-compile tests/conformance/assert.lua + ./luau tests/conformance/assert.luau + ./luau-analyze tests/conformance/assert.luau + ./luau-compile tests/conformance/assert.luau windows: runs-on: windows-latest @@ -81,9 +81,9 @@ jobs: shell: bash # necessary for fail-fast run: | cmake --build . --target Luau.Repl.CLI Luau.Analyze.CLI Luau.Compile.CLI --config Debug # match config with tests to improve build time - Debug/luau tests/conformance/assert.lua - Debug/luau-analyze tests/conformance/assert.lua - Debug/luau-compile tests/conformance/assert.lua + Debug/luau tests/conformance/assert.luau + Debug/luau-analyze tests/conformance/assert.luau + Debug/luau-compile tests/conformance/assert.luau coverage: runs-on: ubuntu-20.04 # needed for clang++-10 to avoid gcov compatibility issues diff --git a/bench/bench.py b/bench/bench.py index 002dfadb5..f609a1d28 100644 --- a/bench/bench.py +++ b/bench/bench.py @@ -889,7 +889,7 @@ def run(args, argsubcb): all_files = [subdir + os.sep + filename for subdir, dirs, files in os.walk(arguments.folder) for filename in files] for filepath in sorted(all_files): subdir, filename = os.path.split(filepath) - if filename.endswith(".lua"): + if filename.endswith(".luau"): if arguments.run_test == None or re.match(arguments.run_test, filename[:-4]): runTest(subdir, filename, filepath) diff --git a/bench/bench_support.lua b/bench/bench_support.luau similarity index 100% rename from bench/bench_support.lua rename to bench/bench_support.luau diff --git a/bench/gc/test_BinaryTree.lua b/bench/gc/test_BinaryTree.luau similarity index 100% rename from bench/gc/test_BinaryTree.lua rename to bench/gc/test_BinaryTree.luau diff --git a/bench/gc/test_GC_Boehm_Trees.lua b/bench/gc/test_GC_Boehm_Trees.luau similarity index 100% rename from bench/gc/test_GC_Boehm_Trees.lua rename to bench/gc/test_GC_Boehm_Trees.luau diff --git a/bench/gc/test_GC_Tree_Pruning_Eager.lua b/bench/gc/test_GC_Tree_Pruning_Eager.luau similarity index 100% rename from bench/gc/test_GC_Tree_Pruning_Eager.lua rename to bench/gc/test_GC_Tree_Pruning_Eager.luau diff --git a/bench/gc/test_GC_Tree_Pruning_Gen.lua b/bench/gc/test_GC_Tree_Pruning_Gen.luau similarity index 100% rename from bench/gc/test_GC_Tree_Pruning_Gen.lua rename to bench/gc/test_GC_Tree_Pruning_Gen.luau diff --git a/bench/gc/test_GC_Tree_Pruning_Lazy.lua b/bench/gc/test_GC_Tree_Pruning_Lazy.luau similarity index 100% rename from bench/gc/test_GC_Tree_Pruning_Lazy.lua rename to bench/gc/test_GC_Tree_Pruning_Lazy.luau diff --git a/bench/gc/test_GC_hashtable_Keyval.lua b/bench/gc/test_GC_hashtable_Keyval.luau similarity index 100% rename from bench/gc/test_GC_hashtable_Keyval.lua rename to bench/gc/test_GC_hashtable_Keyval.luau diff --git a/bench/gc/test_LB_mandel.lua b/bench/gc/test_LB_mandel.luau similarity index 100% rename from bench/gc/test_LB_mandel.lua rename to bench/gc/test_LB_mandel.luau diff --git a/bench/gc/test_LargeTableCtor_array.lua b/bench/gc/test_LargeTableCtor_array.luau similarity index 100% rename from bench/gc/test_LargeTableCtor_array.lua rename to bench/gc/test_LargeTableCtor_array.luau diff --git a/bench/gc/test_LargeTableCtor_hash.lua b/bench/gc/test_LargeTableCtor_hash.luau similarity index 100% rename from bench/gc/test_LargeTableCtor_hash.lua rename to bench/gc/test_LargeTableCtor_hash.luau diff --git a/bench/gc/test_Pcall_pcall_yield.lua b/bench/gc/test_Pcall_pcall_yield.luau similarity index 100% rename from bench/gc/test_Pcall_pcall_yield.lua rename to bench/gc/test_Pcall_pcall_yield.luau diff --git a/bench/gc/test_SunSpider_3d-raytrace.lua b/bench/gc/test_SunSpider_3d-raytrace.luau similarity index 100% rename from bench/gc/test_SunSpider_3d-raytrace.lua rename to bench/gc/test_SunSpider_3d-raytrace.luau diff --git a/bench/gc/test_TableCreate_nil.lua b/bench/gc/test_TableCreate_nil.luau similarity index 100% rename from bench/gc/test_TableCreate_nil.lua rename to bench/gc/test_TableCreate_nil.luau diff --git a/bench/gc/test_TableCreate_number.lua b/bench/gc/test_TableCreate_number.luau similarity index 100% rename from bench/gc/test_TableCreate_number.lua rename to bench/gc/test_TableCreate_number.luau diff --git a/bench/gc/test_TableCreate_zerofill.lua b/bench/gc/test_TableCreate_zerofill.luau similarity index 100% rename from bench/gc/test_TableCreate_zerofill.lua rename to bench/gc/test_TableCreate_zerofill.luau diff --git a/bench/gc/test_TableMarshal_select.lua b/bench/gc/test_TableMarshal_select.luau similarity index 100% rename from bench/gc/test_TableMarshal_select.lua rename to bench/gc/test_TableMarshal_select.luau diff --git a/bench/gc/test_TableMarshal_table_pack.lua b/bench/gc/test_TableMarshal_table_pack.luau similarity index 100% rename from bench/gc/test_TableMarshal_table_pack.lua rename to bench/gc/test_TableMarshal_table_pack.luau diff --git a/bench/gc/test_TableMarshal_varargs.lua b/bench/gc/test_TableMarshal_varargs.luau similarity index 100% rename from bench/gc/test_TableMarshal_varargs.lua rename to bench/gc/test_TableMarshal_varargs.luau diff --git a/bench/micro_tests/test_AbsSum_abs.lua b/bench/micro_tests/test_AbsSum_abs.luau similarity index 100% rename from bench/micro_tests/test_AbsSum_abs.lua rename to bench/micro_tests/test_AbsSum_abs.luau diff --git a/bench/micro_tests/test_AbsSum_and_or.lua b/bench/micro_tests/test_AbsSum_and_or.luau similarity index 100% rename from bench/micro_tests/test_AbsSum_and_or.lua rename to bench/micro_tests/test_AbsSum_and_or.luau diff --git a/bench/micro_tests/test_AbsSum_math_abs.lua b/bench/micro_tests/test_AbsSum_math_abs.luau similarity index 100% rename from bench/micro_tests/test_AbsSum_math_abs.lua rename to bench/micro_tests/test_AbsSum_math_abs.luau diff --git a/bench/micro_tests/test_Assert.lua b/bench/micro_tests/test_Assert.luau similarity index 100% rename from bench/micro_tests/test_Assert.lua rename to bench/micro_tests/test_Assert.luau diff --git a/bench/micro_tests/test_Factorial.lua b/bench/micro_tests/test_Factorial.luau similarity index 100% rename from bench/micro_tests/test_Factorial.lua rename to bench/micro_tests/test_Factorial.luau diff --git a/bench/micro_tests/test_Failure_pcall_a_bar.lua b/bench/micro_tests/test_Failure_pcall_a_bar.luau similarity index 100% rename from bench/micro_tests/test_Failure_pcall_a_bar.lua rename to bench/micro_tests/test_Failure_pcall_a_bar.luau diff --git a/bench/micro_tests/test_Failure_pcall_game_Foo.lua b/bench/micro_tests/test_Failure_pcall_game_Foo.luau similarity index 100% rename from bench/micro_tests/test_Failure_pcall_game_Foo.lua rename to bench/micro_tests/test_Failure_pcall_game_Foo.luau diff --git a/bench/micro_tests/test_Failure_xpcall_a_bar.lua b/bench/micro_tests/test_Failure_xpcall_a_bar.luau similarity index 100% rename from bench/micro_tests/test_Failure_xpcall_a_bar.lua rename to bench/micro_tests/test_Failure_xpcall_a_bar.luau diff --git a/bench/micro_tests/test_Failure_xpcall_game_Foo.lua b/bench/micro_tests/test_Failure_xpcall_game_Foo.luau similarity index 100% rename from bench/micro_tests/test_Failure_xpcall_game_Foo.lua rename to bench/micro_tests/test_Failure_xpcall_game_Foo.luau diff --git a/bench/micro_tests/test_LargeTableCtor_array.lua b/bench/micro_tests/test_LargeTableCtor_array.luau similarity index 100% rename from bench/micro_tests/test_LargeTableCtor_array.lua rename to bench/micro_tests/test_LargeTableCtor_array.luau diff --git a/bench/micro_tests/test_LargeTableCtor_hash.lua b/bench/micro_tests/test_LargeTableCtor_hash.luau similarity index 100% rename from bench/micro_tests/test_LargeTableCtor_hash.lua rename to bench/micro_tests/test_LargeTableCtor_hash.luau diff --git a/bench/micro_tests/test_LargeTableSum_loop_index.lua b/bench/micro_tests/test_LargeTableSum_loop_index.luau similarity index 100% rename from bench/micro_tests/test_LargeTableSum_loop_index.lua rename to bench/micro_tests/test_LargeTableSum_loop_index.luau diff --git a/bench/micro_tests/test_LargeTableSum_loop_ipairs.lua b/bench/micro_tests/test_LargeTableSum_loop_ipairs.luau similarity index 100% rename from bench/micro_tests/test_LargeTableSum_loop_ipairs.lua rename to bench/micro_tests/test_LargeTableSum_loop_ipairs.luau diff --git a/bench/micro_tests/test_LargeTableSum_loop_iter.lua b/bench/micro_tests/test_LargeTableSum_loop_iter.luau similarity index 100% rename from bench/micro_tests/test_LargeTableSum_loop_iter.lua rename to bench/micro_tests/test_LargeTableSum_loop_iter.luau diff --git a/bench/micro_tests/test_LargeTableSum_loop_pairs.lua b/bench/micro_tests/test_LargeTableSum_loop_pairs.luau similarity index 100% rename from bench/micro_tests/test_LargeTableSum_loop_pairs.lua rename to bench/micro_tests/test_LargeTableSum_loop_pairs.luau diff --git a/bench/micro_tests/test_MethodCalls.lua b/bench/micro_tests/test_MethodCalls.luau similarity index 100% rename from bench/micro_tests/test_MethodCalls.lua rename to bench/micro_tests/test_MethodCalls.luau diff --git a/bench/micro_tests/test_OOP_constructor.lua b/bench/micro_tests/test_OOP_constructor.luau similarity index 100% rename from bench/micro_tests/test_OOP_constructor.lua rename to bench/micro_tests/test_OOP_constructor.luau diff --git a/bench/micro_tests/test_OOP_method_call.lua b/bench/micro_tests/test_OOP_method_call.luau similarity index 100% rename from bench/micro_tests/test_OOP_method_call.lua rename to bench/micro_tests/test_OOP_method_call.luau diff --git a/bench/micro_tests/test_OOP_virtual_constructor.lua b/bench/micro_tests/test_OOP_virtual_constructor.luau similarity index 100% rename from bench/micro_tests/test_OOP_virtual_constructor.lua rename to bench/micro_tests/test_OOP_virtual_constructor.luau diff --git a/bench/micro_tests/test_Pcall_call_return.lua b/bench/micro_tests/test_Pcall_call_return.luau similarity index 100% rename from bench/micro_tests/test_Pcall_call_return.lua rename to bench/micro_tests/test_Pcall_call_return.luau diff --git a/bench/micro_tests/test_Pcall_pcall_return.lua b/bench/micro_tests/test_Pcall_pcall_return.luau similarity index 100% rename from bench/micro_tests/test_Pcall_pcall_return.lua rename to bench/micro_tests/test_Pcall_pcall_return.luau diff --git a/bench/micro_tests/test_Pcall_pcall_yield.lua b/bench/micro_tests/test_Pcall_pcall_yield.luau similarity index 100% rename from bench/micro_tests/test_Pcall_pcall_yield.lua rename to bench/micro_tests/test_Pcall_pcall_yield.luau diff --git a/bench/micro_tests/test_Pcall_xpcall_return.lua b/bench/micro_tests/test_Pcall_xpcall_return.luau similarity index 100% rename from bench/micro_tests/test_Pcall_xpcall_return.lua rename to bench/micro_tests/test_Pcall_xpcall_return.luau diff --git a/bench/micro_tests/test_SqrtSum_exponent.lua b/bench/micro_tests/test_SqrtSum_exponent.luau similarity index 100% rename from bench/micro_tests/test_SqrtSum_exponent.lua rename to bench/micro_tests/test_SqrtSum_exponent.luau diff --git a/bench/micro_tests/test_SqrtSum_math_sqrt.lua b/bench/micro_tests/test_SqrtSum_math_sqrt.luau similarity index 100% rename from bench/micro_tests/test_SqrtSum_math_sqrt.lua rename to bench/micro_tests/test_SqrtSum_math_sqrt.luau diff --git a/bench/micro_tests/test_SqrtSum_sqrt.lua b/bench/micro_tests/test_SqrtSum_sqrt.luau similarity index 100% rename from bench/micro_tests/test_SqrtSum_sqrt.lua rename to bench/micro_tests/test_SqrtSum_sqrt.luau diff --git a/bench/micro_tests/test_SqrtSum_sqrt_getfenv.lua b/bench/micro_tests/test_SqrtSum_sqrt_getfenv.luau similarity index 100% rename from bench/micro_tests/test_SqrtSum_sqrt_getfenv.lua rename to bench/micro_tests/test_SqrtSum_sqrt_getfenv.luau diff --git a/bench/micro_tests/test_SqrtSum_sqrt_roundabout.lua b/bench/micro_tests/test_SqrtSum_sqrt_roundabout.luau similarity index 100% rename from bench/micro_tests/test_SqrtSum_sqrt_roundabout.lua rename to bench/micro_tests/test_SqrtSum_sqrt_roundabout.luau diff --git a/bench/micro_tests/test_StringInterp.lua b/bench/micro_tests/test_StringInterp.luau similarity index 100% rename from bench/micro_tests/test_StringInterp.lua rename to bench/micro_tests/test_StringInterp.luau diff --git a/bench/micro_tests/test_TableCreate_nil.lua b/bench/micro_tests/test_TableCreate_nil.luau similarity index 100% rename from bench/micro_tests/test_TableCreate_nil.lua rename to bench/micro_tests/test_TableCreate_nil.luau diff --git a/bench/micro_tests/test_TableCreate_number.lua b/bench/micro_tests/test_TableCreate_number.luau similarity index 100% rename from bench/micro_tests/test_TableCreate_number.lua rename to bench/micro_tests/test_TableCreate_number.luau diff --git a/bench/micro_tests/test_TableCreate_zerofill.lua b/bench/micro_tests/test_TableCreate_zerofill.luau similarity index 100% rename from bench/micro_tests/test_TableCreate_zerofill.lua rename to bench/micro_tests/test_TableCreate_zerofill.luau diff --git a/bench/micro_tests/test_TableFind_loop_ipairs.lua b/bench/micro_tests/test_TableFind_loop_ipairs.luau similarity index 100% rename from bench/micro_tests/test_TableFind_loop_ipairs.lua rename to bench/micro_tests/test_TableFind_loop_ipairs.luau diff --git a/bench/micro_tests/test_TableFind_table_find.lua b/bench/micro_tests/test_TableFind_table_find.luau similarity index 100% rename from bench/micro_tests/test_TableFind_table_find.lua rename to bench/micro_tests/test_TableFind_table_find.luau diff --git a/bench/micro_tests/test_TableInsertion_index_cached.lua b/bench/micro_tests/test_TableInsertion_index_cached.luau similarity index 100% rename from bench/micro_tests/test_TableInsertion_index_cached.lua rename to bench/micro_tests/test_TableInsertion_index_cached.luau diff --git a/bench/micro_tests/test_TableInsertion_index_len.lua b/bench/micro_tests/test_TableInsertion_index_len.luau similarity index 100% rename from bench/micro_tests/test_TableInsertion_index_len.lua rename to bench/micro_tests/test_TableInsertion_index_len.luau diff --git a/bench/micro_tests/test_TableInsertion_table_insert.lua b/bench/micro_tests/test_TableInsertion_table_insert.luau similarity index 100% rename from bench/micro_tests/test_TableInsertion_table_insert.lua rename to bench/micro_tests/test_TableInsertion_table_insert.luau diff --git a/bench/micro_tests/test_TableInsertion_table_insert_index.lua b/bench/micro_tests/test_TableInsertion_table_insert_index.luau similarity index 100% rename from bench/micro_tests/test_TableInsertion_table_insert_index.lua rename to bench/micro_tests/test_TableInsertion_table_insert_index.luau diff --git a/bench/micro_tests/test_TableIteration.lua b/bench/micro_tests/test_TableIteration.luau similarity index 100% rename from bench/micro_tests/test_TableIteration.lua rename to bench/micro_tests/test_TableIteration.luau diff --git a/bench/micro_tests/test_TableMarshal_select.lua b/bench/micro_tests/test_TableMarshal_select.luau similarity index 100% rename from bench/micro_tests/test_TableMarshal_select.lua rename to bench/micro_tests/test_TableMarshal_select.luau diff --git a/bench/micro_tests/test_TableMarshal_table_pack.lua b/bench/micro_tests/test_TableMarshal_table_pack.luau similarity index 100% rename from bench/micro_tests/test_TableMarshal_table_pack.lua rename to bench/micro_tests/test_TableMarshal_table_pack.luau diff --git a/bench/micro_tests/test_TableMarshal_table_unpack_array.lua b/bench/micro_tests/test_TableMarshal_table_unpack_array.luau similarity index 100% rename from bench/micro_tests/test_TableMarshal_table_unpack_array.lua rename to bench/micro_tests/test_TableMarshal_table_unpack_array.luau diff --git a/bench/micro_tests/test_TableMarshal_table_unpack_range.lua b/bench/micro_tests/test_TableMarshal_table_unpack_range.luau similarity index 100% rename from bench/micro_tests/test_TableMarshal_table_unpack_range.lua rename to bench/micro_tests/test_TableMarshal_table_unpack_range.luau diff --git a/bench/micro_tests/test_TableMarshal_varargs.lua b/bench/micro_tests/test_TableMarshal_varargs.luau similarity index 100% rename from bench/micro_tests/test_TableMarshal_varargs.lua rename to bench/micro_tests/test_TableMarshal_varargs.luau diff --git a/bench/micro_tests/test_TableMove_empty_table.lua b/bench/micro_tests/test_TableMove_empty_table.luau similarity index 100% rename from bench/micro_tests/test_TableMove_empty_table.lua rename to bench/micro_tests/test_TableMove_empty_table.luau diff --git a/bench/micro_tests/test_TableMove_same_table.lua b/bench/micro_tests/test_TableMove_same_table.luau similarity index 100% rename from bench/micro_tests/test_TableMove_same_table.lua rename to bench/micro_tests/test_TableMove_same_table.luau diff --git a/bench/micro_tests/test_TableMove_table_create.lua b/bench/micro_tests/test_TableMove_table_create.luau similarity index 100% rename from bench/micro_tests/test_TableMove_table_create.lua rename to bench/micro_tests/test_TableMove_table_create.luau diff --git a/bench/micro_tests/test_TableRemoval_table_remove.lua b/bench/micro_tests/test_TableRemoval_table_remove.luau similarity index 100% rename from bench/micro_tests/test_TableRemoval_table_remove.lua rename to bench/micro_tests/test_TableRemoval_table_remove.luau diff --git a/bench/micro_tests/test_TableSort.lua b/bench/micro_tests/test_TableSort.luau similarity index 100% rename from bench/micro_tests/test_TableSort.lua rename to bench/micro_tests/test_TableSort.luau diff --git a/bench/micro_tests/test_ToNumberString.lua b/bench/micro_tests/test_ToNumberString.luau similarity index 100% rename from bench/micro_tests/test_ToNumberString.lua rename to bench/micro_tests/test_ToNumberString.luau diff --git a/bench/micro_tests/test_UpvalueCapture.lua b/bench/micro_tests/test_UpvalueCapture.luau similarity index 100% rename from bench/micro_tests/test_UpvalueCapture.lua rename to bench/micro_tests/test_UpvalueCapture.luau diff --git a/bench/micro_tests/test_VariadicSelect.lua b/bench/micro_tests/test_VariadicSelect.luau similarity index 100% rename from bench/micro_tests/test_VariadicSelect.lua rename to bench/micro_tests/test_VariadicSelect.luau diff --git a/bench/micro_tests/test_string_lib.lua b/bench/micro_tests/test_string_lib.luau similarity index 100% rename from bench/micro_tests/test_string_lib.lua rename to bench/micro_tests/test_string_lib.luau diff --git a/bench/micro_tests/test_table_concat.lua b/bench/micro_tests/test_table_concat.luau similarity index 100% rename from bench/micro_tests/test_table_concat.lua rename to bench/micro_tests/test_table_concat.luau diff --git a/bench/other/LuauPolyfillMap.lua b/bench/other/LuauPolyfillMap.luau similarity index 100% rename from bench/other/LuauPolyfillMap.lua rename to bench/other/LuauPolyfillMap.luau diff --git a/bench/other/boatbomber-HashLib/Base64.lua b/bench/other/boatbomber-HashLib/Base64.luau similarity index 100% rename from bench/other/boatbomber-HashLib/Base64.lua rename to bench/other/boatbomber-HashLib/Base64.luau diff --git a/bench/other/boatbomber-HashLib/HashLib.spec.lua b/bench/other/boatbomber-HashLib/HashLib.spec.luau similarity index 100% rename from bench/other/boatbomber-HashLib/HashLib.spec.lua rename to bench/other/boatbomber-HashLib/HashLib.spec.luau diff --git a/bench/other/boatbomber-HashLib/init.lua b/bench/other/boatbomber-HashLib/init.luau similarity index 100% rename from bench/other/boatbomber-HashLib/init.lua rename to bench/other/boatbomber-HashLib/init.luau diff --git a/bench/other/regex.lua b/bench/other/regex.luau similarity index 100% rename from bench/other/regex.lua rename to bench/other/regex.luau diff --git a/bench/tests/base64.lua b/bench/tests/base64.luau similarity index 100% rename from bench/tests/base64.lua rename to bench/tests/base64.luau diff --git a/bench/tests/chess.lua b/bench/tests/chess.luau similarity index 100% rename from bench/tests/chess.lua rename to bench/tests/chess.luau diff --git a/bench/tests/life.lua b/bench/tests/life.luau similarity index 100% rename from bench/tests/life.lua rename to bench/tests/life.luau diff --git a/bench/tests/matrixmult.lua b/bench/tests/matrixmult.luau similarity index 100% rename from bench/tests/matrixmult.lua rename to bench/tests/matrixmult.luau diff --git a/bench/tests/mesh-normal-scalar.lua b/bench/tests/mesh-normal-scalar.luau similarity index 100% rename from bench/tests/mesh-normal-scalar.lua rename to bench/tests/mesh-normal-scalar.luau diff --git a/bench/tests/mesh-normal-vector.lua b/bench/tests/mesh-normal-vector.luau similarity index 100% rename from bench/tests/mesh-normal-vector.lua rename to bench/tests/mesh-normal-vector.luau diff --git a/bench/tests/pcmmix.lua b/bench/tests/pcmmix.luau similarity index 100% rename from bench/tests/pcmmix.lua rename to bench/tests/pcmmix.luau diff --git a/bench/tests/qsort.lua b/bench/tests/qsort.luau similarity index 100% rename from bench/tests/qsort.lua rename to bench/tests/qsort.luau diff --git a/bench/tests/sha256.lua b/bench/tests/sha256.luau similarity index 100% rename from bench/tests/sha256.lua rename to bench/tests/sha256.luau diff --git a/bench/tests/shootout/ack.lua b/bench/tests/shootout/ack.luau similarity index 100% rename from bench/tests/shootout/ack.lua rename to bench/tests/shootout/ack.luau diff --git a/bench/tests/shootout/binary-trees.lua b/bench/tests/shootout/binary-trees.luau similarity index 100% rename from bench/tests/shootout/binary-trees.lua rename to bench/tests/shootout/binary-trees.luau diff --git a/bench/tests/shootout/fannkuch-redux.lua b/bench/tests/shootout/fannkuch-redux.luau similarity index 100% rename from bench/tests/shootout/fannkuch-redux.lua rename to bench/tests/shootout/fannkuch-redux.luau diff --git a/bench/tests/shootout/fixpoint-fact.lua b/bench/tests/shootout/fixpoint-fact.luau similarity index 100% rename from bench/tests/shootout/fixpoint-fact.lua rename to bench/tests/shootout/fixpoint-fact.luau diff --git a/bench/tests/shootout/heapsort.lua b/bench/tests/shootout/heapsort.luau similarity index 100% rename from bench/tests/shootout/heapsort.lua rename to bench/tests/shootout/heapsort.luau diff --git a/bench/tests/shootout/mandel.lua b/bench/tests/shootout/mandel.luau similarity index 100% rename from bench/tests/shootout/mandel.lua rename to bench/tests/shootout/mandel.luau diff --git a/bench/tests/shootout/n-body.lua b/bench/tests/shootout/n-body.luau similarity index 100% rename from bench/tests/shootout/n-body.lua rename to bench/tests/shootout/n-body.luau diff --git a/bench/tests/shootout/qt.lua b/bench/tests/shootout/qt.luau similarity index 100% rename from bench/tests/shootout/qt.lua rename to bench/tests/shootout/qt.luau diff --git a/bench/tests/shootout/queen.lua b/bench/tests/shootout/queen.luau similarity index 100% rename from bench/tests/shootout/queen.lua rename to bench/tests/shootout/queen.luau diff --git a/bench/tests/shootout/scimark.lua b/bench/tests/shootout/scimark.luau similarity index 100% rename from bench/tests/shootout/scimark.lua rename to bench/tests/shootout/scimark.luau diff --git a/bench/tests/shootout/spectral-norm.lua b/bench/tests/shootout/spectral-norm.luau similarity index 100% rename from bench/tests/shootout/spectral-norm.lua rename to bench/tests/shootout/spectral-norm.luau diff --git a/bench/tests/sieve.lua b/bench/tests/sieve.luau similarity index 100% rename from bench/tests/sieve.lua rename to bench/tests/sieve.luau diff --git a/bench/tests/sunspider/3d-cube.lua b/bench/tests/sunspider/3d-cube.luau similarity index 100% rename from bench/tests/sunspider/3d-cube.lua rename to bench/tests/sunspider/3d-cube.luau diff --git a/bench/tests/sunspider/3d-morph.lua b/bench/tests/sunspider/3d-morph.luau similarity index 100% rename from bench/tests/sunspider/3d-morph.lua rename to bench/tests/sunspider/3d-morph.luau diff --git a/bench/tests/sunspider/3d-raytrace.lua b/bench/tests/sunspider/3d-raytrace.luau similarity index 100% rename from bench/tests/sunspider/3d-raytrace.lua rename to bench/tests/sunspider/3d-raytrace.luau diff --git a/bench/tests/sunspider/controlflow-recursive.lua b/bench/tests/sunspider/controlflow-recursive.luau similarity index 100% rename from bench/tests/sunspider/controlflow-recursive.lua rename to bench/tests/sunspider/controlflow-recursive.luau diff --git a/bench/tests/sunspider/crypto-aes.lua b/bench/tests/sunspider/crypto-aes.luau similarity index 100% rename from bench/tests/sunspider/crypto-aes.lua rename to bench/tests/sunspider/crypto-aes.luau diff --git a/bench/tests/sunspider/fannkuch.lua b/bench/tests/sunspider/fannkuch.luau similarity index 100% rename from bench/tests/sunspider/fannkuch.lua rename to bench/tests/sunspider/fannkuch.luau diff --git a/bench/tests/sunspider/math-cordic.lua b/bench/tests/sunspider/math-cordic.luau similarity index 100% rename from bench/tests/sunspider/math-cordic.lua rename to bench/tests/sunspider/math-cordic.luau diff --git a/bench/tests/sunspider/math-partial-sums.lua b/bench/tests/sunspider/math-partial-sums.luau similarity index 100% rename from bench/tests/sunspider/math-partial-sums.lua rename to bench/tests/sunspider/math-partial-sums.luau diff --git a/bench/tests/sunspider/n-body-oop.lua b/bench/tests/sunspider/n-body-oop.luau similarity index 100% rename from bench/tests/sunspider/n-body-oop.lua rename to bench/tests/sunspider/n-body-oop.luau diff --git a/bench/tests/tictactoe.lua b/bench/tests/tictactoe.luau similarity index 100% rename from bench/tests/tictactoe.lua rename to bench/tests/tictactoe.luau diff --git a/bench/tests/trig.lua b/bench/tests/trig.luau similarity index 100% rename from bench/tests/trig.lua rename to bench/tests/trig.luau diff --git a/bench/tests/vector-math.lua b/bench/tests/vector-math.luau similarity index 100% rename from bench/tests/vector-math.lua rename to bench/tests/vector-math.luau diff --git a/bench/tests/voxelgen.lua b/bench/tests/voxelgen.luau similarity index 100% rename from bench/tests/voxelgen.lua rename to bench/tests/voxelgen.luau diff --git a/fuzz/basic.lua b/fuzz/basic.luau similarity index 100% rename from fuzz/basic.lua rename to fuzz/basic.luau diff --git a/tests/Conformance.test.cpp b/tests/Conformance.test.cpp index c0e81371d..58f2aadf8 100644 --- a/tests/Conformance.test.cpp +++ b/tests/Conformance.test.cpp @@ -644,30 +644,30 @@ TEST_CASE("CodegenSupported") TEST_CASE("Assert") { - runConformance("assert.lua"); + runConformance("assert.luau"); } TEST_CASE("Basic") { - runConformance("basic.lua"); + runConformance("basic.luau"); } TEST_CASE("Buffers") { - runConformance("buffers.lua"); + runConformance("buffers.luau"); } TEST_CASE("Math") { ScopedFastFlag LuauMathMap{FFlag::LuauMathMap, true}; - runConformance("math.lua"); + runConformance("math.luau"); } TEST_CASE("Tables") { runConformance( - "tables.lua", + "tables.luau", [](lua_State* L) { lua_pushcfunction( @@ -696,99 +696,99 @@ TEST_CASE("Tables") TEST_CASE("PatternMatch") { - runConformance("pm.lua"); + runConformance("pm.luau"); } TEST_CASE("Sort") { - runConformance("sort.lua"); + runConformance("sort.luau"); } TEST_CASE("Move") { - runConformance("move.lua"); + runConformance("move.luau"); } TEST_CASE("Clear") { - runConformance("clear.lua"); + runConformance("clear.luau"); } TEST_CASE("Strings") { - runConformance("strings.lua"); + runConformance("strings.luau"); } TEST_CASE("StringInterp") { - runConformance("stringinterp.lua"); + runConformance("stringinterp.luau"); } TEST_CASE("VarArg") { - runConformance("vararg.lua"); + runConformance("vararg.luau"); } TEST_CASE("Locals") { - runConformance("locals.lua"); + runConformance("locals.luau"); } TEST_CASE("Literals") { - runConformance("literals.lua"); + runConformance("literals.luau"); } TEST_CASE("Errors") { - runConformance("errors.lua"); + runConformance("errors.luau"); } TEST_CASE("Events") { - runConformance("events.lua"); + runConformance("events.luau"); } TEST_CASE("Constructs") { - runConformance("constructs.lua"); + runConformance("constructs.luau"); } TEST_CASE("Closure") { - runConformance("closure.lua"); + runConformance("closure.luau"); } TEST_CASE("Calls") { ScopedFastFlag LuauStackLimit{DFFlag::LuauStackLimit, true}; - runConformance("calls.lua"); + runConformance("calls.luau"); } TEST_CASE("Attrib") { - runConformance("attrib.lua"); + runConformance("attrib.luau"); } TEST_CASE("GC") { - runConformance("gc.lua"); + runConformance("gc.luau"); } TEST_CASE("Bitwise") { - runConformance("bitwise.lua"); + runConformance("bitwise.luau"); } TEST_CASE("UTF8") { - runConformance("utf8.lua"); + runConformance("utf8.luau"); } TEST_CASE("Coroutine") { - runConformance("coroutine.lua"); + runConformance("coroutine.luau"); } static int cxxthrow(lua_State* L) @@ -805,7 +805,7 @@ TEST_CASE("PCall") ScopedFastFlag LuauStackLimit{DFFlag::LuauStackLimit, true}; runConformance( - "pcall.lua", + "pcall.luau", [](lua_State* L) { lua_pushcfunction(L, cxxthrow, "cxxthrow"); @@ -831,7 +831,7 @@ TEST_CASE("PCall") TEST_CASE("Pack") { - runConformance("tpack.lua"); + runConformance("tpack.luau"); } TEST_CASE("Vector") @@ -876,7 +876,7 @@ TEST_CASE("Vector") } runConformance( - "vector.lua", + "vector.luau", [](lua_State* L) { setupVectorHelpers(L); @@ -911,9 +911,7 @@ TEST_CASE("VectorLibrary") copts.optimizationLevel = 2; } - runConformance( - "vector_library.lua", [](lua_State* L) {}, nullptr, nullptr, &copts - ); + runConformance("vector_library.luau", [](lua_State* L) {}, nullptr, nullptr, &copts); } static void populateRTTI(lua_State* L, Luau::TypeId type) @@ -990,7 +988,7 @@ TEST_CASE("Types") ScopedFastFlag luauVectorDefinitions{FFlag::LuauVectorDefinitions, true}; runConformance( - "types.lua", + "types.luau", [](lua_State* L) { Luau::NullModuleResolver moduleResolver; @@ -1015,14 +1013,14 @@ TEST_CASE("Types") TEST_CASE("DateTime") { - runConformance("datetime.lua"); + runConformance("datetime.luau"); } TEST_CASE("Debug") { ScopedFastFlag luauDebugInfoInvArgLeftovers{DFFlag::LuauDebugInfoInvArgLeftovers, true}; - runConformance("debug.lua"); + runConformance("debug.luau"); } TEST_CASE("Debugger") @@ -1049,7 +1047,7 @@ TEST_CASE("Debugger") copts.debugLevel = 2; runConformance( - "debugger.lua", + "debugger.luau", [](lua_State* L) { lua_Callbacks* cb = lua_callbacks(L); @@ -1224,7 +1222,7 @@ TEST_CASE("NDebugGetUpValue") copts.optimizationLevel = 0; runConformance( - "ndebug_upvalues.lua", + "ndebug_upvalues.luau", nullptr, [](lua_State* L) { @@ -1436,7 +1434,7 @@ TEST_CASE("ApiIter") TEST_CASE("ApiCalls") { - StateRef globalState = runConformance("apicalls.lua", nullptr, nullptr, lua_newstate(limitedRealloc, nullptr)); + StateRef globalState = runConformance("apicalls.luau", nullptr, nullptr, lua_newstate(limitedRealloc, nullptr)); lua_State* L = globalState.get(); // lua_call @@ -1739,7 +1737,7 @@ TEST_CASE("ExceptionObject") return ExceptionResult{false, ""}; }; - StateRef globalState = runConformance("exceptions.lua", nullptr, nullptr, lua_newstate(limitedRealloc, nullptr)); + StateRef globalState = runConformance("exceptions.luau", nullptr, nullptr, lua_newstate(limitedRealloc, nullptr)); lua_State* L = globalState.get(); { @@ -1778,7 +1776,7 @@ TEST_CASE("ExceptionObject") TEST_CASE("IfElseExpression") { - runConformance("ifelseexpr.lua"); + runConformance("ifelseexpr.luau"); } // Optionally returns debug info for the first Luau stack frame that is encountered on the callstack. @@ -1816,7 +1814,7 @@ TEST_CASE("TagMethodError") auto yieldCallback = [](lua_State* L) {}; runConformance( - "tmerror.lua", + "tmerror.luau", [](lua_State* L) { auto* cb = lua_callbacks(L); @@ -1854,7 +1852,7 @@ TEST_CASE("Coverage") copts.coverageLevel = 2; runConformance( - "coverage.lua", + "coverage.luau", [](lua_State* L) { lua_pushcfunction( @@ -1908,7 +1906,7 @@ TEST_CASE("Coverage") TEST_CASE("StringConversion") { - runConformance("strconv.lua"); + runConformance("strconv.luau"); } TEST_CASE("GCDump") @@ -2021,7 +2019,7 @@ TEST_CASE("Interrupt") static int index; - StateRef globalState = runConformance("interrupt.lua", nullptr, nullptr, nullptr, &copts); + StateRef globalState = runConformance("interrupt.luau", nullptr, nullptr, nullptr, &copts); lua_State* L = globalState.get(); @@ -2322,7 +2320,7 @@ TEST_CASE("DebugApi") TEST_CASE("Iter") { - runConformance("iter.lua"); + runConformance("iter.luau"); } const int kInt64Tag = 1; @@ -2351,7 +2349,7 @@ static void pushInt64(lua_State* L, int64_t value) TEST_CASE("Userdata") { runConformance( - "userdata.lua", + "userdata.luau", [](lua_State* L) { // create metatable with all the metamethods @@ -2573,7 +2571,7 @@ TEST_CASE("Userdata") TEST_CASE("SafeEnv") { - runConformance("safeenv.lua"); + runConformance("safeenv.luau"); } TEST_CASE("Native") @@ -2593,7 +2591,7 @@ TEST_CASE("Native") } runConformance( - "native.lua", + "native.luau", [](lua_State* L) { setupNativeHelpers(L); @@ -2608,7 +2606,7 @@ TEST_CASE("NativeTypeAnnotations") return; runConformance( - "native_types.lua", + "native_types.luau", [](lua_State* L) { setupNativeHelpers(L); @@ -2671,7 +2669,7 @@ TEST_CASE("NativeUserdata") } runConformance( - "native_userdata.lua", + "native_userdata.luau", [](lua_State* L) { Luau::CodeGen::setUserdataRemapper( diff --git a/tests/RequireByString.test.cpp b/tests/RequireByString.test.cpp index bc1161b04..359e3fca9 100644 --- a/tests/RequireByString.test.cpp +++ b/tests/RequireByString.test.cpp @@ -349,7 +349,7 @@ TEST_CASE_FIXTURE(ReplWithPathFixture, "CheckCacheAfterRequireLua") std::string absolutePath = getLuauDirectory(PathType::Absolute) + "/tests/require/without_config/lua_dependency"; luaL_findtable(L, LUA_REGISTRYINDEX, "_MODULES", 1); - lua_getfield(L, -1, (absolutePath + ".luau").c_str()); + lua_getfield(L, -1, (absolutePath + ".lua").c_str()); REQUIRE_MESSAGE(lua_isnil(L, -1), "Cache already contained module result"); runProtectedRequire(relativePath); diff --git a/tests/conformance/apicalls.lua b/tests/conformance/apicalls.luau similarity index 100% rename from tests/conformance/apicalls.lua rename to tests/conformance/apicalls.luau diff --git a/tests/conformance/assert.lua b/tests/conformance/assert.luau similarity index 100% rename from tests/conformance/assert.lua rename to tests/conformance/assert.luau diff --git a/tests/conformance/attrib.lua b/tests/conformance/attrib.luau similarity index 100% rename from tests/conformance/attrib.lua rename to tests/conformance/attrib.luau diff --git a/tests/conformance/basic.lua b/tests/conformance/basic.lua deleted file mode 100644 index 05d851ea9..000000000 --- a/tests/conformance/basic.lua +++ /dev/null @@ -1,1018 +0,0 @@ --- 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) - --- assignments with local conflicts -assert((function() local a, b = 1, {} a, b[a] = 43, -1 return a + b[1] end)() == 42) -assert((function() local a = {} local b = a a[1], a = 43, -1 return a + b[1] end)() == 42) -assert((function() local a, b = 1, {} a, b[a] = (function() return 43, -1 end)() return a + b[1] end)() == 42) -assert((function() local a = {} local b = a a[1], a = (function() return 43, -1 end)() return a + b[1] end)() == 42) - --- 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) - --- binary ops with fp specials, neg zero, large constants --- argument is passed into anonymous function to prevent constant folding -assert((function(a) return tostring(a + 0) end)(-0) == "0") -assert((function(a) return tostring(a - 0) end)(-0) == "-0") -assert((function(a) return tostring(0 - a) end)(0) == "0") -assert((function(a) return tostring(a - a) end)(1 / 0) == "nan") -assert((function(a) return tostring(a * 0) end)(0 / 0) == "nan") -assert((function(a) return tostring(a / (2^1000)) end)(2^1000) == "1") -assert((function(a) return tostring(a / (2^-1000)) end)(2^-1000) == "1") - --- floor division should always round towards -Infinity -assert((function() local a = 1 a = a // 2 return a end)() == 0) -assert((function() local a = 3 a = a // 2 return a end)() == 1) -assert((function() local a = 3.5 a = a // 2 return a end)() == 1) -assert((function() local a = -1 a = a // 2 return a end)() == -1) -assert((function() local a = -3 a = a // 2 return a end)() == -2) -assert((function() local a = -3.5 a = a // 2 return a end)() == -2) - -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 = 3 a = a ^ 3 return a end)() == 27) -assert((function() local a = 9 a = a ^ 0.5 return a end)() == 3) -assert((function() local a = -2 a = a ^ 2 return a end)() == 4) -assert((function() local a = -2 a = a ^ 0.5 return tostring(a) end)() == "nan") - -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) - -assert((function() local a a = 1 local b = 2 b = a and b return b end)() == 2) -assert((function() local a a = nil local b = 2 b = a and b return b end)() == nil) -assert((function() local a a = 1 local b = 2 b = a or b return b end)() == 1) -assert((function() local a a = nil local b = 2 b = a or b return b end)() == 2) - -assert((function(a) return 12 % a end)(5) == 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) - --- __len metamethod -assert((function() local ud = newproxy(true) getmetatable(ud).__len = function() return 42 end return #ud end)() == 42) -assert((function() local t = {} setmetatable(t, { __len = function() return 42 end }) return #t end)() == 42) - --- 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) - --- make sure that when step is 0, we treat it as backward iteration (and as such, iterate zero times or indefinitely) --- this is consistent with Lua 5.1; future Lua versions emit an error when step is 0; LuaJIT instead treats 0 as forward iteration --- we repeat tests twice, with and without constant folding -local zero = tonumber("0") -assert((function() local c = 0 for i=1,10,0 do c += 1 if c > 10 then break end end return c end)() == 0) -assert((function() local c = 0 for i=10,1,0 do c += 1 if c > 10 then break end end return c end)() == 11) -assert((function() local c = 0 for i=1,10,zero do c += 1 if c > 10 then break end end return c end)() == 0) -assert((function() local c = 0 for i=10,1,zero do c += 1 if c > 10 then break end end return c end)() == 11) - --- make sure that when limit is nan, we iterate zero times (this is consistent with Lua 5.1; future Lua versions break this) --- we repeat tests twice, with and without constant folding -local nan = tonumber("nan") -assert((function() local c = 0 for i=1,0/0 do c += 1 end return c end)() == 0) -assert((function() local c = 0 for i=1,0/0,-1 do c += 1 end return c end)() == 0) -assert((function() local c = 0 for i=1,nan do c += 1 end return c end)() == 0) -assert((function() local c = 0 for i=1,nan,-1 do c += 1 end return c end)() == 0) - --- make sure that when step is nan, we treat it as backward iteration and as such iterate once iff start<=limit -assert((function() local c = 0 for i=1,10,0/0 do c += 1 end return c end)() == 0) -assert((function() local c = 0 for i=10,1,0/0 do c += 1 end return c end)() == 1) -assert((function() local c = 0 for i=1,10,nan do c += 1 end return c end)() == 0) -assert((function() local c = 0 for i=10,1,nan do c += 1 end return c end)() == 1) - --- make sure that when index becomes nan mid-iteration, we correctly exit the loop (this is broken in Lua 5.1; future Lua versions fix this) -assert((function() local c = 0 for i=-math.huge,0,math.huge do c += 1 end return c end)() == 1) -assert((function() local c = 0 for i=math.huge,math.huge,-math.huge do c += 1 end return c end)() == 1) - --- 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") - --- table literals may contain duplicate fields; the language doesn't specify assignment order but we currently assign left to right -assert((function() local t = {data = 4, data = nil, data = 42} return t.data end)() == 42) -assert((function() local t = {data = 4, data = nil, data = 42, data = nil} return t.data end)() == nil) - --- 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") - --- userdata access -assert((function() local ud = newproxy(true) getmetatable(ud).__index = function(ud,i) return i * 10 end return ud[2] end)() == 20) -assert((function() local ud = newproxy(true) getmetatable(ud).__index = function() return function(self, i) return i * 10 end end return ud:meow(2) end)() == 20) - --- 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") - --- rawequal fallback -assert(concat(pcall(rawequal, "a", "a")) == "true,true") -assert(concat(pcall(rawequal, "a", "b")) == "true,false") -assert(concat(pcall(rawequal, "a", nil)) == "true,false") -assert(pcall(rawequal, "a") == 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, - __idiv = 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") -assert((function() return tostring(vec3t(1,2,3) // vec3t(2,4,2)) end)() == "0, 0, 1") -assert((function() return tostring(vec3t(1,2,3) // vec3t(-2,-4,-2)) end)() == "-1, -1, -2") - --- 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") -assert((function() return tostring(vec3t(1,2,3) // 2) end)() == "0, 1, 1") - --- unary -assert((function() return tostring(-vec3t(1,2,3)) end)() == "-1, -2, -3") - --- string comparison -assert((function() function cmp(a,b) return ab,a>=b end return concat(cmp('a', 'b')) end)() == "true,true,false,false") -assert((function() function cmp(a,b) return ab,a>=b end return concat(cmp('a', 'a')) end)() == "false,true,false,true") -assert((function() function cmp(a,b) return ab,a>=b end return concat(cmp('a', '')) end)() == "false,false,true,true") -assert((function() function cmp(a,b) return ab,a>=b end return concat(cmp('', '\\0')) end)() == "true,true,false,false") -assert((function() function cmp(a,b) return ab,a>=b end return concat(cmp('abc', 'abd')) end)() == "true,true,false,false") -assert((function() function cmp(a,b) return ab,a>=b end return concat(cmp('ab\\0c', 'ab\\0d')) end)() == "true,true,false,false") -assert((function() function cmp(a,b) return ab,a>=b end return concat(cmp('ab\\0c', 'ab\\0')) end)() == "false,false,true,true") -assert((function() function cmp(a,b) return ab,a>=b end return concat(cmp('\\0a', '\\0b')) end)() == "true,true,false,false") -assert((function() function cmp(a,b) return ab,a>=b end return concat(cmp('a', 'a\\0')) end)() == "true,true,false,false") -assert((function() function cmp(a,b) return ab,a>=b end return concat(cmp('a', '\200')) end)() == "true,true,false,false") - --- 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) - --- ipairs will not iterate through hash part -assert((function() - local arr = { [1] = 1, [42] = 42, x = 10 } - local sum = 0 - for i,v in ipairs(arr) do - sum = sum + v - end - return sum -end)() == 1) - --- 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(idontexist == nil) -assert(math.idontexist == nil) -assert(pcall(function() return idontexist.a end) == false) -assert(pcall(function() return math.pow.a end) == false) -assert(pcall(function() return math.a.b 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() - - -- declare constant so that at O2 this test doesn't interfere with constant folding which we can't deoptimize - local negfive negfive = -5 - - -- getfenv breaks fastcalls (we assume we can't rely on knowing the semantics), but behavior shouldn't change - assert((function() return math.abs(negfive) end)() == 5) - assert((function() local abs = math.abs return abs(negfive) end)() == 5) - assert((function() local abs = math.abs function foo() return abs(negfive) 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(negfive) end)() == 25) - assert((function() local abs = math.abs return abs(negfive) end)() == 25) - assert((function() local abs = math.abs function foo() return abs(negfive) 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) - --- shrinking array part -assert((function() - local t = table.create(100, 42) - for i=1,90 do t[i] = nil end - t[101] = 42 - local sum = 0 - for _,v in ipairs(t) do sum += v end - for _,v in pairs(t) do sum += v end - return sum -end)() == 462) - --- 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") - --- checking for a CFG issue that was missed in IR -assert((function(b) - local res = 0 - - if b then - for i = 1, 100 do - res += i - end - else - res += 100000 - end - - return res -end)(true) == 5050) - --- typeof and type require an argument -assert(pcall(typeof) == false) -assert(pcall(type) == false) - -function nothing() end - -assert(pcall(function() return typeof(nothing()) end) == false) -assert(pcall(function() return type(nothing()) end) == false) - --- typeof == type in absence of custom userdata -assert(concat(typeof(5), typeof(nil), typeof({}), typeof(newproxy())) == "number,nil,table,userdata") - --- type/typeof/newproxy interaction with metatables: __type doesn't work intentionally to avoid spoofing -assert((function() - local ud = newproxy(true) - getmetatable(ud).__type = "number" - - return concat(type(ud),typeof(ud)) -end)() == "userdata,userdata") - -testgetfenv() -- DONT MOVE THIS LINE - -return 'OK' diff --git a/tests/conformance/basic.luau b/tests/conformance/basic.luau new file mode 100644 index 000000000..52483f28b --- /dev/null +++ b/tests/conformance/basic.luau @@ -0,0 +1,2877 @@ +-- 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.luau:63: 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) + +-- assignments with local conflicts +assert((function() + local a, b = 1, {} + a, b[a] = 43, -1 + return a + b[1] +end)() == 42) +assert((function() + local a = {} + local b = a + a[1], a = 43, -1 + return a + b[1] +end)() == 42) +assert((function() + local a, b = 1, {} + a, b[a] = (function() + return 43, -1 + end)() + return a + b[1] +end)() == 42) +assert((function() + local a = {} + local b = a + a[1], a = (function() + return 43, -1 + end)() + return a + b[1] +end)() == 42) + +-- 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) + +-- binary ops with fp specials, neg zero, large constants +-- argument is passed into anonymous function to prevent constant folding +assert((function(a) + return tostring(a + 0) +end)(-0) == "0") +assert((function(a) + return tostring(a - 0) +end)(-0) == "-0") +assert((function(a) + return tostring(0 - a) +end)(0) == "0") +assert((function(a) + return tostring(a - a) +end)(1 / 0) == "nan") +assert((function(a) + return tostring(a * 0) +end)(0 / 0) == "nan") +assert((function(a) + return tostring(a / (2 ^ 1000)) +end)(2 ^ 1000) == "1") +assert((function(a) + return tostring(a / (2 ^ -1000)) +end)(2 ^ -1000) == "1") + +-- floor division should always round towards -Infinity +assert((function() + local a = 1 + a = a // 2 + return a +end)() == 0) +assert((function() + local a = 3 + a = a // 2 + return a +end)() == 1) +assert((function() + local a = 3.5 + a = a // 2 + return a +end)() == 1) +assert((function() + local a = -1 + a = a // 2 + return a +end)() == -1) +assert((function() + local a = -3 + a = a // 2 + return a +end)() == -2) +assert((function() + local a = -3.5 + a = a // 2 + return a +end)() == -2) + +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 = 3 + a = a ^ 3 + return a +end)() == 27) +assert((function() + local a = 9 + a = a ^ 0.5 + return a +end)() == 3) +assert((function() + local a = -2 + a = a ^ 2 + return a +end)() == 4) +assert((function() + local a = -2 + a = a ^ 0.5 + return tostring(a) +end)() == "nan") + +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) + +assert((function() + local a + a = 1 + local b = 2 + b = a and b + return b +end)() == 2) +assert((function() + local a + a = nil + local b = 2 + b = a and b + return b +end)() == nil) +assert((function() + local a + a = 1 + local b = 2 + b = a or b + return b +end)() == 1) +assert((function() + local a + a = nil + local b = 2 + b = a or b + return b +end)() == 2) + +assert((function(a) + return 12 % a +end)(5) == 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) + +-- __len metamethod +assert((function() + local ud = newproxy(true) + getmetatable(ud).__len = function() + return 42 + end + return #ud +end)() == 42) +assert((function() + local t = {} + setmetatable(t, { + __len = function() + return 42 + end, + }) + return #t +end)() == 42) + +-- 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) + +-- make sure that when step is 0, we treat it as backward iteration (and as such, iterate zero times or indefinitely) +-- this is consistent with Lua 5.1; future Lua versions emit an error when step is 0; LuaJIT instead treats 0 as forward iteration +-- we repeat tests twice, with and without constant folding +local zero = tonumber("0") +assert((function() + local c = 0 + for i = 1, 10, 0 do + c += 1 + if c > 10 then + break + end + end + return c +end)() == 0) +assert((function() + local c = 0 + for i = 10, 1, 0 do + c += 1 + if c > 10 then + break + end + end + return c +end)() == 11) +assert((function() + local c = 0 + for i = 1, 10, zero do + c += 1 + if c > 10 then + break + end + end + return c +end)() == 0) +assert((function() + local c = 0 + for i = 10, 1, zero do + c += 1 + if c > 10 then + break + end + end + return c +end)() == 11) + +-- make sure that when limit is nan, we iterate zero times (this is consistent with Lua 5.1; future Lua versions break this) +-- we repeat tests twice, with and without constant folding +local nan = tonumber("nan") +assert((function() + local c = 0 + for i = 1, 0 / 0 do + c += 1 + end + return c +end)() == 0) +assert((function() + local c = 0 + for i = 1, 0 / 0, -1 do + c += 1 + end + return c +end)() == 0) +assert((function() + local c = 0 + for i = 1, nan do + c += 1 + end + return c +end)() == 0) +assert((function() + local c = 0 + for i = 1, nan, -1 do + c += 1 + end + return c +end)() == 0) + +-- make sure that when step is nan, we treat it as backward iteration and as such iterate once iff start<=limit +assert((function() + local c = 0 + for i = 1, 10, 0 / 0 do + c += 1 + end + return c +end)() == 0) +assert((function() + local c = 0 + for i = 10, 1, 0 / 0 do + c += 1 + end + return c +end)() == 1) +assert((function() + local c = 0 + for i = 1, 10, nan do + c += 1 + end + return c +end)() == 0) +assert((function() + local c = 0 + for i = 10, 1, nan do + c += 1 + end + return c +end)() == 1) + +-- make sure that when index becomes nan mid-iteration, we correctly exit the loop (this is broken in Lua 5.1; future Lua versions fix this) +assert((function() + local c = 0 + for i = -math.huge, 0, math.huge do + c += 1 + end + return c +end)() == 1) +assert((function() + local c = 0 + for i = math.huge, math.huge, -math.huge do + c += 1 + end + return c +end)() == 1) + +-- 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") + +-- table literals may contain duplicate fields; the language doesn't specify assignment order but we currently assign left to right +assert((function() + local t = { data = 4, data = nil, data = 42 } + return t.data +end)() == 42) +assert((function() + local t = { data = 4, data = nil, data = 42, data = nil } + return t.data +end)() == nil) + +-- 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") + +-- userdata access +assert((function() + local ud = newproxy(true) + getmetatable(ud).__index = function(ud, i) + return i * 10 + end + return ud[2] +end)() == 20) +assert((function() + local ud = newproxy(true) + getmetatable(ud).__index = function() + return function(self, i) + return i * 10 + end + end + return ud:meow(2) +end)() == 20) + +-- 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") + +-- rawequal fallback +assert(concat(pcall(rawequal, "a", "a")) == "true,true") +assert(concat(pcall(rawequal, "a", "b")) == "true,false") +assert(concat(pcall(rawequal, "a", nil)) == "true,false") +assert(pcall(rawequal, "a") == 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, + __idiv = 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") +assert((function() + return tostring(vec3t(1, 2, 3) // vec3t(2, 4, 2)) +end)() == "0, 0, 1") +assert((function() + return tostring(vec3t(1, 2, 3) // vec3t(-2, -4, -2)) +end)() == "-1, -1, -2") + +-- 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") +assert((function() + return tostring(vec3t(1, 2, 3) // 2) +end)() == "0, 1, 1") + +-- 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") +assert((function() + function cmp(a, b) + return a < b, a <= b, a > b, a >= b + end + return concat(cmp("\\0a", "\\0b")) +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\\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("a", "\200")) +end)() == "true,true,false,false") + +-- 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) + +-- ipairs will not iterate through hash part +assert((function() + local arr = { [1] = 1, [42] = 42, x = 10 } + local sum = 0 + for i, v in ipairs(arr) do + sum = sum + v + end + return sum +end)() == 1) + +-- 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(idontexist == nil) +assert(math.idontexist == nil) +assert(pcall(function() + return idontexist.a +end) == false) +assert(pcall(function() + return math.pow.a +end) == false) +assert(pcall(function() + return math.a.b +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() + + -- declare constant so that at O2 this test doesn't interfere with constant folding which we can't deoptimize + local negfive + negfive = -5 + + -- getfenv breaks fastcalls (we assume we can't rely on knowing the semantics), but behavior shouldn't change + assert((function() + return math.abs(negfive) + end)() == 5) + assert((function() + local abs = math.abs + return abs(negfive) + end)() == 5) + assert((function() + local abs = math.abs + function foo() + return abs(negfive) + 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(negfive) + end)() == 25) + assert((function() + local abs = math.abs + return abs(negfive) + end)() == 25) + assert((function() + local abs = math.abs + function foo() + return abs(negfive) + 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) + +-- shrinking array part +assert((function() + local t = table.create(100, 42) + for i = 1, 90 do + t[i] = nil + end + t[101] = 42 + local sum = 0 + for _, v in ipairs(t) do + sum += v + end + for _, v in pairs(t) do + sum += v + end + return sum +end)() == 462) + +-- 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") + +-- checking for a CFG issue that was missed in IR +assert((function(b) + local res = 0 + + if b then + for i = 1, 100 do + res += i + end + else + res += 100000 + end + + return res +end)(true) == 5050) + +-- typeof and type require an argument +assert(pcall(typeof) == false) +assert(pcall(type) == false) + +function nothing() end + +assert(pcall(function() + return typeof(nothing()) +end) == false) +assert(pcall(function() + return type(nothing()) +end) == false) + +-- typeof == type in absence of custom userdata +assert(concat(typeof(5), typeof(nil), typeof({}), typeof(newproxy())) == "number,nil,table,userdata") + +-- type/typeof/newproxy interaction with metatables: __type doesn't work intentionally to avoid spoofing +assert((function() + local ud = newproxy(true) + getmetatable(ud).__type = "number" + + return concat(type(ud), typeof(ud)) +end)() == "userdata,userdata") + +testgetfenv() -- DONT MOVE THIS LINE + +return "OK" diff --git a/tests/conformance/bitwise.lua b/tests/conformance/bitwise.luau similarity index 100% rename from tests/conformance/bitwise.lua rename to tests/conformance/bitwise.luau diff --git a/tests/conformance/buffers.lua b/tests/conformance/buffers.luau similarity index 100% rename from tests/conformance/buffers.lua rename to tests/conformance/buffers.luau diff --git a/tests/conformance/calls.lua b/tests/conformance/calls.luau similarity index 100% rename from tests/conformance/calls.lua rename to tests/conformance/calls.luau diff --git a/tests/conformance/clear.lua b/tests/conformance/clear.luau similarity index 100% rename from tests/conformance/clear.lua rename to tests/conformance/clear.luau diff --git a/tests/conformance/closure.lua b/tests/conformance/closure.lua deleted file mode 100644 index 10dc322fb..000000000 --- a/tests/conformance/closure.lua +++ /dev/null @@ -1,404 +0,0 @@ --- This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details --- This file is based on Lua 5.x tests -- https://github.com/lua/lua/tree/master/testes -print "testing closures and coroutines" - -local unpack = table.unpack - -local A,B = 0,{g=10} -function f(x) - local a = {} - for i=1,1000 do - local y = 0 - do - a[i] = function () B.g = B.g+1; y = y+x; return y+A end - end - end - local dummy = function () return a[A] end - collectgarbage() - A = 1; assert(dummy() == a[1]); A = 0; - assert(a[1]() == x) - assert(a[3]() == x) - collectgarbage() - assert(B.g == 12) - return a -end - -a = f(10) --- force a GC in this level -local x = {[1] = {}} -- to detect a GC -setmetatable(x, {__mode = 'kv'}) -while x[1] do -- repeat until GC - local a = A..A..A..A -- create garbage - A = A+1 -end -assert(a[1]() == 20+A) -assert(a[1]() == 30+A) -assert(a[2]() == 10+A) -collectgarbage() -assert(a[2]() == 20+A) -assert(a[2]() == 30+A) -assert(a[3]() == 20+A) -assert(a[8]() == 10+A) -assert(getmetatable(x).__mode == 'kv') -assert(B.g == 19) - --- testing closures with 'for' control variable -a = {} -for i=1,10 do - a[i] = {set = function(x) i=x end, get = function () return i end} - if i == 3 then break end -end -assert(a[4] == nil) -a[1].set(10) -assert(a[2].get() == 2) -a[2].set('a') -assert(a[3].get() == 3) -assert(a[2].get() == 'a') - -a = {} -for i, k in pairs{'a', 'b'} do - a[i] = {set = function(x, y) i=x; k=y end, - get = function () return i, k end} - if i == 2 then break end -end -a[1].set(10, 20) -local r,s = a[2].get() -assert(r == 2 and s == 'b') -r,s = a[1].get() -assert(r == 10 and s == 20) -a[2].set('a', 'b') -r,s = a[2].get() -assert(r == "a" and s == "b") - - --- testing closures with 'for' control variable x break -for i=1,3 do - f = function () return i end - break -end -assert(f() == 1) - -for k, v in pairs{"a", "b"} do - f = function () return k, v end - break -end -assert(({f()})[1] == 1) -assert(({f()})[2] == "a") - - --- testing closure x break x return x errors - -local b -function f(x) - local first = 1 - while 1 do - if x == 3 and not first then return end - local a = 'xuxu' - b = function (op, y) - if op == 'set' then - a = x+y - else - return a - end - end - if x == 1 then do break end - elseif x == 2 then return - else if x ~= 3 then error() end - end - first = nil - end -end - -for i=1,3 do - f(i) - assert(b('get') == 'xuxu') - b('set', 10); assert(b('get') == 10+i) - b = nil -end - -pcall(f, 4); -assert(b('get') == 'xuxu') -b('set', 10); assert(b('get') == 14) - - -local w --- testing multi-level closure -function f(x) - return function (y) - return function (z) return w+x+y+z end - end -end - -y = f(10) -w = 1.345 -assert(y(20)(30) == 60+w) - --- testing closures x repeat-until - -local a = {} -local i = 1 -repeat - local x = i - a[i] = function () i = x+1; return x end -until i > 10 or a[i]() ~= x -assert(i == 11 and a[1]() == 1 and a[3]() == 3 and i == 4) - -print'+' - - --- test for correctly closing upvalues in tail calls of vararg functions -local function t () - local function c(a,b) assert(a=="test" and b=="OK") end - local function v(f, ...) c("test", f() ~= 1 and "FAILED" or "OK") end - local x = 1 - return v(function() return x end) -end -t() - - --- coroutine tests - -local f - --- assert(coroutine.running() == nil) - - --- tests for global environment -local _G = getfenv() - -local function foo (a) - setfenv(0, a) - coroutine.yield(getfenv()) - assert(getfenv(0) == a) - assert(getfenv(1) == _G) - assert(getfenv(loadstring"") == a) - return getfenv() -end - -f = coroutine.wrap(foo) -local a = {} -assert(f(a) == _G) -local a,b = pcall(f) -assert(a and b == _G) - - --- tests for multiple yield/resume arguments - -local function eqtab (t1, t2) - assert(table.getn(t1) == table.getn(t2)) - for i,v in ipairs(t1) do - assert(t2[i] == v) - end -end - -_G.x = nil -- declare x -function foo (a, ...) - assert(coroutine.running() == f) - assert(coroutine.status(f) == "running") - local arg = {...} - for i=1,table.getn(arg) do - _G.x = {coroutine.yield(unpack(arg[i]))} - end - return unpack(a) -end - -f = coroutine.create(foo) -assert(type(f) == "thread" and coroutine.status(f) == "suspended") -assert(string.find(tostring(f), "thread")) -local s,a,b,c,d -s,a,b,c,d = coroutine.resume(f, {1,2,3}, {}, {1}, {'a', 'b', 'c'}) -assert(s and a == nil and coroutine.status(f) == "suspended") -s,a,b,c,d = coroutine.resume(f) -eqtab(_G.x, {}) -assert(s and a == 1 and b == nil) -s,a,b,c,d = coroutine.resume(f, 1, 2, 3) -eqtab(_G.x, {1, 2, 3}) -assert(s and a == 'a' and b == 'b' and c == 'c' and d == nil) -s,a,b,c,d = coroutine.resume(f, "xuxu") -eqtab(_G.x, {"xuxu"}) -assert(s and a == 1 and b == 2 and c == 3 and d == nil) -assert(coroutine.status(f) == "dead") -s, a = coroutine.resume(f, "xuxu") -assert(not s and string.find(a, "dead") and coroutine.status(f) == "dead") - - --- yields in tail calls -local function foo (i) return coroutine.yield(i) end -f = coroutine.wrap(function () - for i=1,10 do - assert(foo(i) == _G.x) - end - return 'a' -end) -for i=1,10 do _G.x = i; assert(f(i) == i) end -_G.x = 'xuxu'; assert(f('xuxu') == 'a') - --- recursive -function pf (n, i) - coroutine.yield(n) - pf(n*i, i+1) -end - -f = coroutine.wrap(pf) -local s=1 -for i=1,10 do - assert(f(1, 1) == s) - s = s*i -end - --- sieve -function gen (n) - return coroutine.wrap(function () - for i=2,n do coroutine.yield(i) end - end) -end - - -function filter (p, g) - return coroutine.wrap(function () - while 1 do - local n = g() - if n == nil then return end - if n%p ~= 0 then coroutine.yield(n) end - end - end) -end - -local x = gen(100) -local a = {} -while 1 do - local n = x() - if n == nil then break end - table.insert(a, n) - x = filter(n, x) -end - -assert(table.getn(a) == 25 and a[table.getn(a)] == 97) - - --- errors in coroutines -function foo () - -- assert(debug.getinfo(1).currentline == debug.getinfo(foo).linedefined + 1) - -- assert(debug.getinfo(2).currentline == debug.getinfo(goo).linedefined) - coroutine.yield(3) - error("foo") -end - -local fooerr = "closure.lua:284: foo" - -function goo() foo() end -x = coroutine.wrap(goo) -assert(x() == 3) -local a,b = pcall(x) -assert(not a and b == fooerr) - -x = coroutine.create(goo) -a,b = coroutine.resume(x) -assert(a and b == 3) -a,b = coroutine.resume(x) -assert(not a and b == fooerr and coroutine.status(x) == "dead") -a,b = coroutine.resume(x) -assert(not a and string.find(b, "dead") and coroutine.status(x) == "dead") - - --- co-routines x for loop -function all (a, n, k) - if k == 0 then coroutine.yield(a) - else - for i=1,n do - a[k] = i - all(a, n, k-1) - end - end -end - -local a = 0 -for t in coroutine.wrap(function () all({}, 5, 4) end) do - a = a+1 -end -assert(a == 5^4) - - --- access to locals of collected coroutines -local C = {}; setmetatable(C, {__mode = "kv"}) -local x = coroutine.wrap (function () - local a = 10 - local function f () a = a+10; return a end - while true do - a = a+1 - coroutine.yield(f) - end - end) - -C[1] = x; - -local f = x() -assert(f() == 21 and x()() == 32 and x() == f) -x = nil -collectgarbage() --- assert(C[1] == nil) -assert(f() == 43 and f() == 53) - - --- old bug: attempt to resume itself - -function co_func (current_co) - assert(coroutine.running() == current_co) - assert(coroutine.resume(current_co) == false) - assert(coroutine.resume(current_co) == false) - return 10 -end - -local co = coroutine.create(co_func) -local a,b = coroutine.resume(co, co) -assert(a == true and b == 10) -assert(coroutine.resume(co, co) == false) -assert(coroutine.resume(co, co) == false) - --- access to locals of erroneous coroutines -local x = coroutine.create (function () - local a = 10 - _G.f = function () a=a+1; return a end - error('x') - end) - -assert(not coroutine.resume(x)) - --- overwrite previous position of local `a' -assert(not coroutine.resume(x, 1, 1, 1, 1, 1, 1, 1)) -assert(_G.f() == 11) -assert(_G.f() == 12) - --- leaving a pending coroutine open -_X = coroutine.wrap(function () - local a = 10 - local x = function () a = a+1 end - coroutine.yield() - end) - -_X() - - --- coroutine environments -co = coroutine.create(function () - coroutine.yield(getfenv(0)) - return loadstring("return a")() - end) - --- large closure size -do - local a1, a2, a3, a4, a5, a6, a7, a8, a9, a0 - local b1, b2, b3, b4, b5, b6, b7, b8, b9, b0 - local c1, c2, c3, c4, c5, c6, c7, c8, c9, c0 - local d1, d2, d3, d4, d5, d6, d7, d8, d9, d0 - - local f = function() - return - a1 + a2 + a3 + a4 + a5 + a6 + a7 + a8 + a9 + a0 + - b1 + b2 + b3 + b4 + b5 + b6 + b7 + b8 + b9 + b0 + - c1 + c2 + c3 + c4 + c5 + c6 + c7 + c8 + c9 + c0 + - d1 + d2 + d3 + d4 + d5 + d6 + d7 + d8 + d9 + d0 - end -end - -return 'OK' diff --git a/tests/conformance/closure.luau b/tests/conformance/closure.luau new file mode 100644 index 000000000..84ed90d15 --- /dev/null +++ b/tests/conformance/closure.luau @@ -0,0 +1,507 @@ +-- This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details +-- This file is based on Lua 5.x tests -- https://github.com/lua/lua/tree/master/testes +print("testing closures and coroutines") + +local unpack = table.unpack + +local A, B = 0, { g = 10 } +function f(x) + local a = {} + for i = 1, 1000 do + local y = 0 + do + a[i] = function() + B.g = B.g + 1 + y = y + x + return y + A + end + end + end + local dummy = function() + return a[A] + end + collectgarbage() + A = 1 + assert(dummy() == a[1]) + A = 0 + assert(a[1]() == x) + assert(a[3]() == x) + collectgarbage() + assert(B.g == 12) + return a +end + +a = f(10) +-- force a GC in this level +local x = { [1] = {} } -- to detect a GC +setmetatable(x, { __mode = "kv" }) +while x[1] do -- repeat until GC + local a = A .. A .. A .. A -- create garbage + A = A + 1 +end +assert(a[1]() == 20 + A) +assert(a[1]() == 30 + A) +assert(a[2]() == 10 + A) +collectgarbage() +assert(a[2]() == 20 + A) +assert(a[2]() == 30 + A) +assert(a[3]() == 20 + A) +assert(a[8]() == 10 + A) +assert(getmetatable(x).__mode == "kv") +assert(B.g == 19) + +-- testing closures with 'for' control variable +a = {} +for i = 1, 10 do + a[i] = { + set = function(x) + i = x + end, + get = function() + return i + end, + } + if i == 3 then + break + end +end +assert(a[4] == nil) +a[1].set(10) +assert(a[2].get() == 2) +a[2].set("a") +assert(a[3].get() == 3) +assert(a[2].get() == "a") + +a = {} +for i, k in pairs({ "a", "b" }) do + a[i] = { + set = function(x, y) + i = x + k = y + end, + get = function() + return i, k + end, + } + if i == 2 then + break + end +end +a[1].set(10, 20) +local r, s = a[2].get() +assert(r == 2 and s == "b") +r, s = a[1].get() +assert(r == 10 and s == 20) +a[2].set("a", "b") +r, s = a[2].get() +assert(r == "a" and s == "b") + +-- testing closures with 'for' control variable x break +for i = 1, 3 do + f = function() + return i + end + break +end +assert(f() == 1) + +for k, v in pairs({ "a", "b" }) do + f = function() + return k, v + end + break +end +assert(({ f() })[1] == 1) +assert(({ f() })[2] == "a") + +-- testing closure x break x return x errors + +local b +function f(x) + local first = 1 + while 1 do + if x == 3 and not first then + return + end + local a = "xuxu" + b = function(op, y) + if op == "set" then + a = x + y + else + return a + end + end + if x == 1 then + do + break + end + elseif x == 2 then + return + else + if x ~= 3 then + error() + end + end + first = nil + end +end + +for i = 1, 3 do + f(i) + assert(b("get") == "xuxu") + b("set", 10) + assert(b("get") == 10 + i) + b = nil +end + +pcall(f, 4) +assert(b("get") == "xuxu") +b("set", 10) +assert(b("get") == 14) + +local w +-- testing multi-level closure +function f(x) + return function(y) + return function(z) + return w + x + y + z + end + end +end + +y = f(10) +w = 1.345 +assert(y(20)(30) == 60 + w) + +-- testing closures x repeat-until + +local a = {} +local i = 1 +repeat + local x = i + a[i] = function() + i = x + 1 + return x + end +until i > 10 or a[i]() ~= x +assert(i == 11 and a[1]() == 1 and a[3]() == 3 and i == 4) + +print("+") + +-- test for correctly closing upvalues in tail calls of vararg functions +local function t() + local function c(a, b) + assert(a == "test" and b == "OK") + end + local function v(f, ...) + c("test", f() ~= 1 and "FAILED" or "OK") + end + local x = 1 + return v(function() + return x + end) +end +t() + +-- coroutine tests + +local f + +-- assert(coroutine.running() == nil) + +-- tests for global environment +local _G = getfenv() + +local function foo(a) + setfenv(0, a) + coroutine.yield(getfenv()) + assert(getfenv(0) == a) + assert(getfenv(1) == _G) + assert(getfenv(loadstring("")) == a) + return getfenv() +end + +f = coroutine.wrap(foo) +local a = {} +assert(f(a) == _G) +local a, b = pcall(f) +assert(a and b == _G) + +-- tests for multiple yield/resume arguments + +local function eqtab(t1, t2) + assert(table.getn(t1) == table.getn(t2)) + for i, v in ipairs(t1) do + assert(t2[i] == v) + end +end + +_G.x = nil -- declare x +function foo(a, ...) + assert(coroutine.running() == f) + assert(coroutine.status(f) == "running") + local arg = { ... } + for i = 1, table.getn(arg) do + _G.x = { coroutine.yield(unpack(arg[i])) } + end + return unpack(a) +end + +f = coroutine.create(foo) +assert(type(f) == "thread" and coroutine.status(f) == "suspended") +assert(string.find(tostring(f), "thread")) +local s, a, b, c, d +s, a, b, c, d = coroutine.resume(f, { 1, 2, 3 }, {}, { 1 }, { "a", "b", "c" }) +assert(s and a == nil and coroutine.status(f) == "suspended") +s, a, b, c, d = coroutine.resume(f) +eqtab(_G.x, {}) +assert(s and a == 1 and b == nil) +s, a, b, c, d = coroutine.resume(f, 1, 2, 3) +eqtab(_G.x, { 1, 2, 3 }) +assert(s and a == "a" and b == "b" and c == "c" and d == nil) +s, a, b, c, d = coroutine.resume(f, "xuxu") +eqtab(_G.x, { "xuxu" }) +assert(s and a == 1 and b == 2 and c == 3 and d == nil) +assert(coroutine.status(f) == "dead") +s, a = coroutine.resume(f, "xuxu") +assert(not s and string.find(a, "dead") and coroutine.status(f) == "dead") + +-- yields in tail calls +local function foo(i) + return coroutine.yield(i) +end +f = coroutine.wrap(function() + for i = 1, 10 do + assert(foo(i) == _G.x) + end + return "a" +end) +for i = 1, 10 do + _G.x = i + assert(f(i) == i) +end +_G.x = "xuxu" +assert(f("xuxu") == "a") + +-- recursive +function pf(n, i) + coroutine.yield(n) + pf(n * i, i + 1) +end + +f = coroutine.wrap(pf) +local s = 1 +for i = 1, 10 do + assert(f(1, 1) == s) + s = s * i +end + +-- sieve +function gen(n) + return coroutine.wrap(function() + for i = 2, n do + coroutine.yield(i) + end + end) +end + +function filter(p, g) + return coroutine.wrap(function() + while 1 do + local n = g() + if n == nil then + return + end + if n % p ~= 0 then + coroutine.yield(n) + end + end + end) +end + +local x = gen(100) +local a = {} +while 1 do + local n = x() + if n == nil then + break + end + table.insert(a, n) + x = filter(n, x) +end + +assert(table.getn(a) == 25 and a[table.getn(a)] == 97) + +-- errors in coroutines +function foo() + -- assert(debug.getinfo(1).currentline == debug.getinfo(foo).linedefined + 1) + -- assert(debug.getinfo(2).currentline == debug.getinfo(goo).linedefined) + coroutine.yield(3) + error("foo") +end + +local fooerr = "closure.luau:340: foo" + +function goo() + foo() +end +x = coroutine.wrap(goo) +assert(x() == 3) +local a, b = pcall(x) +assert(not a and b == fooerr) + +x = coroutine.create(goo) +a, b = coroutine.resume(x) +assert(a and b == 3) +a, b = coroutine.resume(x) +assert(not a and b == fooerr and coroutine.status(x) == "dead") +a, b = coroutine.resume(x) +assert(not a and string.find(b, "dead") and coroutine.status(x) == "dead") + +-- co-routines x for loop +function all(a, n, k) + if k == 0 then + coroutine.yield(a) + else + for i = 1, n do + a[k] = i + all(a, n, k - 1) + end + end +end + +local a = 0 +for t in + coroutine.wrap(function() + all({}, 5, 4) + end) +do + a = a + 1 +end +assert(a == 5 ^ 4) + +-- access to locals of collected coroutines +local C = {} +setmetatable(C, { __mode = "kv" }) +local x = coroutine.wrap(function() + local a = 10 + local function f() + a = a + 10 + return a + end + while true do + a = a + 1 + coroutine.yield(f) + end +end) + +C[1] = x + +local f = x() +assert(f() == 21 and x()() == 32 and x() == f) +x = nil +collectgarbage() +-- assert(C[1] == nil) +assert(f() == 43 and f() == 53) + +-- old bug: attempt to resume itself + +function co_func(current_co) + assert(coroutine.running() == current_co) + assert(coroutine.resume(current_co) == false) + assert(coroutine.resume(current_co) == false) + return 10 +end + +local co = coroutine.create(co_func) +local a, b = coroutine.resume(co, co) +assert(a == true and b == 10) +assert(coroutine.resume(co, co) == false) +assert(coroutine.resume(co, co) == false) + +-- access to locals of erroneous coroutines +local x = coroutine.create(function() + local a = 10 + _G.f = function() + a = a + 1 + return a + end + error("x") +end) + +assert(not coroutine.resume(x)) + +-- overwrite previous position of local `a' +assert(not coroutine.resume(x, 1, 1, 1, 1, 1, 1, 1)) +assert(_G.f() == 11) +assert(_G.f() == 12) + +-- leaving a pending coroutine open +_X = coroutine.wrap(function() + local a = 10 + local x = function() + a = a + 1 + end + coroutine.yield() +end) + +_X() + +-- coroutine environments +co = coroutine.create(function() + coroutine.yield(getfenv(0)) + return loadstring("return a")() +end) + +-- large closure size +do + local a1, a2, a3, a4, a5, a6, a7, a8, a9, a0 + local b1, b2, b3, b4, b5, b6, b7, b8, b9, b0 + local c1, c2, c3, c4, c5, c6, c7, c8, c9, c0 + local d1, d2, d3, d4, d5, d6, d7, d8, d9, d0 + + local f = function() + return a1 + + a2 + + a3 + + a4 + + a5 + + a6 + + a7 + + a8 + + a9 + + a0 + + b1 + + b2 + + b3 + + b4 + + b5 + + b6 + + b7 + + b8 + + b9 + + b0 + + c1 + + c2 + + c3 + + c4 + + c5 + + c6 + + c7 + + c8 + + c9 + + c0 + + d1 + + d2 + + d3 + + d4 + + d5 + + d6 + + d7 + + d8 + + d9 + + d0 + end +end + +return "OK" diff --git a/tests/conformance/constructs.lua b/tests/conformance/constructs.luau similarity index 100% rename from tests/conformance/constructs.lua rename to tests/conformance/constructs.luau diff --git a/tests/conformance/coroutine.lua b/tests/conformance/coroutine.luau similarity index 100% rename from tests/conformance/coroutine.lua rename to tests/conformance/coroutine.luau diff --git a/tests/conformance/coverage.lua b/tests/conformance/coverage.luau similarity index 100% rename from tests/conformance/coverage.lua rename to tests/conformance/coverage.luau diff --git a/tests/conformance/datetime.lua b/tests/conformance/datetime.luau similarity index 100% rename from tests/conformance/datetime.lua rename to tests/conformance/datetime.luau diff --git a/tests/conformance/debug.lua b/tests/conformance/debug.luau similarity index 88% rename from tests/conformance/debug.lua rename to tests/conformance/debug.luau index 9fa02e274..0d644b00b 100644 --- a/tests/conformance/debug.lua +++ b/tests/conformance/debug.luau @@ -1,5 +1,5 @@ -- This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details -print "testing debug library" +print("testing debug library") -- traceback function foo(...) @@ -35,7 +35,7 @@ end local co2 = coroutine.create(halp) coroutine.resume(co2, 0 / 0, 42) -assert(debug.traceback(co2) == "debug.lua:31 function halp\n") +assert(debug.traceback(co2) == "debug.luau:31 function halp\n") assert(debug.info(co2, 0, "l") == 31) assert(debug.info(co2, 0, "f") == halp) @@ -64,7 +64,7 @@ assert(baz(1, "n") == "baz") assert(baz(2, "n") == "") -- main/anonymous assert(baz(3, "n") == nil) assert(baz(0, "s") == "[C]") -assert(baz(1, "s") == "debug.lua") +assert(baz(1, "s") == "debug.luau") assert(baz(0, "l") == -1) assert(baz(1, "l") > 42) assert(baz(0, "f") == debug.info) @@ -76,18 +76,22 @@ assert(baz(co, 2, "n") == nil) assert(baz(math.sqrt, "n") == "sqrt") assert(baz(math.sqrt, "f") == math.sqrt) -- yes this is pointless -local t = { foo = function() return 1 end } +local t = { + foo = function() + return 1 + end, +} assert(baz(t.foo, "n") == "foo") -- info multi-arg returns function quux(...) - return {debug.info(...)} + return { debug.info(...) } end assert(#(quux(1, "nlsf")) == 4) assert(quux(1, "nlsf")[1] == "quux") assert(quux(1, "nlsf")[2] > 64) -assert(quux(1, "nlsf")[3] == "debug.lua") +assert(quux(1, "nlsf")[3] == "debug.luau") assert(quux(1, "nlsf")[4] == quux) -- info arity @@ -114,7 +118,9 @@ testlinedefined() -- don't leave garbage on the other thread local wrapped1 = coroutine.create(function() local thread = coroutine.create(function(target) - for i = 1, 100 do pcall(debug.info, target, 0, "llf") end + for i = 1, 100 do + pcall(debug.info, target, 0, "llf") + end return 123 end) @@ -127,7 +133,9 @@ coroutine.resume(wrapped1) local wrapped2 = coroutine.create(function() local thread = coroutine.create(function(target) - for i = 1, 100 do pcall(debug.info, target, 0, "ff") end + for i = 1, 100 do + pcall(debug.info, target, 0, "ff") + end return 123 end) @@ -140,7 +148,9 @@ coroutine.resume(wrapped2) local wrapped3 = coroutine.create(function() local thread = coroutine.create(function(target) - for i = 1, 100 do pcall(debug.info, target, 0, "?f") end + for i = 1, 100 do + pcall(debug.info, target, 0, "?f") + end return 123 end) @@ -151,4 +161,4 @@ end) coroutine.resume(wrapped3) -return 'OK' +return "OK" diff --git a/tests/conformance/debugger.lua b/tests/conformance/debugger.luau similarity index 100% rename from tests/conformance/debugger.lua rename to tests/conformance/debugger.luau diff --git a/tests/conformance/errors.lua b/tests/conformance/errors.luau similarity index 100% rename from tests/conformance/errors.lua rename to tests/conformance/errors.luau diff --git a/tests/conformance/events.lua b/tests/conformance/events.luau similarity index 100% rename from tests/conformance/events.lua rename to tests/conformance/events.luau diff --git a/tests/conformance/exceptions.lua b/tests/conformance/exceptions.luau similarity index 100% rename from tests/conformance/exceptions.lua rename to tests/conformance/exceptions.luau diff --git a/tests/conformance/gc.lua b/tests/conformance/gc.luau similarity index 100% rename from tests/conformance/gc.lua rename to tests/conformance/gc.luau diff --git a/tests/conformance/ifelseexpr.lua b/tests/conformance/ifelseexpr.luau similarity index 100% rename from tests/conformance/ifelseexpr.lua rename to tests/conformance/ifelseexpr.luau diff --git a/tests/conformance/interrupt.lua b/tests/conformance/interrupt.luau similarity index 100% rename from tests/conformance/interrupt.lua rename to tests/conformance/interrupt.luau diff --git a/tests/conformance/iter.lua b/tests/conformance/iter.luau similarity index 100% rename from tests/conformance/iter.lua rename to tests/conformance/iter.luau diff --git a/tests/conformance/literals.lua b/tests/conformance/literals.luau similarity index 100% rename from tests/conformance/literals.lua rename to tests/conformance/literals.luau diff --git a/tests/conformance/locals.lua b/tests/conformance/locals.luau similarity index 100% rename from tests/conformance/locals.lua rename to tests/conformance/locals.luau diff --git a/tests/conformance/math.lua b/tests/conformance/math.luau similarity index 100% rename from tests/conformance/math.lua rename to tests/conformance/math.luau diff --git a/tests/conformance/move.lua b/tests/conformance/move.luau similarity index 100% rename from tests/conformance/move.lua rename to tests/conformance/move.luau diff --git a/tests/conformance/native.lua b/tests/conformance/native.luau similarity index 100% rename from tests/conformance/native.lua rename to tests/conformance/native.luau diff --git a/tests/conformance/native_types.lua b/tests/conformance/native_types.luau similarity index 100% rename from tests/conformance/native_types.lua rename to tests/conformance/native_types.luau diff --git a/tests/conformance/native_userdata.lua b/tests/conformance/native_userdata.luau similarity index 100% rename from tests/conformance/native_userdata.lua rename to tests/conformance/native_userdata.luau diff --git a/tests/conformance/ndebug_upvalues.lua b/tests/conformance/ndebug_upvalues.luau similarity index 100% rename from tests/conformance/ndebug_upvalues.lua rename to tests/conformance/ndebug_upvalues.luau diff --git a/tests/conformance/pcall.lua b/tests/conformance/pcall.lua deleted file mode 100644 index 265c397bd..000000000 --- a/tests/conformance/pcall.lua +++ /dev/null @@ -1,271 +0,0 @@ --- This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details -print("testing pcall") - -function checkresults(e, ...) - local t = table.pack(...) - assert(t.n == #e) - for i=1,t.n do - assert(t[i] == e[i]) - end -end - -function checkerror(...) - local t = table.pack(...) - assert(t.n == 2) - assert(t[1] == false) - assert(type(t[2]) == "string") -end - -function corun(f) - local co = coroutine.create(f) - local res = {} - while coroutine.status(co) == "suspended" do - res = {coroutine.resume(co)} - end - assert(coroutine.status(co) == "dead") - return table.unpack(res) -end - -function colog(f) - local co = coroutine.create(f) - local res = {} - while coroutine.status(co) == "suspended" do - local run = {coroutine.resume(co)} - if run[1] then - table.insert(res, coroutine.status(co) == "suspended" and "yield" or "return"); - else - table.insert(res, "error"); - end - table.move(run, 2, #run, 1 + #res, res) -- equivalent to table.append(res, run) - print(coroutine.status(co), table.unpack(res)) - end - assert(coroutine.status(co) == "dead") - return table.unpack(res) -end - --- basic behavior tests - no error/yielding, just checking argument passing -checkresults({ true, 42 }, pcall(function() return 42 end)) -checkresults({ true, 1, 2, 42 }, pcall(function(a, b) return a, b, 42 end, 1, 2)) -checkresults({ true, 2 }, pcall(function(...) return select('#', ...) end, 1, 2)) - --- the argument could be a C function or a callable -checkresults({ true, 42 }, pcall(math.abs, -42)) -checkresults({ true, 42 }, pcall(setmetatable({}, { __call = function(self, arg) return math.abs(arg) end }), -42)) - --- basic error tests - including interpreter errors and errors generated by C APIs -checkerror(pcall(function() local a = nil / 5 end)) -checkerror(pcall(function() select(-100) end)) - -if not limitedstack then - -- complex error tests - stack overflow, and stack overflow through pcall - function stackinfinite() return stackinfinite() end - checkerror(pcall(stackinfinite)) - - function stackover() return pcall(stackover) end - local res = {pcall(stackover)} - assert(#res == 200) -- stack limit (MAXCCALLS) is 200 -end - --- yield tests -checkresults({ "yield", "return", true, 42 }, colog(function() return pcall(function() coroutine.yield() return 42 end) end)) -checkresults({ "yield", 1, "return", true, 42 }, colog(function() return pcall(function() coroutine.yield(1) return 42 end) end)) -checkresults({ "yield", 1, 2, 3, "return", true, 42 }, colog(function() return pcall(function() coroutine.yield(1, 2, 3) return 42 end) end)) -checkresults({ "yield", 1, "yield", 2, "yield", 3, "return", true, 42 }, colog(function() return pcall(function() for i=1,3 do coroutine.yield(i) end return 42 end) end)) -checkresults({ "yield", "return", true, 1, 2, 3}, colog(function() return pcall(function() coroutine.yield() return 1, 2, 3 end) end)) - --- recursive yield tests -checkresults({ "yield", 1, "yield", 2, "return", true, true, 3}, colog(function() return pcall(function() coroutine.yield(1) return pcall(function() coroutine.yield(2) return 3 end) end) end)) - --- error after yield tests -checkresults({ "yield", "return", false, "pcall.lua:80: foo" }, colog(function() return pcall(function() coroutine.yield() error("foo") end) end)) -checkresults({ "yield", "yield", "return", true, false, "pcall.lua:81: foo" }, colog(function() return pcall(function() coroutine.yield() return pcall(function() coroutine.yield() error("foo") end) end) end)) -checkresults({ "yield", "yield", "return", false, "pcall.lua:82: bar" }, colog(function() return pcall(function() coroutine.yield() pcall(function() coroutine.yield() error("foo") end) error("bar") end) end)) - --- returning lots of results (past MINSTACK limits) -local res = {pcall(function() return table.unpack(table.create(100, 'a')) end)} -assert(#res == 101 and res[1] == true and res[2] == 'a' and res[101] == 'a') - -local res = {corun(function() return pcall(function() coroutine.yield() return table.unpack(table.create(100, 'a')) end) end)} -assert(#res == 102 and res[1] == true and res[2] == true and res[3] == 'a' and res[102] == 'a') - --- pcall a C function after yield; resume gets multiple C entries this way -checkresults({ "yield", 1, 2, 3, "return", true }, colog(function() return pcall(coroutine.yield, 1, 2, 3) end)) -checkresults({ "yield", 1, 2, 3, "return", true, true, true }, colog(function() return pcall(pcall, pcall, coroutine.yield, 1, 2, 3) end)) -checkresults({ "yield", "return", true, true, true, 42 }, colog(function() return pcall(pcall, pcall, function() coroutine.yield() return 42 end) end)) - --- xpcall basic tests, including yielding; xpcall uses the same infra as pcall so the main testing opportunity is for error handling -checkresults({ true, 42 }, xpcall(function() return 42 end, error)) -checkresults({ true, 1, 2, 42 }, xpcall(function(a, b) return a, b, 42 end, error, 1, 2)) -checkresults({ true, 2 }, xpcall(function(...) return select('#', ...) end, error, 1, 2)) -checkresults({ "yield", "return", true, 42 }, colog(function() return xpcall(function() coroutine.yield() return 42 end, error) end)) - --- xpcall immediate error handling -checkresults({ false, "pcall.lua:103: foo" }, xpcall(function() error("foo") end, function(err) return err end)) -checkresults({ false, "bar" }, xpcall(function() error("foo") end, function(err) return "bar" end)) -checkresults({ false, 1 }, xpcall(function() error("foo") end, function(err) return 1, 2 end)) -checkresults({ false, "pcall.lua:106: foo\npcall.lua:106\npcall.lua:106\n" }, xpcall(function() error("foo") end, debug.traceback)) -checkresults({ false, "error in error handling" }, xpcall(function() error("foo") end, function(err) error("bar") end)) - --- xpcall error handling after yields -checkresults({ "yield", "return", false, "pcall.lua:110: foo" }, colog(function() return xpcall(function() coroutine.yield() error("foo") end, function(err) return err end) end)) -checkresults({ "yield", "return", false, "pcall.lua:111: foo\npcall.lua:111\npcall.lua:111\n" }, colog(function() return xpcall(function() coroutine.yield() error("foo") end, debug.traceback) end)) - --- xpcall error handling during error handling inside xpcall after yields -checkresults({ "yield", "return", true, false, "error in error handling" }, colog(function() return xpcall(function() return xpcall(function() coroutine.yield() error("foo") end, function(err) error("bar") end) end, error) end)) - --- xpcall + pcall + yield -checkresults({"yield", 42, "return", true, true, true}, colog(function() return xpcall(pcall, function (...) return ... end, function() return pcall(function() coroutine.yield(42) end) end) end)) - --- xpcall error -checkresults({ false, "missing argument #2 to 'xpcall' (function expected)" }, pcall(xpcall, function() return 42 end)) -checkresults({ false, "invalid argument #2 to 'xpcall' (function expected, got boolean)" }, pcall(xpcall, function() return 42 end, true)) - --- stack overflow during coroutine resumption -function weird() -coroutine.yield(weird) -weird() -end - -checkresults({ false, "pcall.lua:129: cannot resume dead coroutine" }, pcall(function() for _ in coroutine.wrap(pcall), weird do end end)) - --- c++ exception -checkresults({ false, "oops" }, pcall(cxxthrow)) - --- resumeerror -local co = coroutine.create(function() - local ok, err = pcall(function() - coroutine.yield() - end) - coroutine.yield() - return ok, err -end) - -coroutine.resume(co) -resumeerror(co, "fail") -checkresults({ true, false, "fail" }, coroutine.resume(co)) - --- stack overflow needs to happen at the call limit -local calllimit = 20000 -function recurse(n) return n <= 1 and 1 or recurse(n-1) + 1 end - --- we use one frame for top-level function and one frame is the service frame for coroutines -assert(recurse(calllimit - 2) == calllimit - 2) - --- note that when calling through pcall, pcall eats one more frame -checkresults({ true, calllimit - 3 }, pcall(recurse, calllimit - 3)) -checkerror(pcall(recurse, calllimit - 2)) - --- xpcall handler runs in context of the stack frame, but this works just fine since we allow extra stack consumption past stack overflow -checkresults({ false, "ok" }, xpcall(recurse, function() return string.reverse("ko") end, calllimit - 2)) - --- however, if xpcall handler itself runs out of extra stack space, we get "error in error handling" -checkresults({ false, "error in error handling" }, xpcall(recurse, function() return recurse(calllimit) end, calllimit - 2)) - --- simulate OOM and make sure we can catch it with pcall or xpcall -checkresults({ false, "not enough memory" }, pcall(function() table.create(1e6) end)) -checkresults({ false, "not enough memory" }, xpcall(function() table.create(1e6) end, function(e) return e end)) -checkresults({ false, "oops" }, xpcall(function() table.create(1e6) end, function(e) return "oops" end)) -checkresults({ false, "error in error handling" }, xpcall(function() error("oops") end, function(e) table.create(1e6) end)) -checkresults({ false, "not enough memory" }, xpcall(function() table.create(1e6) end, function(e) table.create(1e6) end)) - -co = coroutine.create(function() table.create(1e6) end) -coroutine.resume(co) -checkresults({ false, "not enough memory" }, coroutine.close(co)) - --- ensure that pcall and xpcall close upvalues when handling error -local upclo -local function uptest(y) - local a, b, c, d = 1, 2, 3, 4 - upclo = function() - local t = table.pack("a", "b", "d", "e", "f", "g", "h", "i") - assert(a == 1) - assert(b == 2) - assert(c == 3) - assert(d == 4) - a, b, c, d = table.unpack(t) - return "ok" - end - if y then - y() - end - error("oops") -end - --- ensure that pcall and xpcall close upvalues when handling error (immediate) -do - upclo = nil - pcall(uptest, nil) - assert(upclo() == "ok") -end - -do - upclo = nil - xpcall(uptest, function(err) end, nil) - assert(upclo() == "ok") -end - -do - upclo = nil - xpcall(uptest, function(err) return "e", "e", "e", "e", "e", "e", "e", "e" end, nil) - assert(upclo() == "ok") -end - --- ensure that pcall and xpcall close upvalues when handling error (deferred) -do - upclo = nil - checkresults({"yield", "return", "ok"}, colog(function() - pcall(uptest, coroutine.yield) - return upclo() - end)) -end - -do - upclo = nil - checkresults({"yield", "return", "ok"}, colog(function() - xpcall(uptest, function(err) end, coroutine.yield) - return upclo() - end)) -end - -do - upclo = nil - checkresults({"yield", "return", "ok"}, colog(function() - xpcall(uptest, function(err) return "e", "e", "e", "e", "e", "e", "e", "e" end, coroutine.yield) - return upclo() - end)) -end - --- also cover an edge case where xpcall's error handler may want access to the upvalues (immediate + deferred) -do - local ur - upclo = nil - xpcall(uptest, function(err) - ur = upclo() - end, nil) - assert(ur == "ok") -end - -do - local ur - upclo = nil - checkresults({"yield", "return"}, colog(function() - xpcall(uptest, function(err) - ur = upclo() - end, coroutine.yield) - end)) - assert(ur == "ok") -end - --- test stack overflow from a thread that had an error, recovered, and subsequently called coroutine.resume again -if not limitedstack then - local count = 0 - local function foo() - count += 1 - pcall(1) -- create an error - coroutine.wrap(foo)() -- call another coroutine - end - checkerror(pcall(foo)) -- triggers C stack overflow - assert(count + 1 == 200) -- stack limit (MAXCCALLS) is 200, -1 for first pcall -end - -return 'OK' diff --git a/tests/conformance/pcall.luau b/tests/conformance/pcall.luau new file mode 100644 index 000000000..db2db1831 --- /dev/null +++ b/tests/conformance/pcall.luau @@ -0,0 +1,577 @@ +-- This file is part of the Luau programming language and is licensed under MIT License; see LICENSE.txt for details +print("testing pcall") + +function checkresults(e, ...) + local t = table.pack(...) + assert(t.n == #e) + for i = 1, t.n do + assert(t[i] == e[i]) + end +end + +function checkerror(...) + local t = table.pack(...) + assert(t.n == 2) + assert(t[1] == false) + assert(type(t[2]) == "string") +end + +function corun(f) + local co = coroutine.create(f) + local res = {} + while coroutine.status(co) == "suspended" do + res = { coroutine.resume(co) } + end + assert(coroutine.status(co) == "dead") + return table.unpack(res) +end + +function colog(f) + local co = coroutine.create(f) + local res = {} + while coroutine.status(co) == "suspended" do + local run = { coroutine.resume(co) } + if run[1] then + table.insert(res, coroutine.status(co) == "suspended" and "yield" or "return") + else + table.insert(res, "error") + end + table.move(run, 2, #run, 1 + #res, res) -- equivalent to table.append(res, run) + print(coroutine.status(co), table.unpack(res)) + end + assert(coroutine.status(co) == "dead") + return table.unpack(res) +end + +-- basic behavior tests - no error/yielding, just checking argument passing +checkresults( + { true, 42 }, + pcall(function() + return 42 + end) +) +checkresults( + { true, 1, 2, 42 }, + pcall(function(a, b) + return a, b, 42 + end, 1, 2) +) +checkresults( + { true, 2 }, + pcall(function(...) + return select("#", ...) + end, 1, 2) +) + +-- the argument could be a C function or a callable +checkresults({ true, 42 }, pcall(math.abs, -42)) +checkresults( + { true, 42 }, + pcall( + setmetatable({}, { + __call = function(self, arg) + return math.abs(arg) + end, + }), + -42 + ) +) + +-- basic error tests - including interpreter errors and errors generated by C APIs +checkerror(pcall(function() + local a = nil / 5 +end)) +checkerror(pcall(function() + select(-100) +end)) + +if not limitedstack then + -- complex error tests - stack overflow, and stack overflow through pcall + function stackinfinite() + return stackinfinite() + end + checkerror(pcall(stackinfinite)) + + function stackover() + return pcall(stackover) + end + local res = { pcall(stackover) } + assert(#res == 200) -- stack limit (MAXCCALLS) is 200 +end + +-- yield tests +checkresults( + { "yield", "return", true, 42 }, + colog(function() + return pcall(function() + coroutine.yield() + return 42 + end) + end) +) +checkresults( + { "yield", 1, "return", true, 42 }, + colog(function() + return pcall(function() + coroutine.yield(1) + return 42 + end) + end) +) +checkresults( + { "yield", 1, 2, 3, "return", true, 42 }, + colog(function() + return pcall(function() + coroutine.yield(1, 2, 3) + return 42 + end) + end) +) +checkresults( + { "yield", 1, "yield", 2, "yield", 3, "return", true, 42 }, + colog(function() + return pcall(function() + for i = 1, 3 do + coroutine.yield(i) + end + return 42 + end) + end) +) +checkresults( + { "yield", "return", true, 1, 2, 3 }, + colog(function() + return pcall(function() + coroutine.yield() + return 1, 2, 3 + end) + end) +) + +-- recursive yield tests +checkresults( + { "yield", 1, "yield", 2, "return", true, true, 3 }, + colog(function() + return pcall(function() + coroutine.yield(1) + return pcall(function() + coroutine.yield(2) + return 3 + end) + end) + end) +) + +-- error after yield tests +checkresults( + { "yield", "return", false, "pcall.luau:171: foo" }, + colog(function() + return pcall(function() + coroutine.yield() + error("foo") + end) + end) +) +checkresults( + { "yield", "yield", "return", true, false, "pcall.luau:182: foo" }, + colog(function() + return pcall(function() + coroutine.yield() + return pcall(function() + coroutine.yield() + error("foo") + end) + end) + end) +) +checkresults( + { "yield", "yield", "return", false, "pcall.luau:196: bar" }, + colog(function() + return pcall(function() + coroutine.yield() + pcall(function() + coroutine.yield() + error("foo") + end) + error("bar") + end) + end) +) + +-- returning lots of results (past MINSTACK limits) +local res = { pcall(function() + return table.unpack(table.create(100, "a")) +end) } +assert(#res == 101 and res[1] == true and res[2] == "a" and res[101] == "a") + +local res = + { corun(function() + return pcall(function() + coroutine.yield() + return table.unpack(table.create(100, "a")) + end) + end) } +assert(#res == 102 and res[1] == true and res[2] == true and res[3] == "a" and res[102] == "a") + +-- pcall a C function after yield; resume gets multiple C entries this way +checkresults( + { "yield", 1, 2, 3, "return", true }, + colog(function() + return pcall(coroutine.yield, 1, 2, 3) + end) +) +checkresults( + { "yield", 1, 2, 3, "return", true, true, true }, + colog(function() + return pcall(pcall, pcall, coroutine.yield, 1, 2, 3) + end) +) +checkresults( + { "yield", "return", true, true, true, 42 }, + colog(function() + return pcall(pcall, pcall, function() + coroutine.yield() + return 42 + end) + end) +) + +-- xpcall basic tests, including yielding; xpcall uses the same infra as pcall so the main testing opportunity is for error handling +checkresults( + { true, 42 }, + xpcall(function() + return 42 + end, error) +) +checkresults( + { true, 1, 2, 42 }, + xpcall(function(a, b) + return a, b, 42 + end, error, 1, 2) +) +checkresults( + { true, 2 }, + xpcall(function(...) + return select("#", ...) + end, error, 1, 2) +) +checkresults( + { "yield", "return", true, 42 }, + colog(function() + return xpcall(function() + coroutine.yield() + return 42 + end, error) + end) +) + +-- xpcall immediate error handling +checkresults( + { false, "pcall.luau:272: foo" }, + xpcall(function() + error("foo") + end, function(err) + return err + end) +) +checkresults( + { false, "bar" }, + xpcall(function() + error("foo") + end, function(err) + return "bar" + end) +) +checkresults( + { false, 1 }, + xpcall(function() + error("foo") + end, function(err) + return 1, 2 + end) +) +checkresults({ false, "pcall.luau:293: foo\npcall.luau:293\npcall.luau:293\n" }, xpcall(function() error("foo") end, debug.traceback)) +checkresults( + { false, "error in error handling" }, + xpcall(function() + error("foo") + end, function(err) + error("bar") + end) +) + +-- xpcall error handling after yields +checkresults( + { "yield", "return", false, "pcall.luau:309: foo" }, + colog(function() + return xpcall(function() + coroutine.yield() + error("foo") + end, function(err) + return err + end) + end) +) +checkresults({ "yield", "return", false, "pcall.luau:315: foo\npcall.luau:315\npcall.luau:315\n" }, colog(function() return xpcall(function() coroutine.yield() error("foo") end, debug.traceback) end)) + +-- xpcall error handling during error handling inside xpcall after yields +checkresults( + { "yield", "return", true, false, "error in error handling" }, + colog(function() + return xpcall(function() + return xpcall(function() + coroutine.yield() + error("foo") + end, function(err) + error("bar") + end) + end, error) + end) +) + +-- xpcall + pcall + yield +checkresults( + { "yield", 42, "return", true, true, true }, + colog(function() + return xpcall(pcall, function(...) + return ... + end, function() + return pcall(function() + coroutine.yield(42) + end) + end) + end) +) + +-- xpcall error +checkresults( + { false, "missing argument #2 to 'xpcall' (function expected)" }, + pcall(xpcall, function() + return 42 + end) +) +checkresults( + { false, "invalid argument #2 to 'xpcall' (function expected, got boolean)" }, + pcall(xpcall, function() + return 42 + end, true) +) + +-- stack overflow during coroutine resumption +function weird() + coroutine.yield(weird) + weird() +end + +checkresults( + { false, "pcall.luau:369: cannot resume dead coroutine" }, + pcall(function() + for _ in coroutine.wrap(pcall), weird do + end + end) +) + +-- c++ exception +checkresults({ false, "oops" }, pcall(cxxthrow)) + +-- resumeerror +local co = coroutine.create(function() + local ok, err = pcall(function() + coroutine.yield() + end) + coroutine.yield() + return ok, err +end) + +coroutine.resume(co) +resumeerror(co, "fail") +checkresults({ true, false, "fail" }, coroutine.resume(co)) + +-- stack overflow needs to happen at the call limit +local calllimit = 20000 +function recurse(n) + return n <= 1 and 1 or recurse(n - 1) + 1 +end + +-- we use one frame for top-level function and one frame is the service frame for coroutines +assert(recurse(calllimit - 2) == calllimit - 2) + +-- note that when calling through pcall, pcall eats one more frame +checkresults({ true, calllimit - 3 }, pcall(recurse, calllimit - 3)) +checkerror(pcall(recurse, calllimit - 2)) + +-- xpcall handler runs in context of the stack frame, but this works just fine since we allow extra stack consumption past stack overflow +checkresults( + { false, "ok" }, + xpcall(recurse, function() + return string.reverse("ko") + end, calllimit - 2) +) + +-- however, if xpcall handler itself runs out of extra stack space, we get "error in error handling" +checkresults( + { false, "error in error handling" }, + xpcall(recurse, function() + return recurse(calllimit) + end, calllimit - 2) +) + +-- simulate OOM and make sure we can catch it with pcall or xpcall +checkresults( + { false, "not enough memory" }, + pcall(function() + table.create(1e6) + end) +) +checkresults( + { false, "not enough memory" }, + xpcall(function() + table.create(1e6) + end, function(e) + return e + end) +) +checkresults( + { false, "oops" }, + xpcall(function() + table.create(1e6) + end, function(e) + return "oops" + end) +) +checkresults( + { false, "error in error handling" }, + xpcall(function() + error("oops") + end, function(e) + table.create(1e6) + end) +) +checkresults( + { false, "not enough memory" }, + xpcall(function() + table.create(1e6) + end, function(e) + table.create(1e6) + end) +) + +co = coroutine.create(function() + table.create(1e6) +end) +coroutine.resume(co) +checkresults({ false, "not enough memory" }, coroutine.close(co)) + +-- ensure that pcall and xpcall close upvalues when handling error +local upclo +local function uptest(y) + local a, b, c, d = 1, 2, 3, 4 + upclo = function() + local t = table.pack("a", "b", "d", "e", "f", "g", "h", "i") + assert(a == 1) + assert(b == 2) + assert(c == 3) + assert(d == 4) + a, b, c, d = table.unpack(t) + return "ok" + end + if y then + y() + end + error("oops") +end + +-- ensure that pcall and xpcall close upvalues when handling error (immediate) +do + upclo = nil + pcall(uptest, nil) + assert(upclo() == "ok") +end + +do + upclo = nil + xpcall(uptest, function(err) end, nil) + assert(upclo() == "ok") +end + +do + upclo = nil + xpcall(uptest, function(err) + return "e", "e", "e", "e", "e", "e", "e", "e" + end, nil) + assert(upclo() == "ok") +end + +-- ensure that pcall and xpcall close upvalues when handling error (deferred) +do + upclo = nil + checkresults( + { "yield", "return", "ok" }, + colog(function() + pcall(uptest, coroutine.yield) + return upclo() + end) + ) +end + +do + upclo = nil + checkresults( + { "yield", "return", "ok" }, + colog(function() + xpcall(uptest, function(err) end, coroutine.yield) + return upclo() + end) + ) +end + +do + upclo = nil + checkresults( + { "yield", "return", "ok" }, + colog(function() + xpcall(uptest, function(err) + return "e", "e", "e", "e", "e", "e", "e", "e" + end, coroutine.yield) + return upclo() + end) + ) +end + +-- also cover an edge case where xpcall's error handler may want access to the upvalues (immediate + deferred) +do + local ur + upclo = nil + xpcall(uptest, function(err) + ur = upclo() + end, nil) + assert(ur == "ok") +end + +do + local ur + upclo = nil + checkresults( + { "yield", "return" }, + colog(function() + xpcall(uptest, function(err) + ur = upclo() + end, coroutine.yield) + end) + ) + assert(ur == "ok") +end + +-- test stack overflow from a thread that had an error, recovered, and subsequently called coroutine.resume again +if not limitedstack then + local count = 0 + local function foo() + count += 1 + pcall(1) -- create an error + coroutine.wrap(foo)() -- call another coroutine + end + checkerror(pcall(foo)) -- triggers C stack overflow + assert(count + 1 == 200) -- stack limit (MAXCCALLS) is 200, -1 for first pcall +end + +return "OK" diff --git a/tests/conformance/pm.lua b/tests/conformance/pm.luau similarity index 100% rename from tests/conformance/pm.lua rename to tests/conformance/pm.luau diff --git a/tests/conformance/safeenv.lua b/tests/conformance/safeenv.luau similarity index 100% rename from tests/conformance/safeenv.lua rename to tests/conformance/safeenv.luau diff --git a/tests/conformance/sort.lua b/tests/conformance/sort.luau similarity index 100% rename from tests/conformance/sort.lua rename to tests/conformance/sort.luau diff --git a/tests/conformance/strconv.lua b/tests/conformance/strconv.luau similarity index 100% rename from tests/conformance/strconv.lua rename to tests/conformance/strconv.luau diff --git a/tests/conformance/stringinterp.lua b/tests/conformance/stringinterp.luau similarity index 100% rename from tests/conformance/stringinterp.lua rename to tests/conformance/stringinterp.luau diff --git a/tests/conformance/strings.lua b/tests/conformance/strings.luau similarity index 100% rename from tests/conformance/strings.lua rename to tests/conformance/strings.luau diff --git a/tests/conformance/tables.lua b/tests/conformance/tables.luau similarity index 100% rename from tests/conformance/tables.lua rename to tests/conformance/tables.luau diff --git a/tests/conformance/tmerror.lua b/tests/conformance/tmerror.luau similarity index 100% rename from tests/conformance/tmerror.lua rename to tests/conformance/tmerror.luau diff --git a/tests/conformance/tpack.lua b/tests/conformance/tpack.luau similarity index 100% rename from tests/conformance/tpack.lua rename to tests/conformance/tpack.luau diff --git a/tests/conformance/types.lua b/tests/conformance/types.luau similarity index 100% rename from tests/conformance/types.lua rename to tests/conformance/types.luau diff --git a/tests/conformance/userdata.lua b/tests/conformance/userdata.luau similarity index 100% rename from tests/conformance/userdata.lua rename to tests/conformance/userdata.luau diff --git a/tests/conformance/utf8.lua b/tests/conformance/utf8.luau similarity index 100% rename from tests/conformance/utf8.lua rename to tests/conformance/utf8.luau diff --git a/tests/conformance/vararg.lua b/tests/conformance/vararg.luau similarity index 100% rename from tests/conformance/vararg.lua rename to tests/conformance/vararg.luau diff --git a/tests/conformance/vector.lua b/tests/conformance/vector.luau similarity index 100% rename from tests/conformance/vector.lua rename to tests/conformance/vector.luau diff --git a/tests/conformance/vector_library.lua b/tests/conformance/vector_library.luau similarity index 100% rename from tests/conformance/vector_library.lua rename to tests/conformance/vector_library.luau diff --git a/tests/require/without_config/lua/init.lua b/tests/require/without_config/lua/init.lua index 7c28b735f..2c5e75071 100644 --- a/tests/require/without_config/lua/init.lua +++ b/tests/require/without_config/lua/init.lua @@ -1 +1 @@ -return {"result from init.lua"} +return { "result from init.lua" }