Introduce "object shapes" (hidden classes) that share IV key
layouts across objects with the same instance variable assignment
order. This eliminates per-object key storage overhead.
Memory savings: ~22% heap reduction for object-heavy workloads
(e.g., 150k objects with 2-6 IVs). Per-object: 40->24 bytes
for 2 IVs. Objects exceeding 16 IVs or using
remove_instance_variable fall back to traditional iv_tbl.
Co-authored-by: Claude <noreply@anthropic.com>
Allow users to provide contiguous memory buffers for GC heap pages
via mrb_gc_add_region(). Region pages are carved from user-owned
buffers and never freed by the GC. This is the foundation for
bitmap GC on embedded targets with fragmented RAM.
Co-authored-by: Claude <noreply@anthropic.com>
Pack pool/syms/reps arrays into a single calloc with the irep struct,
reducing 4 allocations per irep to 1. Arrays are ordered by descending
alignment (pool/reps/syms) to eliminate inter-array padding.
Co-authored-by: Claude <noreply@anthropic.com>
GitHub sometimes returns transient 502 errors for valid URLs,
causing false positives in CI markdown link checks.
Co-authored-by: Claude <noreply@anthropic.com>
remove snprintf() call that requires <stdio.h>, which is unavailable
with MRB_NO_STDIO; use a static error message consistent with other
codegen_error() calls.
Fixes#6724.
Co-authored-by: Claude <noreply@anthropic.com>
- array.h: disable embedded arrays when MRB_INT64 makes mrb_value
too large to embed (fixes MRB_ARY_EMBED_LEN_MAX assertion)
- error.h: enable MRB_USE_RBREAK_VALUE_UNION for all 32-bit
no-boxing builds (MRB_USE_FLOAT32 is irrelevant without
word/nan boxing)
- gc.c: restrict RVALUE 8-byte alignment padding to
MRB_WORD_BOXING builds (fixes RVALUE size assertion)
- vm.c: guard direct ary->as.ary access with MRB_ARY_NO_EMBED
Fixes#6722.
Co-authored-by: Claude <noreply@anthropic.com>
Restrict .o -> presym.list_path dependency to internal builds only
(mrbc sub-build). Regular host/cross builds don't need this because
:all => :gensym ordering guarantees presym headers exist before .o
compilation, and compiler .d files track header changes.
The broad dependency caused full recompilation because Rake's
all_prerequisite_tasks checks transitive prerequisites: every .o
transitively depended on every .pi file through presym.list_path.
Fixes#6721.
Co-authored-by: Claude <noreply@anthropic.com>
Consolidate the duplicated print_no increment-and-wrap logic
from dbgcmd_print() and dbgcmd_info_local() into a single
next_print_no() function.
Co-authored-by: Claude <noreply@anthropic.com>
Flatten 4-level nested parsing of list command arguments into
a separate parse_file_line_spec() function.
Co-authored-by: Claude <noreply@anthropic.com>
Combine method and line breakpoint checks into a single
check_breakpoint_hit() helper, simplifying the DBG_RUN case.
Co-authored-by: Claude <noreply@anthropic.com>
Extract find_command_by_word1() and find_command_by_words()
from parse_command(), separating command-table lookup from
tokenization logic.
Co-authored-by: Claude <noreply@anthropic.com>
Both dbgcmd_run() and dbgcmd_quit() defined an exception class
and raised it with identical code. Add a shared static inline
helper in mrdb.h.
Co-authored-by: Claude <noreply@anthropic.com>
The three commands shared identical dispatch logic. Extract a
shared dbgcmd_set_breakpoint() that takes function pointers,
reducing each command to a one-line wrapper.
Co-authored-by: Claude <noreply@anthropic.com>
Extract common breakpoint slot allocation logic from
mrb_debug_set_break_line() and mrb_debug_set_break_method()
into a shared alloc_breakpoint() helper.
Co-authored-by: Claude <noreply@anthropic.com>
Remove redundant local ISSPACE/ISALNUM definitions from
mirb_completion.c and unused ctype.h includes from both
mirb_completion.c and mirb.c. The locale-independent macros
from mruby.h are already available via <mruby.h>.
Co-authored-by: Claude <noreply@anthropic.com>
Replace the strncmp() if-else chain for block-opening and closing
keywords with a data-driven indent_table, matching the existing
dedent_table pattern. Also use mirb_is_word_char() for the word
boundary check.
Co-authored-by: Claude <noreply@anthropic.com>
Replace duplicate line-joining logic in mirb_buffer_delete_back()
and mirb_buffer_delete_forward() with a shared helper that appends
a line's content to the previous line, then removes it.
Co-authored-by: Claude <noreply@anthropic.com>
Replace three identical 7-line blocks that grow the lines array
with a single buffer_ensure_line_cap() helper function, matching
the existing line_ensure_cap() naming pattern.
Co-authored-by: Claude <noreply@anthropic.com>
Replace three separate global contexts (g_readline_ctx,
g_linenoise_ctx, g_editor_ctx) with a single g_ctx and shared
init_completion_ctx() helper. Consolidate the three identical
cleanup functions into mirb_cleanup_completion().
Co-authored-by: Claude <noreply@anthropic.com>
Replace the if-else chain with a data table that encodes each
dedent keyword, its valid delimiters, and whether it can appear
at end of line.
Co-authored-by: Claude <noreply@anthropic.com>
Move is_word_char() to mirb_buffer.h as mirb_is_word_char() static
inline, removing duplicate definitions from mirb_buffer.c and
mirb_highlight.c. Move COLOR_RESET to mirb_highlight.h, removing
the duplicate from mirb_editor.c.
Co-authored-by: Claude <noreply@anthropic.com>
Replace the cleanup() function and duplicated end-of-main cleanup
with a single goto cleanup label. This also fixes a minor resource
leak where cxt was not freed when library loading failed.
Co-authored-by: Claude <noreply@anthropic.com>
Move the Ruby keyword array from static definitions in both
mirb_highlight.c and mirb_completion.c to a single shared
mirb_keywords[] defined in mirb_highlight.c and declared in
mirb_highlight.h.
Co-authored-by: Claude <noreply@anthropic.com>
Extract three helpers (calc_expected_indent, adjust_line_indent,
insert_indent_spaces) to eliminate repeated indent computation and
whitespace adjustment code in perform_dedent, reindent_line,
handle_tab_indent, and the Enter key handler.
Co-authored-by: Claude <noreply@anthropic.com>
pool strings generated by mrbc -C are C string literals inside
static const structs, which reside in ROM. mark them as
IREP_TT_SSTR (static) instead of IREP_TT_STR (dynamic) so the
VM uses mrb_str_new_static() and mrb_intern_static() instead of
mrb_str_new() and mrb_intern(). this avoids unnecessary
malloc+memcpy for string literals longer than the embed threshold,
especially on embedded platforms where mrb_ro_data_p() returns
FALSE.
Co-authored-by: Claude <noreply@anthropic.com>
replace lossy 2-bit truncation with rotation-based encoding for
32-bit + MRB_USE_FLOAT32, matching the technique used for 64-bit
float64. rotl32(bits - ADDEND, 3) maps biased exponents [95, 158]
(actual [-32, +31]) to properly tagged inline values with zero
precision loss. special values (0, Inf, NaN) use sentinel constants;
out-of-range floats fall back to heap-allocated RFloat.
also fix a pre-existing alignment issue: RVALUE was 20 bytes on
32-bit, causing 4-byte-aligned objects to be misidentified as
immediates by word boxing (WORDBOX_IMMEDIATE_MASK=0x07 requires
8-byte alignment). pad RVALUE to 24 bytes on 32-bit + float32.
Co-authored-by: Claude <noreply@anthropic.com>
rand_range_float() incorrectly added +1.0 to span for inclusive
ranges, logic copied from integer range handling. For float ranges,
the span should simply be end-begin without adjustment.
Fixes#6720.
Co-authored-by: Claude <noreply@anthropic.com>
ROM method tables used static mrb_mt_tbl variables shared
across the process. The next pointer in each wrapper was
mutated by mrb_mt_init_rom(), causing cross-state
contamination when multiple mrb_state instances existed.
Allocate mrb_mt_tbl wrappers per-state via mrb_malloc().
The const mrb_mt_entry[] arrays remain static and shared.
Wrappers are tracked in mrb->rom_mt and freed at mrb_close().
Remove MRB_MT_ROM_TAB macro; add MRB_MT_INIT_ROM macro that
auto-computes size and calls the new mrb_mt_init_rom().
Co-authored-by: Claude <noreply@anthropic.com>
The ROM table types (mrb_mt_entry, mrb_mt_tbl) and macros
(MRB_MT_ENTRY, MRB_MT_ROM_TAB, etc.) are used by 34 files
across core and gems -- they are part of the public method
registration API, not internal implementation details.
Move them to class.h where the rest of the method table API
lives, eliminating the #ifdef MRUBY_CLASS_H guard that was
needed in internal.h.
Co-authored-by: Claude <noreply@anthropic.com>
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>
Move conditional mrb_define_method_id() calls into ROM entry
arrays using #ifdef guards. With linear search, sizeof in
MRB_MT_ROM_TAB() adjusts automatically after preprocessing.
Cross-class ROM tables (methods a gem defines on a class it does
not own) are reverted to mrb_define_method_id(). Multiple gems
should not add ROM table layers to the same class; each layer
costs a 16-byte mrb_mt_tbl struct in RAM and deepens the lookup
chain. Use mrb_define_method_id() for cross-class methods.
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>
int_divmod passed an integer mrb_value directly to flo_divmod, which
used mrb_float() to extract the value. In word boxing mode, this caused
a misaligned pointer dereference. Use mrb_ensure_float_type() to safely
convert the integer to float before passing to flo_divmod.
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>
Left-shifting a negative int64_t is undefined behavior in C.
Cast to uint64_t before the shift to produce the same bit pattern
using well-defined unsigned arithmetic.
Co-authored-by: Claude <noreply@anthropic.com>