Replace check_method_noarg() with check_argument_count() that validates
min <= argc <= max using the full aspec stored in mrb_method_t.flags.
This catches ArgumentError earlier at dispatch time, before entering
the C function.
The old check only handled the special case of aspec==0 (NOARG).
The new check extracts REQ, OPT, REST, POST, KEY, and KDICT from
the aspec and validates accordingly. Keyword hash is counted as
a positional arg only when the method doesn't accept keywords.
Remove MRB_METHOD_NOARG_P macro from proc.h (subsumed by aspec check).
Fix 15 incorrect aspec declarations across the codebase that were
exposed by the stricter enforcement.
Co-authored-by: Claude <noreply@anthropic.com>
Move MRB_METHOD_FUNC_FL to bit 24 and visibility flags to
bits 25-26 so that MRB_ARGS_*() values (bits 0-23) can be
stored directly without shifting. This makes MRB_MT_PRIVATE
and MRB_METHOD_PRIVATE_FL the same value, eliminating the
dual-constant confusion and simplifying the MRB_MT_ENTRY()
macro to a single OR operation.
Co-authored-by: Claude <noreply@anthropic.com>
The NOARG flag (bit 2) is now redundant since the full aspec is
stored in bits 4+ of the flags field. Replace the dedicated bit
check with aspec==0 check. Store aspec in define_method_id() for
dynamically defined methods too.
Co-authored-by: Claude <noreply@anthropic.com>
Restore MRB_ARGS_* argument specs and ISO section comments to all
709 ROM method table entries. The aspec is encoded in bits 4-27 of
the flags field; MRB_MT_NOARG is now auto-derived from aspec==0.
Add MRB_MT_ENTRY_PRIVATE() macro for private methods (53 entries)
and MRB_MT_ASPEC() accessor for extracting aspec from flags.
Co-authored-by: Claude <noreply@anthropic.com>
Since ROM table entries are always C functions, have the
MRB_MT_ENTRY() macro set MRB_MT_FUNC automatically. This
simplifies entry definitions across all 32 source files.
Co-authored-by: Claude <noreply@anthropic.com>
Replace binary search with linear scan in mt_get(), mt_put(),
mt_del(), mt_chain_has(), and mrb_mt_foreach(). The method cache
makes repeated lookups O(1), so linear scan on cache misses is
acceptable.
This removes the sorting requirement, allowing ROM entry arrays
to be declared const. On embedded systems, const static data
resides in flash/ROM instead of RAM, saving ~8.4KB for ~700
method entries on 32-bit MCUs.
Co-authored-by: Claude <noreply@anthropic.com>
Add a dedicated uint32_t flags field to mrb_mt_entry instead of
packing flags into the lower bits of mrb_sym via MRB_MT_KEY().
The key field now stores the pure symbol ID with no shift.
On 64-bit, the flags field fills the alignment gap after mrb_sym,
so entry size remains 16 bytes (zero overhead). On 32-bit, entry
size grows from 8 to 12 bytes.
This eliminates the risk of symbol ID overflow from the 4-bit
shift, and the flags field can later store aspec (MRB_ARGS_*)
information that was previously discarded.
Co-authored-by: Claude <noreply@anthropic.com>
Replace the parallel-arrays (struct-of-arrays) ROM method table
layout with an array-of-structs layout where each mrb_mt_entry
bundles its function pointer and symbol key together.
New MRB_MT_ENTRY() and MRB_MT_ROM_TAB() macros simplify ROM table
definitions from a 3-part pattern (SIZE define + anonymous struct +
mrb_mt_tbl) to a 2-part pattern (entries array + mrb_mt_tbl).
Internal mt_* functions in class.c are simplified: single memmove/
memcpy operations replace paired key+value operations.
Co-authored-by: Claude <noreply@anthropic.com>
When mrb_mt_init_rom() is called on a class that already has a
mutable method table (from prior mrb_define_method_id() calls),
the mutable top layer is now frozen in place instead of being
left as a writable layer that wastes RAM on embedded systems.
The frozen bit (bit 29 of alloc field) marks heap-allocated
method table layers as temporarily immutable. Unlike the
readonly bit (bit 30, for true ROM), frozen layers are
automatically unfrozen when methods are later added via
mrb_define_method_raw() or removed via mrb_remove_method().
This preserves the c->mt pointer, which is critical because
iclasses (from module inclusion) hold a copy of it.
Co-authored-by: Claude <noreply@anthropic.com>
`&nil` is recently introduced in CRuby to explicitly declare that
a method does not accept a block. When a block is passed,
ArgumentError "no block accepted" is raised. This is analogous to
`**nil` for keyword arguments.
The noblock flag is encoded in bit 23 of OP_ENTER's aspec operand
(24=n1:m5:o5:r1:m5:k5:d1:b1), avoiding the need for a new opcode.
Co-authored-by: Claude <noreply@anthropic.com>
Previously, removing a ROM method required flattening all chain layers
into a single mutable table. This was O(n) and allocated RAM for all
previously-ROM methods.
Use a tombstone marker (MT_FUNC flag with func=NULL) instead. The
mt_get() lookup treats this as "not found" and stops the chain walk,
hiding the ROM entry while allowing superclass lookup.
Co-authored-by: Claude <noreply@anthropic.com>
Since disable.h was removed, enable.h was the only remaining
dispatch target from presym.h. Inline its contents into presym.h
and delete the now-redundant enable.h. Also define _2 backward
compatibility macros in terms of the standard macros.
Co-authored-by: Claude <noreply@anthropic.com>
The _2 suffix variants accept an mrb_state* parameter that is
always ignored with presym enabled. Replace all uses in codegen.c,
parse.y, and y.tab.c with the standard macros. The _2 macro
definitions are kept in presym headers for backward compatibility.
Co-authored-by: Claude <noreply@anthropic.com>
Move String's 46 method definitions from runtime
mrb_define_method_id() calls to a static ROM method table
sorted at init time. mrb_mt_init_rom() sorts the parallel
vals/keys arrays by presym ID and sets the readonly flag.
Expose mt_tbl and related types in internal.h so ROM tables
can be defined in individual source files.
When MRB_NO_PRESYM is defined, falls back to traditional
runtime method registration.
Co-authored-by: Claude <noreply@anthropic.com>
Replace lossy 2-bit truncation with rotation-based encoding for
64-bit word boxing with float64. The new scheme uses
rotl64(float_bits - ADDEND, 3) to embed floats inline with full
52-bit mantissa precision. Floats with exponents outside [-255,+256]
(0.0, NaN, Inf, very small/large values) fall back to heap-allocated
RFloat.
Co-authored-by: Claude <noreply@anthropic.com>
remove per-object gcnext pointer from MRB_OBJECT_HEADER, saving one
word (8 bytes on 64-bit) per object slot. the gray list for tri-color
marking is replaced by a fixed-size stack (MRB_GRAY_STACK_SIZE=1024)
in mrb_gc. when the stack overflows, a linear heap rescan recovers
gray objects.
object slot size: 48 -> 40 bytes (16.7% reduction on 64-bit).
benchmarks show up to 12% RSS reduction on object-heavy workloads
with neutral performance impact.
Co-authored-by: Claude <noreply@anthropic.com>
Replace per-symbol mrb_malloc() with a chunk-based string pool that
batches allocations into 4KB chunks. This reduces malloc call count
by ~12x (e.g. 909 vs 10,887 for 10k dynamic symbols) and eliminates
per-allocation malloc metadata overhead (~16 bytes/symbol).
Pool allocations are rounded up to even size to preserve LSB pointer
tagging used for literal detection.
Co-authored-by: Claude <noreply@anthropic.com>
These opcodes use BB format instead of BBB, saving 1 byte per call.
In the standard library, this saves ~790 bytes (568 SEND0 + 222 SSEND0).
Co-authored-by: Claude <noreply@anthropic.com>
Add single-byte opcodes for returning true/false directly, completing
the set of literal return opcodes (RETSELF, RETNIL, RETTRUE, RETFALSE).
Codegen applies peephole optimization to fuse LOADTRUE/LOADFALSE + RETURN.
Co-authored-by: Claude <noreply@anthropic.com>
Rename boolean load opcodes for consistency with LOADNIL/LOADSELF.
Backward compatibility aliases are provided in opcode.h.
Co-authored-by: Claude <noreply@anthropic.com>
Add a new opcode that returns nil without requiring LOADNIL + RETURN.
This avoids loading nil into a register by setting the return value (v)
directly. The implementation uses a separate label (L_RETURN_NIL) to
bypass v = regs[a], preserving self in regs[0] for ensure blocks.
Codegen applies peephole optimization to fuse LOADNIL + RETURN -> RETNIL.
Co-authored-by: Claude <noreply@anthropic.com>
Bypass method dispatch when calling blocks via yield. The new OP_BLKCALL
instruction directly invokes the proc without looking up Proc#call,
resulting in 13-17% faster yield performance.
Co-authored-by: Claude <noreply@anthropic.com>
TDEF fuses TCLASS+METHOD+DEF for normal method definitions.
SDEF fuses SCLASS+METHOD+DEF for singleton method definitions.
Saves 4 bytes per method definition (8 bytes -> 4 bytes).
Falls back to unfused instructions if irep index exceeds 255.
Co-authored-by: Claude <noreply@anthropic.com>
Fuses MOVE+LOADI_0+GETIDX pattern into single instruction.
Saves 4 bytes per arr[0] access (7 bytes -> 3 bytes).
Co-authored-by: Claude <noreply@anthropic.com>
fuse MOVE+ADDI+MOVE and MOVE+SUBI+MOVE patterns into single instructions.
ADDILV/SUBILV add/subtract an immediate to a local variable in-place.
BBB format: a=local, b=working space for method call, c=immediate.
saves 5 bytes per instance (9->4 bytes), 40 occurrences in stdlib.
Co-authored-by: Claude <noreply@anthropic.com>
Change OP_MATCHERR from Z format (unconditional) to B format
(conditional on register). This allows fusing JMPIF + MATCHERR
sequence into a single MATCHERR instruction for simple patterns.
Before: JMPIF R2 target (4 bytes) + MATCHERR (1 byte) = 5 bytes
After: MATCHERR R2 (2 bytes)
Saves 3 bytes per pattern match with raise_on_fail.
Co-authored-by: Claude <noreply@anthropic.com>
Replace 4-instruction sequence (GETCONST + STRING + SEND + RAISEIF)
with single OP_MATCHERR instruction that raises NoMatchingPatternError
with "pattern not matched" message.
Bump RITE binary format version from 0300 to 0400 due to opcode
number shift.
Co-authored-by: Claude <noreply@anthropic.com>
- Add mrb_likely/mrb_unlikely macros to common.h for branch prediction
- Optimize OP_GETIDX array fast path:
- Cache RArray pointer to avoid repeated RARRAY() calls
- Single ARY_EMBED_P check instead of two (via RARRAY_LEN + RARRAY_PTR)
- Use unsigned comparison for bounds check
- Add branch prediction hints for common cases
- Convert switch statement to if-else chain for better branch prediction
Benchmark shows ~3% improvement for array read operations.
Co-authored-by: Claude <noreply@anthropic.com>
expose the previously internal outer_class() function as a public API
for mrbgems to retrieve the enclosing class/module of a given class.
closes#6705.
Co-authored-by: Claude <noreply@anthropic.com>
Document that this header is for mruby core internal use only and
should not be included in user code or mrbgems. When MRB_USE_CXX_EXCEPTION
is defined, C source files including this header fail to compile.
Add example showing mrb_protect_error() as the recommended alternative.
Co-authored-by: Claude <noreply@anthropic.com>
With this PR, I can remove the original task.c in picoruby/picoruby and future development will be much easier.
## Add
### General
- C API functions exported with MRB_API for external integration:
- mrb_execute_proc_synchronously() for synchronous proc execution
- Task control APIs (mrb_create_task, mrb_suspend_task, mrb_resume_task, mrb_terminate_task, mrb_stop_task, mrb_task_value, mrb_task_status)
- Task context management APIs for picoruby-sandbox (mrb_task_init_context, mrb_task_reset_context, mrb_task_proc_set)
- Task.tick class method to get current tick count
- Comprehensive C API documentation with WASM integration examples
### For PicoRuby.wasm
- WASM/Emscripten support: Disable SIGALRM timer when __EMSCRIPTEN__ is defined, as JavaScript handles tick calls via setInterval
- Scheduler lock mechanism to prevent asynchronous task operations during synchronous execution (scheduler_lock counter in mrb_task_state)
- mrb_task_run_once() for single-step execution (event loop integration)
## Fix
### task.c
- Replace MRB_FIBER_TERMINATED with MRB_TASK_STOPPED just for clarity
- Allow suspending DORMANT and WAITING tasks in mrb_task_suspend (See comment in the source)
- Task context initialization by removing dummy callinfo push/pop
*NOTE*
With the dummy callinfo code that I deleted, IRB in PicoRuby ended SEGV.
If that code is mandatory, we need to discuss how to solve my problem.
### vm.c
- Handle MRB_TASK_CREATED status in VM's NORMAL_RETURN phase to properly stop tasks
----
These changes are necessary to make PicoRuby work.
Nevertheless, even with this patch, MicroRuby for Raspberry Pi Pico 2 is still unstable.
I would like to merge this PR anyway to make development easier by involving the PicoRuby community.
rename mrb_alloca() to mrb_temp_alloc() for clearer naming - the new name
better describes its purpose as GC-managed temporary allocation. keep
mrb_alloca() as a macro alias for backward compatibility.
apply mrb_temp_alloc() to fix potential memory leaks in:
- mruby-strftime: if mrb_str_cat() raises, allocated buffers now cleaned by GC
- mruby-io File.readlink: if mrb_str_new() raises, buffer now cleaned by GC
Co-authored-by: Claude <noreply@anthropic.com>
Move casecmp_p from mruby-string-ext and mruby-encoding to core as
mrb_strcasecmp_p (predicate function returning mrb_bool). Add
MRB_STR_CASECMP_P macro to internal.h for comparing mrb_value strings
with literal strings.
This eliminates code duplication and avoids static function name
collision for future amalgamation support.
Co-authored-by: Claude <noreply@anthropic.com>
mrb_method_cache_clear() was called unconditionally from class.c and
state.c, but the function definition was guarded by MRB_NO_METHOD_CACHE.
This caused linker errors when building with MRB_NO_METHOD_CACHE defined.
Add empty macro definition when MRB_NO_METHOD_CACHE is defined, matching
the existing pattern used for mrb_mc_clear_by_class().
Co-authored-by: Claude <noreply@anthropic.com>
when running scripts via mruby -e or file, return values are unused.
this adds a no_return_value flag to skip generating unnecessary code.
for parallel assignment like a,b = 1,2:
- before: 18 bytes, 5 registers, creates temporary array
- after: 5 bytes, 3 registers, direct register assignment, no RETURN
the flag is set only for the main program, not for libraries loaded
with -r option. eval() and mirb continue returning values correctly.
Co-authored-by: Claude <noreply@anthropic.com>
Extract duplicated UTF-8 codepoint-to-bytes encoding into a shared
function in src/string.c. Update all gems to use it:
- mruby-sprintf: %c specifier
- mruby-io: putc
- mruby-string-ext: Integer#chr
- mruby-pack: pack("U")
- mruby-compiler: Unicode escapes in parser
Also use existing mrb_utf8len() in io.c for character length detection.
Co-authored-by: Claude <noreply@anthropic.com>
Add support for brace-less hash patterns at top level of case/in.
`in a: x, b: y` is now equivalent to `in {a: x, b: y}`.
`in a:, b:` shorthand now works with newlines (CRuby compatible).
Changes:
- Add EXPR_VALUE to IS_LABEL_POSSIBLE() to recognize labels after `in`
- Add brace-less hash pattern rules to p_expr
- Change p_hash_elem to use p_as instead of p_expr to avoid recursion
- Add in_kwarg flag to parser state for pattern matching context
- Set in_kwarg in lexer when keyword_in is returned
- Use EXPR_ARG after tLABEL_TAG when in_kwarg is set (makes newlines significant)
Co-authored-by: Claude <noreply@anthropic.com>
since all current uses check for failure (!MRB_OPEN_SUCCESS), add
MRB_OPEN_FAILURE() as the primary macro for better readability. define
MRB_OPEN_SUCCESS() in terms of MRB_OPEN_FAILURE() to avoid duplication
and optimize the common case. update all usage sites to use the clearer
MRB_OPEN_FAILURE() form.
Co-authored-by: Claude <noreply@anthropic.com>
changed mrb_open() and mrb_open_core() to return mrb_state with mrb->exc
set (instead of NULL) when initialization fails. this allows callers to
programmatically inspect error details, which is essential for embedded
systems without stderr. return NULL only for true allocation failure.
added MRB_OPEN_SUCCESS(mrb) macro to check initialization success, since
mrb != NULL no longer guarantees success. updated all binary tools
(mruby, mirb, mrdb, mrbtest) to use new pattern: check MRB_OPEN_SUCCESS,
print exception details via mrb_print_error if available, then mrb_close.
mrb_core_init_protect now preserves exception in mrb->exc instead of
printing and clearing it, giving caller control over error handling.
breaking change: callers must use MRB_OPEN_SUCCESS(mrb) or check both
mrb != NULL && mrb->exc == NULL. old NULL-only checks will miss
initialization failures.
Co-authored-by: Claude <noreply@anthropic.com>
during eql? callbacks, array modifications can cause elements in khash to
be freed by GC, leading to use-after-free. create temporary shared copies
of arrays before populating khash to protect elements during callbacks.
Co-authored-by: Claude <noreply@anthropic.com>
when overriding struct#==, array#==, or hash#== with super, the recursion
detection incorrectly treated the super call as a circular reference. this
was caused by commit 5ca2d442 which added recursion detection.
the fix introduces mrb_recursive_func_p that starts from ci[-2] instead of
ci[-1], skipping the immediate parent frame which may be a ruby override
calling super. equality methods (==, eql?) now use this function, while
inspect methods keep using mrb_recursive_method_p for immediate circular
reference detection.
Co-authored-by: Claude <noreply@anthropic.com>
The primary reason is to fix an issue that occurs when an element is removed from the khash data during the `KHASH_FOREACH()` loop.
If the value of `kh_end()` becomes smaller than `k` during the loop, it will repeat a meaningless internal loop until an integer overflow occurs.
__builtin_setjmp/longjmp are x86/x86_64 specific gcc intrinsics
and not supported on arm64. windows arm64 with msys2 clangarm64
now correctly falls through to standard setjmp/longjmp.
Co-authored-by: Claude <noreply@anthropic.com>
remove MRB_TASK_CREATED and MRB_TASK_STOPPED from mrb_fiber_state enum
and define them as aliases to MRB_FIBER_CREATED and MRB_FIBER_TERMINATED.
this makes the relationship between tasks and fibers clearer and avoids
artificially extending the enum with semantically equivalent values.
Co-authored-by: Claude <noreply@anthropic.com>
Implement main task wrapper following Fiber's pattern, where root context
is represented by a special task object. This matches PicoRuby behavior
where Task.current always returns a task object, even from root context.
The main task is lazy-allocated on first Task.current call from root,
stored in mrb->task.main_task, and has name "main", status RUNNING,
priority 0. It wraps the root context without allocating a separate
execution context.
Co-authored-by: Claude <noreply@anthropic.com>