Commit Graph

883 Commits

Author SHA1 Message Date
Yukihiro "Matz" Matsumoto 735b94535c numeric.c: raise TypeError for non-Integer bitwise operands
Integer#&, #| and #^ read the right-hand operand with mrb_integer()
without checking its type. For a Float (or any non-Integer) this reads an
unrelated union field and returns a garbage value instead of raising, as
CRuby does. Raise TypeError via mrb_int_noconv() when the operand is not
an Integer. Bigint operands are still handled before this point, and the
shift operators keep coercing their width as before.

Co-authored-by: Claude <noreply@anthropic.com>
2026-06-05 11:23:43 +09:00
Yukihiro "Matz" Matsumoto f5ca906852 mruby-compiler: fix bare nil? in if/unless to use self as receiver
The if/unless nil? optimization called codegen() on the call node's
receiver, but a bare `nil?` is parsed as an FCALL whose receiver is
NULL. codegen(NULL) emits OP_LOADNIL, so the JMPNIL was testing the
literal nil instead of self, making `if nil?` always behave as
`if nil.nil?` (always true) and `unless nil?` always skip its body.
Load self when the receiver is implicit. Fixes #6874.

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-30 07:39:45 +09:00
Yukihiro "Matz" Matsumoto 36dd9eab88 vformat.rb: fix format/arg type mismatch in %!d test case
The test pushed mrb_int via vf.i but the format read int via %!d. On
x86_64/aarch64 the va_arg slots overlap so reading the low 32 bits of
the pushed int64 returned the right value, but on strict-alignment
ABIs (MIPS o32) va_arg(ap, int) reads the alignment padding and the
format prints 0 instead of the value.

Make the format match the helper: vf.i pairs with %!i (reads mrb_int),
matching the surrounding lines 44-50 convention and the "inspect
mrb_int" label.

Reported by vobloeb in #6857.

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-24 10:31:14 +09:00
Yukihiro "Matz" Matsumoto cddaec7264 mruby-compiler: bail out of array-literal pattern match opt on splat
The optimization that skips `deconstruct` and the size check when the
case/in value is an array literal trusted node count, ignoring splat.
An element like `*a` expands at runtime, so [*a] was treated as length
1 and matched only patterns of that length.

Fixes #6854.

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-23 09:58:30 +09:00
Yukihiro "Matz" Matsumoto 17d124b00d array.c (mrb_ary_splice): re-modify a after self-aset ary_dup
`a[range] = a` on a long-enough array tripped a heap-buffer-overflow
in value_move(). mrb_ary_splice's self-aset branch calls ary_dup(a)
to get an independent copy of the source elements, but ary_dup ->
ary_replace converts the source to shared as a copy-on-write
optimization when the length exceeds ARY_REPLACE_SHARED_MIN. After
that, a->as.heap.aux is reinterpreted as `shared` (the union member)
and ARY_CAPA(a) reads from the shared pointer's bits rather than
the real capacity. The expand-capa check below then silently mis-
sizes and value_move walks past the buffer.

