superacli 1.1.4 → 1.1.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/__tests__/adapter-schema.test.js +2 -0
- package/__tests__/config.test.js +62 -1
- package/__tests__/help-json.test.js +2 -0
- package/__tests__/mcp-adapter.test.js +14 -4
- package/__tests__/mcp-local.test.js +159 -0
- package/__tests__/mcp-stdio-jsonrpc.test.js +105 -0
- package/__tests__/monty-plugin.test.js +121 -0
- package/__tests__/plugin-browser-use-uninstall.test.js +23 -0
- package/__tests__/plugin-browser-use.test.js +77 -0
- package/__tests__/plugins-command.test.js +92 -1
- package/__tests__/plugins-learn.test.js +62 -0
- package/__tests__/plugins-registry.test.js +3 -1
- package/__tests__/resend-plugin.test.js +122 -0
- package/__tests__/skills.test.js +4 -0
- package/cli/adapter-schema.js +3 -2
- package/cli/adapters/mcp.js +22 -3
- package/cli/adapters/process.js +34 -7
- package/cli/config.js +27 -1
- package/cli/help-json.js +2 -2
- package/cli/mcp-diagnostics.js +152 -0
- package/cli/mcp-discovery.js +221 -0
- package/cli/mcp-local.js +267 -25
- package/cli/mcp-stdio-jsonrpc.js +246 -0
- package/cli/plugin-install-guidance.js +25 -0
- package/cli/plugins-command.js +86 -3
- package/cli/plugins-learn.js +177 -0
- package/cli/plugins-manager.js +3 -0
- package/cli/plugins-registry.js +2 -1
- package/cli/skills-mcp.js +102 -0
- package/cli/skills.js +6 -40
- package/cli/supercli.js +7 -2
- package/docs/initial/mcp-local-mode.md +35 -0
- package/docs/mcp-cheatsheet.md +324 -0
- package/docs/plugins.md +7 -0
- package/package.json +1 -1
- package/plugins/browser-use/plugin.json +23 -0
- package/plugins/browser-use/scripts/post-install.js +146 -0
- package/plugins/browser-use/scripts/post-uninstall.js +28 -0
- package/plugins/browser-use/skills/quickstart/SKILL.md +47 -0
- package/plugins/monty/README.md +49 -0
- package/plugins/monty/plugin.json +69 -0
- package/plugins/monty/scripts/post-install.js +73 -0
- package/plugins/monty/scripts/post-uninstall.js +23 -0
- package/plugins/monty/scripts/run-python.js +140 -0
- package/plugins/monty/scripts/setup-monty.js +27 -0
- package/plugins/plugins.json +29 -0
- package/plugins/resend/plugin.json +371 -0
- package/plugins/resend/scripts/post-install.js +59 -0
- package/plugins/resend/scripts/post-uninstall.js +23 -0
- package/plugins/resend/scripts/setup-resend.js +27 -0
- package/plugins/resend/skills/quickstart/SKILL.md +80 -0
- package/ref-monty/.cargo/config.toml +3 -0
- package/ref-monty/.claude/settings.json +60 -0
- package/ref-monty/.claude/skills/fastmod/SKILL.md +22 -0
- package/ref-monty/.claude/skills/python-playground/SKILL.md +47 -0
- package/ref-monty/.codecov.yml +12 -0
- package/ref-monty/.github/actions/build-pgo-wheel/action.yml +72 -0
- package/ref-monty/.github/workflows/ci.yml +776 -0
- package/ref-monty/.github/workflows/codspeed.yml +45 -0
- package/ref-monty/.github/workflows/init-npm-packages.yml +82 -0
- package/ref-monty/.pre-commit-config.yaml +47 -0
- package/ref-monty/.python-version +1 -0
- package/ref-monty/.rustfmt.toml +4 -0
- package/ref-monty/.zed/settings.json +11 -0
- package/ref-monty/CLAUDE.md +535 -0
- package/ref-monty/Cargo.lock +3798 -0
- package/ref-monty/Cargo.toml +87 -0
- package/ref-monty/LICENSE +21 -0
- package/ref-monty/Makefile +216 -0
- package/ref-monty/README.md +430 -0
- package/ref-monty/RELEASING.md +47 -0
- package/ref-monty/crates/fuzz/Cargo.toml +30 -0
- package/ref-monty/crates/fuzz/fuzz_targets/string_input_panic.rs +37 -0
- package/ref-monty/crates/fuzz/fuzz_targets/tokens_input_panic.rs +552 -0
- package/ref-monty/crates/monty/Cargo.toml +68 -0
- package/ref-monty/crates/monty/benches/main.rs +247 -0
- package/ref-monty/crates/monty/build.rs +10 -0
- package/ref-monty/crates/monty/src/args.rs +733 -0
- package/ref-monty/crates/monty/src/asyncio.rs +179 -0
- package/ref-monty/crates/monty/src/builtins/abs.rs +55 -0
- package/ref-monty/crates/monty/src/builtins/all.rs +30 -0
- package/ref-monty/crates/monty/src/builtins/any.rs +30 -0
- package/ref-monty/crates/monty/src/builtins/bin.rs +59 -0
- package/ref-monty/crates/monty/src/builtins/chr.rs +46 -0
- package/ref-monty/crates/monty/src/builtins/divmod.rs +164 -0
- package/ref-monty/crates/monty/src/builtins/enumerate.rs +52 -0
- package/ref-monty/crates/monty/src/builtins/filter.rs +67 -0
- package/ref-monty/crates/monty/src/builtins/getattr.rs +65 -0
- package/ref-monty/crates/monty/src/builtins/hash.rs +28 -0
- package/ref-monty/crates/monty/src/builtins/hex.rs +58 -0
- package/ref-monty/crates/monty/src/builtins/id.rs +24 -0
- package/ref-monty/crates/monty/src/builtins/isinstance.rs +68 -0
- package/ref-monty/crates/monty/src/builtins/len.rs +25 -0
- package/ref-monty/crates/monty/src/builtins/map.rs +98 -0
- package/ref-monty/crates/monty/src/builtins/min_max.rs +113 -0
- package/ref-monty/crates/monty/src/builtins/mod.rs +246 -0
- package/ref-monty/crates/monty/src/builtins/next.rs +21 -0
- package/ref-monty/crates/monty/src/builtins/oct.rs +59 -0
- package/ref-monty/crates/monty/src/builtins/ord.rs +67 -0
- package/ref-monty/crates/monty/src/builtins/pow.rs +365 -0
- package/ref-monty/crates/monty/src/builtins/print.rs +141 -0
- package/ref-monty/crates/monty/src/builtins/repr.rs +16 -0
- package/ref-monty/crates/monty/src/builtins/reversed.rs +28 -0
- package/ref-monty/crates/monty/src/builtins/round.rs +174 -0
- package/ref-monty/crates/monty/src/builtins/sorted.rs +151 -0
- package/ref-monty/crates/monty/src/builtins/sum.rs +66 -0
- package/ref-monty/crates/monty/src/builtins/type_.rs +16 -0
- package/ref-monty/crates/monty/src/builtins/zip.rs +77 -0
- package/ref-monty/crates/monty/src/bytecode/builder.rs +699 -0
- package/ref-monty/crates/monty/src/bytecode/code.rs +310 -0
- package/ref-monty/crates/monty/src/bytecode/compiler.rs +3206 -0
- package/ref-monty/crates/monty/src/bytecode/mod.rs +24 -0
- package/ref-monty/crates/monty/src/bytecode/op.rs +617 -0
- package/ref-monty/crates/monty/src/bytecode/vm/async_exec.rs +1058 -0
- package/ref-monty/crates/monty/src/bytecode/vm/attr.rs +63 -0
- package/ref-monty/crates/monty/src/bytecode/vm/binary.rs +487 -0
- package/ref-monty/crates/monty/src/bytecode/vm/call.rs +767 -0
- package/ref-monty/crates/monty/src/bytecode/vm/collections.rs +741 -0
- package/ref-monty/crates/monty/src/bytecode/vm/compare.rs +147 -0
- package/ref-monty/crates/monty/src/bytecode/vm/exceptions.rs +297 -0
- package/ref-monty/crates/monty/src/bytecode/vm/format.rs +132 -0
- package/ref-monty/crates/monty/src/bytecode/vm/mod.rs +1958 -0
- package/ref-monty/crates/monty/src/bytecode/vm/scheduler.rs +620 -0
- package/ref-monty/crates/monty/src/exception_private.rs +1513 -0
- package/ref-monty/crates/monty/src/exception_public.rs +346 -0
- package/ref-monty/crates/monty/src/expressions.rs +694 -0
- package/ref-monty/crates/monty/src/fstring.rs +854 -0
- package/ref-monty/crates/monty/src/function.rs +119 -0
- package/ref-monty/crates/monty/src/heap.rs +1073 -0
- package/ref-monty/crates/monty/src/heap_data.rs +985 -0
- package/ref-monty/crates/monty/src/heap_traits.rs +312 -0
- package/ref-monty/crates/monty/src/intern.rs +837 -0
- package/ref-monty/crates/monty/src/io.rs +106 -0
- package/ref-monty/crates/monty/src/lib.rs +52 -0
- package/ref-monty/crates/monty/src/modules/asyncio.rs +144 -0
- package/ref-monty/crates/monty/src/modules/math.rs +1453 -0
- package/ref-monty/crates/monty/src/modules/mod.rs +120 -0
- package/ref-monty/crates/monty/src/modules/os.rs +116 -0
- package/ref-monty/crates/monty/src/modules/pathlib.rs +33 -0
- package/ref-monty/crates/monty/src/modules/re.rs +606 -0
- package/ref-monty/crates/monty/src/modules/sys.rs +60 -0
- package/ref-monty/crates/monty/src/modules/typing.rs +70 -0
- package/ref-monty/crates/monty/src/namespace.rs +21 -0
- package/ref-monty/crates/monty/src/object.rs +1040 -0
- package/ref-monty/crates/monty/src/os.rs +215 -0
- package/ref-monty/crates/monty/src/parse.rs +1730 -0
- package/ref-monty/crates/monty/src/prepare.rs +3015 -0
- package/ref-monty/crates/monty/src/repl.rs +1109 -0
- package/ref-monty/crates/monty/src/resource.rs +559 -0
- package/ref-monty/crates/monty/src/run.rs +457 -0
- package/ref-monty/crates/monty/src/run_progress.rs +821 -0
- package/ref-monty/crates/monty/src/signature.rs +651 -0
- package/ref-monty/crates/monty/src/sorting.rs +100 -0
- package/ref-monty/crates/monty/src/types/bytes.rs +2356 -0
- package/ref-monty/crates/monty/src/types/dataclass.rs +345 -0
- package/ref-monty/crates/monty/src/types/dict.rs +879 -0
- package/ref-monty/crates/monty/src/types/dict_view.rs +619 -0
- package/ref-monty/crates/monty/src/types/iter.rs +799 -0
- package/ref-monty/crates/monty/src/types/list.rs +929 -0
- package/ref-monty/crates/monty/src/types/long_int.rs +211 -0
- package/ref-monty/crates/monty/src/types/mod.rs +48 -0
- package/ref-monty/crates/monty/src/types/module.rs +146 -0
- package/ref-monty/crates/monty/src/types/namedtuple.rs +261 -0
- package/ref-monty/crates/monty/src/types/path.rs +596 -0
- package/ref-monty/crates/monty/src/types/property.rs +35 -0
- package/ref-monty/crates/monty/src/types/py_trait.rs +322 -0
- package/ref-monty/crates/monty/src/types/range.rs +285 -0
- package/ref-monty/crates/monty/src/types/re_match.rs +522 -0
- package/ref-monty/crates/monty/src/types/re_pattern.rs +726 -0
- package/ref-monty/crates/monty/src/types/set.rs +1373 -0
- package/ref-monty/crates/monty/src/types/slice.rs +257 -0
- package/ref-monty/crates/monty/src/types/str.rs +2051 -0
- package/ref-monty/crates/monty/src/types/tuple.rs +376 -0
- package/ref-monty/crates/monty/src/types/type.rs +407 -0
- package/ref-monty/crates/monty/src/value.rs +2558 -0
- package/ref-monty/crates/monty/test_cases/args__dict_get_no_args.py +3 -0
- package/ref-monty/crates/monty/test_cases/args__dict_get_too_many.py +3 -0
- package/ref-monty/crates/monty/test_cases/args__dict_items_with_args.py +3 -0
- package/ref-monty/crates/monty/test_cases/args__dict_keys_with_args.py +3 -0
- package/ref-monty/crates/monty/test_cases/args__dict_pop_no_args.py +3 -0
- package/ref-monty/crates/monty/test_cases/args__dict_pop_too_many.py +3 -0
- package/ref-monty/crates/monty/test_cases/args__dict_values_with_args.py +3 -0
- package/ref-monty/crates/monty/test_cases/args__id_too_many.py +2 -0
- package/ref-monty/crates/monty/test_cases/args__len_no_args.py +2 -0
- package/ref-monty/crates/monty/test_cases/args__len_too_many.py +2 -0
- package/ref-monty/crates/monty/test_cases/args__len_type_error_int.py +9 -0
- package/ref-monty/crates/monty/test_cases/args__len_type_error_none.py +9 -0
- package/ref-monty/crates/monty/test_cases/args__list_append_no_args.py +3 -0
- package/ref-monty/crates/monty/test_cases/args__list_append_too_many.py +3 -0
- package/ref-monty/crates/monty/test_cases/args__list_insert_too_few.py +3 -0
- package/ref-monty/crates/monty/test_cases/args__list_insert_too_many.py +3 -0
- package/ref-monty/crates/monty/test_cases/args__repr_no_args.py +2 -0
- package/ref-monty/crates/monty/test_cases/arith__div_zero_float.py +2 -0
- package/ref-monty/crates/monty/test_cases/arith__div_zero_int.py +2 -0
- package/ref-monty/crates/monty/test_cases/arith__floordiv_zero_float.py +2 -0
- package/ref-monty/crates/monty/test_cases/arith__floordiv_zero_int.py +2 -0
- package/ref-monty/crates/monty/test_cases/arith__pow_zero_neg.py +2 -0
- package/ref-monty/crates/monty/test_cases/arith__pow_zero_neg_builtin.py +9 -0
- package/ref-monty/crates/monty/test_cases/assert__expr_fail.py +2 -0
- package/ref-monty/crates/monty/test_cases/assert__fail.py +2 -0
- package/ref-monty/crates/monty/test_cases/assert__fail_msg.py +2 -0
- package/ref-monty/crates/monty/test_cases/assert__fn_fail.py +3 -0
- package/ref-monty/crates/monty/test_cases/assert__ops.py +11 -0
- package/ref-monty/crates/monty/test_cases/async__asyncio_run.py +47 -0
- package/ref-monty/crates/monty/test_cases/async__basic.py +10 -0
- package/ref-monty/crates/monty/test_cases/async__closure.py +14 -0
- package/ref-monty/crates/monty/test_cases/async__double_await_coroutine.py +16 -0
- package/ref-monty/crates/monty/test_cases/async__exception.py +10 -0
- package/ref-monty/crates/monty/test_cases/async__ext_call.py +73 -0
- package/ref-monty/crates/monty/test_cases/async__gather_all.py +85 -0
- package/ref-monty/crates/monty/test_cases/async__nested_await.py +15 -0
- package/ref-monty/crates/monty/test_cases/async__nested_gather_ext.py +37 -0
- package/ref-monty/crates/monty/test_cases/async__not_awaitable.py +10 -0
- package/ref-monty/crates/monty/test_cases/async__not_imported.py +14 -0
- package/ref-monty/crates/monty/test_cases/async__recursion_depth_isolation.py +27 -0
- package/ref-monty/crates/monty/test_cases/async__return_types.py +31 -0
- package/ref-monty/crates/monty/test_cases/async__sequential.py +16 -0
- package/ref-monty/crates/monty/test_cases/async__traceback.py +19 -0
- package/ref-monty/crates/monty/test_cases/async__with_args.py +14 -0
- package/ref-monty/crates/monty/test_cases/attr__get_int_error.py +9 -0
- package/ref-monty/crates/monty/test_cases/attr__get_list_error.py +9 -0
- package/ref-monty/crates/monty/test_cases/attr__set_frozen_nonfield.py +12 -0
- package/ref-monty/crates/monty/test_cases/attr__set_int_error.py +10 -0
- package/ref-monty/crates/monty/test_cases/attr__set_list_error.py +10 -0
- package/ref-monty/crates/monty/test_cases/bench__kitchen_sink.py +68 -0
- package/ref-monty/crates/monty/test_cases/bool__ops.py +20 -0
- package/ref-monty/crates/monty/test_cases/builtin__add_type_error.py +2 -0
- package/ref-monty/crates/monty/test_cases/builtin__filter.py +62 -0
- package/ref-monty/crates/monty/test_cases/builtin__filter_not_iterable.py +11 -0
- package/ref-monty/crates/monty/test_cases/builtin__getattr.py +84 -0
- package/ref-monty/crates/monty/test_cases/builtin__iter_funcs.py +42 -0
- package/ref-monty/crates/monty/test_cases/builtin__iter_next.py +66 -0
- package/ref-monty/crates/monty/test_cases/builtin__map.py +74 -0
- package/ref-monty/crates/monty/test_cases/builtin__map_not_iterable.py +11 -0
- package/ref-monty/crates/monty/test_cases/builtin__math_funcs.py +154 -0
- package/ref-monty/crates/monty/test_cases/builtin__more_iter_funcs.py +148 -0
- package/ref-monty/crates/monty/test_cases/builtin__next_stop_iteration.py +10 -0
- package/ref-monty/crates/monty/test_cases/builtin__print_invalid_kwarg.py +9 -0
- package/ref-monty/crates/monty/test_cases/builtin__print_kwargs.py +12 -0
- package/ref-monty/crates/monty/test_cases/builtin__repr.py +3 -0
- package/ref-monty/crates/monty/test_cases/builtin__string_funcs.py +73 -0
- package/ref-monty/crates/monty/test_cases/bytes__decode_invalid_utf8.py +18 -0
- package/ref-monty/crates/monty/test_cases/bytes__endswith_str_error.py +10 -0
- package/ref-monty/crates/monty/test_cases/bytes__getitem_index_error.py +10 -0
- package/ref-monty/crates/monty/test_cases/bytes__index_start_gt_end.py +10 -0
- package/ref-monty/crates/monty/test_cases/bytes__methods.py +394 -0
- package/ref-monty/crates/monty/test_cases/bytes__negative_count.py +9 -0
- package/ref-monty/crates/monty/test_cases/bytes__ops.py +90 -0
- package/ref-monty/crates/monty/test_cases/bytes__startswith_str_error.py +10 -0
- package/ref-monty/crates/monty/test_cases/call_object.py +3 -0
- package/ref-monty/crates/monty/test_cases/chain_comparison__all.py +79 -0
- package/ref-monty/crates/monty/test_cases/closure__param_shadows_outer.py +81 -0
- package/ref-monty/crates/monty/test_cases/closure__pep448.py +203 -0
- package/ref-monty/crates/monty/test_cases/closure__undefined_nonlocal.py +13 -0
- package/ref-monty/crates/monty/test_cases/compare__mixed_types.py +120 -0
- package/ref-monty/crates/monty/test_cases/comprehension__all.py +208 -0
- package/ref-monty/crates/monty/test_cases/comprehension__scope.py +7 -0
- package/ref-monty/crates/monty/test_cases/comprehension__unbound_local.py +14 -0
- package/ref-monty/crates/monty/test_cases/dataclass__basic.py +238 -0
- package/ref-monty/crates/monty/test_cases/dataclass__call_field_error.py +12 -0
- package/ref-monty/crates/monty/test_cases/dataclass__frozen_set_error.py +12 -0
- package/ref-monty/crates/monty/test_cases/dataclass__get_missing_attr_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/dict__get_unhashable_key.py +3 -0
- package/ref-monty/crates/monty/test_cases/dict__literal_unhashable_key.py +2 -0
- package/ref-monty/crates/monty/test_cases/dict__method_pop_missing_error.py +3 -0
- package/ref-monty/crates/monty/test_cases/dict__methods.py +151 -0
- package/ref-monty/crates/monty/test_cases/dict__ops.py +133 -0
- package/ref-monty/crates/monty/test_cases/dict__pop_unhashable_key.py +4 -0
- package/ref-monty/crates/monty/test_cases/dict__popitem_empty.py +9 -0
- package/ref-monty/crates/monty/test_cases/dict__subscript_missing_key.py +3 -0
- package/ref-monty/crates/monty/test_cases/dict__unhashable_dict_key.py +2 -0
- package/ref-monty/crates/monty/test_cases/dict__unhashable_list_key.py +2 -0
- package/ref-monty/crates/monty/test_cases/dict__unpack_type_error.py +2 -0
- package/ref-monty/crates/monty/test_cases/dict__views.py +165 -0
- package/ref-monty/crates/monty/test_cases/edge__all.py +26 -0
- package/ref-monty/crates/monty/test_cases/edge__float_int_mod.py +2 -0
- package/ref-monty/crates/monty/test_cases/edge__int_float_mod.py +2 -0
- package/ref-monty/crates/monty/test_cases/exc__args.py +16 -0
- package/ref-monty/crates/monty/test_cases/exc__str.py +15 -0
- package/ref-monty/crates/monty/test_cases/execute_ok__all.py +54 -0
- package/ref-monty/crates/monty/test_cases/execute_raise__error_instance_str.py +2 -0
- package/ref-monty/crates/monty/test_cases/execute_raise__error_no_args.py +2 -0
- package/ref-monty/crates/monty/test_cases/execute_raise__error_string_arg.py +2 -0
- package/ref-monty/crates/monty/test_cases/execute_raise__error_string_arg_quotes.py +2 -0
- package/ref-monty/crates/monty/test_cases/execute_raise__error_type.py +2 -0
- package/ref-monty/crates/monty/test_cases/execute_raise__raise_instance_via_var.py +4 -0
- package/ref-monty/crates/monty/test_cases/execute_raise__raise_list.py +2 -0
- package/ref-monty/crates/monty/test_cases/execute_raise__raise_number.py +2 -0
- package/ref-monty/crates/monty/test_cases/execute_raise__raise_type_call_via_var.py +4 -0
- package/ref-monty/crates/monty/test_cases/execute_raise__raise_type_direct.py +3 -0
- package/ref-monty/crates/monty/test_cases/execute_raise__raise_type_via_var.py +4 -0
- package/ref-monty/crates/monty/test_cases/ext_call__arg_side_effect_bug.py +22 -0
- package/ref-monty/crates/monty/test_cases/ext_call__augmented.py +17 -0
- package/ref-monty/crates/monty/test_cases/ext_call__augmented_refcount_bug.py +7 -0
- package/ref-monty/crates/monty/test_cases/ext_call__bare_raise_after_resume.py +34 -0
- package/ref-monty/crates/monty/test_cases/ext_call__basic.py +99 -0
- package/ref-monty/crates/monty/test_cases/ext_call__boolean.py +37 -0
- package/ref-monty/crates/monty/test_cases/ext_call__boolean_side_effect_hang.py +17 -0
- package/ref-monty/crates/monty/test_cases/ext_call__closure_bug.py +16 -0
- package/ref-monty/crates/monty/test_cases/ext_call__comparison.py +26 -0
- package/ref-monty/crates/monty/test_cases/ext_call__deep_call_stack.py +18 -0
- package/ref-monty/crates/monty/test_cases/ext_call__elif.py +171 -0
- package/ref-monty/crates/monty/test_cases/ext_call__exc.py +4 -0
- package/ref-monty/crates/monty/test_cases/ext_call__exc_deep_stack.py +39 -0
- package/ref-monty/crates/monty/test_cases/ext_call__exc_in_function.py +17 -0
- package/ref-monty/crates/monty/test_cases/ext_call__exc_nested_functions.py +31 -0
- package/ref-monty/crates/monty/test_cases/ext_call__ext_exc.py +171 -0
- package/ref-monty/crates/monty/test_cases/ext_call__for.py +114 -0
- package/ref-monty/crates/monty/test_cases/ext_call__fstring.py +12 -0
- package/ref-monty/crates/monty/test_cases/ext_call__if.py +135 -0
- package/ref-monty/crates/monty/test_cases/ext_call__if_condition.py +37 -0
- package/ref-monty/crates/monty/test_cases/ext_call__in_closure.py +14 -0
- package/ref-monty/crates/monty/test_cases/ext_call__in_function.py +40 -0
- package/ref-monty/crates/monty/test_cases/ext_call__in_function_simple.py +7 -0
- package/ref-monty/crates/monty/test_cases/ext_call__literals.py +17 -0
- package/ref-monty/crates/monty/test_cases/ext_call__multi_in_func.py +32 -0
- package/ref-monty/crates/monty/test_cases/ext_call__name_lookup.py +69 -0
- package/ref-monty/crates/monty/test_cases/ext_call__name_lookup_undefined.py +4 -0
- package/ref-monty/crates/monty/test_cases/ext_call__nested_calls.py +14 -0
- package/ref-monty/crates/monty/test_cases/ext_call__recursion_bug.py +19 -0
- package/ref-monty/crates/monty/test_cases/ext_call__return.py +28 -0
- package/ref-monty/crates/monty/test_cases/ext_call__side_effects.py +25 -0
- package/ref-monty/crates/monty/test_cases/ext_call__subscript.py +7 -0
- package/ref-monty/crates/monty/test_cases/ext_call__ternary.py +28 -0
- package/ref-monty/crates/monty/test_cases/ext_call__try.py +280 -0
- package/ref-monty/crates/monty/test_cases/ext_call__try_simple.py +10 -0
- package/ref-monty/crates/monty/test_cases/ext_call__unary.py +13 -0
- package/ref-monty/crates/monty/test_cases/frozenset__ops.py +178 -0
- package/ref-monty/crates/monty/test_cases/fstring__all.py +236 -0
- package/ref-monty/crates/monty/test_cases/fstring__error_eq_align_on_str.py +3 -0
- package/ref-monty/crates/monty/test_cases/fstring__error_float_f_on_str.py +3 -0
- package/ref-monty/crates/monty/test_cases/fstring__error_int_d_on_float.py +3 -0
- package/ref-monty/crates/monty/test_cases/fstring__error_int_d_on_str.py +3 -0
- package/ref-monty/crates/monty/test_cases/fstring__error_invalid_spec.py +4 -0
- package/ref-monty/crates/monty/test_cases/fstring__error_invalid_spec_dynamic.py +4 -0
- package/ref-monty/crates/monty/test_cases/fstring__error_invalid_spec_str.py +4 -0
- package/ref-monty/crates/monty/test_cases/fstring__error_str_s_on_int.py +3 -0
- package/ref-monty/crates/monty/test_cases/function__call_duplicate_kwargs.py +6 -0
- package/ref-monty/crates/monty/test_cases/function__call_unpack.py +42 -0
- package/ref-monty/crates/monty/test_cases/function__defaults.py +117 -0
- package/ref-monty/crates/monty/test_cases/function__err_duplicate_arg.py +7 -0
- package/ref-monty/crates/monty/test_cases/function__err_duplicate_first_arg.py +7 -0
- package/ref-monty/crates/monty/test_cases/function__err_duplicate_kwarg_cleanup.py +9 -0
- package/ref-monty/crates/monty/test_cases/function__err_kwonly_as_positional.py +7 -0
- package/ref-monty/crates/monty/test_cases/function__err_missing_all_posonly.py +7 -0
- package/ref-monty/crates/monty/test_cases/function__err_missing_heap_cleanup.py +9 -0
- package/ref-monty/crates/monty/test_cases/function__err_missing_kwonly.py +7 -0
- package/ref-monty/crates/monty/test_cases/function__err_missing_posonly_with_kwarg.py +7 -0
- package/ref-monty/crates/monty/test_cases/function__err_missing_with_posonly.py +7 -0
- package/ref-monty/crates/monty/test_cases/function__err_posonly_as_kwarg.py +7 -0
- package/ref-monty/crates/monty/test_cases/function__err_posonly_first_as_kwarg.py +7 -0
- package/ref-monty/crates/monty/test_cases/function__err_too_many_posonly.py +7 -0
- package/ref-monty/crates/monty/test_cases/function__err_too_many_with_kwonly.py +7 -0
- package/ref-monty/crates/monty/test_cases/function__err_unexpected_kwarg.py +7 -0
- package/ref-monty/crates/monty/test_cases/function__err_unexpected_kwarg_cleanup.py +9 -0
- package/ref-monty/crates/monty/test_cases/function__err_unexpected_kwarg_quote.py +13 -0
- package/ref-monty/crates/monty/test_cases/function__err_unexpected_kwarg_simple.py +7 -0
- package/ref-monty/crates/monty/test_cases/function__err_unpack_duplicate_arg.py +6 -0
- package/ref-monty/crates/monty/test_cases/function__err_unpack_duplicate_heap.py +8 -0
- package/ref-monty/crates/monty/test_cases/function__err_unpack_int.py +6 -0
- package/ref-monty/crates/monty/test_cases/function__err_unpack_nonstring_key.py +6 -0
- package/ref-monty/crates/monty/test_cases/function__err_unpack_not_mapping.py +6 -0
- package/ref-monty/crates/monty/test_cases/function__kwargs_unpacking.py +173 -0
- package/ref-monty/crates/monty/test_cases/function__ops.py +294 -0
- package/ref-monty/crates/monty/test_cases/function__return_none.py +42 -0
- package/ref-monty/crates/monty/test_cases/function__signatures.py +47 -0
- package/ref-monty/crates/monty/test_cases/function__too_few_args_all.py +6 -0
- package/ref-monty/crates/monty/test_cases/function__too_few_args_one.py +6 -0
- package/ref-monty/crates/monty/test_cases/function__too_few_args_two.py +6 -0
- package/ref-monty/crates/monty/test_cases/function__too_many_args_one.py +6 -0
- package/ref-monty/crates/monty/test_cases/function__too_many_args_two.py +6 -0
- package/ref-monty/crates/monty/test_cases/function__too_many_args_zero.py +6 -0
- package/ref-monty/crates/monty/test_cases/global__error_assigned_before.py +7 -0
- package/ref-monty/crates/monty/test_cases/global__ops.py +163 -0
- package/ref-monty/crates/monty/test_cases/hash__dict_unhashable.py +2 -0
- package/ref-monty/crates/monty/test_cases/hash__list_unhashable.py +2 -0
- package/ref-monty/crates/monty/test_cases/hash__ops.py +153 -0
- package/ref-monty/crates/monty/test_cases/id__bytes_literals_distinct.py +3 -0
- package/ref-monty/crates/monty/test_cases/id__int_copy_distinct.py +5 -0
- package/ref-monty/crates/monty/test_cases/id__is_number_is_number.py +3 -0
- package/ref-monty/crates/monty/test_cases/id__non_overlapping_lifetimes_distinct_types.py +10 -0
- package/ref-monty/crates/monty/test_cases/id__non_overlapping_lifetimes_same_types.py +6 -0
- package/ref-monty/crates/monty/test_cases/id__ops.py +97 -0
- package/ref-monty/crates/monty/test_cases/id__str_literals_same.py +3 -0
- package/ref-monty/crates/monty/test_cases/if__elif_else.py +207 -0
- package/ref-monty/crates/monty/test_cases/if__raise_elif.py +11 -0
- package/ref-monty/crates/monty/test_cases/if__raise_else.py +13 -0
- package/ref-monty/crates/monty/test_cases/if__raise_if.py +9 -0
- package/ref-monty/crates/monty/test_cases/if__raise_in_elif_condition.py +18 -0
- package/ref-monty/crates/monty/test_cases/if__raise_in_if_condition.py +16 -0
- package/ref-monty/crates/monty/test_cases/if_else_expr__all.py +55 -0
- package/ref-monty/crates/monty/test_cases/import__error_cannot_import.py +9 -0
- package/ref-monty/crates/monty/test_cases/import__error_module_not_found.py +9 -0
- package/ref-monty/crates/monty/test_cases/import__local_scope.py +68 -0
- package/ref-monty/crates/monty/test_cases/import__os.py +25 -0
- package/ref-monty/crates/monty/test_cases/import__relative_error.py +9 -0
- package/ref-monty/crates/monty/test_cases/import__relative_no_module_error.py +9 -0
- package/ref-monty/crates/monty/test_cases/import__runtime_error_when_executed.py +14 -0
- package/ref-monty/crates/monty/test_cases/import__star_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/import__sys.py +47 -0
- package/ref-monty/crates/monty/test_cases/import__sys_monty.py +28 -0
- package/ref-monty/crates/monty/test_cases/import__type_checking_guard.py +37 -0
- package/ref-monty/crates/monty/test_cases/import__typing.py +25 -0
- package/ref-monty/crates/monty/test_cases/import__typing_type_ignore.py +4 -0
- package/ref-monty/crates/monty/test_cases/int__bigint.py +467 -0
- package/ref-monty/crates/monty/test_cases/int__bigint_errors.py +260 -0
- package/ref-monty/crates/monty/test_cases/int__ops.py +219 -0
- package/ref-monty/crates/monty/test_cases/int__overflow_division.py +84 -0
- package/ref-monty/crates/monty/test_cases/is_variant__all.py +36 -0
- package/ref-monty/crates/monty/test_cases/isinstance__arg2_list_error.py +2 -0
- package/ref-monty/crates/monty/test_cases/isinstance__arg2_type_error.py +2 -0
- package/ref-monty/crates/monty/test_cases/iter__dict_mutation.py +4 -0
- package/ref-monty/crates/monty/test_cases/iter__for.py +243 -0
- package/ref-monty/crates/monty/test_cases/iter__for_loop_unpacking.py +66 -0
- package/ref-monty/crates/monty/test_cases/iter__generator_expr.py +20 -0
- package/ref-monty/crates/monty/test_cases/iter__generator_expr_type.py +7 -0
- package/ref-monty/crates/monty/test_cases/iter__not_iterable.py +3 -0
- package/ref-monty/crates/monty/test_cases/lambda__all.py +145 -0
- package/ref-monty/crates/monty/test_cases/list__extend_not_iterable.py +7 -0
- package/ref-monty/crates/monty/test_cases/list__getitem_out_of_bounds.py +3 -0
- package/ref-monty/crates/monty/test_cases/list__index_not_found.py +9 -0
- package/ref-monty/crates/monty/test_cases/list__index_start_gt_end.py +10 -0
- package/ref-monty/crates/monty/test_cases/list__ops.py +473 -0
- package/ref-monty/crates/monty/test_cases/list__pop_empty.py +9 -0
- package/ref-monty/crates/monty/test_cases/list__pop_out_of_range.py +9 -0
- package/ref-monty/crates/monty/test_cases/list__pop_type_error.py +9 -0
- package/ref-monty/crates/monty/test_cases/list__remove_not_found.py +9 -0
- package/ref-monty/crates/monty/test_cases/list__setitem_dict_index.py +13 -0
- package/ref-monty/crates/monty/test_cases/list__setitem_huge_int_index.py +13 -0
- package/ref-monty/crates/monty/test_cases/list__setitem_index_error.py +10 -0
- package/ref-monty/crates/monty/test_cases/list__setitem_type_error.py +10 -0
- package/ref-monty/crates/monty/test_cases/list__unpack_type_error.py +2 -0
- package/ref-monty/crates/monty/test_cases/longint__index_error.py +3 -0
- package/ref-monty/crates/monty/test_cases/longint__repeat_error.py +3 -0
- package/ref-monty/crates/monty/test_cases/loop__break_continue.py +113 -0
- package/ref-monty/crates/monty/test_cases/loop__break_finally.py +69 -0
- package/ref-monty/crates/monty/test_cases/loop__break_in_function_error.py +13 -0
- package/ref-monty/crates/monty/test_cases/loop__break_in_if_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/loop__break_nested_except_clears.py +55 -0
- package/ref-monty/crates/monty/test_cases/loop__break_outside_error.py +9 -0
- package/ref-monty/crates/monty/test_cases/loop__continue_finally.py +81 -0
- package/ref-monty/crates/monty/test_cases/loop__continue_in_function_error.py +13 -0
- package/ref-monty/crates/monty/test_cases/loop__continue_in_if_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/loop__continue_nested_except_clears.py +60 -0
- package/ref-monty/crates/monty/test_cases/loop__continue_outside_error.py +9 -0
- package/ref-monty/crates/monty/test_cases/math__acos_domain_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__acosh_domain_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__asin_domain_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__atanh_domain_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__cos_inf_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__cosh_overflow_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__exp_overflow_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__factorial_float_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__factorial_negative_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__floor_inf_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__floor_nan_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__floor_str_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__fmod_inf_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__gamma_neg_int_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__gcd_float_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__isqrt_negative_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__ldexp_overflow_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__log1p_domain_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__log_base1_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__log_zero_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__module.py +1432 -0
- package/ref-monty/crates/monty/test_cases/math__pow_domain_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__sin_inf_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__sqrt_negative_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__tan_inf_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/math__trunc_str_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/method__args_kwargs_unpacking.py +259 -0
- package/ref-monty/crates/monty/test_cases/name_error__unbound_local_func.py +19 -0
- package/ref-monty/crates/monty/test_cases/name_error__unbound_local_module.py +12 -0
- package/ref-monty/crates/monty/test_cases/name_error__undefined_call_chained.py +9 -0
- package/ref-monty/crates/monty/test_cases/name_error__undefined_call_in_expr.py +9 -0
- package/ref-monty/crates/monty/test_cases/name_error__undefined_call_in_function.py +16 -0
- package/ref-monty/crates/monty/test_cases/name_error__undefined_call_with_args.py +9 -0
- package/ref-monty/crates/monty/test_cases/name_error__undefined_global.py +10 -0
- package/ref-monty/crates/monty/test_cases/namedtuple__missing_attr.py +11 -0
- package/ref-monty/crates/monty/test_cases/namedtuple__ops.py +34 -0
- package/ref-monty/crates/monty/test_cases/nonlocal__error_module_level.py +3 -0
- package/ref-monty/crates/monty/test_cases/nonlocal__ops.py +353 -0
- package/ref-monty/crates/monty/test_cases/os__environ.py +40 -0
- package/ref-monty/crates/monty/test_cases/os__getenv_key_list_error.py +5 -0
- package/ref-monty/crates/monty/test_cases/os__getenv_key_type_error.py +5 -0
- package/ref-monty/crates/monty/test_cases/parse_error__complex.py +3 -0
- package/ref-monty/crates/monty/test_cases/pathlib__import.py +11 -0
- package/ref-monty/crates/monty/test_cases/pathlib__os.py +136 -0
- package/ref-monty/crates/monty/test_cases/pathlib__os_read_error.py +12 -0
- package/ref-monty/crates/monty/test_cases/pathlib__pure.py +81 -0
- package/ref-monty/crates/monty/test_cases/pyobject__cycle_dict_self.py +5 -0
- package/ref-monty/crates/monty/test_cases/pyobject__cycle_list_dict.py +6 -0
- package/ref-monty/crates/monty/test_cases/pyobject__cycle_list_self.py +5 -0
- package/ref-monty/crates/monty/test_cases/pyobject__cycle_multiple_refs.py +6 -0
- package/ref-monty/crates/monty/test_cases/range__error_no_args.py +2 -0
- package/ref-monty/crates/monty/test_cases/range__error_step_zero.py +2 -0
- package/ref-monty/crates/monty/test_cases/range__error_too_many_args.py +2 -0
- package/ref-monty/crates/monty/test_cases/range__getitem_index_error.py +10 -0
- package/ref-monty/crates/monty/test_cases/range__ops.py +236 -0
- package/ref-monty/crates/monty/test_cases/re__basic.py +756 -0
- package/ref-monty/crates/monty/test_cases/re__grouping.py +241 -0
- package/ref-monty/crates/monty/test_cases/re__match.py +148 -0
- package/ref-monty/crates/monty/test_cases/recursion__deep_drop.py +26 -0
- package/ref-monty/crates/monty/test_cases/recursion__deep_eq.py +23 -0
- package/ref-monty/crates/monty/test_cases/recursion__deep_hash.py +46 -0
- package/ref-monty/crates/monty/test_cases/recursion__deep_repr.py +12 -0
- package/ref-monty/crates/monty/test_cases/recursion__function_depth.py +13 -0
- package/ref-monty/crates/monty/test_cases/refcount__cycle_mutual_reference.py +18 -0
- package/ref-monty/crates/monty/test_cases/refcount__cycle_self_reference.py +12 -0
- package/ref-monty/crates/monty/test_cases/refcount__dict_basic.py +5 -0
- package/ref-monty/crates/monty/test_cases/refcount__dict_get.py +5 -0
- package/ref-monty/crates/monty/test_cases/refcount__dict_keys_and.py +14 -0
- package/ref-monty/crates/monty/test_cases/refcount__dict_overwrite.py +6 -0
- package/ref-monty/crates/monty/test_cases/refcount__gather_cleanup.py +16 -0
- package/ref-monty/crates/monty/test_cases/refcount__gather_exception.py +18 -0
- package/ref-monty/crates/monty/test_cases/refcount__gather_nested_cancel.py +25 -0
- package/ref-monty/crates/monty/test_cases/refcount__immediate_skipped.py +4 -0
- package/ref-monty/crates/monty/test_cases/refcount__kwargs_unpacking.py +27 -0
- package/ref-monty/crates/monty/test_cases/refcount__list_append_multiple.py +6 -0
- package/ref-monty/crates/monty/test_cases/refcount__list_append_ref.py +5 -0
- package/ref-monty/crates/monty/test_cases/refcount__list_concat.py +5 -0
- package/ref-monty/crates/monty/test_cases/refcount__list_getitem.py +5 -0
- package/ref-monty/crates/monty/test_cases/refcount__list_iadd.py +5 -0
- package/ref-monty/crates/monty/test_cases/refcount__nested_list.py +4 -0
- package/ref-monty/crates/monty/test_cases/refcount__re_pattern_sub_error_paths.py +37 -0
- package/ref-monty/crates/monty/test_cases/refcount__re_search_match.py +34 -0
- package/ref-monty/crates/monty/test_cases/refcount__re_sub_error_paths.py +31 -0
- package/ref-monty/crates/monty/test_cases/refcount__shared_reference.py +4 -0
- package/ref-monty/crates/monty/test_cases/refcount__single_list.py +3 -0
- package/ref-monty/crates/monty/test_cases/repr__cycle_detection.py +24 -0
- package/ref-monty/crates/monty/test_cases/set__ops.py +191 -0
- package/ref-monty/crates/monty/test_cases/set__review_bugs.py +35 -0
- package/ref-monty/crates/monty/test_cases/set__unpack_type_error.py +2 -0
- package/ref-monty/crates/monty/test_cases/slice__invalid_indices.py +2 -0
- package/ref-monty/crates/monty/test_cases/slice__kwargs.py +9 -0
- package/ref-monty/crates/monty/test_cases/slice__no_args.py +9 -0
- package/ref-monty/crates/monty/test_cases/slice__ops.py +149 -0
- package/ref-monty/crates/monty/test_cases/slice__step_zero.py +9 -0
- package/ref-monty/crates/monty/test_cases/slice__step_zero_bytes.py +9 -0
- package/ref-monty/crates/monty/test_cases/slice__step_zero_range.py +9 -0
- package/ref-monty/crates/monty/test_cases/slice__step_zero_str.py +9 -0
- package/ref-monty/crates/monty/test_cases/slice__step_zero_tuple.py +9 -0
- package/ref-monty/crates/monty/test_cases/slice__too_many_args.py +9 -0
- package/ref-monty/crates/monty/test_cases/str__getitem_index_error.py +10 -0
- package/ref-monty/crates/monty/test_cases/str__index_not_found.py +9 -0
- package/ref-monty/crates/monty/test_cases/str__join_no_args.py +9 -0
- package/ref-monty/crates/monty/test_cases/str__join_non_string.py +9 -0
- package/ref-monty/crates/monty/test_cases/str__join_not_iterable.py +9 -0
- package/ref-monty/crates/monty/test_cases/str__join_too_many_args.py +9 -0
- package/ref-monty/crates/monty/test_cases/str__methods.py +327 -0
- package/ref-monty/crates/monty/test_cases/str__ops.py +162 -0
- package/ref-monty/crates/monty/test_cases/str__partition_empty.py +9 -0
- package/ref-monty/crates/monty/test_cases/str__rsplit_empty_sep.py +9 -0
- package/ref-monty/crates/monty/test_cases/str__split_empty_sep.py +9 -0
- package/ref-monty/crates/monty/test_cases/sys__types.py +7 -0
- package/ref-monty/crates/monty/test_cases/traceback__division_error.py +30 -0
- package/ref-monty/crates/monty/test_cases/traceback__index_error.py +17 -0
- package/ref-monty/crates/monty/test_cases/traceback__insert_as_int.py +10 -0
- package/ref-monty/crates/monty/test_cases/traceback__nested_call.py +29 -0
- package/ref-monty/crates/monty/test_cases/traceback__nonlocal_module_scope.py +10 -0
- package/ref-monty/crates/monty/test_cases/traceback__nonlocal_unbound.py +24 -0
- package/ref-monty/crates/monty/test_cases/traceback__range_as_int.py +9 -0
- package/ref-monty/crates/monty/test_cases/traceback__recursion_error.py +23 -0
- package/ref-monty/crates/monty/test_cases/traceback__set_mutation.py +11 -0
- package/ref-monty/crates/monty/test_cases/traceback__undefined_attr_call.py +16 -0
- package/ref-monty/crates/monty/test_cases/traceback__undefined_call.py +16 -0
- package/ref-monty/crates/monty/test_cases/traceback__undefined_raise.py +16 -0
- package/ref-monty/crates/monty/test_cases/try_except__all.py +472 -0
- package/ref-monty/crates/monty/test_cases/try_except__bare_raise_no_context.py +2 -0
- package/ref-monty/crates/monty/test_cases/try_except__invalid_type.py +5 -0
- package/ref-monty/crates/monty/test_cases/tuple__getitem_out_of_bounds.py +3 -0
- package/ref-monty/crates/monty/test_cases/tuple__index_not_found.py +9 -0
- package/ref-monty/crates/monty/test_cases/tuple__index_start_gt_end.py +10 -0
- package/ref-monty/crates/monty/test_cases/tuple__methods.py +19 -0
- package/ref-monty/crates/monty/test_cases/tuple__ops.py +133 -0
- package/ref-monty/crates/monty/test_cases/tuple__unpack_type_error.py +2 -0
- package/ref-monty/crates/monty/test_cases/type__builtin_attr_error.py +9 -0
- package/ref-monty/crates/monty/test_cases/type__bytes_negative.py +2 -0
- package/ref-monty/crates/monty/test_cases/type__cell_not_builtin.py +9 -0
- package/ref-monty/crates/monty/test_cases/type__exception_attr_error.py +11 -0
- package/ref-monty/crates/monty/test_cases/type__float_conversion_error.py +2 -0
- package/ref-monty/crates/monty/test_cases/type__float_repr_both_quotes.py +9 -0
- package/ref-monty/crates/monty/test_cases/type__float_repr_newline.py +9 -0
- package/ref-monty/crates/monty/test_cases/type__float_repr_single_quote.py +9 -0
- package/ref-monty/crates/monty/test_cases/type__int_conversion_error.py +2 -0
- package/ref-monty/crates/monty/test_cases/type__list_not_iterable.py +2 -0
- package/ref-monty/crates/monty/test_cases/type__non_builtin_name_error.py +9 -0
- package/ref-monty/crates/monty/test_cases/type__ops.py +200 -0
- package/ref-monty/crates/monty/test_cases/type__shadow_exc.py +3 -0
- package/ref-monty/crates/monty/test_cases/type__shadow_int.py +9 -0
- package/ref-monty/crates/monty/test_cases/type__shadow_len.py +3 -0
- package/ref-monty/crates/monty/test_cases/type__tuple_not_iterable.py +2 -0
- package/ref-monty/crates/monty/test_cases/type_error__int_add_list.py +2 -0
- package/ref-monty/crates/monty/test_cases/type_error__int_div_str.py +2 -0
- package/ref-monty/crates/monty/test_cases/type_error__int_floordiv_str.py +2 -0
- package/ref-monty/crates/monty/test_cases/type_error__int_iadd_str.py +3 -0
- package/ref-monty/crates/monty/test_cases/type_error__int_mod_str.py +2 -0
- package/ref-monty/crates/monty/test_cases/type_error__int_pow_str.py +2 -0
- package/ref-monty/crates/monty/test_cases/type_error__int_sub_str.py +2 -0
- package/ref-monty/crates/monty/test_cases/type_error__list_add_int.py +2 -0
- package/ref-monty/crates/monty/test_cases/type_error__list_add_str.py +2 -0
- package/ref-monty/crates/monty/test_cases/type_error__list_iadd_int.py +6 -0
- package/ref-monty/crates/monty/test_cases/type_error__str_add_int.py +2 -0
- package/ref-monty/crates/monty/test_cases/type_error__str_iadd_int.py +3 -0
- package/ref-monty/crates/monty/test_cases/type_error__unary_invert_str.py +3 -0
- package/ref-monty/crates/monty/test_cases/type_error__unary_minus_str.py +4 -0
- package/ref-monty/crates/monty/test_cases/type_error__unary_neg_str.py +3 -0
- package/ref-monty/crates/monty/test_cases/type_error__unary_plus_str.py +4 -0
- package/ref-monty/crates/monty/test_cases/typing__types.py +24 -0
- package/ref-monty/crates/monty/test_cases/unpack__nested.py +48 -0
- package/ref-monty/crates/monty/test_cases/unpack__non_sequence.py +9 -0
- package/ref-monty/crates/monty/test_cases/unpack__not_enough.py +9 -0
- package/ref-monty/crates/monty/test_cases/unpack__ops.py +153 -0
- package/ref-monty/crates/monty/test_cases/unpack__star_not_enough.py +9 -0
- package/ref-monty/crates/monty/test_cases/unpack__too_many.py +9 -0
- package/ref-monty/crates/monty/test_cases/version__cpython.py +4 -0
- package/ref-monty/crates/monty/test_cases/walrus__all.py +178 -0
- package/ref-monty/crates/monty/test_cases/while__all.py +206 -0
- package/ref-monty/crates/monty/tests/asyncio.rs +764 -0
- package/ref-monty/crates/monty/tests/binary_serde.rs +185 -0
- package/ref-monty/crates/monty/tests/bytecode_limits.rs +248 -0
- package/ref-monty/crates/monty/tests/datatest_runner.rs +2029 -0
- package/ref-monty/crates/monty/tests/inputs.rs +420 -0
- package/ref-monty/crates/monty/tests/json_serde.rs +250 -0
- package/ref-monty/crates/monty/tests/main.rs +71 -0
- package/ref-monty/crates/monty/tests/math_module.rs +114 -0
- package/ref-monty/crates/monty/tests/name_lookup.rs +482 -0
- package/ref-monty/crates/monty/tests/os_tests.rs +459 -0
- package/ref-monty/crates/monty/tests/parse_errors.rs +441 -0
- package/ref-monty/crates/monty/tests/print_writer.rs +238 -0
- package/ref-monty/crates/monty/tests/py_object.rs +121 -0
- package/ref-monty/crates/monty/tests/regex.rs +90 -0
- package/ref-monty/crates/monty/tests/repl.rs +344 -0
- package/ref-monty/crates/monty/tests/resource_limits.rs +1826 -0
- package/ref-monty/crates/monty/tests/try_from.rs +167 -0
- package/ref-monty/crates/monty-cli/Cargo.toml +25 -0
- package/ref-monty/crates/monty-cli/src/main.rs +541 -0
- package/ref-monty/crates/monty-js/.cargo/config.toml +2 -0
- package/ref-monty/crates/monty-js/.prettierignore +8 -0
- package/ref-monty/crates/monty-js/Cargo.toml +32 -0
- package/ref-monty/crates/monty-js/README.md +207 -0
- package/ref-monty/crates/monty-js/__test__/async.spec.ts +350 -0
- package/ref-monty/crates/monty-js/__test__/basic.spec.ts +114 -0
- package/ref-monty/crates/monty-js/__test__/exceptions.spec.ts +427 -0
- package/ref-monty/crates/monty-js/__test__/external.spec.ts +354 -0
- package/ref-monty/crates/monty-js/__test__/inputs.spec.ts +143 -0
- package/ref-monty/crates/monty-js/__test__/limits.spec.ts +162 -0
- package/ref-monty/crates/monty-js/__test__/package.json +3 -0
- package/ref-monty/crates/monty-js/__test__/print.spec.ts +229 -0
- package/ref-monty/crates/monty-js/__test__/repl.spec.ts +34 -0
- package/ref-monty/crates/monty-js/__test__/serialize.spec.ts +205 -0
- package/ref-monty/crates/monty-js/__test__/start.spec.ts +443 -0
- package/ref-monty/crates/monty-js/__test__/type_check.spec.ts +147 -0
- package/ref-monty/crates/monty-js/__test__/types.spec.ts +319 -0
- package/ref-monty/crates/monty-js/build.rs +61 -0
- package/ref-monty/crates/monty-js/index-header.d.ts +3 -0
- package/ref-monty/crates/monty-js/package-lock.json +4694 -0
- package/ref-monty/crates/monty-js/package.json +100 -0
- package/ref-monty/crates/monty-js/scripts/smoke-test.sh +69 -0
- package/ref-monty/crates/monty-js/smoke-test/package.json +17 -0
- package/ref-monty/crates/monty-js/smoke-test/test.ts +171 -0
- package/ref-monty/crates/monty-js/smoke-test/tsconfig.json +11 -0
- package/ref-monty/crates/monty-js/src/convert.rs +648 -0
- package/ref-monty/crates/monty-js/src/exceptions.rs +293 -0
- package/ref-monty/crates/monty-js/src/lib.rs +41 -0
- package/ref-monty/crates/monty-js/src/limits.rs +53 -0
- package/ref-monty/crates/monty-js/src/monty_cls.rs +1407 -0
- package/ref-monty/crates/monty-js/tsconfig.json +17 -0
- package/ref-monty/crates/monty-js/wrapper.ts +701 -0
- package/ref-monty/crates/monty-python/Cargo.toml +38 -0
- package/ref-monty/crates/monty-python/README.md +134 -0
- package/ref-monty/crates/monty-python/build.rs +4 -0
- package/ref-monty/crates/monty-python/example.py +40 -0
- package/ref-monty/crates/monty-python/exercise.py +46 -0
- package/ref-monty/crates/monty-python/pyproject.toml +57 -0
- package/ref-monty/crates/monty-python/python/pydantic_monty/__init__.py +281 -0
- package/ref-monty/crates/monty-python/python/pydantic_monty/_monty.pyi +677 -0
- package/ref-monty/crates/monty-python/python/pydantic_monty/os_access.py +933 -0
- package/ref-monty/crates/monty-python/python/pydantic_monty/py.typed +0 -0
- package/ref-monty/crates/monty-python/src/convert.rs +273 -0
- package/ref-monty/crates/monty-python/src/dataclass.rs +461 -0
- package/ref-monty/crates/monty-python/src/exceptions.rs +557 -0
- package/ref-monty/crates/monty-python/src/external.rs +165 -0
- package/ref-monty/crates/monty-python/src/lib.rs +77 -0
- package/ref-monty/crates/monty-python/src/limits.rs +142 -0
- package/ref-monty/crates/monty-python/src/monty_cls.rs +1650 -0
- package/ref-monty/crates/monty-python/src/repl.rs +470 -0
- package/ref-monty/crates/monty-python/src/serialization.rs +761 -0
- package/ref-monty/crates/monty-python/tests/test_async.py +1201 -0
- package/ref-monty/crates/monty-python/tests/test_basic.py +66 -0
- package/ref-monty/crates/monty-python/tests/test_dataclasses.py +971 -0
- package/ref-monty/crates/monty-python/tests/test_exceptions.py +361 -0
- package/ref-monty/crates/monty-python/tests/test_external.py +367 -0
- package/ref-monty/crates/monty-python/tests/test_inputs.py +126 -0
- package/ref-monty/crates/monty-python/tests/test_limits.py +257 -0
- package/ref-monty/crates/monty-python/tests/test_os_access.py +1286 -0
- package/ref-monty/crates/monty-python/tests/test_os_access_compat.py +731 -0
- package/ref-monty/crates/monty-python/tests/test_os_access_raw.py +483 -0
- package/ref-monty/crates/monty-python/tests/test_os_calls.py +819 -0
- package/ref-monty/crates/monty-python/tests/test_print.py +208 -0
- package/ref-monty/crates/monty-python/tests/test_re.py +170 -0
- package/ref-monty/crates/monty-python/tests/test_readme_examples.py +20 -0
- package/ref-monty/crates/monty-python/tests/test_repl.py +749 -0
- package/ref-monty/crates/monty-python/tests/test_serialize.py +284 -0
- package/ref-monty/crates/monty-python/tests/test_start.py +346 -0
- package/ref-monty/crates/monty-python/tests/test_threading.py +163 -0
- package/ref-monty/crates/monty-python/tests/test_type_check.py +344 -0
- package/ref-monty/crates/monty-python/tests/test_types.py +553 -0
- package/ref-monty/crates/monty-type-checking/Cargo.toml +32 -0
- package/ref-monty/crates/monty-type-checking/src/db.rs +116 -0
- package/ref-monty/crates/monty-type-checking/src/lib.rs +4 -0
- package/ref-monty/crates/monty-type-checking/src/type_check.rs +280 -0
- package/ref-monty/crates/monty-type-checking/tests/bad_types.py +109 -0
- package/ref-monty/crates/monty-type-checking/tests/bad_types_output.txt +21 -0
- package/ref-monty/crates/monty-type-checking/tests/good_types.py +475 -0
- package/ref-monty/crates/monty-type-checking/tests/main.rs +205 -0
- package/ref-monty/crates/monty-type-checking/tests/reveal_types.py +56 -0
- package/ref-monty/crates/monty-type-checking/tests/reveal_types_output.txt +41 -0
- package/ref-monty/crates/monty-typeshed/Cargo.toml +29 -0
- package/ref-monty/crates/monty-typeshed/README.md +11 -0
- package/ref-monty/crates/monty-typeshed/build.rs +101 -0
- package/ref-monty/crates/monty-typeshed/custom/README.md +1 -0
- package/ref-monty/crates/monty-typeshed/custom/asyncio.pyi +138 -0
- package/ref-monty/crates/monty-typeshed/custom/os.pyi +87 -0
- package/ref-monty/crates/monty-typeshed/custom/sys.pyi +33 -0
- package/ref-monty/crates/monty-typeshed/src/lib.rs +56 -0
- package/ref-monty/crates/monty-typeshed/update.py +321 -0
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/source_commit.txt +1 -0
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/VERSIONS +20 -0
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/_collections_abc.pyi +105 -0
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/_typeshed/__init__.pyi +394 -0
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/asyncio.pyi +138 -0
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/builtins.pyi +1434 -0
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/collections/__init__.pyi +527 -0
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/collections/abc.pyi +2 -0
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/dataclasses.pyi +502 -0
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/enum.pyi +376 -0
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/math.pyi +149 -0
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/os.pyi +87 -0
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/pathlib/__init__.pyi +395 -0
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/pathlib/types.pyi +8 -0
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/re.pyi +337 -0
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/sys.pyi +33 -0
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/types.pyi +741 -0
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/typing.pyi +1217 -0
- package/ref-monty/crates/monty-typeshed/vendor/typeshed/stdlib/typing_extensions.pyi +716 -0
- package/ref-monty/docs/usage-guide.md +117 -0
- package/ref-monty/examples/README.md +3 -0
- package/ref-monty/examples/expense_analysis/README.md +3 -0
- package/ref-monty/examples/expense_analysis/data.py +124 -0
- package/ref-monty/examples/expense_analysis/main.py +115 -0
- package/ref-monty/examples/sql_playground/README.md +20 -0
- package/ref-monty/examples/sql_playground/external_functions.py +129 -0
- package/ref-monty/examples/sql_playground/main.py +81 -0
- package/ref-monty/examples/sql_playground/sandbox_code.py +82 -0
- package/ref-monty/examples/sql_playground/type_stubs.pyi +14 -0
- package/ref-monty/examples/web_scraper/README.md +15 -0
- package/ref-monty/examples/web_scraper/browser.py +56 -0
- package/ref-monty/examples/web_scraper/example_code.py +59 -0
- package/ref-monty/examples/web_scraper/external_functions.py +324 -0
- package/ref-monty/examples/web_scraper/main.py +193 -0
- package/ref-monty/examples/web_scraper/sub_agent.py +79 -0
- package/ref-monty/monty-npm.md +235 -0
- package/ref-monty/pyproject.toml +162 -0
- package/ref-monty/scripts/check_imports.py +91 -0
- package/ref-monty/scripts/codecov_diff.py +412 -0
- package/ref-monty/scripts/complete_tests.py +146 -0
- package/ref-monty/scripts/flamegraph_to_text.py +208 -0
- package/ref-monty/scripts/iter_test_methods.py +540 -0
- package/ref-monty/scripts/run_traceback.py +180 -0
- package/ref-monty/scripts/startup_performance.py +130 -0
- package/ref-monty/uv.lock +1779 -0
- package/temp_resend_cli/repo/.github/scripts/pr-title-check.js +34 -0
- package/temp_resend_cli/repo/.github/workflows/ci.yml +67 -0
- package/temp_resend_cli/repo/.github/workflows/post-release.yml +51 -0
- package/temp_resend_cli/repo/.github/workflows/pr-title-check.yml +13 -0
- package/temp_resend_cli/repo/.github/workflows/release.yml +175 -0
- package/temp_resend_cli/repo/.github/workflows/test-install-unix.yml +34 -0
- package/temp_resend_cli/repo/.github/workflows/test-install-windows.yml +48 -0
- package/temp_resend_cli/repo/CHANGELOG.md +31 -0
- package/temp_resend_cli/repo/LICENSE +21 -0
- package/temp_resend_cli/repo/README.md +450 -0
- package/temp_resend_cli/repo/biome.json +36 -0
- package/temp_resend_cli/repo/install.ps1 +141 -0
- package/temp_resend_cli/repo/install.sh +301 -0
- package/temp_resend_cli/repo/package.json +61 -0
- package/temp_resend_cli/repo/pnpm-lock.yaml +2439 -0
- package/temp_resend_cli/repo/renovate.json +4 -0
- package/temp_resend_cli/repo/src/cli.ts +98 -0
- package/temp_resend_cli/repo/src/commands/api-keys/create.ts +114 -0
- package/temp_resend_cli/repo/src/commands/api-keys/delete.ts +47 -0
- package/temp_resend_cli/repo/src/commands/api-keys/index.ts +26 -0
- package/temp_resend_cli/repo/src/commands/api-keys/list.ts +35 -0
- package/temp_resend_cli/repo/src/commands/api-keys/utils.ts +8 -0
- package/temp_resend_cli/repo/src/commands/auth/index.ts +20 -0
- package/temp_resend_cli/repo/src/commands/auth/login.ts +234 -0
- package/temp_resend_cli/repo/src/commands/auth/logout.ts +105 -0
- package/temp_resend_cli/repo/src/commands/broadcasts/create.ts +196 -0
- package/temp_resend_cli/repo/src/commands/broadcasts/delete.ts +46 -0
- package/temp_resend_cli/repo/src/commands/broadcasts/get.ts +59 -0
- package/temp_resend_cli/repo/src/commands/broadcasts/index.ts +43 -0
- package/temp_resend_cli/repo/src/commands/broadcasts/list.ts +60 -0
- package/temp_resend_cli/repo/src/commands/broadcasts/send.ts +56 -0
- package/temp_resend_cli/repo/src/commands/broadcasts/update.ts +95 -0
- package/temp_resend_cli/repo/src/commands/broadcasts/utils.ts +35 -0
- package/temp_resend_cli/repo/src/commands/contact-properties/create.ts +118 -0
- package/temp_resend_cli/repo/src/commands/contact-properties/delete.ts +48 -0
- package/temp_resend_cli/repo/src/commands/contact-properties/get.ts +46 -0
- package/temp_resend_cli/repo/src/commands/contact-properties/index.ts +48 -0
- package/temp_resend_cli/repo/src/commands/contact-properties/list.ts +68 -0
- package/temp_resend_cli/repo/src/commands/contact-properties/update.ts +88 -0
- package/temp_resend_cli/repo/src/commands/contact-properties/utils.ts +17 -0
- package/temp_resend_cli/repo/src/commands/contacts/add-segment.ts +78 -0
- package/temp_resend_cli/repo/src/commands/contacts/create.ts +122 -0
- package/temp_resend_cli/repo/src/commands/contacts/delete.ts +49 -0
- package/temp_resend_cli/repo/src/commands/contacts/get.ts +53 -0
- package/temp_resend_cli/repo/src/commands/contacts/index.ts +58 -0
- package/temp_resend_cli/repo/src/commands/contacts/list.ts +57 -0
- package/temp_resend_cli/repo/src/commands/contacts/remove-segment.ts +48 -0
- package/temp_resend_cli/repo/src/commands/contacts/segments.ts +39 -0
- package/temp_resend_cli/repo/src/commands/contacts/topics.ts +45 -0
- package/temp_resend_cli/repo/src/commands/contacts/update-topics.ts +90 -0
- package/temp_resend_cli/repo/src/commands/contacts/update.ts +77 -0
- package/temp_resend_cli/repo/src/commands/contacts/utils.ts +119 -0
- package/temp_resend_cli/repo/src/commands/doctor.ts +216 -0
- package/temp_resend_cli/repo/src/commands/domains/create.ts +83 -0
- package/temp_resend_cli/repo/src/commands/domains/delete.ts +42 -0
- package/temp_resend_cli/repo/src/commands/domains/get.ts +47 -0
- package/temp_resend_cli/repo/src/commands/domains/index.ts +35 -0
- package/temp_resend_cli/repo/src/commands/domains/list.ts +53 -0
- package/temp_resend_cli/repo/src/commands/domains/update.ts +75 -0
- package/temp_resend_cli/repo/src/commands/domains/utils.ts +44 -0
- package/temp_resend_cli/repo/src/commands/domains/verify.ts +38 -0
- package/temp_resend_cli/repo/src/commands/emails/batch.ts +140 -0
- package/temp_resend_cli/repo/src/commands/emails/get.ts +44 -0
- package/temp_resend_cli/repo/src/commands/emails/index.ts +30 -0
- package/temp_resend_cli/repo/src/commands/emails/list.ts +84 -0
- package/temp_resend_cli/repo/src/commands/emails/receiving/attachment.ts +55 -0
- package/temp_resend_cli/repo/src/commands/emails/receiving/attachments.ts +68 -0
- package/temp_resend_cli/repo/src/commands/emails/receiving/get.ts +58 -0
- package/temp_resend_cli/repo/src/commands/emails/receiving/index.ts +28 -0
- package/temp_resend_cli/repo/src/commands/emails/receiving/list.ts +59 -0
- package/temp_resend_cli/repo/src/commands/emails/receiving/utils.ts +38 -0
- package/temp_resend_cli/repo/src/commands/emails/send.ts +189 -0
- package/temp_resend_cli/repo/src/commands/open.ts +27 -0
- package/temp_resend_cli/repo/src/commands/segments/create.ts +50 -0
- package/temp_resend_cli/repo/src/commands/segments/delete.ts +47 -0
- package/temp_resend_cli/repo/src/commands/segments/get.ts +38 -0
- package/temp_resend_cli/repo/src/commands/segments/index.ts +36 -0
- package/temp_resend_cli/repo/src/commands/segments/list.ts +58 -0
- package/temp_resend_cli/repo/src/commands/segments/utils.ts +7 -0
- package/temp_resend_cli/repo/src/commands/teams/index.ts +10 -0
- package/temp_resend_cli/repo/src/commands/teams/list.ts +35 -0
- package/temp_resend_cli/repo/src/commands/teams/remove.ts +86 -0
- package/temp_resend_cli/repo/src/commands/teams/switch.ts +76 -0
- package/temp_resend_cli/repo/src/commands/topics/create.ts +73 -0
- package/temp_resend_cli/repo/src/commands/topics/delete.ts +47 -0
- package/temp_resend_cli/repo/src/commands/topics/get.ts +42 -0
- package/temp_resend_cli/repo/src/commands/topics/index.ts +42 -0
- package/temp_resend_cli/repo/src/commands/topics/list.ts +34 -0
- package/temp_resend_cli/repo/src/commands/topics/update.ts +59 -0
- package/temp_resend_cli/repo/src/commands/topics/utils.ts +16 -0
- package/temp_resend_cli/repo/src/commands/webhooks/create.ts +128 -0
- package/temp_resend_cli/repo/src/commands/webhooks/delete.ts +49 -0
- package/temp_resend_cli/repo/src/commands/webhooks/get.ts +42 -0
- package/temp_resend_cli/repo/src/commands/webhooks/index.ts +42 -0
- package/temp_resend_cli/repo/src/commands/webhooks/list.ts +55 -0
- package/temp_resend_cli/repo/src/commands/webhooks/listen.ts +379 -0
- package/temp_resend_cli/repo/src/commands/webhooks/update.ts +83 -0
- package/temp_resend_cli/repo/src/commands/webhooks/utils.ts +36 -0
- package/temp_resend_cli/repo/src/commands/whoami.ts +71 -0
- package/temp_resend_cli/repo/src/lib/actions.ts +157 -0
- package/temp_resend_cli/repo/src/lib/client.ts +37 -0
- package/temp_resend_cli/repo/src/lib/config.ts +217 -0
- package/temp_resend_cli/repo/src/lib/files.ts +15 -0
- package/temp_resend_cli/repo/src/lib/help-text.ts +38 -0
- package/temp_resend_cli/repo/src/lib/output.ts +56 -0
- package/temp_resend_cli/repo/src/lib/pagination.ts +36 -0
- package/temp_resend_cli/repo/src/lib/prompts.ts +149 -0
- package/temp_resend_cli/repo/src/lib/spinner.ts +100 -0
- package/temp_resend_cli/repo/src/lib/table.ts +57 -0
- package/temp_resend_cli/repo/src/lib/tty.ts +28 -0
- package/temp_resend_cli/repo/src/lib/update-check.ts +169 -0
- package/temp_resend_cli/repo/src/lib/version.ts +4 -0
- package/temp_resend_cli/repo/tests/commands/api-keys/create.test.ts +196 -0
- package/temp_resend_cli/repo/tests/commands/api-keys/delete.test.ts +157 -0
- package/temp_resend_cli/repo/tests/commands/api-keys/list.test.ts +134 -0
- package/temp_resend_cli/repo/tests/commands/auth/login.test.ts +153 -0
- package/temp_resend_cli/repo/tests/commands/auth/logout.test.ts +153 -0
- package/temp_resend_cli/repo/tests/commands/broadcasts/create.test.ts +454 -0
- package/temp_resend_cli/repo/tests/commands/broadcasts/delete.test.ts +183 -0
- package/temp_resend_cli/repo/tests/commands/broadcasts/get.test.ts +147 -0
- package/temp_resend_cli/repo/tests/commands/broadcasts/list.test.ts +199 -0
- package/temp_resend_cli/repo/tests/commands/broadcasts/send.test.ts +162 -0
- package/temp_resend_cli/repo/tests/commands/broadcasts/update.test.ts +288 -0
- package/temp_resend_cli/repo/tests/commands/contact-properties/create.test.ts +251 -0
- package/temp_resend_cli/repo/tests/commands/contact-properties/delete.test.ts +184 -0
- package/temp_resend_cli/repo/tests/commands/contact-properties/get.test.ts +145 -0
- package/temp_resend_cli/repo/tests/commands/contact-properties/list.test.ts +181 -0
- package/temp_resend_cli/repo/tests/commands/contact-properties/update.test.ts +217 -0
- package/temp_resend_cli/repo/tests/commands/contacts/add-segment.test.ts +189 -0
- package/temp_resend_cli/repo/tests/commands/contacts/create.test.ts +271 -0
- package/temp_resend_cli/repo/tests/commands/contacts/delete.test.ts +193 -0
- package/temp_resend_cli/repo/tests/commands/contacts/get.test.ts +149 -0
- package/temp_resend_cli/repo/tests/commands/contacts/list.test.ts +176 -0
- package/temp_resend_cli/repo/tests/commands/contacts/remove-segment.test.ts +167 -0
- package/temp_resend_cli/repo/tests/commands/contacts/segments.test.ts +168 -0
- package/temp_resend_cli/repo/tests/commands/contacts/topics.test.ts +164 -0
- package/temp_resend_cli/repo/tests/commands/contacts/update-topics.test.ts +248 -0
- package/temp_resend_cli/repo/tests/commands/contacts/update.test.ts +206 -0
- package/temp_resend_cli/repo/tests/commands/doctor.test.ts +164 -0
- package/temp_resend_cli/repo/tests/commands/domains/create.test.ts +193 -0
- package/temp_resend_cli/repo/tests/commands/domains/delete.test.ts +157 -0
- package/temp_resend_cli/repo/tests/commands/domains/get.test.ts +138 -0
- package/temp_resend_cli/repo/tests/commands/domains/list.test.ts +165 -0
- package/temp_resend_cli/repo/tests/commands/domains/update.test.ts +224 -0
- package/temp_resend_cli/repo/tests/commands/domains/verify.test.ts +118 -0
- package/temp_resend_cli/repo/tests/commands/emails/batch.test.ts +324 -0
- package/temp_resend_cli/repo/tests/commands/emails/get.test.ts +132 -0
- package/temp_resend_cli/repo/tests/commands/emails/receiving/attachment.test.ts +141 -0
- package/temp_resend_cli/repo/tests/commands/emails/receiving/attachments.test.ts +169 -0
- package/temp_resend_cli/repo/tests/commands/emails/receiving/get.test.ts +141 -0
- package/temp_resend_cli/repo/tests/commands/emails/receiving/list.test.ts +182 -0
- package/temp_resend_cli/repo/tests/commands/emails/send.test.ts +312 -0
- package/temp_resend_cli/repo/tests/commands/segments/create.test.ts +164 -0
- package/temp_resend_cli/repo/tests/commands/segments/delete.test.ts +183 -0
- package/temp_resend_cli/repo/tests/commands/segments/get.test.ts +138 -0
- package/temp_resend_cli/repo/tests/commands/segments/list.test.ts +174 -0
- package/temp_resend_cli/repo/tests/commands/teams/list.test.ts +62 -0
- package/temp_resend_cli/repo/tests/commands/teams/remove.test.ts +110 -0
- package/temp_resend_cli/repo/tests/commands/teams/switch.test.ts +103 -0
- package/temp_resend_cli/repo/tests/commands/topics/create.test.ts +192 -0
- package/temp_resend_cli/repo/tests/commands/topics/delete.test.ts +157 -0
- package/temp_resend_cli/repo/tests/commands/topics/get.test.ts +126 -0
- package/temp_resend_cli/repo/tests/commands/topics/list.test.ts +125 -0
- package/temp_resend_cli/repo/tests/commands/topics/update.test.ts +178 -0
- package/temp_resend_cli/repo/tests/commands/webhooks/create.test.ts +225 -0
- package/temp_resend_cli/repo/tests/commands/webhooks/delete.test.ts +157 -0
- package/temp_resend_cli/repo/tests/commands/webhooks/get.test.ts +126 -0
- package/temp_resend_cli/repo/tests/commands/webhooks/list.test.ts +178 -0
- package/temp_resend_cli/repo/tests/commands/webhooks/update.test.ts +207 -0
- package/temp_resend_cli/repo/tests/commands/whoami.test.ts +98 -0
- package/temp_resend_cli/repo/tests/e2e/smoke.test.ts +93 -0
- package/temp_resend_cli/repo/tests/helpers.ts +86 -0
- package/temp_resend_cli/repo/tests/lib/client.test.ts +71 -0
- package/temp_resend_cli/repo/tests/lib/config.test.ts +451 -0
- package/temp_resend_cli/repo/tests/lib/files.test.ts +73 -0
- package/temp_resend_cli/repo/tests/lib/help-text.test.ts +97 -0
- package/temp_resend_cli/repo/tests/lib/output.test.ts +136 -0
- package/temp_resend_cli/repo/tests/lib/prompts.test.ts +185 -0
- package/temp_resend_cli/repo/tests/lib/spinner.test.ts +166 -0
- package/temp_resend_cli/repo/tests/lib/table.test.ts +63 -0
- package/temp_resend_cli/repo/tests/lib/tty.test.ts +89 -0
- package/temp_resend_cli/repo/tests/lib/update-check.test.ts +179 -0
- package/temp_resend_cli/repo/tsconfig.json +14 -0
- package/temp_resend_cli/repo/vitest.config.e2e.ts +8 -0
- package/temp_resend_cli/repo/vitest.config.ts +10 -0
- package/tests/test-mcp-browser-use-smoke.sh +28 -56
- package/tests/test-monty-smoke.sh +32 -0
- package/tests/test-resend-smoke.sh +36 -0
|
@@ -0,0 +1,1958 @@
|
|
|
1
|
+
//! Bytecode virtual machine for executing compiled Python code.
|
|
2
|
+
//!
|
|
3
|
+
//! The VM uses a stack-based execution model with an operand stack for computation
|
|
4
|
+
//! and a call stack for function frames. Each frame owns its instruction pointer (IP).
|
|
5
|
+
|
|
6
|
+
mod async_exec;
|
|
7
|
+
mod attr;
|
|
8
|
+
mod binary;
|
|
9
|
+
mod call;
|
|
10
|
+
mod collections;
|
|
11
|
+
mod compare;
|
|
12
|
+
mod exceptions;
|
|
13
|
+
mod format;
|
|
14
|
+
mod scheduler;
|
|
15
|
+
|
|
16
|
+
use std::cmp::Ordering;
|
|
17
|
+
|
|
18
|
+
pub(crate) use call::CallResult;
|
|
19
|
+
use scheduler::Scheduler;
|
|
20
|
+
|
|
21
|
+
use crate::{
|
|
22
|
+
MontyObject,
|
|
23
|
+
args::ArgValues,
|
|
24
|
+
asyncio::{CallId, TaskId},
|
|
25
|
+
bytecode::{code::Code, op::Opcode},
|
|
26
|
+
exception_private::{ExcType, RunError, RunResult, SimpleException},
|
|
27
|
+
heap::{ContainsHeap, DropWithHeap, Heap, HeapData, HeapGuard, HeapId},
|
|
28
|
+
heap_data::{Closure, FunctionDefaults, HeapDataMut},
|
|
29
|
+
intern::{FunctionId, Interns, StringId},
|
|
30
|
+
io::PrintWriter,
|
|
31
|
+
modules::BuiltinModule,
|
|
32
|
+
os::OsFunction,
|
|
33
|
+
parse::CodeRange,
|
|
34
|
+
resource::ResourceTracker,
|
|
35
|
+
types::{LongInt, MontyIter, PyTrait, iter::advance_on_heap},
|
|
36
|
+
value::{BitwiseOp, EitherStr, Value},
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
/// Result of executing Await opcode.
|
|
40
|
+
///
|
|
41
|
+
/// Indicates what the VM should do after awaiting a value:
|
|
42
|
+
/// - `ValueReady`: the awaited value resolved immediately, push it
|
|
43
|
+
/// - `FramePushed`: a new frame was pushed for coroutine execution
|
|
44
|
+
/// - `Yield`: all tasks blocked, yield to caller with pending futures
|
|
45
|
+
enum AwaitResult {
|
|
46
|
+
/// The awaited value resolved immediately (e.g., resolved ExternalFuture).
|
|
47
|
+
ValueReady(Value),
|
|
48
|
+
/// A new frame was pushed to execute a coroutine.
|
|
49
|
+
FramePushed,
|
|
50
|
+
/// All tasks are blocked - yield to caller with pending futures.
|
|
51
|
+
Yield(Vec<CallId>),
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/// Tries an operation and handles exceptions, reloading cached frame state.
|
|
55
|
+
///
|
|
56
|
+
/// Use this in the main run loop where `cached_frame`
|
|
57
|
+
/// are used. After catching an exception, reloads the cache since the handler
|
|
58
|
+
/// may be in a different frame.
|
|
59
|
+
macro_rules! try_catch_sync {
|
|
60
|
+
($self:expr, $cached_frame:ident, $expr:expr) => {
|
|
61
|
+
if let Err(e) = $expr {
|
|
62
|
+
if let Some(result) = $self.handle_exception(e) {
|
|
63
|
+
return Err(result);
|
|
64
|
+
}
|
|
65
|
+
// Exception was caught - handler may be in different frame, reload cache
|
|
66
|
+
reload_cache!($self, $cached_frame);
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/// Handles an exception and reloads cached frame state if caught.
|
|
72
|
+
///
|
|
73
|
+
/// Use this in the main run loop where `cached_frame`
|
|
74
|
+
/// are used. After catching an exception, reloads the cache since the handler
|
|
75
|
+
/// may be in a different frame.
|
|
76
|
+
///
|
|
77
|
+
/// Wrapped in a block to allow use in match arm expressions.
|
|
78
|
+
macro_rules! catch_sync {
|
|
79
|
+
($self:expr, $cached_frame:ident, $err:expr) => {{
|
|
80
|
+
if let Some(result) = $self.handle_exception($err) {
|
|
81
|
+
return Err(result);
|
|
82
|
+
}
|
|
83
|
+
// Exception was caught - handler may be in different frame, reload cache
|
|
84
|
+
reload_cache!($self, $cached_frame);
|
|
85
|
+
}};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/// Fetches a byte from bytecode using cached code/ip, advancing ip.
|
|
89
|
+
///
|
|
90
|
+
/// Used in the run loop for fast operand fetching without frame access.
|
|
91
|
+
macro_rules! fetch_byte {
|
|
92
|
+
($cached_frame:expr) => {{
|
|
93
|
+
let byte = $cached_frame.code.bytecode()[$cached_frame.ip];
|
|
94
|
+
$cached_frame.ip += 1;
|
|
95
|
+
byte
|
|
96
|
+
}};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/// Fetches a u8 operand using cached code/ip.
|
|
100
|
+
macro_rules! fetch_u8 {
|
|
101
|
+
($cached_frame:expr) => {
|
|
102
|
+
fetch_byte!($cached_frame)
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/// Fetches an i8 operand using cached code/ip.
|
|
107
|
+
macro_rules! fetch_i8 {
|
|
108
|
+
($cached_frame:expr) => {{ i8::from_ne_bytes([fetch_byte!($cached_frame)]) }};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/// Fetches a u16 operand (little-endian) using cached code/ip.
|
|
112
|
+
macro_rules! fetch_u16 {
|
|
113
|
+
($cached_frame:expr) => {{
|
|
114
|
+
let lo = $cached_frame.code.bytecode()[$cached_frame.ip];
|
|
115
|
+
let hi = $cached_frame.code.bytecode()[$cached_frame.ip + 1];
|
|
116
|
+
$cached_frame.ip += 2;
|
|
117
|
+
u16::from_le_bytes([lo, hi])
|
|
118
|
+
}};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/// Fetches an i16 operand (little-endian) using cached code/ip.
|
|
122
|
+
macro_rules! fetch_i16 {
|
|
123
|
+
($cached_frame:expr) => {{
|
|
124
|
+
let lo = $cached_frame.code.bytecode()[$cached_frame.ip];
|
|
125
|
+
let hi = $cached_frame.code.bytecode()[$cached_frame.ip + 1];
|
|
126
|
+
$cached_frame.ip += 2;
|
|
127
|
+
i16::from_le_bytes([lo, hi])
|
|
128
|
+
}};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/// Reloads cached frame state from the current frame.
|
|
132
|
+
///
|
|
133
|
+
/// Call this after any operation that modifies the frame stack (calls, returns,
|
|
134
|
+
/// exception handling).
|
|
135
|
+
macro_rules! reload_cache {
|
|
136
|
+
($self:expr, $cached_frame:ident) => {{
|
|
137
|
+
$cached_frame = $self.new_cached_frame();
|
|
138
|
+
}};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/// Applies a relative jump offset to the cached IP.
|
|
142
|
+
///
|
|
143
|
+
/// Uses checked arithmetic to safely compute the new IP, panicking if the
|
|
144
|
+
/// jump would result in a negative or overflowing instruction pointer.
|
|
145
|
+
macro_rules! jump_relative {
|
|
146
|
+
($ip:expr, $offset:expr) => {{
|
|
147
|
+
let ip_i64 = i64::try_from($ip).expect("instruction pointer exceeds i64");
|
|
148
|
+
let new_ip = ip_i64 + i64::from($offset);
|
|
149
|
+
$ip = usize::try_from(new_ip).expect("jump resulted in negative or overflowing IP");
|
|
150
|
+
}};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/// Handles the result of a load operation that may yield a `FrameExit::NameLookup`.
|
|
154
|
+
///
|
|
155
|
+
/// `load_local` and `load_global` return `Result<Option<FrameExit>, RunError>`:
|
|
156
|
+
/// - `Ok(None)`: load succeeded, value is on the stack
|
|
157
|
+
/// - `Ok(Some(FrameExit::NameLookup { .. }))`: unresolved name, yield to host
|
|
158
|
+
/// - `Err(e)`: exception (e.g., UnboundLocalError)
|
|
159
|
+
macro_rules! handle_load_result {
|
|
160
|
+
($self:expr, $cached_frame:ident, $result:expr) => {
|
|
161
|
+
match $result {
|
|
162
|
+
Ok(None) => {}
|
|
163
|
+
Ok(Some(frame_exit)) => {
|
|
164
|
+
$self.current_frame_mut().ip = $cached_frame.ip;
|
|
165
|
+
return Ok(frame_exit);
|
|
166
|
+
}
|
|
167
|
+
Err(e) => catch_sync!($self, $cached_frame, e),
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/// Handles the result of a call operation that returns `CallResult`.
|
|
173
|
+
///
|
|
174
|
+
/// This macro eliminates the repetitive pattern of matching on `CallResult`
|
|
175
|
+
/// variants that appears in LoadAttr, CallFunction, CallFunctionKw, CallAttr,
|
|
176
|
+
/// CallAttrKw, and CallFunctionExtended opcodes.
|
|
177
|
+
///
|
|
178
|
+
/// Actions taken for each variant:
|
|
179
|
+
/// - `Push(value)`: Push the value onto the stack
|
|
180
|
+
/// - `FramePushed`: Reload the cached frame (a new frame was pushed)
|
|
181
|
+
/// - `External(ext_id, args)`: Return `FrameExit::ExternalCall` to yield to host
|
|
182
|
+
/// - `OsCall(func, args)`: Return `FrameExit::OsCall` to yield to host
|
|
183
|
+
/// - `MethodCall(name, args)`: Return `FrameExit::MethodCall` to yield to host
|
|
184
|
+
/// - `AwaitValue(value)`: Push value, then implicitly await it via `exec_get_awaitable`
|
|
185
|
+
/// - `Err(err)`: Handle the exception via `catch_sync!`
|
|
186
|
+
macro_rules! handle_call_result {
|
|
187
|
+
($self:expr, $cached_frame:ident, $result:expr) => {
|
|
188
|
+
match $result {
|
|
189
|
+
Ok(CallResult::Value(result)) => $self.push(result),
|
|
190
|
+
Ok(CallResult::FramePushed) => reload_cache!($self, $cached_frame),
|
|
191
|
+
Ok(CallResult::External(name, args)) => {
|
|
192
|
+
let call_id = $self.allocate_call_id();
|
|
193
|
+
let name_load_ip = $self.ext_function_load_ip.take();
|
|
194
|
+
// Sync cached IP back to frame before snapshot for resume
|
|
195
|
+
$self.current_frame_mut().ip = $cached_frame.ip;
|
|
196
|
+
return Ok(FrameExit::ExternalCall {
|
|
197
|
+
function_name: name,
|
|
198
|
+
args,
|
|
199
|
+
call_id,
|
|
200
|
+
name_load_ip,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
Ok(CallResult::OsCall(func, args)) => {
|
|
204
|
+
let call_id = $self.allocate_call_id();
|
|
205
|
+
// Sync cached IP back to frame before snapshot for resume
|
|
206
|
+
$self.current_frame_mut().ip = $cached_frame.ip;
|
|
207
|
+
return Ok(FrameExit::OsCall {
|
|
208
|
+
function: func,
|
|
209
|
+
args,
|
|
210
|
+
call_id,
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
Ok(CallResult::MethodCall(method_name, args)) => {
|
|
214
|
+
let call_id = $self.allocate_call_id();
|
|
215
|
+
// Sync cached IP back to frame before snapshot for resume
|
|
216
|
+
$self.current_frame_mut().ip = $cached_frame.ip;
|
|
217
|
+
return Ok(FrameExit::MethodCall {
|
|
218
|
+
method_name,
|
|
219
|
+
args,
|
|
220
|
+
call_id,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
Ok(CallResult::AwaitValue(value)) => {
|
|
224
|
+
// Push the value and implicitly await it (used by asyncio.run())
|
|
225
|
+
$self.push(value);
|
|
226
|
+
$self.current_frame_mut().ip = $cached_frame.ip;
|
|
227
|
+
match $self.exec_get_awaitable() {
|
|
228
|
+
Ok(AwaitResult::ValueReady(value)) => {
|
|
229
|
+
$self.push(value);
|
|
230
|
+
}
|
|
231
|
+
Ok(AwaitResult::FramePushed) => {
|
|
232
|
+
reload_cache!($self, $cached_frame);
|
|
233
|
+
}
|
|
234
|
+
Ok(AwaitResult::Yield(pending_calls)) => {
|
|
235
|
+
return Ok(FrameExit::ResolveFutures(pending_calls));
|
|
236
|
+
}
|
|
237
|
+
Err(e) => {
|
|
238
|
+
catch_sync!($self, $cached_frame, e);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
Err(err) => catch_sync!($self, $cached_frame, err),
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/// Result of VM execution.
|
|
248
|
+
pub enum FrameExit {
|
|
249
|
+
/// Execution completed successfully with a return value.
|
|
250
|
+
Return(Value),
|
|
251
|
+
|
|
252
|
+
/// Execution paused for an external function call.
|
|
253
|
+
///
|
|
254
|
+
/// The caller should execute the external function and call `resume()`
|
|
255
|
+
/// with the result. The `call_id` allows the host to use async resolution
|
|
256
|
+
/// by calling `run_pending()` instead of `run(result)`.
|
|
257
|
+
ExternalCall {
|
|
258
|
+
/// Name of the external function to call (interned or heap-owned).
|
|
259
|
+
function_name: EitherStr,
|
|
260
|
+
/// Arguments for the external function (includes both positional and keyword args).
|
|
261
|
+
args: ArgValues,
|
|
262
|
+
/// Unique ID for this call, used for async correlation.
|
|
263
|
+
call_id: CallId,
|
|
264
|
+
/// Optional bytecode IP of the load instruction that produced this `ExtFunction`.
|
|
265
|
+
///
|
|
266
|
+
/// When a `LoadGlobalCallable`/`LoadLocalCallable` opcode auto-injects an `ExtFunction`
|
|
267
|
+
/// for an undefined name, the load instruction's IP is saved here. In standard execution
|
|
268
|
+
/// (without external function support), this IP is used to restore the frame pointer
|
|
269
|
+
/// before raising `NameError`, so the traceback points to the name rather than the call.
|
|
270
|
+
name_load_ip: Option<usize>,
|
|
271
|
+
},
|
|
272
|
+
|
|
273
|
+
/// Execution paused for an os function call.
|
|
274
|
+
///
|
|
275
|
+
/// The caller should execute a function corresponding to the `os_call` and call `resume()`
|
|
276
|
+
/// with the result. The `call_id` allows the host to use async resolution
|
|
277
|
+
/// by calling `run_pending()` instead of `run(result)`.
|
|
278
|
+
OsCall {
|
|
279
|
+
/// ID of the os function to call.
|
|
280
|
+
function: OsFunction,
|
|
281
|
+
/// Arguments for the external function (includes both positional and keyword args).
|
|
282
|
+
args: ArgValues,
|
|
283
|
+
/// Unique ID for this call, used for async correlation.
|
|
284
|
+
call_id: CallId,
|
|
285
|
+
},
|
|
286
|
+
|
|
287
|
+
/// Execution paused for a dataclass method call.
|
|
288
|
+
///
|
|
289
|
+
/// The caller should invoke the method on the original Python dataclass and call
|
|
290
|
+
/// `resume()` with the result. The `method_name` is the attribute name (e.g.
|
|
291
|
+
/// `"distance"`) and `args` includes the dataclass instance as the first argument
|
|
292
|
+
/// (`self`).
|
|
293
|
+
MethodCall {
|
|
294
|
+
/// Method name (e.g., "distance").
|
|
295
|
+
method_name: EitherStr,
|
|
296
|
+
/// Arguments including the dataclass instance as the first positional arg.
|
|
297
|
+
args: ArgValues,
|
|
298
|
+
/// Unique ID for this call, used for async correlation.
|
|
299
|
+
call_id: CallId,
|
|
300
|
+
},
|
|
301
|
+
|
|
302
|
+
/// All tasks are blocked waiting for external futures to resolve.
|
|
303
|
+
///
|
|
304
|
+
/// The caller must resolve the pending CallIds before calling `resume()`.
|
|
305
|
+
/// This happens when await is called on an ExternalFuture that hasn't
|
|
306
|
+
/// been resolved yet, and there are no other ready tasks to switch to.
|
|
307
|
+
ResolveFutures(Vec<CallId>),
|
|
308
|
+
|
|
309
|
+
/// Execution paused for an unresolved name lookup.
|
|
310
|
+
///
|
|
311
|
+
/// When the VM encounters an `Undefined` value in a `LocalUnassigned` slot
|
|
312
|
+
/// (module level) or a global slot, it yields to the host to resolve the name.
|
|
313
|
+
/// The host can return a value to cache in the slot, or indicate the name is
|
|
314
|
+
/// truly undefined (which will raise `NameError`).
|
|
315
|
+
///
|
|
316
|
+
/// This enables auto-detection of external functions without requiring upfront
|
|
317
|
+
/// declaration: unresolved names are lazily resolved by the host at runtime.
|
|
318
|
+
NameLookup {
|
|
319
|
+
/// The interned name being looked up.
|
|
320
|
+
name_id: StringId,
|
|
321
|
+
/// The namespace slot where the resolved value should be cached.
|
|
322
|
+
namespace_slot: u16,
|
|
323
|
+
/// Whether this is a global slot (true) or a local/function slot (false).
|
|
324
|
+
is_global: bool,
|
|
325
|
+
},
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
/// A single function activation record.
|
|
329
|
+
///
|
|
330
|
+
/// Each frame represents one level in the call stack and owns its own
|
|
331
|
+
/// instruction pointer. This design avoids sync bugs on call/return.
|
|
332
|
+
#[derive(Debug)]
|
|
333
|
+
pub struct CallFrame<'code> {
|
|
334
|
+
/// Bytecode being executed.
|
|
335
|
+
code: &'code Code,
|
|
336
|
+
|
|
337
|
+
/// Instruction pointer within this frame's bytecode.
|
|
338
|
+
ip: usize,
|
|
339
|
+
|
|
340
|
+
/// Base index into the VM stack for this frame's locals region.
|
|
341
|
+
///
|
|
342
|
+
/// The frame's locals occupy `stack[stack_base..stack_base + locals_count]`,
|
|
343
|
+
/// and operands are pushed above that.
|
|
344
|
+
stack_base: usize,
|
|
345
|
+
|
|
346
|
+
/// Number of local variable slots in this frame.
|
|
347
|
+
///
|
|
348
|
+
/// Zero for module-level frames (globals are stored separately).
|
|
349
|
+
/// For function frames, this equals `func.namespace_size`.
|
|
350
|
+
locals_count: u16,
|
|
351
|
+
|
|
352
|
+
/// Function ID (for tracebacks). None for module-level code.
|
|
353
|
+
function_id: Option<FunctionId>,
|
|
354
|
+
|
|
355
|
+
/// Call site position (for tracebacks).
|
|
356
|
+
call_position: Option<CodeRange>,
|
|
357
|
+
|
|
358
|
+
/// When this frame returns (or exits with an exception) the VM should exit the run loop
|
|
359
|
+
/// and return to the caller. Supports `evaluate_function`.
|
|
360
|
+
should_return: bool,
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
impl<'code> CallFrame<'code> {
|
|
364
|
+
/// Creates a new call frame for module-level code.
|
|
365
|
+
///
|
|
366
|
+
/// Module frames have `locals_count = 0` because module-level variables
|
|
367
|
+
/// are stored in the VM's `globals` vec, not in the stack.
|
|
368
|
+
pub fn new_module(code: &'code Code) -> Self {
|
|
369
|
+
Self {
|
|
370
|
+
code,
|
|
371
|
+
ip: 0,
|
|
372
|
+
stack_base: 0,
|
|
373
|
+
locals_count: 0,
|
|
374
|
+
function_id: None,
|
|
375
|
+
call_position: None,
|
|
376
|
+
should_return: false,
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/// Creates a new call frame for a function call.
|
|
381
|
+
///
|
|
382
|
+
/// The frame's locals occupy `stack[stack_base..stack_base + locals_count]`.
|
|
383
|
+
/// Operands are pushed above the locals region.
|
|
384
|
+
pub fn new_function(
|
|
385
|
+
code: &'code Code,
|
|
386
|
+
stack_base: usize,
|
|
387
|
+
locals_count: u16,
|
|
388
|
+
function_id: FunctionId,
|
|
389
|
+
call_position: Option<CodeRange>,
|
|
390
|
+
) -> Self {
|
|
391
|
+
Self {
|
|
392
|
+
code,
|
|
393
|
+
ip: 0,
|
|
394
|
+
stack_base,
|
|
395
|
+
locals_count,
|
|
396
|
+
function_id: Some(function_id),
|
|
397
|
+
call_position,
|
|
398
|
+
should_return: false,
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
/// Cached state of the VM derived from the current frame as an optimization.
|
|
404
|
+
///
|
|
405
|
+
/// Holds the hot fields from the current `CallFrame` to avoid repeated
|
|
406
|
+
/// `frames.last()` lookups in the main opcode loop.
|
|
407
|
+
#[derive(Debug, Copy, Clone)]
|
|
408
|
+
pub struct CachedFrame<'code> {
|
|
409
|
+
/// Bytecode being executed.
|
|
410
|
+
code: &'code Code,
|
|
411
|
+
|
|
412
|
+
/// Instruction pointer within this frame's bytecode.
|
|
413
|
+
ip: usize,
|
|
414
|
+
|
|
415
|
+
/// Base index into the VM stack for this frame's locals.
|
|
416
|
+
stack_base: usize,
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
impl<'code> From<&CallFrame<'code>> for CachedFrame<'code> {
|
|
420
|
+
fn from(frame: &CallFrame<'code>) -> Self {
|
|
421
|
+
Self {
|
|
422
|
+
code: frame.code,
|
|
423
|
+
ip: frame.ip,
|
|
424
|
+
stack_base: frame.stack_base,
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
/// Serializable representation of a call frame.
|
|
430
|
+
///
|
|
431
|
+
/// Cannot store `&Code` (a reference) — instead stores `FunctionId` to look up
|
|
432
|
+
/// the pre-compiled Code object on resume. Module-level code uses `None`.
|
|
433
|
+
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
|
434
|
+
pub struct SerializedFrame {
|
|
435
|
+
/// Which function's code this frame executes (None = module-level).
|
|
436
|
+
function_id: Option<FunctionId>,
|
|
437
|
+
|
|
438
|
+
/// Instruction pointer within this frame's bytecode.
|
|
439
|
+
ip: usize,
|
|
440
|
+
|
|
441
|
+
/// Base index into the VM stack for this frame's locals region.
|
|
442
|
+
stack_base: usize,
|
|
443
|
+
|
|
444
|
+
/// Number of local variable slots (0 for module-level frames).
|
|
445
|
+
locals_count: u16,
|
|
446
|
+
|
|
447
|
+
/// Call site position (for tracebacks).
|
|
448
|
+
call_position: Option<CodeRange>,
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
impl CallFrame<'_> {
|
|
452
|
+
/// Converts this frame to a serializable representation.
|
|
453
|
+
fn serialize(&self) -> SerializedFrame {
|
|
454
|
+
assert!(
|
|
455
|
+
!self.should_return,
|
|
456
|
+
"cannot serialize frame marked for return - not yet supported"
|
|
457
|
+
);
|
|
458
|
+
SerializedFrame {
|
|
459
|
+
function_id: self.function_id,
|
|
460
|
+
ip: self.ip,
|
|
461
|
+
stack_base: self.stack_base,
|
|
462
|
+
locals_count: self.locals_count,
|
|
463
|
+
call_position: self.call_position,
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/// VM state for pause/resume at external function calls.
|
|
469
|
+
///
|
|
470
|
+
/// **Ownership:** This struct OWNS the values (refcounts were already incremented).
|
|
471
|
+
/// Must be used with the serialized Heap - HeapId values are indices into that heap.
|
|
472
|
+
///
|
|
473
|
+
/// **Usage:** When the VM pauses for an external call, call `into_snapshot()` to
|
|
474
|
+
/// create this snapshot. The snapshot can be serialized and stored. On resume,
|
|
475
|
+
/// use `restore()` to reconstruct the VM and continue execution.
|
|
476
|
+
///
|
|
477
|
+
/// Note: This struct does not implement `Clone` because `Value` uses manual
|
|
478
|
+
/// reference counting. Snapshots transfer ownership - they are not copied.
|
|
479
|
+
#[derive(Debug, serde::Serialize, serde::Deserialize)]
|
|
480
|
+
pub struct VMSnapshot {
|
|
481
|
+
/// Operand stack — locals and operands interleaved per frame.
|
|
482
|
+
///
|
|
483
|
+
/// Each function frame's locals occupy `stack[frame.stack_base..frame.stack_base + frame.locals_count]`,
|
|
484
|
+
/// with operands pushed above.
|
|
485
|
+
pub(crate) stack: Vec<Value>,
|
|
486
|
+
|
|
487
|
+
/// Module-level (global) variable storage.
|
|
488
|
+
pub(crate) globals: Vec<Value>,
|
|
489
|
+
|
|
490
|
+
/// Call frames (serializable form — stores FunctionId, not &Code).
|
|
491
|
+
frames: Vec<SerializedFrame>,
|
|
492
|
+
|
|
493
|
+
/// Stack of exceptions being handled for nested except blocks.
|
|
494
|
+
///
|
|
495
|
+
/// When entering an except handler, the exception is pushed onto this stack.
|
|
496
|
+
/// When exiting via `ClearException`, the top is popped. This allows nested
|
|
497
|
+
/// except handlers to restore the outer exception context.
|
|
498
|
+
exception_stack: Vec<Value>,
|
|
499
|
+
|
|
500
|
+
/// IP of the instruction that caused the pause (for exception handling).
|
|
501
|
+
instruction_ip: usize,
|
|
502
|
+
|
|
503
|
+
/// Counter for external call IDs when scheduler is not initialized.
|
|
504
|
+
next_call_id: u32,
|
|
505
|
+
|
|
506
|
+
/// Scheduler state for async execution (optional).
|
|
507
|
+
///
|
|
508
|
+
/// Contains all task state, pending calls, and resolved futures.
|
|
509
|
+
/// This enables async execution to be paused and resumed across host calls.
|
|
510
|
+
/// None if no async operations have been performed yet.
|
|
511
|
+
scheduler: Option<Scheduler>,
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// ============================================================================
|
|
515
|
+
// Virtual Machine
|
|
516
|
+
// ============================================================================
|
|
517
|
+
|
|
518
|
+
/// The bytecode virtual machine.
|
|
519
|
+
///
|
|
520
|
+
/// Executes compiled bytecode using a stack-based execution model.
|
|
521
|
+
/// The instruction pointer (IP) lives in each `CallFrame`, not here,
|
|
522
|
+
/// to avoid sync bugs on call/return.
|
|
523
|
+
///
|
|
524
|
+
/// # Lifetimes
|
|
525
|
+
/// * `'a` - Lifetime of the heap, namespaces, and interns
|
|
526
|
+
/// * `'p` - Lifetime of the print writer's internal references
|
|
527
|
+
pub struct VM<'a, 'p, T: ResourceTracker> {
|
|
528
|
+
/// Operand stack — locals and operands interleaved per frame.
|
|
529
|
+
///
|
|
530
|
+
/// Each function frame's locals occupy `stack[frame.stack_base..frame.stack_base + frame.locals_count]`,
|
|
531
|
+
/// with operands pushed above. Module-level frames have `locals_count = 0`
|
|
532
|
+
/// because globals are stored separately.
|
|
533
|
+
pub(crate) stack: Vec<Value>,
|
|
534
|
+
|
|
535
|
+
/// Module-level (global) variable storage.
|
|
536
|
+
///
|
|
537
|
+
/// Indexed by slot number from `LoadGlobal`/`StoreGlobal` opcodes.
|
|
538
|
+
/// Separated from the stack because globals persist across function calls
|
|
539
|
+
/// and are accessed via dedicated opcodes.
|
|
540
|
+
pub(crate) globals: Vec<Value>,
|
|
541
|
+
|
|
542
|
+
/// Call stack — function frames (each frame has its own IP).
|
|
543
|
+
frames: Vec<CallFrame<'a>>,
|
|
544
|
+
|
|
545
|
+
/// Heap for reference-counted objects.
|
|
546
|
+
pub(crate) heap: &'a mut Heap<T>,
|
|
547
|
+
|
|
548
|
+
/// Interned strings/bytes.
|
|
549
|
+
pub(crate) interns: &'a Interns,
|
|
550
|
+
|
|
551
|
+
/// Print output writer, borrowed so callers retain access to collected output.
|
|
552
|
+
pub(crate) print_writer: PrintWriter<'p>,
|
|
553
|
+
|
|
554
|
+
/// Stack of exceptions being handled for nested except blocks.
|
|
555
|
+
///
|
|
556
|
+
/// Used by bare `raise` to re-raise the current exception.
|
|
557
|
+
/// When entering an except handler, the exception is pushed onto this stack.
|
|
558
|
+
/// When exiting via `ClearException`, the top is popped. This allows nested
|
|
559
|
+
/// except handlers to restore the outer exception context.
|
|
560
|
+
exception_stack: Vec<Value>,
|
|
561
|
+
|
|
562
|
+
/// IP of the instruction being executed (for exception table lookup).
|
|
563
|
+
///
|
|
564
|
+
/// Updated at the start of each instruction before operands are fetched.
|
|
565
|
+
/// This allows us to find the correct exception handler when an error occurs.
|
|
566
|
+
instruction_ip: usize,
|
|
567
|
+
|
|
568
|
+
/// Counter for external call IDs when scheduler is not initialized.
|
|
569
|
+
///
|
|
570
|
+
/// Used by `allocate_call_id()` when no scheduler exists (sync code paths).
|
|
571
|
+
/// When a scheduler is created, this counter is transferred to it.
|
|
572
|
+
next_call_id: u32,
|
|
573
|
+
|
|
574
|
+
/// Scheduler for async task management (lazy — only created when needed).
|
|
575
|
+
///
|
|
576
|
+
/// Manages concurrent tasks, external call tracking, and task switching.
|
|
577
|
+
/// Created lazily on first async operation to avoid allocations for sync code.
|
|
578
|
+
scheduler: Option<Scheduler>,
|
|
579
|
+
|
|
580
|
+
/// Module-level code (for restoring main task frames).
|
|
581
|
+
///
|
|
582
|
+
/// Stored here because the main task's frames have `function_id: None` and
|
|
583
|
+
/// need a reference to the module code when being restored after task switching.
|
|
584
|
+
module_code: Option<&'a Code>,
|
|
585
|
+
|
|
586
|
+
/// Bytecode IP of the most recent `LoadGlobalCallable`/`LoadLocalCallable` that
|
|
587
|
+
/// pushed an `ExtFunction` for an undefined name.
|
|
588
|
+
///
|
|
589
|
+
/// Used to restore the frame IP when standard execution converts an `ExternalCall`
|
|
590
|
+
/// back to a `NameError`, so the traceback points to the name reference rather than
|
|
591
|
+
/// the call expression.
|
|
592
|
+
ext_function_load_ip: Option<usize>,
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
impl<'a, 'p, T: ResourceTracker> VM<'a, 'p, T> {
|
|
596
|
+
/// Creates a new VM with the given runtime context.
|
|
597
|
+
pub fn new(
|
|
598
|
+
globals: Vec<Value>,
|
|
599
|
+
heap: &'a mut Heap<T>,
|
|
600
|
+
interns: &'a Interns,
|
|
601
|
+
print_writer: PrintWriter<'p>,
|
|
602
|
+
) -> Self {
|
|
603
|
+
Self {
|
|
604
|
+
stack: Vec::with_capacity(64),
|
|
605
|
+
globals,
|
|
606
|
+
frames: Vec::with_capacity(16),
|
|
607
|
+
heap,
|
|
608
|
+
interns,
|
|
609
|
+
print_writer,
|
|
610
|
+
exception_stack: Vec::new(),
|
|
611
|
+
instruction_ip: 0,
|
|
612
|
+
next_call_id: 0,
|
|
613
|
+
scheduler: None, // Lazy - no allocation for sync code
|
|
614
|
+
ext_function_load_ip: None, // Set by LoadGlobalCallable/LoadLocalCallable
|
|
615
|
+
module_code: None,
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
/// Reconstructs a VM from a snapshot.
|
|
620
|
+
///
|
|
621
|
+
/// The heap must already be deserialized. `FunctionId` values
|
|
622
|
+
/// in frames are used to look up pre-compiled `Code` objects from the `Interns`.
|
|
623
|
+
/// The `module_code` is used for frames with `function_id = None`.
|
|
624
|
+
///
|
|
625
|
+
/// # Arguments
|
|
626
|
+
/// * `snapshot` - The VM snapshot to restore
|
|
627
|
+
/// * `module_code` - Compiled module code (for frames with function_id = None)
|
|
628
|
+
/// * `heap` - The deserialized heap
|
|
629
|
+
/// * `interns` - Interns for looking up function code
|
|
630
|
+
/// * `print_writer` - Writer for print output
|
|
631
|
+
pub fn restore(
|
|
632
|
+
snapshot: VMSnapshot,
|
|
633
|
+
module_code: &'a Code,
|
|
634
|
+
heap: &'a mut Heap<T>,
|
|
635
|
+
interns: &'a Interns,
|
|
636
|
+
print_writer: PrintWriter<'p>,
|
|
637
|
+
) -> Self {
|
|
638
|
+
// Reconstruct call frames from serialized form
|
|
639
|
+
let frames: Vec<CallFrame<'_>> = snapshot
|
|
640
|
+
.frames
|
|
641
|
+
.into_iter()
|
|
642
|
+
.map(|sf| {
|
|
643
|
+
let code = match sf.function_id {
|
|
644
|
+
Some(func_id) => &interns.get_function(func_id).code,
|
|
645
|
+
None => module_code,
|
|
646
|
+
};
|
|
647
|
+
CallFrame {
|
|
648
|
+
code,
|
|
649
|
+
ip: sf.ip,
|
|
650
|
+
stack_base: sf.stack_base,
|
|
651
|
+
locals_count: sf.locals_count,
|
|
652
|
+
function_id: sf.function_id,
|
|
653
|
+
call_position: sf.call_position,
|
|
654
|
+
should_return: false,
|
|
655
|
+
}
|
|
656
|
+
})
|
|
657
|
+
.collect();
|
|
658
|
+
|
|
659
|
+
// Restore recursion depth to match the number of active function frames.
|
|
660
|
+
// During serialization, recursion_depth is transient (defaults to 0),
|
|
661
|
+
// but cleanup paths call decr_recursion_depth for each non-root frame.
|
|
662
|
+
let current_frame_depth = frames.len().saturating_sub(1); // Subtract 1 for root frame which doesn't contribute to depth
|
|
663
|
+
heap.set_recursion_depth(current_frame_depth);
|
|
664
|
+
|
|
665
|
+
Self {
|
|
666
|
+
stack: snapshot.stack,
|
|
667
|
+
globals: snapshot.globals,
|
|
668
|
+
frames,
|
|
669
|
+
heap,
|
|
670
|
+
interns,
|
|
671
|
+
print_writer,
|
|
672
|
+
exception_stack: snapshot.exception_stack,
|
|
673
|
+
instruction_ip: snapshot.instruction_ip,
|
|
674
|
+
next_call_id: snapshot.next_call_id,
|
|
675
|
+
scheduler: snapshot.scheduler,
|
|
676
|
+
module_code: Some(module_code),
|
|
677
|
+
ext_function_load_ip: None,
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
/// Consumes the VM and creates a snapshot for pause/resume.
|
|
681
|
+
///
|
|
682
|
+
/// **Ownership transfer:** This method takes `self` by value, consuming the VM.
|
|
683
|
+
/// The snapshot owns all Values (refcounts already correct from the live VM).
|
|
684
|
+
/// The heap and namespaces must be serialized alongside this snapshot.
|
|
685
|
+
///
|
|
686
|
+
/// This is NOT a clone - it's a transfer. After calling this, the original VM
|
|
687
|
+
/// is gone and only the snapshot (+ serialized heap/namespaces) represents the state.
|
|
688
|
+
pub fn snapshot(self) -> VMSnapshot {
|
|
689
|
+
VMSnapshot {
|
|
690
|
+
// Move values directly — no clone, no refcount increment needed
|
|
691
|
+
// (the VM owned them, now the snapshot owns them)
|
|
692
|
+
stack: self.stack,
|
|
693
|
+
globals: self.globals,
|
|
694
|
+
frames: self.frames.into_iter().map(|f| f.serialize()).collect(),
|
|
695
|
+
exception_stack: self.exception_stack,
|
|
696
|
+
instruction_ip: self.instruction_ip,
|
|
697
|
+
next_call_id: self.next_call_id,
|
|
698
|
+
scheduler: self.scheduler,
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
/// Pushes an initial frame for module-level code and runs the VM.
|
|
703
|
+
pub fn run_module(&mut self, code: &'a Code) -> Result<FrameExit, RunError> {
|
|
704
|
+
// Store module code for restoring main task frames during task switching
|
|
705
|
+
self.module_code = Some(code);
|
|
706
|
+
self.push_frame(CallFrame::new_module(code))?;
|
|
707
|
+
self.run()
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
/// Cleans up VM state before the VM is dropped.
|
|
711
|
+
///
|
|
712
|
+
/// This method must be called before the VM goes out of scope to ensure
|
|
713
|
+
/// proper reference counting cleanup for any exception values and scheduler state.
|
|
714
|
+
pub fn cleanup(&mut self) {
|
|
715
|
+
// Drop all exceptions in the exception stack
|
|
716
|
+
self.exception_stack.drain(..).drop_with_heap(self.heap);
|
|
717
|
+
// Clean up current task's stack values and frame cell references
|
|
718
|
+
self.cleanup_current_task();
|
|
719
|
+
// Clean up scheduler state (task stacks, pending calls, resolved values, frame cells)
|
|
720
|
+
if let Some(scheduler) = &mut self.scheduler {
|
|
721
|
+
scheduler.cleanup(self.heap);
|
|
722
|
+
}
|
|
723
|
+
self.globals.drain(..).drop_with_heap(self.heap);
|
|
724
|
+
}
|
|
725
|
+
|
|
726
|
+
/// Returns the `stack_base` of the current (topmost) call frame.
|
|
727
|
+
///
|
|
728
|
+
/// Used by `NameLookup` resolution to determine which stack region to cache
|
|
729
|
+
/// resolved values into when the lookup originated from a function scope.
|
|
730
|
+
pub fn current_stack_base(&self) -> usize {
|
|
731
|
+
self.frames
|
|
732
|
+
.last()
|
|
733
|
+
.expect("VM should have at least one frame")
|
|
734
|
+
.stack_base
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
/// Takes ownership of the globals vector, replacing it with an empty vec.
|
|
738
|
+
///
|
|
739
|
+
/// Used by the REPL to reclaim globals after VM execution completes,
|
|
740
|
+
/// before calling `cleanup()` (which would destroy them in ref-count-panic mode).
|
|
741
|
+
pub fn take_globals(&mut self) -> Vec<Value> {
|
|
742
|
+
std::mem::take(&mut self.globals)
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
/// Allocates a new `CallId` for an external function call.
|
|
746
|
+
///
|
|
747
|
+
/// Works with or without a scheduler. If a scheduler exists, delegates to it.
|
|
748
|
+
/// Otherwise, uses the VM's `next_call_id` counter directly, avoiding
|
|
749
|
+
/// scheduler creation overhead for synchronous external calls.
|
|
750
|
+
fn allocate_call_id(&mut self) -> CallId {
|
|
751
|
+
if let Some(scheduler) = &mut self.scheduler {
|
|
752
|
+
scheduler.allocate_call_id()
|
|
753
|
+
} else {
|
|
754
|
+
let id = CallId::new(self.next_call_id);
|
|
755
|
+
self.next_call_id += 1;
|
|
756
|
+
id
|
|
757
|
+
}
|
|
758
|
+
}
|
|
759
|
+
|
|
760
|
+
/// Returns true if we're on the main task (or no async at all).
|
|
761
|
+
///
|
|
762
|
+
/// This is used to determine whether a `ReturnValue` at the last frame means
|
|
763
|
+
/// module-level completion (return to host) or spawned task completion
|
|
764
|
+
/// (handle task completion and switch).
|
|
765
|
+
fn is_main_task(&self) -> bool {
|
|
766
|
+
self.scheduler
|
|
767
|
+
.as_ref()
|
|
768
|
+
.is_none_or(|s| s.current_task_id().is_none_or(TaskId::is_main))
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
/// Main execution loop.
|
|
772
|
+
///
|
|
773
|
+
/// Fetches opcodes from the current frame's bytecode and executes them.
|
|
774
|
+
/// Returns when execution completes, an error occurs, or an external
|
|
775
|
+
/// call is needed.
|
|
776
|
+
///
|
|
777
|
+
/// Uses locally cached `code` and `ip` variables to avoid repeated
|
|
778
|
+
/// `frames.last_mut().expect()` calls during operand fetching. The cache
|
|
779
|
+
/// is reloaded after any operation that modifies the frame stack.
|
|
780
|
+
pub fn run(&mut self) -> Result<FrameExit, RunError> {
|
|
781
|
+
// Cache frame state locally to avoid repeated frames.last_mut() calls.
|
|
782
|
+
// The Code reference has lifetime 'a (lives in Interns), independent of frame borrow.
|
|
783
|
+
let mut cached_frame: CachedFrame<'a> = self.new_cached_frame();
|
|
784
|
+
|
|
785
|
+
loop {
|
|
786
|
+
// Check time limit and trigger GC if needed at each instruction.
|
|
787
|
+
// For NoLimitTracker, these are inlined no-ops that compile away.
|
|
788
|
+
self.heap.check_time()?;
|
|
789
|
+
|
|
790
|
+
if self.heap.should_gc() {
|
|
791
|
+
// Sync IP before GC for safety
|
|
792
|
+
self.current_frame_mut().ip = cached_frame.ip;
|
|
793
|
+
self.run_gc();
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
// Track instruction IP for exception table lookup
|
|
797
|
+
self.instruction_ip = cached_frame.ip;
|
|
798
|
+
|
|
799
|
+
// Fetch opcode using cached values (no frame access)
|
|
800
|
+
let opcode = {
|
|
801
|
+
let byte = cached_frame.code.bytecode()[cached_frame.ip];
|
|
802
|
+
cached_frame.ip += 1;
|
|
803
|
+
Opcode::try_from(byte).expect("invalid opcode in bytecode")
|
|
804
|
+
};
|
|
805
|
+
|
|
806
|
+
match opcode {
|
|
807
|
+
// ============================================================
|
|
808
|
+
// Stack Operations
|
|
809
|
+
// ============================================================
|
|
810
|
+
Opcode::Pop => {
|
|
811
|
+
let value = self.pop();
|
|
812
|
+
value.drop_with_heap(self);
|
|
813
|
+
}
|
|
814
|
+
Opcode::Dup => {
|
|
815
|
+
let value = self.peek().clone_with_heap(self);
|
|
816
|
+
self.push(value);
|
|
817
|
+
}
|
|
818
|
+
Opcode::Dup2 => {
|
|
819
|
+
let len = self.stack.len();
|
|
820
|
+
let first = self.stack[len - 2].clone_with_heap(self);
|
|
821
|
+
let second = self.stack[len - 1].clone_with_heap(self);
|
|
822
|
+
self.push(first);
|
|
823
|
+
self.push(second);
|
|
824
|
+
}
|
|
825
|
+
Opcode::Rot2 => {
|
|
826
|
+
// Swap top two: [a, b] → [b, a]
|
|
827
|
+
let len = self.stack.len();
|
|
828
|
+
self.stack.swap(len - 1, len - 2);
|
|
829
|
+
}
|
|
830
|
+
Opcode::Rot3 => {
|
|
831
|
+
// Rotate top three: [a, b, c] → [c, a, b]
|
|
832
|
+
// Uses in-place rotation without cloning
|
|
833
|
+
let len = self.stack.len();
|
|
834
|
+
// Move c out, then shift a→b→c, then put c at a's position
|
|
835
|
+
// Equivalent to: [..rest, a, b, c] → [..rest, c, a, b]
|
|
836
|
+
self.stack[len - 3..].rotate_right(1);
|
|
837
|
+
}
|
|
838
|
+
// Constants & Literals
|
|
839
|
+
Opcode::LoadConst => {
|
|
840
|
+
let idx = fetch_u16!(cached_frame);
|
|
841
|
+
let value = cached_frame.code.constants().get(idx);
|
|
842
|
+
// Handle InternLongInt specially - convert to heap-allocated LongInt
|
|
843
|
+
if let Value::InternLongInt(long_int_id) = value {
|
|
844
|
+
let bi = self.interns.get_long_int(*long_int_id).clone();
|
|
845
|
+
match LongInt::new(bi).into_value(self.heap) {
|
|
846
|
+
Ok(v) => self.push(v),
|
|
847
|
+
Err(e) => catch_sync!(self, cached_frame, RunError::from(e)),
|
|
848
|
+
}
|
|
849
|
+
} else {
|
|
850
|
+
self.push(value.clone_with_heap(self));
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
Opcode::LoadNone => self.push(Value::None),
|
|
854
|
+
Opcode::LoadTrue => self.push(Value::Bool(true)),
|
|
855
|
+
Opcode::LoadFalse => self.push(Value::Bool(false)),
|
|
856
|
+
Opcode::LoadSmallInt => {
|
|
857
|
+
let n = fetch_i8!(cached_frame);
|
|
858
|
+
self.push(Value::Int(i64::from(n)));
|
|
859
|
+
}
|
|
860
|
+
// Variables - Specialized Local Loads (no operand)
|
|
861
|
+
Opcode::LoadLocal0 => handle_load_result!(self, cached_frame, self.load_local(&cached_frame, 0)),
|
|
862
|
+
Opcode::LoadLocal1 => handle_load_result!(self, cached_frame, self.load_local(&cached_frame, 1)),
|
|
863
|
+
Opcode::LoadLocal2 => handle_load_result!(self, cached_frame, self.load_local(&cached_frame, 2)),
|
|
864
|
+
Opcode::LoadLocal3 => handle_load_result!(self, cached_frame, self.load_local(&cached_frame, 3)),
|
|
865
|
+
// Variables - General Local Operations
|
|
866
|
+
Opcode::LoadLocal => {
|
|
867
|
+
let slot = u16::from(fetch_u8!(cached_frame));
|
|
868
|
+
handle_load_result!(self, cached_frame, self.load_local(&cached_frame, slot));
|
|
869
|
+
}
|
|
870
|
+
Opcode::LoadLocalW => {
|
|
871
|
+
let slot = fetch_u16!(cached_frame);
|
|
872
|
+
handle_load_result!(self, cached_frame, self.load_local(&cached_frame, slot));
|
|
873
|
+
}
|
|
874
|
+
Opcode::StoreLocal => {
|
|
875
|
+
let slot = u16::from(fetch_u8!(cached_frame));
|
|
876
|
+
self.store_local(&cached_frame, slot);
|
|
877
|
+
}
|
|
878
|
+
Opcode::StoreLocalW => {
|
|
879
|
+
let slot = fetch_u16!(cached_frame);
|
|
880
|
+
self.store_local(&cached_frame, slot);
|
|
881
|
+
}
|
|
882
|
+
Opcode::DeleteLocal => {
|
|
883
|
+
let slot = u16::from(fetch_u8!(cached_frame));
|
|
884
|
+
self.delete_local(&cached_frame, slot);
|
|
885
|
+
}
|
|
886
|
+
Opcode::DeleteGlobal => {
|
|
887
|
+
let slot = fetch_u16!(cached_frame);
|
|
888
|
+
self.delete_global(slot);
|
|
889
|
+
}
|
|
890
|
+
// Variables - Callable-context Local Loads
|
|
891
|
+
Opcode::LoadLocalCallable => {
|
|
892
|
+
let slot = u16::from(fetch_u8!(cached_frame));
|
|
893
|
+
let name_id = StringId::from_index(fetch_u16!(cached_frame));
|
|
894
|
+
self.load_local_callable(&cached_frame, slot, name_id);
|
|
895
|
+
}
|
|
896
|
+
Opcode::LoadLocalCallableW => {
|
|
897
|
+
let slot = fetch_u16!(cached_frame);
|
|
898
|
+
let name_id = StringId::from_index(fetch_u16!(cached_frame));
|
|
899
|
+
self.load_local_callable(&cached_frame, slot, name_id);
|
|
900
|
+
}
|
|
901
|
+
// Variables - Global Operations
|
|
902
|
+
Opcode::LoadGlobal => {
|
|
903
|
+
let slot = fetch_u16!(cached_frame);
|
|
904
|
+
handle_load_result!(self, cached_frame, self.load_global(slot));
|
|
905
|
+
}
|
|
906
|
+
Opcode::LoadGlobalCallable => {
|
|
907
|
+
let slot = fetch_u16!(cached_frame);
|
|
908
|
+
let name_id = StringId::from_index(fetch_u16!(cached_frame));
|
|
909
|
+
self.load_global_callable(slot, name_id);
|
|
910
|
+
}
|
|
911
|
+
Opcode::StoreGlobal => {
|
|
912
|
+
let slot = fetch_u16!(cached_frame);
|
|
913
|
+
self.store_global(slot);
|
|
914
|
+
}
|
|
915
|
+
// Variables - Cell Operations (closures)
|
|
916
|
+
Opcode::LoadCell => {
|
|
917
|
+
let slot = fetch_u16!(cached_frame);
|
|
918
|
+
try_catch_sync!(self, cached_frame, self.load_cell(&cached_frame, slot));
|
|
919
|
+
}
|
|
920
|
+
Opcode::StoreCell => {
|
|
921
|
+
let slot = fetch_u16!(cached_frame);
|
|
922
|
+
self.store_cell(&cached_frame, slot);
|
|
923
|
+
}
|
|
924
|
+
// Binary Operations - route through exception handling for tracebacks
|
|
925
|
+
Opcode::BinaryAdd => try_catch_sync!(self, cached_frame, self.binary_add()),
|
|
926
|
+
Opcode::BinarySub => try_catch_sync!(self, cached_frame, self.binary_sub()),
|
|
927
|
+
Opcode::BinaryMul => try_catch_sync!(self, cached_frame, self.binary_mult()),
|
|
928
|
+
Opcode::BinaryDiv => try_catch_sync!(self, cached_frame, self.binary_div()),
|
|
929
|
+
Opcode::BinaryFloorDiv => try_catch_sync!(self, cached_frame, self.binary_floordiv()),
|
|
930
|
+
Opcode::BinaryMod => try_catch_sync!(self, cached_frame, self.binary_mod()),
|
|
931
|
+
Opcode::BinaryPow => try_catch_sync!(self, cached_frame, self.binary_pow()),
|
|
932
|
+
// Bitwise operations - only work on integers
|
|
933
|
+
Opcode::BinaryAnd => try_catch_sync!(self, cached_frame, self.binary_and()),
|
|
934
|
+
Opcode::BinaryOr => try_catch_sync!(self, cached_frame, self.binary_or()),
|
|
935
|
+
Opcode::BinaryXor => try_catch_sync!(self, cached_frame, self.binary_xor()),
|
|
936
|
+
Opcode::BinaryLShift => {
|
|
937
|
+
try_catch_sync!(self, cached_frame, self.binary_bitwise(BitwiseOp::LShift));
|
|
938
|
+
}
|
|
939
|
+
Opcode::BinaryRShift => {
|
|
940
|
+
try_catch_sync!(self, cached_frame, self.binary_bitwise(BitwiseOp::RShift));
|
|
941
|
+
}
|
|
942
|
+
Opcode::BinaryMatMul => try_catch_sync!(self, cached_frame, self.binary_matmul()),
|
|
943
|
+
// Comparison Operations
|
|
944
|
+
Opcode::CompareEq => try_catch_sync!(self, cached_frame, self.compare_eq()),
|
|
945
|
+
Opcode::CompareNe => try_catch_sync!(self, cached_frame, self.compare_ne()),
|
|
946
|
+
Opcode::CompareLt => try_catch_sync!(self, cached_frame, self.compare_ord(Ordering::is_lt)),
|
|
947
|
+
Opcode::CompareLe => try_catch_sync!(self, cached_frame, self.compare_ord(Ordering::is_le)),
|
|
948
|
+
Opcode::CompareGt => try_catch_sync!(self, cached_frame, self.compare_ord(Ordering::is_gt)),
|
|
949
|
+
Opcode::CompareGe => try_catch_sync!(self, cached_frame, self.compare_ord(Ordering::is_ge)),
|
|
950
|
+
Opcode::CompareIs => self.compare_is(false),
|
|
951
|
+
Opcode::CompareIsNot => self.compare_is(true),
|
|
952
|
+
Opcode::CompareIn => try_catch_sync!(self, cached_frame, self.compare_in(false)),
|
|
953
|
+
Opcode::CompareNotIn => try_catch_sync!(self, cached_frame, self.compare_in(true)),
|
|
954
|
+
Opcode::CompareModEq => {
|
|
955
|
+
let const_idx = fetch_u16!(cached_frame);
|
|
956
|
+
let k = cached_frame.code.constants().get(const_idx);
|
|
957
|
+
try_catch_sync!(self, cached_frame, self.compare_mod_eq(k));
|
|
958
|
+
}
|
|
959
|
+
// Unary Operations
|
|
960
|
+
Opcode::UnaryNot => {
|
|
961
|
+
let value = self.pop();
|
|
962
|
+
let result = !value.py_bool(self);
|
|
963
|
+
value.drop_with_heap(self);
|
|
964
|
+
self.push(Value::Bool(result));
|
|
965
|
+
}
|
|
966
|
+
Opcode::UnaryNeg => {
|
|
967
|
+
// Unary minus - negate numeric value
|
|
968
|
+
let value = self.pop();
|
|
969
|
+
match value {
|
|
970
|
+
Value::Int(n) => {
|
|
971
|
+
// Use checked_neg to handle i64::MIN overflow
|
|
972
|
+
if let Some(negated) = n.checked_neg() {
|
|
973
|
+
self.push(Value::Int(negated));
|
|
974
|
+
} else {
|
|
975
|
+
// i64::MIN negated overflows to LongInt
|
|
976
|
+
let li = -LongInt::from(n);
|
|
977
|
+
match li.into_value(self.heap) {
|
|
978
|
+
Ok(v) => self.push(v),
|
|
979
|
+
Err(e) => catch_sync!(self, cached_frame, RunError::from(e)),
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
Value::Float(f) => self.push(Value::Float(-f)),
|
|
984
|
+
Value::Bool(b) => self.push(Value::Int(if b { -1 } else { 0 })),
|
|
985
|
+
Value::Ref(id) => {
|
|
986
|
+
if let HeapData::LongInt(li) = self.heap.get(id) {
|
|
987
|
+
let negated = -LongInt::new(li.inner().clone());
|
|
988
|
+
value.drop_with_heap(self);
|
|
989
|
+
match negated.into_value(self.heap) {
|
|
990
|
+
Ok(v) => self.push(v),
|
|
991
|
+
Err(e) => catch_sync!(self, cached_frame, RunError::from(e)),
|
|
992
|
+
}
|
|
993
|
+
} else {
|
|
994
|
+
let value_type = value.py_type(self.heap);
|
|
995
|
+
value.drop_with_heap(self);
|
|
996
|
+
catch_sync!(self, cached_frame, ExcType::unary_type_error("-", value_type));
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
_ => {
|
|
1000
|
+
let value_type = value.py_type(self.heap);
|
|
1001
|
+
value.drop_with_heap(self);
|
|
1002
|
+
catch_sync!(self, cached_frame, ExcType::unary_type_error("-", value_type));
|
|
1003
|
+
}
|
|
1004
|
+
}
|
|
1005
|
+
}
|
|
1006
|
+
Opcode::UnaryPos => {
|
|
1007
|
+
// Unary plus - converts bools to int, no-op for other numbers
|
|
1008
|
+
let value = self.pop();
|
|
1009
|
+
match value {
|
|
1010
|
+
Value::Int(_) | Value::Float(_) => self.push(value),
|
|
1011
|
+
Value::Bool(b) => self.push(Value::Int(i64::from(b))),
|
|
1012
|
+
Value::Ref(id) => {
|
|
1013
|
+
if matches!(self.heap.get(id), HeapData::LongInt(_)) {
|
|
1014
|
+
// LongInt - return as-is (value already has correct refcount)
|
|
1015
|
+
self.push(value);
|
|
1016
|
+
} else {
|
|
1017
|
+
let value_type = value.py_type(self.heap);
|
|
1018
|
+
value.drop_with_heap(self);
|
|
1019
|
+
catch_sync!(self, cached_frame, ExcType::unary_type_error("+", value_type));
|
|
1020
|
+
}
|
|
1021
|
+
}
|
|
1022
|
+
_ => {
|
|
1023
|
+
let value_type = value.py_type(self.heap);
|
|
1024
|
+
value.drop_with_heap(self);
|
|
1025
|
+
catch_sync!(self, cached_frame, ExcType::unary_type_error("+", value_type));
|
|
1026
|
+
}
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
Opcode::UnaryInvert => {
|
|
1030
|
+
// Bitwise NOT
|
|
1031
|
+
let value = self.pop();
|
|
1032
|
+
match value {
|
|
1033
|
+
Value::Int(n) => self.push(Value::Int(!n)),
|
|
1034
|
+
Value::Bool(b) => self.push(Value::Int(!i64::from(b))),
|
|
1035
|
+
Value::Ref(id) => {
|
|
1036
|
+
if let HeapData::LongInt(li) = self.heap.get(id) {
|
|
1037
|
+
// LongInt bitwise NOT: ~x = -(x + 1)
|
|
1038
|
+
let inverted = -(li.inner() + 1i32);
|
|
1039
|
+
value.drop_with_heap(self);
|
|
1040
|
+
match LongInt::new(inverted).into_value(self.heap) {
|
|
1041
|
+
Ok(v) => self.push(v),
|
|
1042
|
+
Err(e) => catch_sync!(self, cached_frame, RunError::from(e)),
|
|
1043
|
+
}
|
|
1044
|
+
} else {
|
|
1045
|
+
let value_type = value.py_type(self.heap);
|
|
1046
|
+
value.drop_with_heap(self);
|
|
1047
|
+
catch_sync!(self, cached_frame, ExcType::unary_type_error("~", value_type));
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
_ => {
|
|
1051
|
+
let value_type = value.py_type(self.heap);
|
|
1052
|
+
value.drop_with_heap(self);
|
|
1053
|
+
catch_sync!(self, cached_frame, ExcType::unary_type_error("~", value_type));
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
}
|
|
1057
|
+
// In-place Operations - route through exception handling
|
|
1058
|
+
Opcode::InplaceAdd => try_catch_sync!(self, cached_frame, self.inplace_add()),
|
|
1059
|
+
// Other in-place ops use the same logic as binary ops for now
|
|
1060
|
+
Opcode::InplaceSub => try_catch_sync!(self, cached_frame, self.binary_sub()),
|
|
1061
|
+
Opcode::InplaceMul => try_catch_sync!(self, cached_frame, self.binary_mult()),
|
|
1062
|
+
Opcode::InplaceDiv => try_catch_sync!(self, cached_frame, self.binary_div()),
|
|
1063
|
+
Opcode::InplaceFloorDiv => try_catch_sync!(self, cached_frame, self.binary_floordiv()),
|
|
1064
|
+
Opcode::InplaceMod => try_catch_sync!(self, cached_frame, self.binary_mod()),
|
|
1065
|
+
Opcode::InplacePow => try_catch_sync!(self, cached_frame, self.binary_pow()),
|
|
1066
|
+
Opcode::InplaceAnd => {
|
|
1067
|
+
try_catch_sync!(self, cached_frame, self.binary_bitwise(BitwiseOp::And));
|
|
1068
|
+
}
|
|
1069
|
+
Opcode::InplaceOr => try_catch_sync!(self, cached_frame, self.binary_bitwise(BitwiseOp::Or)),
|
|
1070
|
+
Opcode::InplaceXor => {
|
|
1071
|
+
try_catch_sync!(self, cached_frame, self.binary_bitwise(BitwiseOp::Xor));
|
|
1072
|
+
}
|
|
1073
|
+
Opcode::InplaceLShift => {
|
|
1074
|
+
try_catch_sync!(self, cached_frame, self.binary_bitwise(BitwiseOp::LShift));
|
|
1075
|
+
}
|
|
1076
|
+
Opcode::InplaceRShift => {
|
|
1077
|
+
try_catch_sync!(self, cached_frame, self.binary_bitwise(BitwiseOp::RShift));
|
|
1078
|
+
}
|
|
1079
|
+
// Collection Building - route through exception handling
|
|
1080
|
+
Opcode::BuildList => {
|
|
1081
|
+
let count = fetch_u16!(cached_frame) as usize;
|
|
1082
|
+
try_catch_sync!(self, cached_frame, self.build_list(count));
|
|
1083
|
+
}
|
|
1084
|
+
Opcode::BuildTuple => {
|
|
1085
|
+
let count = fetch_u16!(cached_frame) as usize;
|
|
1086
|
+
try_catch_sync!(self, cached_frame, self.build_tuple(count));
|
|
1087
|
+
}
|
|
1088
|
+
Opcode::BuildDict => {
|
|
1089
|
+
let count = fetch_u16!(cached_frame) as usize;
|
|
1090
|
+
try_catch_sync!(self, cached_frame, self.build_dict(count));
|
|
1091
|
+
}
|
|
1092
|
+
Opcode::BuildSet => {
|
|
1093
|
+
let count = fetch_u16!(cached_frame) as usize;
|
|
1094
|
+
try_catch_sync!(self, cached_frame, self.build_set(count));
|
|
1095
|
+
}
|
|
1096
|
+
Opcode::FormatValue => {
|
|
1097
|
+
let flags = fetch_u8!(cached_frame);
|
|
1098
|
+
try_catch_sync!(self, cached_frame, self.format_value(flags));
|
|
1099
|
+
}
|
|
1100
|
+
Opcode::BuildFString => {
|
|
1101
|
+
let count = fetch_u16!(cached_frame) as usize;
|
|
1102
|
+
try_catch_sync!(self, cached_frame, self.build_fstring(count));
|
|
1103
|
+
}
|
|
1104
|
+
Opcode::BuildSlice => {
|
|
1105
|
+
try_catch_sync!(self, cached_frame, self.build_slice());
|
|
1106
|
+
}
|
|
1107
|
+
Opcode::ListExtend => {
|
|
1108
|
+
try_catch_sync!(self, cached_frame, self.list_extend());
|
|
1109
|
+
}
|
|
1110
|
+
Opcode::ListToTuple => {
|
|
1111
|
+
try_catch_sync!(self, cached_frame, self.list_to_tuple());
|
|
1112
|
+
}
|
|
1113
|
+
Opcode::DictMerge => {
|
|
1114
|
+
let func_name_id = fetch_u16!(cached_frame);
|
|
1115
|
+
try_catch_sync!(self, cached_frame, self.dict_merge(func_name_id));
|
|
1116
|
+
}
|
|
1117
|
+
// PEP 448 literal building
|
|
1118
|
+
Opcode::DictUpdate => {
|
|
1119
|
+
let depth = fetch_u8!(cached_frame) as usize;
|
|
1120
|
+
try_catch_sync!(self, cached_frame, self.dict_update(depth));
|
|
1121
|
+
}
|
|
1122
|
+
Opcode::SetExtend => {
|
|
1123
|
+
let depth = fetch_u8!(cached_frame) as usize;
|
|
1124
|
+
try_catch_sync!(self, cached_frame, self.set_extend(depth));
|
|
1125
|
+
}
|
|
1126
|
+
// Comprehension Building - append/add/set items during iteration
|
|
1127
|
+
Opcode::ListAppend => {
|
|
1128
|
+
let depth = fetch_u8!(cached_frame) as usize;
|
|
1129
|
+
try_catch_sync!(self, cached_frame, self.list_append(depth));
|
|
1130
|
+
}
|
|
1131
|
+
Opcode::SetAdd => {
|
|
1132
|
+
let depth = fetch_u8!(cached_frame) as usize;
|
|
1133
|
+
try_catch_sync!(self, cached_frame, self.set_add(depth));
|
|
1134
|
+
}
|
|
1135
|
+
Opcode::DictSetItem => {
|
|
1136
|
+
let depth = fetch_u8!(cached_frame) as usize;
|
|
1137
|
+
try_catch_sync!(self, cached_frame, self.dict_set_item(depth));
|
|
1138
|
+
}
|
|
1139
|
+
// Subscript & Attribute - route through exception handling
|
|
1140
|
+
Opcode::BinarySubscr => {
|
|
1141
|
+
let index = self.pop();
|
|
1142
|
+
let obj = self.pop();
|
|
1143
|
+
let result = obj.py_getitem(&index, self);
|
|
1144
|
+
obj.drop_with_heap(self);
|
|
1145
|
+
index.drop_with_heap(self);
|
|
1146
|
+
match result {
|
|
1147
|
+
Ok(v) => self.push(v),
|
|
1148
|
+
Err(e) => catch_sync!(self, cached_frame, e),
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
Opcode::StoreSubscr => {
|
|
1152
|
+
// Stack order: value, obj, index (TOS)
|
|
1153
|
+
let index = self.pop();
|
|
1154
|
+
let mut obj = self.pop();
|
|
1155
|
+
let value = self.pop();
|
|
1156
|
+
let result = obj.py_setitem(index, value, self);
|
|
1157
|
+
obj.drop_with_heap(self);
|
|
1158
|
+
if let Err(e) = result {
|
|
1159
|
+
catch_sync!(self, cached_frame, e);
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
Opcode::LoadAttr => {
|
|
1163
|
+
let name_idx = fetch_u16!(cached_frame);
|
|
1164
|
+
let name_id = StringId::from_index(name_idx);
|
|
1165
|
+
handle_call_result!(self, cached_frame, self.load_attr(name_id));
|
|
1166
|
+
}
|
|
1167
|
+
Opcode::LoadAttrImport => {
|
|
1168
|
+
let name_idx = fetch_u16!(cached_frame);
|
|
1169
|
+
let name_id = StringId::from_index(name_idx);
|
|
1170
|
+
handle_call_result!(self, cached_frame, self.load_attr_import(name_id));
|
|
1171
|
+
}
|
|
1172
|
+
Opcode::StoreAttr => {
|
|
1173
|
+
let name_idx = fetch_u16!(cached_frame);
|
|
1174
|
+
let name_id = StringId::from_index(name_idx);
|
|
1175
|
+
try_catch_sync!(self, cached_frame, self.store_attr(name_id));
|
|
1176
|
+
}
|
|
1177
|
+
// Control Flow - use cached_frame.ip directly for jumps
|
|
1178
|
+
Opcode::Jump => {
|
|
1179
|
+
let offset = fetch_i16!(cached_frame);
|
|
1180
|
+
jump_relative!(cached_frame.ip, offset);
|
|
1181
|
+
}
|
|
1182
|
+
Opcode::JumpIfTrue => {
|
|
1183
|
+
let offset = fetch_i16!(cached_frame);
|
|
1184
|
+
let cond = self.pop();
|
|
1185
|
+
if cond.py_bool(self) {
|
|
1186
|
+
jump_relative!(cached_frame.ip, offset);
|
|
1187
|
+
}
|
|
1188
|
+
cond.drop_with_heap(self);
|
|
1189
|
+
}
|
|
1190
|
+
Opcode::JumpIfFalse => {
|
|
1191
|
+
let offset = fetch_i16!(cached_frame);
|
|
1192
|
+
let cond = self.pop();
|
|
1193
|
+
if !cond.py_bool(self) {
|
|
1194
|
+
jump_relative!(cached_frame.ip, offset);
|
|
1195
|
+
}
|
|
1196
|
+
cond.drop_with_heap(self);
|
|
1197
|
+
}
|
|
1198
|
+
Opcode::JumpIfTrueOrPop => {
|
|
1199
|
+
let offset = fetch_i16!(cached_frame);
|
|
1200
|
+
if self.peek().py_bool(self) {
|
|
1201
|
+
jump_relative!(cached_frame.ip, offset);
|
|
1202
|
+
} else {
|
|
1203
|
+
let value = self.pop();
|
|
1204
|
+
value.drop_with_heap(self);
|
|
1205
|
+
}
|
|
1206
|
+
}
|
|
1207
|
+
Opcode::JumpIfFalseOrPop => {
|
|
1208
|
+
let offset = fetch_i16!(cached_frame);
|
|
1209
|
+
if self.peek().py_bool(self) {
|
|
1210
|
+
let value = self.pop();
|
|
1211
|
+
value.drop_with_heap(self);
|
|
1212
|
+
} else {
|
|
1213
|
+
jump_relative!(cached_frame.ip, offset);
|
|
1214
|
+
}
|
|
1215
|
+
}
|
|
1216
|
+
// Iteration - route through exception handling
|
|
1217
|
+
Opcode::GetIter => {
|
|
1218
|
+
let value = self.pop();
|
|
1219
|
+
// Create a MontyIter from the value and store on heap
|
|
1220
|
+
match MontyIter::new(value, self) {
|
|
1221
|
+
Ok(iter) => match self.heap.allocate(HeapData::Iter(iter)) {
|
|
1222
|
+
Ok(heap_id) => self.push(Value::Ref(heap_id)),
|
|
1223
|
+
Err(e) => catch_sync!(self, cached_frame, e.into()),
|
|
1224
|
+
},
|
|
1225
|
+
Err(e) => catch_sync!(self, cached_frame, e),
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
Opcode::ForIter => {
|
|
1229
|
+
let offset = fetch_i16!(cached_frame);
|
|
1230
|
+
// Peek at the iterator on TOS and extract heap_id
|
|
1231
|
+
let Value::Ref(heap_id) = *self.peek() else {
|
|
1232
|
+
return Err(RunError::internal("ForIter: expected iterator ref on stack"));
|
|
1233
|
+
};
|
|
1234
|
+
|
|
1235
|
+
// Use advance_iterator which avoids std::mem::replace overhead
|
|
1236
|
+
// by using a two-phase approach: read state, get value, update index
|
|
1237
|
+
match advance_on_heap(self.heap, heap_id, self.interns) {
|
|
1238
|
+
Ok(Some(value)) => self.push(value),
|
|
1239
|
+
Ok(None) => {
|
|
1240
|
+
// Iterator exhausted - pop it and jump to end
|
|
1241
|
+
let iter = self.pop();
|
|
1242
|
+
iter.drop_with_heap(self);
|
|
1243
|
+
jump_relative!(cached_frame.ip, offset);
|
|
1244
|
+
}
|
|
1245
|
+
Err(e) => {
|
|
1246
|
+
// Error during iteration (e.g., dict size changed)
|
|
1247
|
+
let iter = self.pop();
|
|
1248
|
+
iter.drop_with_heap(self);
|
|
1249
|
+
catch_sync!(self, cached_frame, e);
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
// Function Calls - sync IP before call, reload cache after frame changes
|
|
1254
|
+
Opcode::CallFunction => {
|
|
1255
|
+
let arg_count = fetch_u8!(cached_frame) as usize;
|
|
1256
|
+
|
|
1257
|
+
// Sync IP before call (call_function may access frame for traceback)
|
|
1258
|
+
self.current_frame_mut().ip = cached_frame.ip;
|
|
1259
|
+
|
|
1260
|
+
handle_call_result!(self, cached_frame, self.exec_call_function(arg_count));
|
|
1261
|
+
}
|
|
1262
|
+
Opcode::CallBuiltinFunction => {
|
|
1263
|
+
// Fetch operands: builtin_id (u8) + arg_count (u8)
|
|
1264
|
+
let builtin_id = fetch_u8!(cached_frame);
|
|
1265
|
+
let arg_count = fetch_u8!(cached_frame) as usize;
|
|
1266
|
+
|
|
1267
|
+
// Sync IP before call (builtins like map() may call evaluate_function
|
|
1268
|
+
// which pushes frames and runs a nested run() loop)
|
|
1269
|
+
self.current_frame_mut().ip = cached_frame.ip;
|
|
1270
|
+
|
|
1271
|
+
match self.exec_call_builtin_function(builtin_id, arg_count) {
|
|
1272
|
+
Ok(result) => self.push(result),
|
|
1273
|
+
Err(err) => catch_sync!(self, cached_frame, err),
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
Opcode::CallBuiltinType => {
|
|
1277
|
+
// Fetch operands: type_id (u8) + arg_count (u8)
|
|
1278
|
+
let type_id = fetch_u8!(cached_frame);
|
|
1279
|
+
let arg_count = fetch_u8!(cached_frame) as usize;
|
|
1280
|
+
|
|
1281
|
+
match self.exec_call_builtin_type(type_id, arg_count) {
|
|
1282
|
+
Ok(result) => self.push(result),
|
|
1283
|
+
// IP sync deferred to error path (no frame push possible)
|
|
1284
|
+
Err(err) => catch_sync!(self, cached_frame, err),
|
|
1285
|
+
}
|
|
1286
|
+
}
|
|
1287
|
+
Opcode::CallFunctionKw => {
|
|
1288
|
+
// Fetch operands: pos_count, kw_count, then kw_count name indices
|
|
1289
|
+
let pos_count = fetch_u8!(cached_frame) as usize;
|
|
1290
|
+
let kw_count = fetch_u8!(cached_frame) as usize;
|
|
1291
|
+
|
|
1292
|
+
// Read keyword name StringIds
|
|
1293
|
+
let mut kwname_ids = Vec::with_capacity(kw_count);
|
|
1294
|
+
for _ in 0..kw_count {
|
|
1295
|
+
kwname_ids.push(StringId::from_index(fetch_u16!(cached_frame)));
|
|
1296
|
+
}
|
|
1297
|
+
|
|
1298
|
+
// Sync IP before call (call_function may access frame for traceback)
|
|
1299
|
+
self.current_frame_mut().ip = cached_frame.ip;
|
|
1300
|
+
|
|
1301
|
+
handle_call_result!(self, cached_frame, self.exec_call_function_kw(pos_count, kwname_ids));
|
|
1302
|
+
}
|
|
1303
|
+
Opcode::CallAttr => {
|
|
1304
|
+
// CallAttr: u16 name_id, u8 arg_count
|
|
1305
|
+
// Stack: [obj, arg1, arg2, ..., argN] -> [result]
|
|
1306
|
+
let name_idx = fetch_u16!(cached_frame);
|
|
1307
|
+
let arg_count = fetch_u8!(cached_frame) as usize;
|
|
1308
|
+
let name_id = StringId::from_index(name_idx);
|
|
1309
|
+
|
|
1310
|
+
// Sync IP before call (may yield to host for OS/external calls)
|
|
1311
|
+
self.current_frame_mut().ip = cached_frame.ip;
|
|
1312
|
+
|
|
1313
|
+
handle_call_result!(self, cached_frame, self.exec_call_attr(name_id, arg_count));
|
|
1314
|
+
}
|
|
1315
|
+
Opcode::CallAttrKw => {
|
|
1316
|
+
// CallAttrKw: u16 name_id, u8 pos_count, u8 kw_count, then kw_count u16 name indices
|
|
1317
|
+
// Stack: [obj, pos_args..., kw_values...] -> [result]
|
|
1318
|
+
let name_idx = fetch_u16!(cached_frame);
|
|
1319
|
+
let pos_count = fetch_u8!(cached_frame) as usize;
|
|
1320
|
+
let kw_count = fetch_u8!(cached_frame) as usize;
|
|
1321
|
+
let name_id = StringId::from_index(name_idx);
|
|
1322
|
+
|
|
1323
|
+
// Read keyword name StringIds
|
|
1324
|
+
let mut kwname_ids = Vec::with_capacity(kw_count);
|
|
1325
|
+
for _ in 0..kw_count {
|
|
1326
|
+
kwname_ids.push(StringId::from_index(fetch_u16!(cached_frame)));
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
// Sync IP before call (may yield to host for OS/external calls)
|
|
1330
|
+
self.current_frame_mut().ip = cached_frame.ip;
|
|
1331
|
+
|
|
1332
|
+
handle_call_result!(
|
|
1333
|
+
self,
|
|
1334
|
+
cached_frame,
|
|
1335
|
+
self.exec_call_attr_kw(name_id, pos_count, kwname_ids)
|
|
1336
|
+
);
|
|
1337
|
+
}
|
|
1338
|
+
Opcode::CallFunctionExtended => {
|
|
1339
|
+
let flags = fetch_u8!(cached_frame);
|
|
1340
|
+
let has_kwargs = (flags & 0x01) != 0;
|
|
1341
|
+
|
|
1342
|
+
// Sync IP before call
|
|
1343
|
+
self.current_frame_mut().ip = cached_frame.ip;
|
|
1344
|
+
|
|
1345
|
+
handle_call_result!(self, cached_frame, self.exec_call_function_extended(has_kwargs));
|
|
1346
|
+
}
|
|
1347
|
+
Opcode::CallAttrExtended => {
|
|
1348
|
+
let name_idx = fetch_u16!(cached_frame);
|
|
1349
|
+
let flags = fetch_u8!(cached_frame);
|
|
1350
|
+
let name_id = StringId::from_index(name_idx);
|
|
1351
|
+
let has_kwargs = (flags & 0x01) != 0;
|
|
1352
|
+
|
|
1353
|
+
// Sync IP before call (may yield to host for OS/external calls)
|
|
1354
|
+
self.current_frame_mut().ip = cached_frame.ip;
|
|
1355
|
+
|
|
1356
|
+
handle_call_result!(self, cached_frame, self.exec_call_attr_extended(name_id, has_kwargs));
|
|
1357
|
+
}
|
|
1358
|
+
// Function Definition
|
|
1359
|
+
Opcode::MakeFunction => {
|
|
1360
|
+
let func_idx = fetch_u16!(cached_frame);
|
|
1361
|
+
let defaults_count = fetch_u8!(cached_frame) as usize;
|
|
1362
|
+
let func_id = FunctionId::from_index(func_idx);
|
|
1363
|
+
|
|
1364
|
+
if defaults_count == 0 {
|
|
1365
|
+
// No defaults - use inline Value::Function (no heap allocation)
|
|
1366
|
+
self.push(Value::DefFunction(func_id));
|
|
1367
|
+
} else {
|
|
1368
|
+
// Pop default values from stack (drain maintains order: first pushed = first in vec)
|
|
1369
|
+
let defaults = self.pop_n(defaults_count);
|
|
1370
|
+
|
|
1371
|
+
// Create FunctionDefaults on heap and push reference
|
|
1372
|
+
let heap_id = self
|
|
1373
|
+
.heap
|
|
1374
|
+
.allocate(HeapData::FunctionDefaults(FunctionDefaults { func_id, defaults }))?;
|
|
1375
|
+
self.push(Value::Ref(heap_id));
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
Opcode::MakeClosure => {
|
|
1379
|
+
let func_idx = fetch_u16!(cached_frame);
|
|
1380
|
+
let defaults_count = fetch_u8!(cached_frame) as usize;
|
|
1381
|
+
let cell_count = fetch_u8!(cached_frame) as usize;
|
|
1382
|
+
let func_id = FunctionId::from_index(func_idx);
|
|
1383
|
+
|
|
1384
|
+
// Pop cells from stack (pushed after defaults, so on top)
|
|
1385
|
+
// Cells are Value::Ref pointing to HeapData::Cell
|
|
1386
|
+
// We use individual pops which reverses order, so we need to reverse back
|
|
1387
|
+
let mut cells = Vec::with_capacity(cell_count);
|
|
1388
|
+
for _ in 0..cell_count {
|
|
1389
|
+
// mut needed for dec_ref_forget when ref-count-panic feature is enabled
|
|
1390
|
+
#[cfg_attr(not(feature = "ref-count-panic"), expect(unused_mut))]
|
|
1391
|
+
let mut cell_val = self.pop();
|
|
1392
|
+
match &cell_val {
|
|
1393
|
+
Value::Ref(heap_id) => {
|
|
1394
|
+
// Keep the reference - the Closure will own the HeapId
|
|
1395
|
+
cells.push(*heap_id);
|
|
1396
|
+
// Mark the Value as dereferenced since Closure takes ownership
|
|
1397
|
+
// of the reference count (we don't call drop_with_heap because
|
|
1398
|
+
// we're not decrementing the refcount, just transferring it)
|
|
1399
|
+
#[cfg(feature = "ref-count-panic")]
|
|
1400
|
+
cell_val.dec_ref_forget();
|
|
1401
|
+
}
|
|
1402
|
+
_ => {
|
|
1403
|
+
return Err(RunError::internal("MakeClosure: expected cell reference on stack"));
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1406
|
+
}
|
|
1407
|
+
// Reverse to get original order (individual pops reverse the order)
|
|
1408
|
+
cells.reverse();
|
|
1409
|
+
|
|
1410
|
+
// Pop default values from stack (drain maintains order: first pushed = first in vec)
|
|
1411
|
+
let defaults = self.pop_n(defaults_count);
|
|
1412
|
+
|
|
1413
|
+
// Create Closure on heap and push reference
|
|
1414
|
+
let heap_id = self.heap.allocate(HeapData::Closure(Closure {
|
|
1415
|
+
func_id,
|
|
1416
|
+
cells,
|
|
1417
|
+
defaults,
|
|
1418
|
+
}))?;
|
|
1419
|
+
self.push(Value::Ref(heap_id));
|
|
1420
|
+
}
|
|
1421
|
+
// Exception Handling
|
|
1422
|
+
Opcode::Raise => {
|
|
1423
|
+
let exc = self.pop();
|
|
1424
|
+
let error = self.make_exception(exc, true); // is_raise=true, hide caret
|
|
1425
|
+
catch_sync!(self, cached_frame, error);
|
|
1426
|
+
}
|
|
1427
|
+
Opcode::Reraise => {
|
|
1428
|
+
// Pop the current exception from the stack to re-raise it
|
|
1429
|
+
// If caught, handle_exception will push it back
|
|
1430
|
+
let error = if let Some(exc) = self.exception_stack.pop() {
|
|
1431
|
+
self.make_exception(exc, true) // is_raise=true for reraise
|
|
1432
|
+
} else {
|
|
1433
|
+
// No active exception - create a RuntimeError
|
|
1434
|
+
SimpleException::new_msg(ExcType::RuntimeError, "No active exception to reraise").into()
|
|
1435
|
+
};
|
|
1436
|
+
catch_sync!(self, cached_frame, error);
|
|
1437
|
+
}
|
|
1438
|
+
Opcode::ClearException => {
|
|
1439
|
+
// Pop the current exception from the stack
|
|
1440
|
+
// This restores the previous exception context (if any)
|
|
1441
|
+
if let Some(exc) = self.exception_stack.pop() {
|
|
1442
|
+
exc.drop_with_heap(self);
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
Opcode::CheckExcMatch => {
|
|
1446
|
+
// Stack: [exception, exc_type] -> [exception, bool]
|
|
1447
|
+
let exc_type = self.pop();
|
|
1448
|
+
let exception = self.peek();
|
|
1449
|
+
let result = self.check_exc_match(exception, &exc_type);
|
|
1450
|
+
exc_type.drop_with_heap(self);
|
|
1451
|
+
let result = result?;
|
|
1452
|
+
self.push(Value::Bool(result));
|
|
1453
|
+
}
|
|
1454
|
+
// Return - reload cache after popping frame
|
|
1455
|
+
Opcode::ReturnValue => {
|
|
1456
|
+
let value = self.pop();
|
|
1457
|
+
if self.frames.len() == 1 {
|
|
1458
|
+
// Last frame - check if this is main task or spawned task
|
|
1459
|
+
let is_main_task = self.is_main_task();
|
|
1460
|
+
|
|
1461
|
+
if is_main_task {
|
|
1462
|
+
// Module-level return - we're done
|
|
1463
|
+
return Ok(FrameExit::Return(value));
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
// Spawned task completed - handle task completion
|
|
1467
|
+
let result = self.handle_task_completion(value);
|
|
1468
|
+
match result {
|
|
1469
|
+
Ok(AwaitResult::ValueReady(v)) => {
|
|
1470
|
+
self.push(v);
|
|
1471
|
+
}
|
|
1472
|
+
Ok(AwaitResult::FramePushed) => {
|
|
1473
|
+
// Switched to another task - reload cache
|
|
1474
|
+
reload_cache!(self, cached_frame);
|
|
1475
|
+
}
|
|
1476
|
+
Ok(AwaitResult::Yield(pending)) => {
|
|
1477
|
+
// All tasks blocked - return to host
|
|
1478
|
+
return Ok(FrameExit::ResolveFutures(pending));
|
|
1479
|
+
}
|
|
1480
|
+
Err(e) => {
|
|
1481
|
+
catch_sync!(self, cached_frame, e);
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
continue;
|
|
1485
|
+
}
|
|
1486
|
+
// Pop current frame and push return value
|
|
1487
|
+
if self.pop_frame() {
|
|
1488
|
+
// This frame indicated evaluation should stop - return to host with value
|
|
1489
|
+
// e.g. `evaluate_function`
|
|
1490
|
+
return Ok(FrameExit::Return(value));
|
|
1491
|
+
}
|
|
1492
|
+
self.push(value);
|
|
1493
|
+
// Reload cache from parent frame
|
|
1494
|
+
reload_cache!(self, cached_frame);
|
|
1495
|
+
}
|
|
1496
|
+
// Async/Await
|
|
1497
|
+
Opcode::Await => {
|
|
1498
|
+
// Sync IP before exec (may push new frame for coroutine)
|
|
1499
|
+
self.current_frame_mut().ip = cached_frame.ip;
|
|
1500
|
+
let result = self.exec_get_awaitable();
|
|
1501
|
+
match result {
|
|
1502
|
+
Ok(AwaitResult::ValueReady(value)) => {
|
|
1503
|
+
self.push(value);
|
|
1504
|
+
}
|
|
1505
|
+
Ok(AwaitResult::FramePushed) => {
|
|
1506
|
+
// Reload cache after pushing a new frame
|
|
1507
|
+
reload_cache!(self, cached_frame);
|
|
1508
|
+
}
|
|
1509
|
+
Ok(AwaitResult::Yield(pending_calls)) => {
|
|
1510
|
+
// All tasks are blocked - return control to host
|
|
1511
|
+
return Ok(FrameExit::ResolveFutures(pending_calls));
|
|
1512
|
+
}
|
|
1513
|
+
Err(e) => {
|
|
1514
|
+
catch_sync!(self, cached_frame, e);
|
|
1515
|
+
}
|
|
1516
|
+
}
|
|
1517
|
+
}
|
|
1518
|
+
// Unpacking - route through exception handling
|
|
1519
|
+
Opcode::UnpackSequence => {
|
|
1520
|
+
let count = fetch_u8!(cached_frame) as usize;
|
|
1521
|
+
try_catch_sync!(self, cached_frame, self.unpack_sequence(count));
|
|
1522
|
+
}
|
|
1523
|
+
Opcode::UnpackEx => {
|
|
1524
|
+
let before = fetch_u8!(cached_frame) as usize;
|
|
1525
|
+
let after = fetch_u8!(cached_frame) as usize;
|
|
1526
|
+
try_catch_sync!(self, cached_frame, self.unpack_ex(before, after));
|
|
1527
|
+
}
|
|
1528
|
+
// Special
|
|
1529
|
+
Opcode::Nop => {
|
|
1530
|
+
// No operation
|
|
1531
|
+
}
|
|
1532
|
+
// Module Operations
|
|
1533
|
+
Opcode::LoadModule => {
|
|
1534
|
+
let module_id = fetch_u8!(cached_frame);
|
|
1535
|
+
try_catch_sync!(self, cached_frame, self.load_module(module_id));
|
|
1536
|
+
}
|
|
1537
|
+
Opcode::RaiseImportError => {
|
|
1538
|
+
// Fetch the module name from the constant pool and raise ModuleNotFoundError
|
|
1539
|
+
let const_idx = fetch_u16!(cached_frame);
|
|
1540
|
+
let module_name = cached_frame.code.constants().get(const_idx);
|
|
1541
|
+
// The constant should be an InternString from compile_import/compile_import_from
|
|
1542
|
+
let name_str = match module_name {
|
|
1543
|
+
Value::InternString(id) => self.interns.get_str(*id),
|
|
1544
|
+
_ => "<unknown>",
|
|
1545
|
+
};
|
|
1546
|
+
let error = ExcType::module_not_found_error(name_str);
|
|
1547
|
+
catch_sync!(self, cached_frame, error);
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
}
|
|
1551
|
+
}
|
|
1552
|
+
|
|
1553
|
+
/// Loads a built-in module and pushes it onto the stack.
|
|
1554
|
+
fn load_module(&mut self, module_id: u8) -> RunResult<()> {
|
|
1555
|
+
let module = BuiltinModule::from_repr(module_id).expect("unknown module id");
|
|
1556
|
+
|
|
1557
|
+
// Create the module on the heap using pre-interned strings
|
|
1558
|
+
let heap_id = module.create(self)?;
|
|
1559
|
+
self.push(Value::Ref(heap_id));
|
|
1560
|
+
Ok(())
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1563
|
+
/// Resumes execution after an external call completes.
|
|
1564
|
+
///
|
|
1565
|
+
/// Pushes the return value onto the stack and continues execution.
|
|
1566
|
+
pub fn resume(&mut self, obj: MontyObject) -> Result<FrameExit, RunError> {
|
|
1567
|
+
let value = obj
|
|
1568
|
+
.to_value(self)
|
|
1569
|
+
.map_err(|e| SimpleException::new(ExcType::RuntimeError, Some(format!("invalid return type: {e}"))))?;
|
|
1570
|
+
self.push(value);
|
|
1571
|
+
self.run()
|
|
1572
|
+
}
|
|
1573
|
+
|
|
1574
|
+
/// Sets the instruction IP used for exception table lookup and traceback generation.
|
|
1575
|
+
///
|
|
1576
|
+
/// Used by `run()` to restore the IP to the load instruction's position before
|
|
1577
|
+
/// raising `NameError` for auto-injected `ExtFunction` values, so the traceback
|
|
1578
|
+
/// points to the name reference rather than the call expression.
|
|
1579
|
+
pub fn set_instruction_ip(&mut self, ip: usize) {
|
|
1580
|
+
self.instruction_ip = ip;
|
|
1581
|
+
}
|
|
1582
|
+
|
|
1583
|
+
/// Resumes execution after an external call raised an exception.
|
|
1584
|
+
///
|
|
1585
|
+
/// Uses the exception handling mechanism to try to catch the exception.
|
|
1586
|
+
/// If caught, continues execution at the handler. If not, propagates the error.
|
|
1587
|
+
pub fn resume_with_exception(&mut self, error: RunError) -> Result<FrameExit, RunError> {
|
|
1588
|
+
// Use the normal exception handling mechanism
|
|
1589
|
+
// handle_exception returns None if caught, Some(error) if not caught
|
|
1590
|
+
if let Some(uncaught_error) = self.handle_exception(error) {
|
|
1591
|
+
return Err(uncaught_error);
|
|
1592
|
+
}
|
|
1593
|
+
// Exception was caught, continue execution
|
|
1594
|
+
self.run()
|
|
1595
|
+
}
|
|
1596
|
+
|
|
1597
|
+
// ========================================================================
|
|
1598
|
+
// Stack Operations
|
|
1599
|
+
// ========================================================================
|
|
1600
|
+
|
|
1601
|
+
/// Pushes a value onto the operand stack.
|
|
1602
|
+
#[inline]
|
|
1603
|
+
pub(crate) fn push(&mut self, value: Value) {
|
|
1604
|
+
self.stack.push(value);
|
|
1605
|
+
}
|
|
1606
|
+
|
|
1607
|
+
/// Pops a value from the operand stack.
|
|
1608
|
+
#[inline]
|
|
1609
|
+
pub(super) fn pop(&mut self) -> Value {
|
|
1610
|
+
self.stack.pop().expect("stack underflow")
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1613
|
+
/// Peeks at the top of the operand stack without removing it.
|
|
1614
|
+
#[inline]
|
|
1615
|
+
pub(super) fn peek(&self) -> &Value {
|
|
1616
|
+
self.stack.last().expect("stack underflow")
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
/// Pops n values from the stack in reverse order (first popped is last in vec).
|
|
1620
|
+
pub(super) fn pop_n(&mut self, n: usize) -> Vec<Value> {
|
|
1621
|
+
let start = self.stack.len() - n;
|
|
1622
|
+
self.stack.drain(start..).collect()
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1625
|
+
// ========================================================================
|
|
1626
|
+
// Frame Operations
|
|
1627
|
+
// ========================================================================
|
|
1628
|
+
|
|
1629
|
+
/// Returns a reference to the current (topmost) call frame.
|
|
1630
|
+
#[inline]
|
|
1631
|
+
pub(crate) fn current_frame(&self) -> &CallFrame<'a> {
|
|
1632
|
+
self.frames.last().expect("no active frame")
|
|
1633
|
+
}
|
|
1634
|
+
|
|
1635
|
+
/// Creates a new cached frame from the current frame.
|
|
1636
|
+
#[inline]
|
|
1637
|
+
pub(super) fn new_cached_frame(&self) -> CachedFrame<'a> {
|
|
1638
|
+
self.current_frame().into()
|
|
1639
|
+
}
|
|
1640
|
+
|
|
1641
|
+
/// Returns a mutable reference to the current call frame.
|
|
1642
|
+
#[inline]
|
|
1643
|
+
pub(super) fn current_frame_mut(&mut self) -> &mut CallFrame<'a> {
|
|
1644
|
+
self.frames.last_mut().expect("no active frame")
|
|
1645
|
+
}
|
|
1646
|
+
|
|
1647
|
+
/// Pushes the given frame onto the call stack.
|
|
1648
|
+
///
|
|
1649
|
+
/// Returns an error if the recursion depth limit is exceeded by pushing this frame.
|
|
1650
|
+
pub(super) fn push_frame(&mut self, frame: CallFrame<'a>) -> RunResult<()> {
|
|
1651
|
+
// root frame doesn't count towards recursion depth, so only check if there's already a frame on the stack
|
|
1652
|
+
if !self.frames.is_empty()
|
|
1653
|
+
&& let Err(e) = self.heap.incr_recursion_depth()
|
|
1654
|
+
{
|
|
1655
|
+
self.cleanup_frame_state(&frame);
|
|
1656
|
+
return Err(e.into());
|
|
1657
|
+
}
|
|
1658
|
+
self.frames.push(frame);
|
|
1659
|
+
|
|
1660
|
+
Ok(())
|
|
1661
|
+
}
|
|
1662
|
+
|
|
1663
|
+
/// Pops the current frame from the call stack.
|
|
1664
|
+
///
|
|
1665
|
+
/// Cleans up the frame's stack region and namespace (except for global namespace).
|
|
1666
|
+
/// Syncs `instruction_ip` to the parent frame's IP so that exception handling
|
|
1667
|
+
/// looks up handlers in the correct frame's exception table.
|
|
1668
|
+
///
|
|
1669
|
+
/// Returns `true` if this frame indicated evaluation should stop when popped.
|
|
1670
|
+
pub(super) fn pop_frame(&mut self) -> bool {
|
|
1671
|
+
let frame = self.frames.pop().expect("no frame to pop");
|
|
1672
|
+
self.cleanup_frame_state(&frame);
|
|
1673
|
+
// Sync instruction_ip to the parent frame so exception table lookups
|
|
1674
|
+
// target the correct frame after returning from a nested run() call.
|
|
1675
|
+
if let Some(parent) = self.frames.last() {
|
|
1676
|
+
self.instruction_ip = parent.ip;
|
|
1677
|
+
}
|
|
1678
|
+
// Decrement recursion depth if this wasn't the root frame
|
|
1679
|
+
if !self.frames.is_empty() {
|
|
1680
|
+
self.heap.decr_recursion_depth();
|
|
1681
|
+
}
|
|
1682
|
+
frame.should_return
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1685
|
+
fn cleanup_frame_state(&mut self, frame: &CallFrame<'_>) {
|
|
1686
|
+
// Clean up frame's stack region (locals + operands).
|
|
1687
|
+
// Locals occupy stack[frame.stack_base..frame.stack_base + frame.locals_count],
|
|
1688
|
+
// operands are above that. Draining from stack_base covers both.
|
|
1689
|
+
self.stack
|
|
1690
|
+
.drain(frame.stack_base..)
|
|
1691
|
+
.for_each(|value| value.drop_with_heap(&mut *self.heap));
|
|
1692
|
+
|
|
1693
|
+
// Track freed memory for locals
|
|
1694
|
+
if frame.locals_count > 0 {
|
|
1695
|
+
let size = frame.locals_count as usize * std::mem::size_of::<Value>();
|
|
1696
|
+
self.heap.tracker_mut().on_free(|| size);
|
|
1697
|
+
}
|
|
1698
|
+
}
|
|
1699
|
+
|
|
1700
|
+
/// Cleans up all frames and stack values for the current task.
|
|
1701
|
+
///
|
|
1702
|
+
/// Used when a task completes or fails and we need to switch to another task.
|
|
1703
|
+
/// Drains the stack with proper `drop_with_heap` for each value (since locals
|
|
1704
|
+
/// are inlined on the stack), then cleans up each frame's cell references.
|
|
1705
|
+
pub(super) fn cleanup_current_task(&mut self) {
|
|
1706
|
+
self.stack.drain(..).drop_with_heap(self.heap);
|
|
1707
|
+
self.frames.clear();
|
|
1708
|
+
}
|
|
1709
|
+
|
|
1710
|
+
/// Runs garbage collection with proper GC roots.
|
|
1711
|
+
///
|
|
1712
|
+
/// GC roots include values in the stack (locals + operands), globals, and exception stack.
|
|
1713
|
+
fn run_gc(&mut self) {
|
|
1714
|
+
// Collect roots from all reachable values
|
|
1715
|
+
let stack_roots = self.stack.iter().filter_map(Value::ref_id);
|
|
1716
|
+
let globals_roots = self.globals.iter().filter_map(Value::ref_id);
|
|
1717
|
+
let exc_roots = self.exception_stack.iter().filter_map(Value::ref_id);
|
|
1718
|
+
|
|
1719
|
+
// Collect all roots into a vec to avoid lifetime issues
|
|
1720
|
+
let roots: Vec<HeapId> = stack_roots.chain(globals_roots).chain(exc_roots).collect();
|
|
1721
|
+
|
|
1722
|
+
self.heap.collect_garbage(roots);
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1725
|
+
/// Returns the current source position for traceback generation.
|
|
1726
|
+
///
|
|
1727
|
+
/// Uses `instruction_ip` which is set at the start of each instruction in the run loop,
|
|
1728
|
+
/// ensuring accurate position tracking even when using cached IP for bytecode fetching.
|
|
1729
|
+
pub(super) fn current_position(&self) -> CodeRange {
|
|
1730
|
+
let frame = self.current_frame();
|
|
1731
|
+
// Use instruction_ip which points to the start of the current instruction
|
|
1732
|
+
// (set at the beginning of each loop iteration in run())
|
|
1733
|
+
frame
|
|
1734
|
+
.code
|
|
1735
|
+
.location_for_offset(self.instruction_ip)
|
|
1736
|
+
.map(crate::bytecode::code::LocationEntry::range)
|
|
1737
|
+
.unwrap_or_default()
|
|
1738
|
+
}
|
|
1739
|
+
|
|
1740
|
+
// ========================================================================
|
|
1741
|
+
// Variable Operations
|
|
1742
|
+
// ========================================================================
|
|
1743
|
+
|
|
1744
|
+
/// Loads a local variable and pushes it onto the stack.
|
|
1745
|
+
///
|
|
1746
|
+
/// For true locals (assigned somewhere in the function), returns `UnboundLocalError`
|
|
1747
|
+
/// if accessed before assignment. For unassigned names (never assigned in this scope),
|
|
1748
|
+
/// returns `NameLookup` to signal that the host should resolve the name.
|
|
1749
|
+
///
|
|
1750
|
+
/// Returns `Ok(None)` for normal loads, `Ok(Some(FrameExit::NameLookup))` when
|
|
1751
|
+
/// the host needs to resolve an unknown name, or `Err` for true unbound locals.
|
|
1752
|
+
fn load_local(&mut self, cached_frame: &CachedFrame<'a>, slot: u16) -> Result<Option<FrameExit>, RunError> {
|
|
1753
|
+
let value = &self.stack[cached_frame.stack_base + slot as usize];
|
|
1754
|
+
|
|
1755
|
+
// Check for undefined value — raise appropriate error based on whether
|
|
1756
|
+
// this is a true local (assigned somewhere) or an undefined reference
|
|
1757
|
+
if matches!(value, Value::Undefined) {
|
|
1758
|
+
let name = cached_frame.code.local_name(slot);
|
|
1759
|
+
if cached_frame.code.is_assigned_local(slot) {
|
|
1760
|
+
// True local accessed before assignment
|
|
1761
|
+
return Err(self.unbound_local_error(slot, name));
|
|
1762
|
+
}
|
|
1763
|
+
// Name doesn't exist in any scope — yield to host for resolution.
|
|
1764
|
+
let name_id = name.expect("LocalUnassigned should always have a name");
|
|
1765
|
+
return Ok(Some(FrameExit::NameLookup {
|
|
1766
|
+
name_id,
|
|
1767
|
+
namespace_slot: slot,
|
|
1768
|
+
is_global: false,
|
|
1769
|
+
}));
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1772
|
+
self.push(value.clone_with_heap(self.heap));
|
|
1773
|
+
Ok(None)
|
|
1774
|
+
}
|
|
1775
|
+
|
|
1776
|
+
/// Loads a local variable in call context, pushing `ExtFunction` for undefined names.
|
|
1777
|
+
///
|
|
1778
|
+
/// Unlike `load_local`, this never yields `NameLookup`. When the variable is undefined
|
|
1779
|
+
/// (a `LocalUnassigned` name), it pushes `Value::ExtFunction(name_id)` so that the
|
|
1780
|
+
/// subsequent `CallFunction` opcode can yield `FunctionCall` instead.
|
|
1781
|
+
fn load_local_callable(&mut self, cached_frame: &CachedFrame<'a>, slot: u16, name_id: StringId) {
|
|
1782
|
+
let value = &self.stack[cached_frame.stack_base + slot as usize];
|
|
1783
|
+
|
|
1784
|
+
if matches!(value, Value::Undefined) {
|
|
1785
|
+
// LocalUnassigned in call context — push ExtFunction for the host to handle.
|
|
1786
|
+
self.ext_function_load_ip = Some(self.instruction_ip);
|
|
1787
|
+
self.push(Value::ExtFunction(name_id));
|
|
1788
|
+
} else {
|
|
1789
|
+
self.push(value.clone_with_heap(self.heap));
|
|
1790
|
+
}
|
|
1791
|
+
}
|
|
1792
|
+
|
|
1793
|
+
/// Loads a global variable in call context, pushing `ExtFunction` for undefined names.
|
|
1794
|
+
///
|
|
1795
|
+
/// Unlike `load_global`, this never yields `NameLookup`. When the variable is undefined,
|
|
1796
|
+
/// it pushes `Value::ExtFunction(name_id)` so that the subsequent `CallFunction` opcode
|
|
1797
|
+
/// can yield `FunctionCall` instead.
|
|
1798
|
+
fn load_global_callable(&mut self, slot: u16, name_id: StringId) {
|
|
1799
|
+
let value = self.globals[slot as usize].clone_with_heap(self.heap);
|
|
1800
|
+
|
|
1801
|
+
if matches!(value, Value::Undefined) {
|
|
1802
|
+
// Save the load instruction's IP so NameError tracebacks point to the name
|
|
1803
|
+
self.ext_function_load_ip = Some(self.instruction_ip);
|
|
1804
|
+
self.push(Value::ExtFunction(name_id));
|
|
1805
|
+
} else {
|
|
1806
|
+
self.push(value);
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
|
|
1810
|
+
/// Creates an UnboundLocalError for a local variable accessed before assignment.
|
|
1811
|
+
fn unbound_local_error(&self, slot: u16, name: Option<StringId>) -> RunError {
|
|
1812
|
+
let name_str = match name {
|
|
1813
|
+
Some(id) => self.interns.get_str(id).to_string(),
|
|
1814
|
+
None => format!("<local {slot}>"),
|
|
1815
|
+
};
|
|
1816
|
+
ExcType::unbound_local_error(&name_str).into()
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1819
|
+
/// Creates a NameError for an undefined global variable.
|
|
1820
|
+
fn name_error(&self, slot: u16, name: Option<StringId>) -> RunError {
|
|
1821
|
+
let name_str = match name {
|
|
1822
|
+
Some(id) => self.interns.get_str(id).to_string(),
|
|
1823
|
+
None => format!("<global {slot}>"),
|
|
1824
|
+
};
|
|
1825
|
+
ExcType::name_error(&name_str).into()
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
/// Pops the top of stack and stores it in a local variable.
|
|
1829
|
+
fn store_local(&mut self, cached_frame: &CachedFrame<'a>, slot: u16) {
|
|
1830
|
+
let value = self.pop();
|
|
1831
|
+
let target = &mut self.stack[cached_frame.stack_base + slot as usize];
|
|
1832
|
+
let old_value = std::mem::replace(target, value);
|
|
1833
|
+
old_value.drop_with_heap(self);
|
|
1834
|
+
}
|
|
1835
|
+
|
|
1836
|
+
/// Deletes a local variable (sets it to Undefined).
|
|
1837
|
+
fn delete_local(&mut self, cached_frame: &CachedFrame<'a>, slot: u16) {
|
|
1838
|
+
let target = &mut self.stack[cached_frame.stack_base + slot as usize];
|
|
1839
|
+
let old_value = std::mem::replace(target, Value::Undefined);
|
|
1840
|
+
old_value.drop_with_heap(self);
|
|
1841
|
+
}
|
|
1842
|
+
|
|
1843
|
+
/// Loads a global variable and pushes it onto the stack.
|
|
1844
|
+
///
|
|
1845
|
+
/// When the variable is undefined, yields `NameLookup` to the host for resolution
|
|
1846
|
+
/// instead of immediately raising `NameError`. This allows the host to provide
|
|
1847
|
+
/// external function bindings lazily.
|
|
1848
|
+
fn load_global(&mut self, slot: u16) -> Result<Option<FrameExit>, RunError> {
|
|
1849
|
+
let value = self.globals[slot as usize].clone_with_heap(self.heap);
|
|
1850
|
+
|
|
1851
|
+
// Check for undefined value — raise appropriate error or yield to host
|
|
1852
|
+
if matches!(value, Value::Undefined) {
|
|
1853
|
+
let name = self.current_frame().code.local_name(slot);
|
|
1854
|
+
|
|
1855
|
+
// If the name is registered as an assigned local (e.g. a module-level
|
|
1856
|
+
// variable or comprehension loop variable), raise UnboundLocalError
|
|
1857
|
+
// immediately rather than yielding NameLookup.
|
|
1858
|
+
if self.current_frame().code.is_assigned_local(slot) {
|
|
1859
|
+
return Err(self.unbound_local_error(slot, name));
|
|
1860
|
+
}
|
|
1861
|
+
|
|
1862
|
+
let Some(name_id) = name else {
|
|
1863
|
+
// No name available — raise NameError directly
|
|
1864
|
+
return Err(self.name_error(slot, None));
|
|
1865
|
+
};
|
|
1866
|
+
Ok(Some(FrameExit::NameLookup {
|
|
1867
|
+
name_id,
|
|
1868
|
+
namespace_slot: slot,
|
|
1869
|
+
is_global: true,
|
|
1870
|
+
}))
|
|
1871
|
+
} else {
|
|
1872
|
+
self.push(value);
|
|
1873
|
+
Ok(None)
|
|
1874
|
+
}
|
|
1875
|
+
}
|
|
1876
|
+
|
|
1877
|
+
/// Pops the top of stack and stores it in a global variable.
|
|
1878
|
+
fn store_global(&mut self, slot: u16) {
|
|
1879
|
+
let value = self.pop();
|
|
1880
|
+
let old_value = std::mem::replace(&mut self.globals[slot as usize], value);
|
|
1881
|
+
old_value.drop_with_heap(self);
|
|
1882
|
+
}
|
|
1883
|
+
|
|
1884
|
+
/// Deletes a global variable (sets it to Undefined).
|
|
1885
|
+
fn delete_global(&mut self, slot: u16) {
|
|
1886
|
+
let old_value = std::mem::replace(&mut self.globals[slot as usize], Value::Undefined);
|
|
1887
|
+
old_value.drop_with_heap(self);
|
|
1888
|
+
}
|
|
1889
|
+
|
|
1890
|
+
/// Loads from a closure cell and pushes onto the stack.
|
|
1891
|
+
///
|
|
1892
|
+
/// The cell `HeapId` is read from the frame's local variable slot on the stack
|
|
1893
|
+
/// (cells are stored as `Value::Ref(cell_id)` at known positions in the locals region).
|
|
1894
|
+
/// Returns a `NameError` if the cell value is undefined (free variable not bound).
|
|
1895
|
+
fn load_cell(&mut self, cached_frame: &CachedFrame<'a>, slot: u16) -> RunResult<()> {
|
|
1896
|
+
let cell_id = self.cell_id_from_local(cached_frame, slot);
|
|
1897
|
+
let value = match self.heap.get(cell_id) {
|
|
1898
|
+
HeapData::Cell(c) => c.0.clone_with_heap(self),
|
|
1899
|
+
_ => panic!("LoadCell: entry is not a Cell"),
|
|
1900
|
+
};
|
|
1901
|
+
|
|
1902
|
+
// Check for undefined value - raise NameError for unbound free variable
|
|
1903
|
+
if matches!(value, Value::Undefined) {
|
|
1904
|
+
value.drop_with_heap(self);
|
|
1905
|
+
let name = cached_frame.code.local_name(slot);
|
|
1906
|
+
return Err(self.free_var_error(name));
|
|
1907
|
+
}
|
|
1908
|
+
|
|
1909
|
+
self.push(value);
|
|
1910
|
+
Ok(())
|
|
1911
|
+
}
|
|
1912
|
+
|
|
1913
|
+
/// Extracts the cell `HeapId` from a local variable slot on the stack.
|
|
1914
|
+
///
|
|
1915
|
+
/// Cell variables are stored as `Value::Ref(cell_id)` in the frame's locals region.
|
|
1916
|
+
fn cell_id_from_local(&self, cached_frame: &CachedFrame<'_>, slot: u16) -> HeapId {
|
|
1917
|
+
match &self.stack[cached_frame.stack_base + slot as usize] {
|
|
1918
|
+
Value::Ref(cell_id) => *cell_id,
|
|
1919
|
+
other => panic!("LoadCell/StoreCell: expected cell reference in local slot {slot}, found {other:?}"),
|
|
1920
|
+
}
|
|
1921
|
+
}
|
|
1922
|
+
|
|
1923
|
+
/// Creates a NameError for an unbound free variable.
|
|
1924
|
+
fn free_var_error(&self, name: Option<StringId>) -> RunError {
|
|
1925
|
+
let name_str = match name {
|
|
1926
|
+
Some(id) => self.interns.get_str(id).to_string(),
|
|
1927
|
+
None => "<free var>".to_string(),
|
|
1928
|
+
};
|
|
1929
|
+
ExcType::name_error_free_variable(&name_str).into()
|
|
1930
|
+
}
|
|
1931
|
+
|
|
1932
|
+
/// Pops the top of stack and stores it in a closure cell.
|
|
1933
|
+
///
|
|
1934
|
+
/// The cell `HeapId` is read from the frame's local variable slot on the stack.
|
|
1935
|
+
fn store_cell(&mut self, cached_frame: &CachedFrame<'_>, slot: u16) {
|
|
1936
|
+
let value = self.pop();
|
|
1937
|
+
// The guard will clean up the new value if we panic, or the old value if we swap
|
|
1938
|
+
let mut guard = HeapGuard::new(value, self);
|
|
1939
|
+
let (value, this) = guard.as_parts_mut();
|
|
1940
|
+
|
|
1941
|
+
let cell_id = this.cell_id_from_local(cached_frame, slot);
|
|
1942
|
+
match this.heap.get_mut(cell_id) {
|
|
1943
|
+
HeapDataMut::Cell(c) => std::mem::swap(&mut c.0, value),
|
|
1944
|
+
_ => panic!("StoreCell: entry is not a Cell"),
|
|
1945
|
+
}
|
|
1946
|
+
}
|
|
1947
|
+
}
|
|
1948
|
+
|
|
1949
|
+
// `heap` is not a public field on VM, so this implementation needs to go here rather than in `heap.rs`
|
|
1950
|
+
impl<T: ResourceTracker> ContainsHeap for VM<'_, '_, T> {
|
|
1951
|
+
type ResourceTracker = T;
|
|
1952
|
+
fn heap(&self) -> &Heap<T> {
|
|
1953
|
+
self.heap
|
|
1954
|
+
}
|
|
1955
|
+
fn heap_mut(&mut self) -> &mut Heap<T> {
|
|
1956
|
+
self.heap
|
|
1957
|
+
}
|
|
1958
|
+
}
|