split mrb_obj_alloc() into type-validation wrapper and allocation
core (mrb_obj_alloc_core). internal callers (mrb_proc_new,
mrb_env_new) use the core directly, skipping 15+ lines of type
validation per allocation.
most impactful for workloads with heavy Proc/Env allocation
(lambda calculus, block-intensive code).
Co-authored-by: Claude <noreply@anthropic.com>
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>
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>
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>
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>
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>
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>
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>
Implements dual-mechanism GC protection and optimizes task lookup
using pointer arithmetic based on PicoRuby reference implementation.
GC Protection:
- Add mrb_gc_register/unregister to protect Task objects
- Implement mrb_task_mark_all() to mark task contexts during GC
- Store proc reference in mrb_task to prevent premature collection
- Integrate marking into gc.c root_scan_phase
Performance Optimizations:
- Add MRB2TASK macro for O(1) context-to-task conversion
- Optimize Task.current: O(n) queue search -> O(1) pointer arithmetic
- Optimize Task.pass: simplify to root context check
- Optimize Task.join: use MRB2TASK for current task lookup
Bug Fixes:
- Fix MRB_TASK_CREATED/STOPPED to use MRB_FIBER_TERMINATED
- Add safety check to prevent execution of terminated tasks
- Initialize callinfo PC to bytecode start in task_init_context
Co-authored-by: Claude <noreply@anthropic.com>
implement Integer#gcd and Integer#lcm methods in mruby-numeric-ext with full
support for both regular integers and bigints.
key changes:
- add mrb_int_gcd euclidean algorithm for regular integer gcd calculation
- implement int_gcd and int_lcm methods with proper type checking and bigint fallback
- add mrb_bint_gcd, mrb_bint_lcm, mrb_bint_abs functions to bigint api
- register gcd and lcm methods with integer class
- add comprehensive test coverage for both regular and bigint cases
Co-authored-by: Claude <noreply@anthropic.com>
To achieve this, the following changes were made:
- Exported `mrb_bint_size`, `mrb_bint_from_bytes`, and `mrb_bint_sign`
functions from `mruby-bigint` to be used in other mrbgems.
- Modified `mruby-random` to use these new functions to handle Bigint
arguments in the `rand` method.
Co-authored-by: Gemini <gemini@google.com>
This commit addresses feedback on the initial Set GC marking implementation.
Changes include:
- Renamed set marking function to `mrb_gc_mark_set` and updated its
return type to `size_t`.
- Introduced an explicit `mrb_gc_free_set` function for Set objects.
- Updated `gc_mark_children` to use the new mark function signature.
- Added an explicit `case MRB_TT_SET:` in `obj_free` to call `mrb_gc_free_set`.
- Adjusted `set_get_khash` in `mruby-set` to work with `MRB_TT_SET` directly,
rather than relying on `mrb_data_get_ptr`.
- Corrected type checks in `set_init_copy` to use `MRB_TT_SET`.
- Updated function prototypes in internal headers and stubs in mrbc.
There was a problem with visibility state from proc that straddles a fiber or is independent.
Therefore, it has been changed to give priority to env objects, if any.
Also, added "separate module" flag to block traversal to a higher level env object.
Note that the "separate module" flag is now set when calling blocks with the `mrb_yield_with_class()` function.
fixed https://github.com/mruby/mruby/issues/6494
Recent changes make mrb_irep_remove_lv() used no longer. Removing this
function would not make any compatibility issue, since it's an internal
function.
However, on C, there is no easy way to pass keyword arguments.
Therefore, when called `Kernel#instance_exec` on C, keyword arguments are converted to positional arguments.
This is a limitation of current mruby.
fixed#6389
As #6359 pointed out, calling const_missing hook from E_XXX_ERROR (that
calls mrb_exc_get_id()) can be an attack vector. Since E_XXX_ERROR is
supposed to be a defined error class, we think that the situation where
it is undefined and the const_missing hook is called should be detected
as an error; fix#6359
This reverts commit ad2e626e7a.
Because of the changes made by #6282, the following code caused a problem.
```ruby
b = proc { break "BAD!" }
p self.tap { b.call }
# (expected) => break from proc-closure (LocalJumpError)
# (after #6282) => "BAD!"
```
I revived the `mrb_callinfo::blk` field to fix this, but it did not overcome the following problem.
```ruby
def m(&b); b = b.clone; GC.start; b.call; end
p m { break "OK!" }
# (expected) => "OK!"
# (revived blk) => break from proc-closure (LocalJumpError)
```