Re-modify `a` immediately after ary_dup to un-share before the in-
place mutation. The buffer reads through `argv` (which now points
into the dup's storage) stay valid because ary_modify on a multi-
reference shared array allocates a fresh buffer for `a` and leaves
the original buffer owned by the dup.

Found via clusterfuzz mruby_fuzzer testcase 6525563811725312;
regression test covers a[3, 2] = a on a 31-element array (above
the ARY_REPLACE_SHARED_MIN=20 threshold).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-20 14:27:22 +09:00
Yukihiro "Matz" Matsumoto 40264c9aad mruby-compiler: accept double-quoted strings in case/in patterns
The p_value rule only accepted bare tSTRING tokens, which the lexer
emits for single-quoted strings.  Double-quoted strings emit
tSTRING_BEG ... tSTRING (or with interpolation, tSTRING_BEG
string_rep tSTRING), so

  case "hello"
  in "hello"
    :match
  end

raised "syntax error, unexpected string literal" at the `"` after
`in`.  Use the existing `string` non-terminal instead of bare
tSTRING, which also enables alternation (`"a" | "b"`), interpolation
(`"hel#{x}"`), and concatenation by juxtaposition in patterns.

close #6830

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-16 22:01:52 +09:00
Yukihiro "Matz" Matsumoto 403b75fbeb fp_uscale.c: clamp parser underflow guard to POW10_MIN
mrb_read_float's underflow short-circuit used `final_p < -342 - nd`,
which for nd > 1 can be below POW10_MIN (-343). That let
parse_decimal call prescale() with a final_p below POW10_MIN, causing
an out-of-bounds read of pow10_tab. Tighten the guard to
`final_p < POW10_MIN`; values below that threshold cannot be
represented as a non-zero double for any mantissa within the parser's
19-digit cap.

Reported by OSS-Fuzz (testcase 6097379597287424).

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-13 12:02:30 +09:00
Yukihiro "Matz" Matsumoto 16151a0daa proc.c: mark Proc#dup / Proc#clone copies as orphan blocks
A copied Proc now always carries `MRB_PROC_ORPHAN`, so calling a
`dup`'d block that contains `break` or `return` raises
`LocalJumpError` even while the original yielding method is still on
the stack.

This is stricter than CRuby — which only marks the copy orphan once
the original yielding method returns — but matches mruby's
memory-first design: tracking the original via a back pointer in
RProc would also enlarge the GC mark set. dearblue's option (1) in
the linked issue, accepted for the simpler RProc layout.

Document the divergence in `doc/limitations.md` and add a regression
test in `test/t/proc.rb`.

close #6345

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-11 15:06:24 +09:00
Yukihiro "Matz" Matsumoto b4f6450509 mruby-compiler: do not flip + and - opcode for negative literal
The peephole in gen_addsub() rewrote `q + -n` into OP_SUBI n (and
`q - -n` into OP_ADDI n) by negating n. For numeric receivers this is
equivalent, but for receivers overriding + or - the runtime fallback
dispatches the flipped method, losing the original operator. Restrict
the fold to non-negative immediates; negative falls through to the
normal OP_ADD/OP_SUB path with LOADI of the literal value.

close #2557
ref #2579

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-09 19:47:27 +09:00
Pete Kinnecom ebfdf8ae43 Fix Class inherited hook ordering
In CRuby the inherited method is invoked before Class.new yields to the
block.

An "already initialized error" exception is also removed since the
inherited method is now invoked before initialization and can set
instance variables on a class.
2026-05-07 15:51:30 +00:00
Yukihiro "Matz" Matsumoto 4b866a84da gc.c: add step_limit and malloc_threshold for GC tuning
GC.step_limit caps the per-step work in incremental GC,
enabling more predictable pause times for real-time use.
GC.malloc_threshold triggers GC based on allocation bytes,
addressing memory pressure from large buffers.
Both default to 0 (disabled), preserving existing behavior.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 19:25:23 +09:00
Yukihiro "Matz" Matsumoto 01ce2f8c71 Merge pull request #6747 from katafrakt/handle-hash-default-arg 2026-03-20 16:44:27 +09:00
Chris Hasiński c0b1e87c09 Fix attr_reader-generated methods accepting extra arguments
attr_reader-generated getter methods silently ignored any arguments
passed to them. CRuby raises ArgumentError in this case.

Add mrb_get_args(mrb, "") to enforce zero arguments, matching CRuby.
2026-03-19 23:12:14 +01:00
Paweł Świątkowski 13d9d770fc Correctly handle empty hash as default named argument
```
def func(arg: {})
  p arg
end
```

This used to work in earlier mruby versions, but broke somewhere recently.
2026-03-18 08:53:20 +01:00
Yukihiro "Matz" Matsumoto d8de35b635 codegen.c: raise NoMatchingPatternError in case/in without else
case/in without else clause now raises NoMatchingPatternError
when no pattern matches, matching CRuby behavior. Fixes #6741.

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-12 14:28:17 +09:00
Yukihiro "Matz" Matsumoto a1ee420aea test/t/syntax.rb: add test for &nil in formal parameters
Co-authored-by: Claude <noreply@anthropic.com>
2026-02-19 17:26:04 +09:00
Yukihiro "Matz" Matsumoto 9b66ec82c4 mruby-compiler: fix hash pattern matching for CRuby compatibility
Add key existence check using key?() before value access, so that
missing keys correctly fail to match (e.g. {b: 1} no longer matches
{a: nil} pattern). Implement **nil and empty {} exact match via
hash.size == num_keys check. Fix **rest to properly exclude matched
keys using dup + __delete instead of copying the entire hash.

Co-authored-by: Claude <noreply@anthropic.com>
2026-02-16 17:33:41 +09:00
dearblue d94ec9786e Fixes identity for proc object
Previously, the identity of the proc object was verified solely based on the identity of irep.
This patch makes the behavior consistent with CRuby.

The reason I noticed this issue was that when adding multiple proc objects with the same irep to a set object, only one was added.

```ruby
p Set.new(Array.new(3) { -> {} }).size
# => 3 (Ruby 4.0)
# => 1 (mruby without this patch)
```

If the block scope is the same, there is only one in CRuby as well.
However, in CRuby, the result of `Proc#to_s` is not affected by the block scope, so it has been changed to be based on the object's address.
The reason no test for `Proc#to_s` was added is that I couldn't determine whether it should be based on `Proc#hash` or the object's address.

```ruby
b = []
t = 3
while t > 0
  b << -> {}
  t -= 1
end

p Set.new(b).size
# => 1 (Ruby 4.0 and mruby)

p b[0].to_s == b[1].to_s
# => false (Ruby 4.0)
# => true (mruby without this patch)
```
2026-02-07 16:47:39 +09:00
Yukihiro "Matz" Matsumoto 32a27216bb test: add parentheses to method calls on assignment RHS
Preparation for future grammar simplification that may
require parentheses for method calls with arguments on
the right-hand side of assignments.

Co-authored-by: Claude <noreply@anthropic.com>
2026-01-27 14:58:14 +09:00
Yukihiro "Matz" Matsumoto 56d4dab6ed test/syntax.rb: add find pattern tests
Co-authored-by: Claude <noreply@anthropic.com>
2025-12-18 16:27:27 +09:00
Yukihiro "Matz" Matsumoto 42f5215466 test/syntax.rb: add pattern matching test cases
add comprehensive tests for pattern matching features:
- basic case/in with literals and variables
- array patterns with rest and nested structures
- hash patterns with shorthand and rest
- guard clauses (if/unless)
- alternative patterns (|)
- pin operator (^)
- as pattern (=>)
- one-line pattern matching (in and =>)
- NoMatchingPatternError handling

Co-authored-by: Claude <noreply@anthropic.com>
2025-12-18 16:27:26 +09:00
Yukihiro "Matz" Matsumoto fa8f66ed1b enum.rb: fix Array#hash infinite loop with self-referencing enumerables
Modify `Enumerable#hash` to use `__method_recursive?(:hash)` for recursion
detection, preventing infinite loops when hashing self-referencing enumerables.
Add a test case to verify the fix.

Co-authored-by: Gemini <gemini@google.com>
2025-08-14 10:53:04 +09:00
Yukihiro "Matz" Matsumoto 451f67e0ae test/syntax.rb: add at-least-once loop tests
Co-authored-by: Claude <noreply@anthropic.com>
2025-07-11 10:09:34 +09:00
John Bampton b4a1e98f2e Rubocop: fix target Ruby version; add two more cops; fix lint error
https://docs.rubocop.org/rubocop/cops_layout.html#layoutassignmentindentation

https://docs.rubocop.org/rubocop/cops_layout.html#layoutblockendnewline
2025-06-25 18:02:53 +10:00
Yukihiro "Matz" Matsumoto ebd9f29b38 The method #initialize_copy should be private, no direct call
Fixed test/t/array.rb and test/t/string.rb in standard tests.
And mrbgems/mruby-struct/test/struct.rb as well.
2025-06-14 13:38:22 +09:00
Paweł Świątkowski bdcd496bc2 Fix calling extended callback
The callback of extending module should be called, not of a singleton
class of an extended object.
2025-05-27 23:03:10 +02:00
dearblue 4417321d1c Add more test code for method visibility
This test corresponds to the first issue of #6494.
Complement to #6512.
2025-05-18 18:50:02 +09:00
Yukihiro "Matz" Matsumoto 21d6bfb6dd test/kernel.rb: disable some Kernel#p tests; ref #6524 2025-05-08 16:21:01 +09:00
Yukihiro "Matz" Matsumoto 5b10fdf684 Merge pull request #6524 from hasumikin/fix/Kernel#p 2025-05-08 16:01:35 +09:00
Yukihiro "Matz" Matsumoto aec8d0c58b Merge branch 'visibility' of github.com:dearblue/mruby into dearblue-visibility 2025-05-07 15:37:08 +09:00
Yukihiro "Matz" Matsumoto a87b4b6bc3 test/exception.rb: update for new Exception#inspect 2025-05-07 12:19:05 +09:00
HASUMI Hitoshi fdfd67ff40 Fix Kernel#p when no argument
## Expected
```ruby
p
=> nil
```

## Actual
```ruby
p
=> []
```
2025-05-04 10:17:37 +09:00
Yukihiro "Matz" Matsumoto 6c72f8b378 class.c (extend_object): remove method; implement Kernel#extend in C 2025-04-28 10:30:00 +09:00
Yukihiro "Matz" Matsumoto 6c25b5896a class.c (prepend_features): remove the method
Just like `#append_features`, we remove `#prepend_features` and
implemented `#prepend` directly in C.
2025-04-28 10:30:00 +09:00
Yukihiro "Matz" Matsumoto 9387cd382e class.c (append_features): remove the method
The technique is called "double dispatch" (that was popular in
Smalltalk), but it does not work well with mruby. It's slower and
consumes more memory. Even thought `#append_features` defined in ISO
standard (15.2.2.4.11), we decided to remove it. Strictly speaking, it
is mruby limitation. And it should be documented clearly.
2025-04-28 10:30:00 +09:00
Yukihiro "Matz" Matsumoto 9cc6d39956 test/module.rb: add test for #6506 2025-04-14 22:09:35 +09:00
dearblue 3fd5e1c250 Fixed visibility at method definition
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
2025-04-11 21:36:26 +09:00
dearblue 84e1b0045c Fixed class method visibility via module_function
The following code should work

```ruby
module M
  def me
    p self
  end

  module_function :me
end

M.me
```
2025-04-07 21:40:56 +09:00
Mark Delk 6396aac434 fix a typo, update specs 2025-03-17 10:47:33 -05:00
Yukihiro "Matz" Matsumoto fbaedfeaad test/hash.rb: remove Hash#freeze before Hash#rehash; ref #6485 2025-03-08 11:25:49 +09:00
Yukihiro "Matz" Matsumoto 18674c0237 test/module.rb: add visibility tests; ref #1835 2025-03-07 17:17:48 +09:00
Yukihiro "Matz" Matsumoto 384579fd21 test/module.rb: rename to pass the spell check 2025-03-07 17:17:48 +09:00
Yukihiro "Matz" Matsumoto 95b30a38dc kernel.rb: make Kernel#loop private 2025-03-07 17:17:44 +09:00
Yukihiro "Matz" Matsumoto ff6149dea7 kernel.c: removed some Kernel singleton methods
Since virtually no one calls `Kernel.block_given?` but just
`block_given?`. For the cases `raise` is redefined (like Ruby/Tk),
`Kernel.raise` is kept.
2025-03-07 17:17:44 +09:00
Yukihiro "Matz" Matsumoto efc03f60af proc.c: make #lambda private 2025-03-07 17:17:43 +09:00
Yukihiro "Matz" Matsumoto 4d547b6cbb test/module.rb: call #remove_const via #__send__ to skip private check 2025-03-07 17:17:40 +09:00
Yukihiro "Matz" Matsumoto e0cb99aad4 test/module.rb: call #extend_object via #__send__ to skip private check 2025-03-07 17:17:40 +09:00
Yukihiro "Matz" Matsumoto 8a409feaad test/module.rb: call #prepend_features via #__send__ to skip private check 2025-03-07 17:17:40 +09:00
Yukihiro "Matz" Matsumoto 701a18deda test/module.rb: call #append_features via #__send__ to skip private check 2025-03-07 17:17:40 +09:00
Yukihiro "Matz" Matsumoto 7653bdd05e test/codegen.rb: call #remove_const via #__send__ to skip private check 2025-03-07 17:17:39 +09:00