Compare commits

...

87 Commits

Author SHA1 Message Date
mimaki 65eb1df5a3 Update version to 4.0.0RC4. 2026-04-13 18:54:26 +09:00
mimaki 3b483db61d Merge branch 'master' into stable 2026-04-13 18:51:16 +09:00
Yukihiro "Matz" Matsumoto d65f7a9c5a Merge pull request #6784 from mruby/fix/news-security-updates 2026-04-13 09:01:38 +09:00
Yukihiro "Matz" Matsumoto e0aadaf6c4 NEWS.md: add security fixes and merged PRs (#6780, #6781, #6783)
Co-authored-by: Claude <noreply@anthropic.com>
2026-04-13 08:39:21 +09:00
Yukihiro "Matz" Matsumoto dfd4eb52cc Merge pull request #6783 from jbampton/pin-actions 2026-04-13 08:16:14 +09:00
John Bampton ebed2d32a2 gha: pin workflows to hash 2026-04-11 17:51:28 +10:00
Yukihiro "Matz" Matsumoto 48fc4220d3 Merge pull request #6781 from mruby/fix/sprintf-uaf 2026-04-11 16:33:05 +09:00
Yukihiro "Matz" Matsumoto 18ba02662b Merge pull request #6780 from mruby/fix/string-prepend-overflow 2026-04-11 16:32:24 +09:00
Yukihiro "Matz" Matsumoto 59552ecb8e mruby-sprintf: protect format string from mutation during callbacks
mrb_str_format captured raw C pointers (p, end) into the format
string's buffer before the main loop. The %s and %p specifiers call
to_s and inspect, which can invoke Ruby code that mutates the format
string via String#replace, freeing or reallocating its buffer. The
loop then continued iterating with dangling pointers, reading freed
memory and potentially leaking adjacent heap contents into the result.

Duplicate the format string with mrb_str_dup() before the loop. This
is O(1) because mrb_str_dup shares the underlying buffer; if the
original is later mutated via String#replace, str_replace decrements
the shared refcount, leaving our duplicate's buffer intact.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-10 14:51:59 +09:00
Yukihiro "Matz" Matsumoto af6f23ddb3 mruby-string-ext: fix String#prepend with self-referencing arguments
String#prepend(s, s) read RSTRING_LEN(argv[i]) in the copy loop after
mrb_str_resize had already updated the receiver's length, causing the
memcpy to write past the allocated buffer.

Detect self-references with mrb_obj_eq() and read from the memmoved
original data at p + total_prepend_len using the captured self_len.
This also handles mixed cases like s.prepend("X", s) where earlier
writes would otherwise corrupt the source of later reads.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-10 14:44:46 +09:00
Yukihiro "Matz" Matsumoto 4eb4884219 Merge pull request #6778 from mruby/dependabot/github_actions/github-actions-dependencies-6289042708 2026-04-09 07:55:37 +09:00
dependabot[bot] c394525da6 build(deps): bump super-linter/super-linter
Bumps the github-actions-dependencies group with 1 update: [super-linter/super-linter](https://github.com/super-linter/super-linter).


Updates `super-linter/super-linter` from 8.5.0 to 8.6.0
- [Release notes](https://github.com/super-linter/super-linter/releases)
- [Changelog](https://github.com/super-linter/super-linter/blob/main/CHANGELOG.md)
- [Commits](https://github.com/super-linter/super-linter/compare/v8.5.0...v8.6.0)

---
updated-dependencies:
- dependency-name: super-linter/super-linter
  dependency-version: 8.6.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: github-actions-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-04-08 14:54:46 +00:00
Yukihiro "Matz" Matsumoto 5d5cf0c3ea Merge pull request #6772 from mruby/fix/update-news 2026-04-02 20:33:46 +09:00
Yukihiro "Matz" Matsumoto 3a5c45f282 NEWS.md: update for recent changes
Add entries for language changes (case/in NoMatchingPatternError,
compound statement in tLPAREN_ARG), C API additions (mrb_bigint_p(),
RVALUE union), compiler optimizations (literal chunking), build
fixes (MSYS2), security fixes, and 26 merged pull requests.

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-02 20:17:19 +09:00
mimaki 6c121b9708 Update version to 4.0.0RC3. 2026-04-02 11:23:31 +09:00
mimaki d06cb40725 Merge branch 'master' into stable 2026-04-02 10:31:49 +09:00
Yukihiro "Matz" Matsumoto 0cc4caad2b Merge pull request #6769 from khasinski/fix-socket-recvfrom-nonblock 2026-04-01 09:23:00 +09:00
Yukihiro "Matz" Matsumoto 0cef3e5414 Merge pull request #6770 from jbampton/patch-2 2026-04-01 09:17:38 +09:00
Yukihiro "Matz" Matsumoto 825d4c388e Merge pull request #6771 from mruby/dependabot/pre_commit/pre-commit-hooks-820b35f878 2026-04-01 09:16:17 +09:00
dependabot[bot] 141a8bc406 build(deps): bump https://github.com/rhysd/actionlint
Bumps the pre-commit-hooks group with 1 update: [https://github.com/rhysd/actionlint](https://github.com/rhysd/actionlint).


Updates `https://github.com/rhysd/actionlint` from v1.7.11 to 1.7.12
- [Release notes](https://github.com/rhysd/actionlint/releases)
- [Changelog](https://github.com/rhysd/actionlint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rhysd/actionlint/compare/v1.7.11...v1.7.12)

---
updated-dependencies:
- dependency-name: https://github.com/rhysd/actionlint
  dependency-version: 1.7.12
  dependency-type: direct:production
  dependency-group: pre-commit-hooks
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-31 15:00:01 +00:00
John Bampton a6aa0fdb24 Dependabot: add cooldown to pre-commit ecosystem 2026-04-01 00:46:24 +10:00
Chris Hasiński b85c520843 Remove stale self-corruption workaround in recvfrom_nonblock
The s = self workaround and XXX comment in recvfrom_nonblock date back
to the initial import of mruby-socket. The underlying bug where self
became a SystemcallException inside ensure blocks has since been fixed.

Verified that self correctly refers to the socket object in ensure
blocks after exceptions from recvfrom.
2026-03-30 22:14:26 +02:00
Yukihiro "Matz" Matsumoto d522bd5aa2 Merge pull request #6768 from hasumikin/fix/envadjust 2026-03-30 21:18:06 +09:00
HASUMI Hitoshi d95ebe4a23 Fix stack extension bug causing HardFault
This patch fixes a bug in the stack extension logic that could cause a HardFault on certain configurations when the stack is reallocated to a new address.

## Background

When the mruby VM's stack runs out, stack_extend_alloc() calls mrb_realloc to grow it.
If reallocation moves the block to a new address, envadjust() adjusts all ci->stack pointers to point into the new allocation.

## The bug

The bug happened under the configuration below:

- MRB_INT64 on MRB_32BIT (`sizeof(mrb_value) == 16` because MRB_NO_BOXING is now mandatory)
- Allocator with 8-byte alignment (eg. PICORB_ALLOC_ALIGN=8 in PicoRuby for Raspi Pico)

The delta was computed via mrb_value* pointer subtraction:

```c
ptrdiff_t delta = newbase - oldbase;  // units of sizeof(mrb_value)
```

If :
- Old address: 0x2004c508
- New address: 0x2004c510 (8-byte difference)

The pointer subtraction truncated: 8 / 16 = 0.
envadjust() was misleaded as `delta == 0` and returned early without adjusting any ci->stack pointers.
The stbase was updated to the new address, but all stack pointers still pointed 8 bytes before it.
Every register access was shifted, reading garbage, ultimately causing a HardFault.

## The fix

Byte-level char* calculation instead of mrb_value* calculation:

```c
ptrdiff_t off = (char*)newbase - (char*)oldbase;
// ...
ci->stack = (mrb_value*)((char*)ci->stack + off);
```

This ensures the adjustment is exact regardless of sizeof(mrb_value) and allocator alignment.
2026-03-30 16:25:46 +09:00
Yukihiro "Matz" Matsumoto ac9f7aedd2 Merge pull request #6767 from mruby/fix/lparen-arg-compstmt 2026-03-30 07:54:31 +09:00
Yukihiro "Matz" Matsumoto 919cbd8fea mruby-compiler: allow compound statement in tLPAREN_ARG
Change the grammar rule for tLPAREN_ARG from accepting only a
single stmt to accepting compstmt. This allows compound
statements with semicolons inside parenthesized arguments when
the parenthesis is preceded by a space, e.g., `p (f1; f2)`.

This matches the behavior of CRuby 3.3+.

Fixes #6766.

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-30 07:06:30 +09:00
Yukihiro "Matz" Matsumoto 801eefeab6 Merge pull request #6765 from khasinski/fix-lazy-flat-map 2026-03-29 22:55:13 +09:00
Chris Hasiński 8f71887e46 Improve flat_map test descriptions for clarity 2026-03-28 23:14:46 +01:00
Chris Hasiński 3f52ef6cfc Fix Lazy#flat_map to handle non-enumerable block return values
When the block passed to Lazy#flat_map returns a non-enumerable value
(e.g. an Integer), mruby raised NoMethodError because it unconditionally
called #each on the result. CRuby yields non-enumerable values directly.

Use respond_to?(:each) to match CRuby behavior: iterate enumerable
results, yield non-enumerable results as-is.
2026-03-28 22:59:11 +01:00
Yukihiro "Matz" Matsumoto 28c5b1b17b Merge pull request #6763 from mruby/dependabot/github_actions/github-actions-dependencies-19ba90ca6e 2026-03-26 14:27:05 +09:00
dependabot[bot] d50932c50f build(deps): bump j178/prek-action
Bumps the github-actions-dependencies group with 1 update: [j178/prek-action](https://github.com/j178/prek-action).


Updates `j178/prek-action` from 1 to 2
- [Release notes](https://github.com/j178/prek-action/releases)
- [Commits](https://github.com/j178/prek-action/compare/v1...v2)

---
updated-dependencies:
- dependency-name: j178/prek-action
  dependency-version: '2'
  dependency-type: direct:production
  update-type: version-update:semver-major
  dependency-group: github-actions-dependencies
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-25 14:54:41 +00:00
Yukihiro "Matz" Matsumoto b7e3743130 Merge pull request #6762 from mruby/fix-test-build-race 2026-03-25 15:49:46 +09:00
Yukihiro "Matz" Matsumoto 805e6dbc33 Merge pull request #6761 from mruby/fix-gc-unregister-leak 2026-03-25 15:44:44 +09:00
Yukihiro "Matz" Matsumoto e8c5e7c0cd mruby-test: write generated C files atomically to avoid race condition
With `rake -m`, the C compiler can start reading a partially-written
gem_test.c before generation completes. Write to a .tmp file first,
then rename to the final path.

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-25 15:32:36 +09:00
Yukihiro "Matz" Matsumoto ab249864cc gc.c: remove all matching entries in mrb_gc_unregister()
Previously only the first match was removed, leaking duplicate
entries when the same object was registered multiple times.
Use two-pointer compaction for O(N) removal.

Fixes #6760.

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-25 15:29:27 +09:00
Yukihiro "Matz" Matsumoto c2b588139f Merge pull request #6758 from dearblue/vm 2026-03-25 15:17:02 +09:00
Yukihiro "Matz" Matsumoto 21dc829903 Merge pull request #6759 from dearblue/bigint 2026-03-25 15:15:49 +09:00
dearblue 6c4a8c09db Define mrb_bigint_p() always.
Define the `mrb_bigint_p()` macro function, which returns false if `MRB_USE_BIGINT` is undefined.
2026-03-24 22:16:15 +09:00
dearblue c52faebb7f Don't assign the result of mrb_funcall() directly to regs
There are two reasons:

  - If the mruby call stack is extended, the `ci` variable may become invalid.
  - The C language does not specify the order in which the left-hand and right-hand sides of an assignment expression are evaluated.
    Therefore, if the mruby data stack is extended, `ci->stack` may become invalid.
2026-03-24 21:25:46 +09:00
Yukihiro "Matz" Matsumoto f61ab96689 Merge pull request #6756 from dearblue/array-ext 2026-03-24 15:04:58 +09:00
Yukihiro "Matz" Matsumoto 07a6c6e56b Merge pull request #6757 from mruby/dependabot/pre_commit/pre-commit-hooks-ae77450b09 2026-03-24 09:30:46 +09:00
dependabot[bot] 45ebb3d0c8 build(deps): bump https://github.com/rubocop/rubocop
Bumps the pre-commit-hooks group with 1 update: [https://github.com/rubocop/rubocop](https://github.com/rubocop/rubocop).


Updates `https://github.com/rubocop/rubocop` from v1.85.1 to 1.86.0
- [Release notes](https://github.com/rubocop/rubocop/releases)
- [Changelog](https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rubocop/rubocop/compare/v1.85.1...v1.86.0)

---
updated-dependencies:
- dependency-name: https://github.com/rubocop/rubocop
  dependency-version: 1.86.0
  dependency-type: direct:production
  dependency-group: pre-commit-hooks
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-23 15:23:09 +00:00
dearblue 2135088ada Avoid the impact of object modifications caused by calls to mrb_vm_exec()
Several methods defined in mruby-array-ext are written in C and may call `mrb_vm_exec()`.
If array objects are modified on the Ruby side, problems may arise in subsequent processing.

  - Using objects that have been removed from the array and garbage collected
  - Using pointers or array lengths that have become invalid due to changes to the array object
  - Modifying the contents of a shared array object directly

ref: https://github.com/mruby/mruby/issues/6662
2026-03-22 23:05:37 +09:00
Yukihiro "Matz" Matsumoto 04f998238e Merge pull request #6755 from leviongit/vm/karg/delete-return 2026-03-21 17:03:27 +09:00
Yukihiro "Matz" Matsumoto affcbb000b Merge pull request #6754 from leviongit/core/attr_accessor/nullary 2026-03-21 17:02:24 +09:00
Yukihiro "Matz" Matsumoto ff5e6a2491 Merge pull request #6753 from dearblue/array.product 2026-03-21 16:57:49 +09:00
leviongit e8d0750458 reload ci after mrb_hash_delete_key 2026-03-20 21:35:26 +01:00
leviongit 88e356e7da remove redundant mrb_hash_get call
`mrb_hash_delete` returns the removed element (which is guaranteed to
exist due to the `mrb_hash_key_p` check), this prevents the hash from
being searched twice.
2026-03-20 21:20:18 +01:00
leviongit 95ece95e37 prefer marking the procs implementing attr_reader methods as noarg
this commit works on #6752 so it doesn't require a call to
`mrb_get_args`
2026-03-20 19:14:54 +01:00
dearblue 98d763603c Further optimize Array#product
Replace `__product_group` method with `__product_generate` and `__product_next`.
This change eliminates the need for Ruby to perform internal state calculations, allowing it to simply receive the results.
2026-03-20 21:13:52 +09:00
Yukihiro "Matz" Matsumoto 01ce2f8c71 Merge pull request #6747 from katafrakt/handle-hash-default-arg 2026-03-20 16:44:27 +09:00
Yukihiro "Matz" Matsumoto f08dede1d9 Merge pull request #6752 from khasinski/fix-attr-reader-arity 2026-03-20 16:40:45 +09:00
Yukihiro "Matz" Matsumoto 3bfe703f80 Merge pull request #6750 from dearblue/array.product 2026-03-20 10:24:10 +09:00
Yukihiro "Matz" Matsumoto e82fa70004 Merge pull request #6749 from hasumikin/fix/microcontroller-profile 2026-03-20 10:22:32 +09:00
Yukihiro "Matz" Matsumoto 8d53f65b96 Merge pull request #6748 from jbampton/clean-up-workflows 2026-03-20 10:21:45 +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
dearblue 8441eaf633 Fixed "Out-of-bounds Read" and "Divide-by-Zero" in ary_product_group()
Reproduction:

  - Out-of-bounds Read

    ```console
    % build/host/bin/mruby -e '([nil] * 256).__product_group([[nil] * 256], 1 << 32, 256)'
    zsh: segmentation fault (core dumped)  build/host/bin/mruby -e
    ```

  - Divide-by-Zero

    ```console
    % build/host/bin/mruby -e '([nil] * 256).__product_group([[]], 1 << 32, 256)'
    zsh: floating point exception (core dumped)  build/host/bin/mruby -e '([nil] * 256).__product_group([[]], 1 << 32, 256)'
    ```
2026-03-19 23:11:06 +09:00
HASUMI Hitoshi 736a72cdc9 [skip ci] Update include/mrbconf.h
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
2026-03-19 09:50:15 +09:00
HASUMI Hitoshi 5e9eed2d0c Fix KHASH_DEFAULT_SIZE to KHASH_INITIAL_SIZE rename inconsistencies
Commit 250bf6edd renamed KHASH_DEFAULT_SIZE to KHASH_INITIAL_SIZE but
missed updating build_config files and documentation. Also restore the
default value in khash.h to 32, consistent with the documented default
and the profile hierarchy (MRB_CONSTRAINED_BASELINE_PROFILE reduces it
to 16).
2026-03-19 09:38:36 +09:00
John Bampton 80d84188fd pre-commit bump Node.js 2026-03-18 17:57:33 +10:00
John Bampton 55ef8e1728 Update workflows 2026-03-18 17:57:05 +10: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 a41eeaed33 Merge pull request #6735 from dearblue/presym 2026-03-16 21:55:22 +09:00
Yukihiro "Matz" Matsumoto 4ed4326dd5 Merge pull request #6746 from mruby/dependabot/pre_commit/pre-commit-hooks-6e50fbf5c1 2026-03-16 21:47:31 +09:00
dearblue 000acedc35 Prevent full recompilation without changes to presym file
Commit b9a1a1fb23 is a revert of commit 8df9a22a85, differing only in the comment.
This means the issue from https://github.com/mruby/mruby/issues/6721 has reappeared.

The cause of https://github.com/mruby/mruby/issues/6721, as stated in the commit message for commit 8df9a22a85, is that each ".o" file has an indirect dependency on all ".pi" files through the presym file.

This patch therefore adds a proxy-like task `gensym:update:#{build.name}` between tasks.
Its purpose is to hide the direct dependency from ".o" files to the presym file from the rake system.
2026-03-14 17:14:37 +09:00
dependabot[bot] 14e98c60c3 build(deps): bump the pre-commit-hooks group across 1 directory with 8 updates
Bumps the pre-commit-hooks group with 8 updates in the / directory:

| Package | From | To |
| --- | --- | --- |
| [https://github.com/gitleaks/gitleaks](https://github.com/gitleaks/gitleaks) | `v8.30.0` | `8.30.1` |
| [https://github.com/oxipng/oxipng](https://github.com/oxipng/oxipng) | `v10.0.0` | `10.1.0` |
| [https://github.com/Lucas-C/pre-commit-hooks](https://github.com/Lucas-C/pre-commit-hooks) | `v1.5.5` | `1.5.6` |
| [https://github.com/rhysd/actionlint](https://github.com/rhysd/actionlint) | `v1.7.9` | `1.7.11` |
| [https://github.com/codespell-project/codespell](https://github.com/codespell-project/codespell) | `v2.4.1` | `2.4.2` |
| [https://github.com/igorshubovych/markdownlint-cli](https://github.com/igorshubovych/markdownlint-cli) | `v0.46.0` | `0.48.0` |
| [https://github.com/rubocop/rubocop](https://github.com/rubocop/rubocop) | `v1.81.7` | `1.85.1` |
| [https://github.com/adrienverge/yamllint](https://github.com/adrienverge/yamllint) | `v1.37.1` | `1.38.0` |



Updates `https://github.com/gitleaks/gitleaks` from v8.30.0 to 8.30.1
- [Release notes](https://github.com/gitleaks/gitleaks/releases)
- [Commits](https://github.com/gitleaks/gitleaks/compare/v8.30.0...v8.30.1)

Updates `https://github.com/oxipng/oxipng` from v10.0.0 to 10.1.0
- [Release notes](https://github.com/oxipng/oxipng/releases)
- [Changelog](https://github.com/oxipng/oxipng/blob/master/CHANGELOG.md)
- [Commits](https://github.com/oxipng/oxipng/compare/v10.0.0...v10.1.0)

Updates `https://github.com/Lucas-C/pre-commit-hooks` from v1.5.5 to 1.5.6
- [Release notes](https://github.com/Lucas-C/pre-commit-hooks/releases)
- [Commits](https://github.com/Lucas-C/pre-commit-hooks/compare/v1.5.5...v1.5.6)

Updates `https://github.com/rhysd/actionlint` from v1.7.9 to 1.7.11
- [Release notes](https://github.com/rhysd/actionlint/releases)
- [Changelog](https://github.com/rhysd/actionlint/blob/main/CHANGELOG.md)
- [Commits](https://github.com/rhysd/actionlint/compare/v1.7.9...v1.7.11)

Updates `https://github.com/codespell-project/codespell` from v2.4.1 to 2.4.2
- [Release notes](https://github.com/codespell-project/codespell/releases)
- [Commits](https://github.com/codespell-project/codespell/compare/v2.4.1...v2.4.2)

Updates `https://github.com/igorshubovych/markdownlint-cli` from v0.46.0 to 0.48.0
- [Release notes](https://github.com/igorshubovych/markdownlint-cli/releases)
- [Commits](https://github.com/igorshubovych/markdownlint-cli/compare/v0.46.0...v0.48.0)

Updates `https://github.com/rubocop/rubocop` from v1.81.7 to 1.85.1
- [Release notes](https://github.com/rubocop/rubocop/releases)
- [Changelog](https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md)
- [Commits](https://github.com/rubocop/rubocop/compare/v1.81.7...v1.85.1)

Updates `https://github.com/adrienverge/yamllint` from v1.37.1 to 1.38.0
- [Release notes](https://github.com/adrienverge/yamllint/releases)
- [Changelog](https://github.com/adrienverge/yamllint/blob/master/CHANGELOG.rst)
- [Commits](https://github.com/adrienverge/yamllint/compare/v1.37.1...v1.38.0)

---
updated-dependencies:
- dependency-name: https://github.com/gitleaks/gitleaks
  dependency-version: 8.30.1
  dependency-type: direct:production
  dependency-group: pre-commit-hooks
- dependency-name: https://github.com/oxipng/oxipng
  dependency-version: 10.1.0
  dependency-type: direct:production
  dependency-group: pre-commit-hooks
- dependency-name: https://github.com/Lucas-C/pre-commit-hooks
  dependency-version: 1.5.6
  dependency-type: direct:production
  dependency-group: pre-commit-hooks
- dependency-name: https://github.com/rhysd/actionlint
  dependency-version: 1.7.11
  dependency-type: direct:production
  dependency-group: pre-commit-hooks
- dependency-name: https://github.com/codespell-project/codespell
  dependency-version: 2.4.2
  dependency-type: direct:production
  dependency-group: pre-commit-hooks
- dependency-name: https://github.com/igorshubovych/markdownlint-cli
  dependency-version: 0.48.0
  dependency-type: direct:production
  dependency-group: pre-commit-hooks
- dependency-name: https://github.com/rubocop/rubocop
  dependency-version: 1.85.1
  dependency-type: direct:production
  dependency-group: pre-commit-hooks
- dependency-name: https://github.com/adrienverge/yamllint
  dependency-version: 1.38.0
  dependency-type: direct:production
  dependency-group: pre-commit-hooks
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-13 14:57:23 +00:00
mimaki 9d523e2f74 Update version to 4.0.0RC2. 2026-03-12 19:07:19 +09:00
mimaki 380c459d37 Merge branch 'master' into stable 2026-03-12 19:01:29 +09:00
Yukihiro "Matz" Matsumoto eb7d0b86d7 Merge pull request #6736 from jbampton/add-dependabot-pre-commit 2026-03-12 17:16:48 +09:00
John Bampton 9bdf998faa Add pre-commit ecosystem to Dependabot
Group dependabot updates to reduce repo noise

Add descriptive group labels

https://github.blog/changelog/2026-03-10-dependabot-now-supports-pre-commit-hooks/

https://docs.github.com/en/code-security/reference/supply-chain-security/dependabot-options-reference#package-ecosystem-
2026-03-12 16:59:11 +10:00
Yukihiro "Matz" Matsumoto 457a50b485 Merge pull request #6744 from mruby/fix/case-in-no-match-error 2026-03-12 14:36:46 +09:00
Yukihiro "Matz" Matsumoto 143959b94b doc: fix prettier formatting in markdown files
Co-authored-by: Claude <noreply@anthropic.com>
2026-03-12 14:33:37 +09: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 b34b7206f9 Merge pull request #6738 from jbampton/add-manual-hooks-workflow 2026-03-12 14:20:02 +09:00
Yukihiro "Matz" Matsumoto 03d1f7d6ec Merge pull request #6743 from mruby/fix/words-chunking 2026-03-12 14:17:38 +09:00
Yukihiro "Matz" Matsumoto 9239a9e0ef pre-commit: fix prettier formatting in doc/guides/capi.md
Co-authored-by: Claude <noreply@anthropic.com>
2026-03-12 14:05:48 +09:00
Yukihiro "Matz" Matsumoto 62cf0dc17a codegen.c: chunk %w() and %i() literals to reduce register pressure
Apply the same chunking strategy used for regular array literals
to %w() and %i() literal arrays in gen_literal_array(). Fixes #6740.

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-12 12:12:50 +09:00
Yukihiro "Matz" Matsumoto 12af8f513e Merge pull request #6742 from mruby/fix/remove-markdown-link-check 2026-03-12 11:52:45 +09:00
Yukihiro "Matz" Matsumoto ab378ec01e Merge pull request #6690 from jbampton/cleanup-prettier-pre-commit 2026-03-12 11:37:25 +09:00
Yukihiro "Matz" Matsumoto 2d00f4a284 Merge pull request #6739 from mimaki/fix-msys2-build-error 2026-03-12 11:36:53 +09:00
mimaki 77f6ffecc7 Updates build script to support MSYS2 drive letters, fixing build error with MSYS2. 2026-03-11 11:31:55 +09:00
John Bampton 626b2fa469 Put manual hooks in separate workflow file 2026-03-11 10:29:57 +10:00
mimaki 1d34c3bed5 Update version to 4.0.0RC. 2026-03-05 11:58:41 +09:00
mimaki 18a1c33fd4 Merge remote-tracking branch 'origin' into stable 2026-03-05 11:14:03 +09:00
John Bampton 3c7d19d41b prettier: set pass_filenames: false to avoid multiple passes
Updated `.prettierignore` to ingnore the typical Python environment files from `.venv`

If you are running pre-commit locally you probably have a Python environment setup.

This PR speeds up the prettier hook and avoids multiple passes through the targetted files.
2025-12-30 01:53:03 +10:00
Yukihiro "Matz" Matsumoto 40436e4e3c Merge pull request #6627 from suetanvil/stable-3.4.0-plus-fixes 2025-09-23 07:54:35 +01:00
Hendrik 651a183228 fix bigint on raspberry pi
this fixes an issue where base can be out of range on a raspberry pi.
2025-09-18 12:39:05 -04:00
55 changed files with 3467 additions and 3111 deletions
+18
View File
@@ -5,11 +5,29 @@ updates:
directory: "/"
schedule:
interval: "daily"
groups:
bundler-dependencies:
patterns:
- "*"
cooldown:
default-days: 7
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
groups:
github-actions-dependencies:
patterns:
- "*"
cooldown:
default-days: 7
- package-ecosystem: "pre-commit"
directory: "/"
schedule:
interval: "daily"
groups:
pre-commit-hooks:
patterns:
- "*"
cooldown:
default-days: 7
+10 -4
View File
@@ -31,7 +31,9 @@ jobs:
LD: ${{ matrix.cc }}
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Ruby version
run: ruby -v
- name: Compiler version
@@ -44,11 +46,13 @@ jobs:
timeout-minutes: 15
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Ruby version
run: ruby -v
- name: Cache cosmocc
uses: actions/cache@v5
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
id: cache-cosmocc
with:
path: ~/cosmo
@@ -70,7 +74,9 @@ jobs:
MRUBY_CONFIG: ci/msvc
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Ruby version
run: ruby -v
- name: Build and test
+3 -1
View File
@@ -18,7 +18,9 @@ jobs:
language: ["actions"]
steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Initialize CodeQL
uses: github/codeql-action/init@v4
with:
+7 -2
View File
@@ -2,6 +2,9 @@ name: Coverage
on: [push]
permissions:
contents: read
jobs:
coverage:
name: Coverage
@@ -16,7 +19,9 @@ jobs:
LDFLAGS: --coverage
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Ruby version
run: ruby -v
- name: Compiler version
@@ -34,7 +39,7 @@ jobs:
echo \`\`\`
} > "$GITHUB_STEP_SUMMARY"
- name: Upload coverage report
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: coverage-${{ github.sha }}
path: coverage/
+1 -1
View File
@@ -9,7 +9,7 @@ jobs:
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@v6
- uses: actions/labeler@634933edcd8ababfe52f92936142cc22ac488b1b # v6.0.1
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
sync-labels: true
+4 -2
View File
@@ -11,7 +11,9 @@ jobs:
name: Run ls-lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: ls-lint/action@v2.3.1
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: ls-lint/action@02e380fe8733d499cbfc9e22276de5085508a5bd # v2.3.1
with:
config: .github/linters/.ls-lint.yml
+1 -1
View File
@@ -20,7 +20,7 @@ jobs:
fuzz-seconds: 600
dry-run: false
- name: Upload Crash
uses: actions/upload-artifact@v7
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
if: failure()
with:
name: artifacts
+22
View File
@@ -0,0 +1,22 @@
# https://github.com/j178/prek
name: Manual hooks
on: [pull_request]
permissions:
contents: read
jobs:
pre-commit:
name: Run pre-commit
runs-on: ubuntu-latest
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: j178/prek-action@53276d8b0d10f8b6672aa85b4588c6921d0370cc # v2.0.1
with:
install-only: true
- name: Run manual pre-commit hooks
run: prek run --color=always --all-files --hook-stage manual
+4 -4
View File
@@ -12,9 +12,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@v6
- uses: j178/prek-action@v1
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: j178/prek-action@53276d8b0d10f8b6672aa85b4588c6921d0370cc # v2.0.1
with:
extra-args: --all-files
- name: Run manual pre-commit hooks
run: prek run --color=always --all-files --hook-stage manual
+3 -1
View File
@@ -19,7 +19,9 @@ jobs:
fail-fast: false
steps:
- name: "Checkout ${{ github.ref_name }} ( ${{ github.sha }} )"
uses: actions/checkout@v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Builds
id: builds
run: |
+3 -2
View File
@@ -15,12 +15,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@v6
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
# Full git history is needed to get a proper list of changed files within `super-linter`
fetch-depth: 0
persist-credentials: false
- name: Lint Code Base
uses: super-linter/super-linter/slim@v8.5.0
uses: super-linter/super-linter/slim@9e863354e3ff62e0727d37183162c4a88873df41 # v8.6.0
env:
# VALIDATE_BASH_EXEC: true
VALIDATE_DOCKERFILE_HADOLINT: true
+10 -9
View File
@@ -3,7 +3,7 @@
default_stages: [pre-commit, pre-push]
default_language_version:
python: python3
node: 24.11.1
node: 24.14.0
minimum_pre_commit_version: "3.2.0"
exclude: "^tools/lrama/"
repos:
@@ -24,6 +24,7 @@ repos:
files: \.(md|ya?ml)$
language: node
additional_dependencies: ["prettier@3.7.4"]
pass_filenames: false
stages: [manual]
- id: check-zip-file-is-not-committed
name: disallow zip files
@@ -34,13 +35,13 @@ repos:
track and have security implications. Please remove the zip file from the repository.
files: \.zip$
- repo: https://github.com/gitleaks/gitleaks
rev: v8.30.0
rev: v8.30.1
hooks:
- id: gitleaks
name: run gitleaks
description: detect hardcoded secrets with gitleaks
- repo: https://github.com/oxipng/oxipng
rev: v10.0.0
rev: v10.1.0
hooks:
- id: oxipng
name: run oxipng
@@ -72,7 +73,7 @@ repos:
- id: mixed-line-ending
- id: trailing-whitespace
- repo: https://github.com/Lucas-C/pre-commit-hooks
rev: v1.5.5
rev: v1.5.6
hooks:
- id: forbid-tabs
name: run no-tabs checker
@@ -84,19 +85,19 @@ repos:
args: [--whitespaces-count, "2"]
exclude: Makefile$
- repo: https://github.com/rhysd/actionlint
rev: v1.7.9
rev: v1.7.12
hooks:
- id: actionlint
name: run actionlint
description: lint GitHub Actions workflow files
- repo: https://github.com/codespell-project/codespell
rev: v2.4.1
rev: v2.4.2
hooks:
- id: codespell
name: run codespell
description: check spelling with codespell
- repo: https://github.com/igorshubovych/markdownlint-cli
rev: v0.46.0
rev: v0.48.0
hooks:
- id: markdownlint
name: run markdownlint
@@ -105,7 +106,7 @@ repos:
types: [markdown]
files: \.md$
- repo: https://github.com/rubocop/rubocop
rev: v1.81.7
rev: v1.86.0
hooks:
- id: rubocop
name: run rubocop
@@ -119,7 +120,7 @@ repos:
name: run shellcheck
description: check shell scripts with a static analysis tool
- repo: https://github.com/adrienverge/yamllint
rev: v1.37.1
rev: v1.38.0
hooks:
- id: yamllint
name: run yamllint
+1
View File
@@ -3,3 +3,4 @@ build
coverage
doc/internal/opcode.md
tools/lrama
.venv
+44
View File
@@ -23,6 +23,8 @@ mruby now supports pattern matching (case/in) syntax:
- Trailing comma in method definition parameters: `def foo(a, b,)` ([f78334b](https://github.com/mruby/mruby/commit/f78334b))
- Array/Hash/String subclasses can now override `[]` and `[]=` methods ([#6675](https://github.com/mruby/mruby/pull/6675))
- `OP_SETIDX` optimization for Array and Hash ([ddd8fe1](https://github.com/mruby/mruby/commit/ddd8fe1))
- `case`/`in` without `else` now raises `NoMatchingPatternError` ([d8de35b](https://github.com/mruby/mruby/commit/d8de35b))
- Allow compound statement in parenthesized argument context ([919cbd8](https://github.com/mruby/mruby/commit/919cbd8))
# Changes in C API
@@ -37,6 +39,8 @@ mruby now supports pattern matching (case/in) syntax:
- `mrb_open()` returns mrb_state with exc set on init failure ([05ffe0c](https://github.com/mruby/mruby/commit/05ffe0c))
- `mrb_utf8_to_buf()` for UTF-8 encoding consolidation ([7e28e68](https://github.com/mruby/mruby/commit/7e28e68))
- `kh_is_end()` macro for safe khash iteration ([893cc75](https://github.com/mruby/mruby/commit/893cc75))
- `mrb_bigint_p()` always defined regardless of bigint gem presence ([6c4a8c0](https://github.com/mruby/mruby/commit/6c4a8c0))
- `RInteger` and `RFloat` added to `RVALUE` union ([13dbca0](https://github.com/mruby/mruby/commit/13dbca0))
# ROM Method Tables
@@ -76,6 +80,7 @@ mruby-symbol-ext, mruby-range-ext, mruby-object-ext.
- Emscripten: use native WASM exception handling ([ca364e3](https://github.com/mruby/mruby/commit/ca364e3))
- HAL (Hardware Abstraction Layer) for platform abstraction in mruby-io, mruby-socket, mruby-dir, mruby-task ([74ca22f](https://github.com/mruby/mruby/commit/74ca22f))
- `MRUBY_MIRB_READLINE` environment variable to control readline library selection ([0aafb83](https://github.com/mruby/mruby/commit/0aafb83))
- MSYS2 drive letter support in build script ([77f6ffe](https://github.com/mruby/mruby/commit/77f6ffe))
- Inter-gem headers separated from external API headers ([#6671](https://github.com/mruby/mruby/pull/6671))
# Changes in mrbgems
@@ -110,6 +115,7 @@ mruby-symbol-ext, mruby-range-ext, mruby-object-ext.
## Other Gem Changes
- **_NOTE_**: `Hash#deconstruct_keys` removed for CRuby compatibility ([34b9412](https://github.com/mruby/mruby/commit/34b9412))
- **mruby-enum-lazy**: Fix `Lazy#flat_map` to handle non-enumerable block return values ([#6765](https://github.com/mruby/mruby/pull/6765))
- **mruby-array-ext**: Add `Array#find` and `Array#rfind` methods
- **mruby-io**: Add `IO#putc` and `Kernel#putc` ([baff6e6](https://github.com/mruby/mruby/commit/baff6e6))
- **mruby-random**: Replace xoshiro with PCG for better memory efficiency ([f1bab01](https://github.com/mruby/mruby/commit/f1bab01))
@@ -125,6 +131,8 @@ mruby-symbol-ext, mruby-range-ext, mruby-object-ext.
- Optimized masgn to generate literals directly into target registers ([fb5d966](https://github.com/mruby/mruby/commit/fb5d966))
- Optimized splat of literal arrays in args/literals ([1cb8d73](https://github.com/mruby/mruby/commit/1cb8d73))
- Early termination after too many parse errors ([510ebd7](https://github.com/mruby/mruby/commit/510ebd7))
- Chunk array literals at 64 elements to reduce register pressure ([f98d641](https://github.com/mruby/mruby/commit/f98d641))
- Chunk `%w()` and `%i()` literals to reduce register pressure ([62cf0dc](https://github.com/mruby/mruby/commit/62cf0dc))
# VM Optimizations
@@ -185,6 +193,10 @@ Other optimizations:
- [#6705](https://github.com/mruby/mruby/issues/6705) Can't get outer class of an object in C
- [#6713](https://github.com/mruby/mruby/issues/6713) mruby-polarssl not work
- [#6720](https://github.com/mruby/mruby/issues/6720) Random float range: different behavior from CRuby
- [#6722](https://github.com/mruby/mruby/issues/6722) RBreak size overflow on 32-bit platforms with MRB_NO_BOXING
- [#6740](https://github.com/mruby/mruby/issues/6740) `%w()`/`%i()` register pressure with large literals
- [#6741](https://github.com/mruby/mruby/issues/6741) `case`/`in` without `else` should raise `NoMatchingPatternError`
- [#6760](https://github.com/mruby/mruby/issues/6760) `mrb_gc_unregister()` not removing all matching entries
# Merged Pull Requests
@@ -260,6 +272,7 @@ Other optimizations:
- [#6589](https://github.com/mruby/mruby/pull/6589) Add pre-commit hook `check-zip-file-is-not-committed`
- [#6591](https://github.com/mruby/mruby/pull/6591) mruby-eval fix license link in README
- [#6593](https://github.com/mruby/mruby/pull/6593) README: Add Contributors Avatars, Star History, Table of Contents
- [#6598](https://github.com/mruby/mruby/pull/6598) Fix heap buffer overflow in `#method_missing`
- [#6599](https://github.com/mruby/mruby/pull/6599) pre-commit: run `markdown-link-check`, `oxipng`, `prettier` manually
- [#6600](https://github.com/mruby/mruby/pull/6600) `dreamcast_shelf build config`: update to use KallistiOS wrappers
- [#6601](https://github.com/mruby/mruby/pull/6601) fix: skip local build_config.rb when working in MRUBY_ROOT
@@ -303,6 +316,33 @@ Other optimizations:
- [#6716](https://github.com/mruby/mruby/pull/6716) Fixes identity for proc object
- [#6717](https://github.com/mruby/mruby/pull/6717) Fix mruby-task: wrapping by critical section and setting initial task receiver
- [#6718](https://github.com/mruby/mruby/pull/6718) Add installation instructions for conda and Homebrew
- [#6723](https://github.com/mruby/mruby/pull/6723) Add `RInteger` and `RFloat` to `RVALUE`
- [#6727](https://github.com/mruby/mruby/pull/6727) Language documentation: update wording of "overloading" section
- [#6729](https://github.com/mruby/mruby/pull/6729) Simplifying dependency addition for gensym task
- [#6730](https://github.com/mruby/mruby/pull/6730) Simplifying presym file generation actions
- [#6733](https://github.com/mruby/mruby/pull/6733) Include `mruby/presym.h` for all source files
- [#6734](https://github.com/mruby/mruby/pull/6734) Chunk array literals at 64 elements to reduce register pressure
- [#6735](https://github.com/mruby/mruby/pull/6735) Prevent full recompilation without changes to presym file
- [#6739](https://github.com/mruby/mruby/pull/6739) Fix MSYS2 build error with drive letters
- [#6743](https://github.com/mruby/mruby/pull/6743) Chunk `%w()` and `%i()` literals to reduce register pressure
- [#6744](https://github.com/mruby/mruby/pull/6744) Raise `NoMatchingPatternError` in `case`/`in` without `else`
- [#6747](https://github.com/mruby/mruby/pull/6747) Correctly handle empty hash as default named argument
- [#6749](https://github.com/mruby/mruby/pull/6749) Fix microcontroller profile
- [#6750](https://github.com/mruby/mruby/pull/6750) Fix out-of-bounds read and divide-by-zero in `Array#product`
- [#6752](https://github.com/mruby/mruby/pull/6752) Fix `attr_reader`-generated methods accepting extra arguments
- [#6753](https://github.com/mruby/mruby/pull/6753) Further optimize `Array#product`
- [#6754](https://github.com/mruby/mruby/pull/6754) Mark `attr_reader` procs as noarg
- [#6755](https://github.com/mruby/mruby/pull/6755) Reload `ci` after `mrb_hash_delete_key()` in keyword argument handling
- [#6756](https://github.com/mruby/mruby/pull/6756) Avoid impact of object modifications caused by `mrb_vm_exec()` calls
- [#6758](https://github.com/mruby/mruby/pull/6758) Don't assign result of `mrb_funcall()` directly to `regs`
- [#6759](https://github.com/mruby/mruby/pull/6759) Define `mrb_bigint_p()` always
- [#6761](https://github.com/mruby/mruby/pull/6761) Fix `mrb_gc_unregister()` to remove all matching entries
- [#6762](https://github.com/mruby/mruby/pull/6762) Write generated test C files atomically to avoid build race condition
- [#6765](https://github.com/mruby/mruby/pull/6765) Fix `Lazy#flat_map` to handle non-enumerable block return values
- [#6767](https://github.com/mruby/mruby/pull/6767) Allow compound statement in parenthesized argument context
- [#6780](https://github.com/mruby/mruby/pull/6780) Fix `String#prepend` with self-referencing arguments
- [#6781](https://github.com/mruby/mruby/pull/6781) Protect `sprintf` format string from mutation during callbacks
- [#6783](https://github.com/mruby/mruby/pull/6783) Pin GitHub Actions workflows to commit hashes
# Security Fixes
@@ -320,4 +360,8 @@ Other optimizations:
- Heap-use-after-free in insertion_sort ([099d2c47](https://github.com/mruby/mruby/commit/099d2c47))
- Integer overflow in str_check_length ([6afff1c3](https://github.com/mruby/mruby/commit/6afff1c3))
- Integer overflow in Integer#lcm ([070bef24](https://github.com/mruby/mruby/commit/070bef24))
- Heap buffer overflow in `#method_missing` ([550d10a](https://github.com/mruby/mruby/commit/550d10a))
- Out-of-bounds read and divide-by-zero in `Array#product` ([8441eaf](https://github.com/mruby/mruby/commit/8441eaf))
- Heap buffer overflow in `String#prepend` with self-referencing arguments ([18ba026](https://github.com/mruby/mruby/commit/18ba026))
- Use-after-free in `sprintf` via `to_s` callback mutating format string ([48fc422](https://github.com/mruby/mruby/commit/48fc422))
- Multiple memory leak fixes in bigint, Set, Array, and Task gems
+2
View File
@@ -51,6 +51,8 @@ To get mruby, you can download the stable version 4.0.0 from the official mruby
GitHub repository or clone the trunk of the mruby source tree with the "git
clone" command. You can also install and compile mruby using [ruby-install](https://github.com/postmodern/ruby-install), [ruby-build](https://github.com/rbenv/ruby-build), [rvm](https://github.com/rvm/rvm), [conda](https://anaconda.org/channels/conda-forge/packages/mruby/overview) or [Homebrew](https://formulae.brew.sh/formula/mruby).
The release candidate version 4.0.0 of mruby can be downloaded via the following URL: [https://github.com/mruby/mruby/archive/4.0.0-rc3.zip](https://github.com/mruby/mruby/archive/4.0.0-rc3.zip)
The latest development version of mruby can be downloaded via the following URL: [https://github.com/mruby/mruby/zipball/master](https://github.com/mruby/mruby/zipball/master)
The trunk of the mruby source tree can be checked out with the
+1 -1
View File
@@ -30,7 +30,7 @@ MRuby::CrossBuild.new("ArduinoDue") do |conf|
#configuration for low memory environment
cc.defines << %w(MRB_HEAP_PAGE_SIZE=64)
cc.defines << %w(KHASH_DEFAULT_SIZE=8)
cc.defines << %w(KHASH_INITIAL_SIZE=8)
cc.defines << %w(MRB_GC_STRESS)
#cc.defines << %w(MRB_NO_STDIO) #if you don't need stdio.
#cc.defines << %w(POOL_PAGE_SIZE=1000) #effective only for use with mruby-eval
+1 -1
View File
@@ -16,7 +16,7 @@ MRuby::CrossBuild.new("RX630") do |conf|
#configuration for low memory environment
cc.defines << %w(MRB_USE_FLOAT32)
cc.defines << %w(MRB_HEAP_PAGE_SIZE=64)
cc.defines << %w(KHASH_DEFAULT_SIZE=8)
cc.defines << %w(KHASH_INITIAL_SIZE=8)
cc.defines << %w(MRB_GC_STRESS)
cc.defines << %w(MRB_NO_STDIO) #if you don't need stdio.
#cc.defines << %w(POOL_PAGE_SIZE=1000) #effective only for use with mruby-eval
+1 -1
View File
@@ -27,7 +27,7 @@ MRuby::CrossBuild.new("chipKITMax32") do |conf|
#configuration for low memory environment
cc.defines << %w(MRB_HEAP_PAGE_SIZE=64)
cc.defines << %w(KHASH_DEFAULT_SIZE=8)
cc.defines << %w(KHASH_INITIAL_SIZE=8)
cc.defines << %w(MRB_GC_STRESS)
#cc.defines << %w(MRB_NO_STDIO) #if you don't need stdio.
#cc.defines << %w(POOL_PAGE_SIZE=1000) #effective only for use with mruby-eval
+30 -30
View File
@@ -4,44 +4,44 @@
New to mruby? Start here:
| Document | Description |
| -------- | ----------- |
| Document | Description |
| -------------------------------------------- | -------------------------------------- |
| [Getting Started](guides/getting-started.md) | Build mruby and run your first program |
| [Language Features](guides/language.md) | Ruby subset supported by mruby |
| [Limitations](limitations.md) | Behavioral differences from CRuby |
| [Language Features](guides/language.md) | Ruby subset supported by mruby |
| [Limitations](limitations.md) | Behavioral differences from CRuby |
## Guides (for embedders and gem authors)
### Embedding mruby in C
| Document | Description |
| -------- | ----------- |
| [C API Reference](guides/capi.md) | Values, classes, methods, error handling, fibers |
| [GC Arena](guides/gc-arena-howto.md) | Managing temporary objects in C extensions |
| [Linking](guides/link.md) | Linking with `libmruby` |
| [Amalgamation](guides/amalgamation.md) | Single-file build for easy integration |
| [Precompiled Symbols](guides/symbol.md) | Compile-time symbol allocation |
| Document | Description |
| --------------------------------------- | ------------------------------------------------ |
| [C API Reference](guides/capi.md) | Values, classes, methods, error handling, fibers |
| [GC Arena](guides/gc-arena-howto.md) | Managing temporary objects in C extensions |
| [Linking](guides/link.md) | Linking with `libmruby` |
| [Amalgamation](guides/amalgamation.md) | Single-file build for easy integration |
| [Precompiled Symbols](guides/symbol.md) | Compile-time symbol allocation |
### Building and Configuring
| Document | Description |
| -------- | ----------- |
| [Compilation](guides/compile.md) | Build system, cross-compilation, toolchains |
| [Build Configuration](guides/mrbconf.md) | Compile-time macros (`MRB_*` flags) |
| [mrbgems](guides/mrbgems.md) | Creating and managing gems |
| [Memory](guides/memory.md) | Allocator customization and heap regions |
| Document | Description |
| ---------------------------------------- | ------------------------------------------- |
| [Compilation](guides/compile.md) | Build system, cross-compilation, toolchains |
| [Build Configuration](guides/mrbconf.md) | Compile-time macros (`MRB_*` flags) |
| [mrbgems](guides/mrbgems.md) | Creating and managing gems |
| [Memory](guides/memory.md) | Allocator customization and heap regions |
### Tools
| Document | Description |
| -------- | ----------- |
| [Debugger](guides/debugger.md) | Using `mrdb` for debugging |
| Document | Description |
| ----------------------------------------------- | ----------------------------------------------- |
| [Debugger](guides/debugger.md) | Using `mrdb` for debugging |
| [ROM Method Tables](guides/rom-method-table.md) | Read-only method tables for constrained devices |
### Reference
| Document | Description |
| -------- | ----------- |
| Document | Description |
| ------------------------------------- | ------------------ |
| [Directory Structure](guides/hier.md) | Source tree layout |
## Internals (for mruby contributors)
@@ -49,14 +49,14 @@ New to mruby? Start here:
Start with [Architecture](internal/architecture.md) for an overview,
then dive into the subsystem you need:
| Document | Description |
| -------- | ----------- |
| [Architecture](internal/architecture.md) | Overview of object model, VM, GC, compiler |
| [Virtual Machine](internal/vm.md) | Dispatch loop, call frames, method lookup, fibers |
| [Garbage Collector](internal/gc.md) | Tri-color marking, write barriers, generational GC |
| [Compiler Pipeline](internal/compiler.md) | Parser, code generator, IRep, binary format |
| [Opcodes](internal/opcode.md) | VM instruction set reference |
| [Value Boxing](internal/boxing.md) | How `mrb_value` encodes types |
| Document | Description |
| ----------------------------------------- | -------------------------------------------------- |
| [Architecture](internal/architecture.md) | Overview of object model, VM, GC, compiler |
| [Virtual Machine](internal/vm.md) | Dispatch loop, call frames, method lookup, fibers |
| [Garbage Collector](internal/gc.md) | Tri-color marking, write barriers, generational GC |
| [Compiler Pipeline](internal/compiler.md) | Parser, code generator, IRep, binary format |
| [Opcodes](internal/opcode.md) | VM instruction set reference |
| [Value Boxing](internal/boxing.md) | How `mrb_value` encodes types |
## Release Notes
+68 -68
View File
@@ -99,23 +99,23 @@ mrb_str_to_cstr(mrb, v) /* const char* from String value */
### Value Types
| `mrb_vtype` | Ruby Class | Notes |
| ----------- | ---------- | ----- |
| `MRB_TT_FALSE` | FalseClass/NilClass | `nil` has `MRB_TT_FALSE` |
| `MRB_TT_TRUE` | TrueClass | |
| `MRB_TT_INTEGER` | Integer | Immediate value |
| `MRB_TT_FLOAT` | Float | May be immediate |
| `MRB_TT_SYMBOL` | Symbol | Immediate value |
| `MRB_TT_STRING` | String | Heap object |
| `MRB_TT_ARRAY` | Array | Heap object |
| `MRB_TT_HASH` | Hash | Heap object |
| `MRB_TT_OBJECT` | Object | User-defined classes |
| `MRB_TT_CLASS` | Class | |
| `MRB_TT_MODULE` | Module | |
| `MRB_TT_PROC` | Proc | |
| `MRB_TT_CDATA` | (C data) | Wrapped C structs |
| `MRB_TT_EXCEPTION` | Exception | |
| `MRB_TT_FIBER` | Fiber | |
| `mrb_vtype` | Ruby Class | Notes |
| ------------------ | ------------------- | ------------------------ |
| `MRB_TT_FALSE` | FalseClass/NilClass | `nil` has `MRB_TT_FALSE` |
| `MRB_TT_TRUE` | TrueClass | |
| `MRB_TT_INTEGER` | Integer | Immediate value |
| `MRB_TT_FLOAT` | Float | May be immediate |
| `MRB_TT_SYMBOL` | Symbol | Immediate value |
| `MRB_TT_STRING` | String | Heap object |
| `MRB_TT_ARRAY` | Array | Heap object |
| `MRB_TT_HASH` | Hash | Heap object |
| `MRB_TT_OBJECT` | Object | User-defined classes |
| `MRB_TT_CLASS` | Class | |
| `MRB_TT_MODULE` | Module | |
| `MRB_TT_PROC` | Proc | |
| `MRB_TT_CDATA` | (C data) | Wrapped C structs |
| `MRB_TT_EXCEPTION` | Exception | |
| `MRB_TT_FIBER` | Fiber | |
## Defining Classes and Modules
@@ -165,15 +165,15 @@ mrb_define_module_function(mrb, mod, "name", my_method, MRB_ARGS_ANY());
### Argument Specifiers
| Macro | Meaning |
| ----- | ------- |
| `MRB_ARGS_NONE()` | No arguments |
| `MRB_ARGS_REQ(n)` | `n` required arguments |
| `MRB_ARGS_OPT(n)` | `n` optional arguments |
| `MRB_ARGS_ARG(r,o)` | `r` required + `o` optional |
| `MRB_ARGS_REST()` | Splat (`*args`) |
| `MRB_ARGS_BLOCK()` | Block (`&block`) |
| `MRB_ARGS_ANY()` | Any number (same as REST) |
| Macro | Meaning |
| ---------------------- | ------------------------------------- |
| `MRB_ARGS_NONE()` | No arguments |
| `MRB_ARGS_REQ(n)` | `n` required arguments |
| `MRB_ARGS_OPT(n)` | `n` optional arguments |
| `MRB_ARGS_ARG(r,o)` | `r` required + `o` optional |
| `MRB_ARGS_REST()` | Splat (`*args`) |
| `MRB_ARGS_BLOCK()` | Block (`&block`) |
| `MRB_ARGS_ANY()` | Any number (same as REST) |
| `MRB_ARGS_KEY(n,rest)` | `n` keyword args, `rest`=1 for `**kw` |
These can be combined with `|`:
@@ -192,27 +192,27 @@ mrb_int mrb_get_args(mrb_state *mrb, const char *format, ...);
### Format Specifiers
| Spec | Ruby Type | C Type(s) | Notes |
| ---- | --------- | --------- | ----- |
| `o` | any | `mrb_value` | No type check |
| `i` | Numeric | `mrb_int` | Coerces to integer |
| `f` | Numeric | `mrb_float` | Coerces to float |
| `b` | any | `mrb_bool` | Truthiness |
| `n` | String/Symbol | `mrb_sym` | Converts to symbol |
| `s` | String | `const char*, mrb_int` | Pointer + length |
| `z` | String | `const char*` | Null-terminated |
| `S` | String | `mrb_value` | String value |
| `A` | Array | `mrb_value` | Array value |
| `H` | Hash | `mrb_value` | Hash value |
| `C` | Class | `mrb_value` | Class/Module value |
| `c` | Class | `struct RClass*` | Class pointer |
| `a` | Array | `const mrb_value*, mrb_int` | Array pointer + length |
| `d` | C Data | `void*` | Requires `mrb_data_type*` |
| `&` | Block | `mrb_value` | Block argument |
| `*` | rest | `const mrb_value*, mrb_int` | Rest arguments |
| `\|` | — | — | Following args are optional |
| `?` | — | `mrb_bool` | Was previous optional arg given? |
| `:` | keywords | `mrb_kwargs` | Keyword arguments |
| Spec | Ruby Type | C Type(s) | Notes |
| ---- | ------------- | --------------------------- | -------------------------------- |
| `o` | any | `mrb_value` | No type check |
| `i` | Numeric | `mrb_int` | Coerces to integer |
| `f` | Numeric | `mrb_float` | Coerces to float |
| `b` | any | `mrb_bool` | Truthiness |
| `n` | String/Symbol | `mrb_sym` | Converts to symbol |
| `s` | String | `const char*, mrb_int` | Pointer + length |
| `z` | String | `const char*` | Null-terminated |
| `S` | String | `mrb_value` | String value |
| `A` | Array | `mrb_value` | Array value |
| `H` | Hash | `mrb_value` | Hash value |
| `C` | Class | `mrb_value` | Class/Module value |
| `c` | Class | `struct RClass*` | Class pointer |
| `a` | Array | `const mrb_value*, mrb_int` | Array pointer + length |
| `d` | C Data | `void*` | Requires `mrb_data_type*` |
| `&` | Block | `mrb_value` | Block argument |
| `*` | rest | `const mrb_value*, mrb_int` | Rest arguments |
| `\|` | — | — | Following args are optional |
| `?` | — | `mrb_bool` | Was previous optional arg given? |
| `:` | keywords | `mrb_kwargs` | Keyword arguments |
Adding `!` to `S`, `A`, `H`, `C`, `c`, `s`, `z`, `a`, `d` allows `nil`
(returns NULL/zero for nil).
@@ -608,14 +608,14 @@ my_yield_method(mrb_state *mrb, mrb_value self)
### Fiber States
| State | Meaning |
| ----- | ------- |
| `MRB_FIBER_CREATED` | Created but not yet resumed |
| `MRB_FIBER_RUNNING` | Currently executing |
| `MRB_FIBER_RESUMED` | Resumed another fiber |
| `MRB_FIBER_SUSPENDED` | Yielded, waiting to resume |
| State | Meaning |
| ----------------------- | -------------------------------- |
| `MRB_FIBER_CREATED` | Created but not yet resumed |
| `MRB_FIBER_RUNNING` | Currently executing |
| `MRB_FIBER_RESUMED` | Resumed another fiber |
| `MRB_FIBER_SUSPENDED` | Yielded, waiting to resume |
| `MRB_FIBER_TRANSFERRED` | Transferred via `Fiber#transfer` |
| `MRB_FIBER_TERMINATED` | Finished execution |
| `MRB_FIBER_TERMINATED` | Finished execution |
**Limitation:** fibers cannot yield across C function boundaries.
You cannot call `mrb_fiber_yield` from within a C-implemented
@@ -644,13 +644,13 @@ mrb_ccontext_free(mrb, cxt);
The `mrb_ccontext` structure provides several flags:
| Field | Purpose |
| ----- | ------- |
| Field | Purpose |
| ---------------- | --------------------------------------- |
| `capture_errors` | Collect parse errors instead of raising |
| `no_exec` | Compile without executing (get RProc) |
| `no_optimize` | Disable peephole optimizations |
| `no_ext_ops` | Disable extended operand instructions |
| `keep_lv` | Preserve local variables across loads |
| `no_exec` | Compile without executing (get RProc) |
| `no_optimize` | Disable peephole optimizations |
| `no_ext_ops` | Disable extended operand instructions |
| `keep_lv` | Preserve local variables across loads |
### Loading with Context
@@ -843,14 +843,14 @@ $ build/host/bin/mruby-config --libs # libraries
Key macros that affect ABI:
| Macro | Effect |
| ----- | ------ |
| `MRB_NO_BOXING` | Struct-based values (larger, debuggable) |
| `MRB_WORD_BOXING` | Single-word values (fast, 32-bit safe) |
| `MRB_NAN_BOXING` | NaN-tagged values (default on 32-bit) |
| `MRB_NO_FLOAT` | Disable Float support |
| `MRB_INT64` | 64-bit integers |
| `MRB_USE_FLOAT32` | 32-bit floats |
| Macro | Effect |
| ----------------- | ---------------------------------------- |
| `MRB_NO_BOXING` | Struct-based values (larger, debuggable) |
| `MRB_WORD_BOXING` | Single-word values (fast, 32-bit safe) |
| `MRB_NAN_BOXING` | NaN-tagged values (default on 32-bit) |
| `MRB_NO_FLOAT` | Disable Float support |
| `MRB_INT64` | 64-bit integers |
| `MRB_USE_FLOAT32` | 32-bit floats |
Mismatching these between library and application causes silent
data corruption.
+87 -87
View File
@@ -205,11 +205,11 @@ mruby's numeric type sizes depend on the boxing mode and platform.
### Integer
| Configuration | Range |
| ------------- | ----- |
| Configuration | Range |
| -------------------------------------- | ---------------- |
| 64-bit word boxing (default on 64-bit) | roughly +/- 2^62 |
| 32-bit word boxing (default on 32-bit) | roughly +/- 2^30 |
| NaN boxing (64-bit only) | -2^31 to 2^31-1 |
| NaN boxing (64-bit only) | -2^31 to 2^31-1 |
Integer overflow raises a `RangeError` unless the `mruby-bigint` gem
is included, in which case integers automatically promote to
@@ -235,32 +235,32 @@ With word boxing on 64-bit, many float values are stored inline
These classes are always available in mruby (no gem required):
| Class | Notes |
| ----- | ----- |
| Object | Base class for all objects |
| Module | Module definition and mixin |
| Class | Class definition and instantiation |
| NilClass | Singleton `nil` |
| TrueClass | Singleton `true` |
| FalseClass | Singleton `false` |
| Integer | Fixed-precision integer |
| Float | Floating-point (unless `MRB_NO_FLOAT`) |
| Symbol | Interned identifier |
| String | Mutable byte string |
| Array | Ordered collection |
| Hash | Key-value mapping |
| Range | Interval representation |
| Proc | Closure / callable object |
| Exception | Exception hierarchy root |
| StandardError | Common error base |
| Class | Notes |
| ------------- | -------------------------------------- |
| Object | Base class for all objects |
| Module | Module definition and mixin |
| Class | Class definition and instantiation |
| NilClass | Singleton `nil` |
| TrueClass | Singleton `true` |
| FalseClass | Singleton `false` |
| Integer | Fixed-precision integer |
| Float | Floating-point (unless `MRB_NO_FLOAT`) |
| Symbol | Interned identifier |
| String | Mutable byte string |
| Array | Ordered collection |
| Hash | Key-value mapping |
| Range | Interval representation |
| Proc | Closure / callable object |
| Exception | Exception hierarchy root |
| StandardError | Common error base |
### Core Modules
| Module | Notes |
| ------ | ----- |
| Kernel | Core methods (`puts`, `p`, `raise`, etc.) |
| Comparable | Comparison operators via `<=>` |
| Enumerable | Collection iteration methods |
| Module | Notes |
| ---------- | ----------------------------------------- |
| Kernel | Core methods (`puts`, `p`, `raise`, etc.) |
| Comparable | Comparison operators via `<=>` |
| Enumerable | Collection iteration methods |
## Standard Library (via gemboxes)
@@ -270,70 +270,70 @@ gembox provides the class or feature you need:
### Classes and Modules
| Class/Module | Gembox | Gem |
| ------------ | ------ | --- |
| Fiber | stdlib | mruby-fiber |
| Enumerator | stdlib | mruby-enumerator |
| Enumerator::Lazy | stdlib | mruby-enum-lazy |
| Set | stdlib | mruby-set |
| ObjectSpace | stdlib | mruby-objectspace |
| Time | stdlib-ext | mruby-time |
| Struct | stdlib-ext | mruby-struct |
| Data | stdlib-ext | mruby-data |
| Random | stdlib-ext | mruby-random |
| IO, File | stdlib-io | mruby-io |
| Socket | stdlib-io | mruby-socket |
| Dir | stdlib-io | mruby-dir |
| Errno | stdlib-io | mruby-errno |
| Math | math | mruby-math |
| Rational | math | mruby-rational |
| Complex | math | mruby-complex |
| Bigint | math | mruby-bigint |
| Method, UnboundMethod | metaprog | mruby-method |
| Class/Module | Gembox | Gem |
| --------------------- | ---------- | ----------------- |
| Fiber | stdlib | mruby-fiber |
| Enumerator | stdlib | mruby-enumerator |
| Enumerator::Lazy | stdlib | mruby-enum-lazy |
| Set | stdlib | mruby-set |
| ObjectSpace | stdlib | mruby-objectspace |
| Time | stdlib-ext | mruby-time |
| Struct | stdlib-ext | mruby-struct |
| Data | stdlib-ext | mruby-data |
| Random | stdlib-ext | mruby-random |
| IO, File | stdlib-io | mruby-io |
| Socket | stdlib-io | mruby-socket |
| Dir | stdlib-io | mruby-dir |
| Errno | stdlib-io | mruby-errno |
| Math | math | mruby-math |
| Rational | math | mruby-rational |
| Complex | math | mruby-complex |
| Bigint | math | mruby-bigint |
| Method, UnboundMethod | metaprog | mruby-method |
### Methods and Features
| Feature | Gembox | Gem |
| ------- | ------ | --- |
| `catch`/`throw` | stdlib | mruby-catch |
| `Kernel#sprintf`, `String#%` | stdlib-ext | mruby-sprintf |
| `Array#pack`, `String#unpack` | stdlib-ext | mruby-pack |
| `Kernel#rand` | stdlib-ext | mruby-random |
| `Kernel#eval` | metaprog | mruby-eval |
| `Kernel#binding` | metaprog | mruby-binding |
| `Proc#binding` | metaprog | mruby-proc-binding |
| Runtime compiler | metaprog | mruby-compiler |
| Feature | Gembox | Gem |
| ----------------------------- | ---------- | ------------------ |
| `catch`/`throw` | stdlib | mruby-catch |
| `Kernel#sprintf`, `String#%` | stdlib-ext | mruby-sprintf |
| `Array#pack`, `String#unpack` | stdlib-ext | mruby-pack |
| `Kernel#rand` | stdlib-ext | mruby-random |
| `Kernel#eval` | metaprog | mruby-eval |
| `Kernel#binding` | metaprog | mruby-binding |
| `Proc#binding` | metaprog | mruby-proc-binding |
| Runtime compiler | metaprog | mruby-compiler |
### Core Class Extensions
The `stdlib` gembox also extends built-in classes with additional
methods. These are included by default:
| Extension | Examples |
| --------- | -------- |
| Array extensions | `#dig`, `#union`, `#difference` |
| Hash extensions | `#dig`, `#transform_keys`, `#transform_values` |
| String extensions | `#encode`, `#bytes`, `#chars` |
| Numeric extensions | `Integer#digits`, `Integer#pow` |
| Comparable extensions | `#clamp` |
| Enumerable extensions | `#sort_by`, `#min_by`, `#max_by`, `#tally` |
| Range extensions | `#size`, `#cover?` |
| Proc extensions | `#<<`, `#>>` (composition) |
| Symbol extensions | `#to_proc` |
| Object extensions | `#then`, `#yield_self` |
| Kernel extensions | `#__method__` |
| Class/Module extensions | `Module#name` |
| Extension | Examples |
| ----------------------- | ---------------------------------------------- |
| Array extensions | `#dig`, `#union`, `#difference` |
| Hash extensions | `#dig`, `#transform_keys`, `#transform_values` |
| String extensions | `#encode`, `#bytes`, `#chars` |
| Numeric extensions | `Integer#digits`, `Integer#pow` |
| Comparable extensions | `#clamp` |
| Enumerable extensions | `#sort_by`, `#min_by`, `#max_by`, `#tally` |
| Range extensions | `#size`, `#cover?` |
| Proc extensions | `#<<`, `#>>` (composition) |
| Symbol extensions | `#to_proc` |
| Object extensions | `#then`, `#yield_self` |
| Kernel extensions | `#__method__` |
| Class/Module extensions | `Module#name` |
### Gembox Summary
| Gembox | Contents | Notes |
| ------ | -------- | ----- |
| `stdlib` | Core class extensions, Fiber, Enumerator, Set | Works with `MRB_NO_STDIO` and `MRB_NO_FLOAT` |
| `stdlib-ext` | Time, Struct, Data, Random, sprintf, pack | Works with `MRB_NO_STDIO` and `MRB_NO_FLOAT` |
| `stdlib-io` | IO, File, Dir, Socket, Errno | Requires stdio |
| `math` | Math, Rational, Complex, Bigint | Works with `MRB_NO_STDIO` |
| `metaprog` | eval, binding, Method, compiler | Works with `MRB_NO_STDIO` and `MRB_NO_FLOAT` |
| `default` | All of the above + CLI tools | Full installation |
| Gembox | Contents | Notes |
| ------------ | --------------------------------------------- | -------------------------------------------- |
| `stdlib` | Core class extensions, Fiber, Enumerator, Set | Works with `MRB_NO_STDIO` and `MRB_NO_FLOAT` |
| `stdlib-ext` | Time, Struct, Data, Random, sprintf, pack | Works with `MRB_NO_STDIO` and `MRB_NO_FLOAT` |
| `stdlib-io` | IO, File, Dir, Socket, Errno | Requires stdio |
| `math` | Math, Rational, Complex, Bigint | Works with `MRB_NO_STDIO` |
| `metaprog` | eval, binding, Method, compiler | Works with `MRB_NO_STDIO` and `MRB_NO_FLOAT` |
| `default` | All of the above + CLI tools | Full installation |
## Key Differences from CRuby
@@ -416,15 +416,15 @@ differently on 32-bit or NaN boxing configurations.
Key compile-time macros that affect language behavior:
| Macro | Effect |
| ----- | ------ |
| `MRB_NO_FLOAT` | Remove all float support |
| `MRB_USE_FLOAT32` | Use 32-bit float instead of double |
| `MRB_UTF8_STRING` | Enable UTF-8 string handling |
| `MRB_INT32` | Force 32-bit integer |
| `MRB_INT64` | Force 64-bit integer |
| `MRB_STR_LENGTH_MAX` | Max string length (default 1MB) |
| `MRB_ARY_LENGTH_MAX` | Max array length (default 2^17) |
| Macro | Effect |
| -------------------- | ---------------------------------- |
| `MRB_NO_FLOAT` | Remove all float support |
| `MRB_USE_FLOAT32` | Use 32-bit float instead of double |
| `MRB_UTF8_STRING` | Enable UTF-8 string handling |
| `MRB_INT32` | Force 32-bit integer |
| `MRB_INT64` | Force 64-bit integer |
| `MRB_STR_LENGTH_MAX` | Max string length (default 1MB) |
| `MRB_ARY_LENGTH_MAX` | Max array length (default 2^17) |
See [mrbconf.md](mrbconf.md) for the complete list of configuration
macros.
+4 -4
View File
@@ -202,10 +202,10 @@ any other heap page. The only differences are:
The page size is controlled by `MRB_HEAP_PAGE_SIZE` (default: 1024 slots).
Each page occupies:
| Platform | Slot size | Page size (approx) |
|----------|-----------|---------------------|
| 64-bit | 40 bytes | ~41 KB |
| 32-bit | 24 bytes | ~25 KB |
| Platform | Slot size | Page size (approx) |
| -------- | --------- | ------------------ |
| 64-bit | 40 bytes | ~41 KB |
| 32-bit | 24 bytes | ~25 KB |
To estimate pages for a given buffer: `pages = buffer_size / sizeof(mrb_heap_page)`.
Each page provides `MRB_HEAP_PAGE_SIZE` object slots.
+3 -3
View File
@@ -233,10 +233,10 @@ end
- Specifies 4th argument(`argc`) max value of `mrb_funcall`.
- Raises `ArgumentError` when the `argc` argument is bigger then this value `mrb_funcall`.
`KHASH_DEFAULT_SIZE`
`KHASH_INITIAL_SIZE`
- Default value is `32`.
- Specifies default size of khash table bucket.
- Specifies initial size of khash table bucket.
- Used in `kh_init_ ## name` function.
`MRB_NO_METHOD_CACHE`
@@ -272,7 +272,7 @@ deployment targets. Define one of the following:
`MRB_CONSTRAINED_BASELINE_PROFILE`
- For micro controllers.
- Enables `MRB_NO_METHOD_CACHE`, reduces `KHASH_DEFAULT_SIZE` to `16`,
- Enables `MRB_NO_METHOD_CACHE`, reduces `KHASH_INITIAL_SIZE` to `16`,
and `MRB_HEAP_PAGE_SIZE` to `256`.
`MRB_BASELINE_PROFILE`
+51 -51
View File
@@ -31,17 +31,17 @@ struct RBasic (8 bytes on 64-bit)
All object structs embed this header via `MRB_OBJECT_HEADER`:
| Struct | Ruby Type | Extra Fields |
| ------ | --------- | ------------ |
| `RObject` | Object instances | `iv` (instance variables) |
| `RClass` | Class/Module | `iv`, `mt` (method table), `super` |
| `RString` | String | embedded or heap buffer, length |
| `RArray` | Array | embedded or heap buffer, length |
| `RHash` | Hash | hash table or k-v array |
| `RProc` | Proc/Lambda | `irep` or C function, environment |
| `RData` | C data wrapper | `void *data`, `mrb_data_type` |
| `RFiber` | Fiber | `mrb_context` |
| `RException` | Exception | `iv` |
| Struct | Ruby Type | Extra Fields |
| ------------ | ---------------- | ---------------------------------- |
| `RObject` | Object instances | `iv` (instance variables) |
| `RClass` | Class/Module | `iv`, `mt` (method table), `super` |
| `RString` | String | embedded or heap buffer, length |
| `RArray` | Array | embedded or heap buffer, length |
| `RHash` | Hash | hash table or k-v array |
| `RProc` | Proc/Lambda | `irep` or C function, environment |
| `RData` | C data wrapper | `void *data`, `mrb_data_type` |
| `RFiber` | Fiber | `mrb_context` |
| `RException` | Exception | `iv` |
Immediate values (Integer, Symbol, `true`, `false`, `nil`) are encoded
directly in `mrb_value` without heap allocation. The encoding depends on
@@ -111,52 +111,52 @@ See [compiler.md](compiler.md) for detailed compiler internals,
### Core (`src/`)
| File | Responsibility |
| ---- | -------------- |
| `vm.c` | Bytecode dispatch loop, method invocation |
| `state.c` | `mrb_state` init/close, irep management |
| `gc.c` | Garbage collector (mark-sweep, incremental) |
| `class.c` | Class/module definition, method tables |
| `object.c` | Core object operations |
| `variable.c` | Instance/class/global variables, object shapes |
| `proc.c` | Proc/Lambda/closure handling |
| `array.c` | Array implementation |
| `string.c` | String implementation (embedded, shared, heap) |
| `hash.c` | Hash implementation (open addressing) |
| `numeric.c` | Integer/Float arithmetic |
| `symbol.c` | Symbol table and interning |
| `range.c` | Range implementation |
| `error.c` | Exception creation, raise, backtrace |
| `kernel.c` | Kernel module methods |
| `load.c` | `.mrb` bytecode loading |
| `dump.c` | Bytecode serialization (write `.mrb`) |
| `print.c` | Print/puts/p output |
| `backtrace.c` | Stack trace generation |
| File | Responsibility |
| ------------- | ---------------------------------------------- |
| `vm.c` | Bytecode dispatch loop, method invocation |
| `state.c` | `mrb_state` init/close, irep management |
| `gc.c` | Garbage collector (mark-sweep, incremental) |
| `class.c` | Class/module definition, method tables |
| `object.c` | Core object operations |
| `variable.c` | Instance/class/global variables, object shapes |
| `proc.c` | Proc/Lambda/closure handling |
| `array.c` | Array implementation |
| `string.c` | String implementation (embedded, shared, heap) |
| `hash.c` | Hash implementation (open addressing) |
| `numeric.c` | Integer/Float arithmetic |
| `symbol.c` | Symbol table and interning |
| `range.c` | Range implementation |
| `error.c` | Exception creation, raise, backtrace |
| `kernel.c` | Kernel module methods |
| `load.c` | `.mrb` bytecode loading |
| `dump.c` | Bytecode serialization (write `.mrb`) |
| `print.c` | Print/puts/p output |
| `backtrace.c` | Stack trace generation |
### Compiler (`mrbgems/mruby-compiler/core/`)
| File | Responsibility |
| ---- | -------------- |
| `parse.y` | Yacc grammar → AST |
| `y.tab.c` | Generated parser (from parse.y) |
| `codegen.c` | AST → bytecode (irep) |
| `node.h` | AST node type definitions |
| File | Responsibility |
| ----------- | ------------------------------- |
| `parse.y` | Yacc grammar → AST |
| `y.tab.c` | Generated parser (from parse.y) |
| `codegen.c` | AST → bytecode (irep) |
| `node.h` | AST node type definitions |
### Key Headers (`include/mruby/`)
| Header | Contents |
| ------ | -------- |
| `mruby.h` | `mrb_state`, core API declarations |
| `value.h` | `mrb_value`, type enums, value macros |
| `object.h` | `RBasic`, `RObject`, object header |
| `class.h` | `RClass`, method table types |
| `string.h` | `RString`, string macros |
| `array.h` | `RArray`, array macros |
| `hash.h` | `RHash`, hash API |
| `data.h` | `RData`, C data wrapping |
| `irep.h` | `mrb_irep`, bytecode structures |
| `compile.h` | Compiler context, `mrb_load_string` |
| `boxing_*.h` | Value boxing implementations |
| Header | Contents |
| ------------ | ------------------------------------- |
| `mruby.h` | `mrb_state`, core API declarations |
| `value.h` | `mrb_value`, type enums, value macros |
| `object.h` | `RBasic`, `RObject`, object header |
| `class.h` | `RClass`, method table types |
| `string.h` | `RString`, string macros |
| `array.h` | `RArray`, array macros |
| `hash.h` | `RHash`, hash API |
| `data.h` | `RData`, C data wrapping |
| `irep.h` | `mrb_irep`, bytecode structures |
| `compile.h` | Compiler context, `mrb_load_string` |
| `boxing_*.h` | Value boxing implementations |
## mrbgems System
+10 -10
View File
@@ -77,16 +77,16 @@ No boxing represents `mrb_value` by the C struct with `type` and the value union
## Comparison
| Property | Word Boxing | NaN Boxing | No Boxing |
| ---------------------- | ------------------ | ------------------ | -------------------- |
| `mrb_value` size | 1 word (4/8 byte) | 8 bytes | 2 words (8/16 bytes) |
| Default on | most platforms | (manual opt-in) | `host-debug` |
| Macro | `MRB_WORD_BOXING` | `MRB_NAN_BOXING` | `MRB_NO_BOXING` |
| Inline integers | yes (31/63 bit) | yes (32 bit) | yes (full width) |
| Inline floats (64-bit) | yes (rotation) | yes (native) | yes (struct field) |
| Inline floats (32-bit) | no (heap RFloat) | yes (native) | yes (struct field) |
| Pointer size limit | none | 48 bits | none |
| Debugger friendly | no | no | yes |
| Property | Word Boxing | NaN Boxing | No Boxing |
| ---------------------- | ----------------- | ---------------- | -------------------- |
| `mrb_value` size | 1 word (4/8 byte) | 8 bytes | 2 words (8/16 bytes) |
| Default on | most platforms | (manual opt-in) | `host-debug` |
| Macro | `MRB_WORD_BOXING` | `MRB_NAN_BOXING` | `MRB_NO_BOXING` |
| Inline integers | yes (31/63 bit) | yes (32 bit) | yes (full width) |
| Inline floats (64-bit) | yes (rotation) | yes (native) | yes (struct field) |
| Inline floats (32-bit) | no (heap RFloat) | yes (native) | yes (struct field) |
| Pointer size limit | none | 48 bits | none |
| Debugger friendly | no | no | yes |
## ABI Compatibility
+59 -59
View File
@@ -40,18 +40,18 @@ file: `mrbgems/mruby-compiler/core/parse.y`.
The parser maintains extensive state in `mrb_parser_state`:
- **lstate**: current lexer state (EXPR\_BEG, EXPR\_END, EXPR\_ARG,
EXPR\_DOT, EXPR\_FNAME, etc.). Controls how tokens like `+`/`-`
- **lstate**: current lexer state (EXPR_BEG, EXPR_END, EXPR_ARG,
EXPR_DOT, EXPR_FNAME, etc.). Controls how tokens like `+`/`-`
are interpreted (sign vs operator) and whether newlines are
significant.
- **locals**: stack of local variable lists (one per scope), stored
as cons-lists of symbols.
- **lex\_strterm**: string/heredoc parsing state for handling nested
- **lex_strterm**: string/heredoc parsing state for handling nested
interpolation.
- **cond\_stack**, **cmdarg\_stack**: bit stacks tracking
- **cond_stack**, **cmdarg_stack**: bit stacks tracking
conditional and command argument contexts.
- **tree**: root AST node after successful parse.
- **error\_buffer**: accumulated parse errors.
- **error_buffer**: accumulated parse errors.
### AST Nodes
@@ -123,10 +123,10 @@ reuse temporaries within an expression.
Instructions are emitted via helper functions:
- `genop_0(opcode)`: no operands
- `genop_1(opcode, a)`: one operand (auto-extends with OP\_EXT1
- `genop_1(opcode, a)`: one operand (auto-extends with OP_EXT1
if a > 255)
- `genop_2(opcode, a, b)`: two operands (auto-extends with
OP\_EXT1/2/3 as needed)
OP_EXT1/2/3 as needed)
- `genop_3(opcode, a, b, c)`: three operands
- `genop_W(opcode, a)`: 24-bit operand
- `genop_2S(opcode, a, b)`: one 8-bit + one 16-bit operand
@@ -176,14 +176,14 @@ mrb_irep
Pool entries store constants referenced by instructions:
| Type | Tag | Description |
| ---- | --- | ----------- |
| `IREP_TT_STR` | 0 | Dynamic string (heap allocated) |
| `IREP_TT_SSTR` | 2 | Static string (read-only) |
| `IREP_TT_INT32` | 1 | 32-bit integer |
| `IREP_TT_INT64` | 3 | 64-bit integer |
| `IREP_TT_FLOAT` | 5 | Floating-point number |
| `IREP_TT_BIGINT` | 7 | Arbitrary-precision integer |
| Type | Tag | Description |
| ---------------- | --- | ------------------------------- |
| `IREP_TT_STR` | 0 | Dynamic string (heap allocated) |
| `IREP_TT_SSTR` | 2 | Static string (read-only) |
| `IREP_TT_INT32` | 1 | 32-bit integer |
| `IREP_TT_INT64` | 3 | 64-bit integer |
| `IREP_TT_FLOAT` | 5 | Floating-point number |
| `IREP_TT_BIGINT` | 7 | Arbitrary-precision integer |
The code generator deduplicates pool entries: identical strings
and equal numeric values share the same pool index.
@@ -209,28 +209,28 @@ During exception unwinding, handlers are searched in reverse order
Standard instructions use 8-bit operands. When a value exceeds
255, extension prefixes widen operands to 16 bits:
| Prefix | Effect |
| ------ | ------ |
| `OP_EXT1` | First operand (a) becomes 16-bit |
| Prefix | Effect |
| --------- | --------------------------------- |
| `OP_EXT1` | First operand (a) becomes 16-bit |
| `OP_EXT2` | Second operand (b) becomes 16-bit |
| `OP_EXT3` | Both a and b become 16-bit |
| `OP_EXT3` | Both a and b become 16-bit |
Instruction formats:
| Format | Layout | Size |
| ------ | ------ | ---- |
| Z | opcode only | 1 byte |
| B | opcode + a(8) | 2 bytes |
| BB | opcode + a(8) + b(8) | 3 bytes |
| BBB | opcode + a(8) + b(8) + c(8) | 4 bytes |
| BS | opcode + a(8) + b(16) | 4 bytes |
| BSS | opcode + a(8) + b(16) + c(16) | 6 bytes |
| S | opcode + a(16) | 3 bytes |
| W | opcode + a(24) | 4 bytes |
| Format | Layout | Size |
| ------ | ----------------------------- | ------- |
| Z | opcode only | 1 byte |
| B | opcode + a(8) | 2 bytes |
| BB | opcode + a(8) + b(8) | 3 bytes |
| BBB | opcode + a(8) + b(8) + c(8) | 4 bytes |
| BS | opcode + a(8) + b(16) | 4 bytes |
| BSS | opcode + a(8) + b(16) + c(16) | 6 bytes |
| S | opcode + a(16) | 3 bytes |
| W | opcode + a(24) | 4 bytes |
See [opcode.md](opcode.md) for the full instruction table.
## OP\_ENTER: Argument Specification
## OP_ENTER: Argument Specification
`OP_ENTER` encodes a method's argument layout in a 24-bit value
(W format). The bit fields are defined by the `MRB_ARGS_*` macros:
@@ -258,16 +258,16 @@ string interning for common symbols.
Generated by `lib/mruby/presym.rb`, the presym table maps symbol
names to compile-time constants:
| Macro | Example | Symbol |
| ----- | ------- | ------ |
| `MRB_SYM(name)` | `MRB_SYM(initialize)` | `:initialize` |
| `MRB_SYM_B(name)` | `MRB_SYM_B(map)` | `:map!` |
| `MRB_SYM_Q(name)` | `MRB_SYM_Q(nil)` | `:nil?` |
| `MRB_SYM_E(name)` | `MRB_SYM_E(name)` | `:name=` |
| `MRB_OPSYM(op)` | `MRB_OPSYM(add)` | `:+` |
| `MRB_IVSYM(name)` | `MRB_IVSYM(name)` | `:@name` |
| `MRB_CVSYM(name)` | `MRB_CVSYM(count)` | `:@@count` |
| `MRB_GVSYM(name)` | `MRB_GVSYM(stdout)` | `:$stdout` |
| Macro | Example | Symbol |
| ----------------- | --------------------- | ------------- |
| `MRB_SYM(name)` | `MRB_SYM(initialize)` | `:initialize` |
| `MRB_SYM_B(name)` | `MRB_SYM_B(map)` | `:map!` |
| `MRB_SYM_Q(name)` | `MRB_SYM_Q(nil)` | `:nil?` |
| `MRB_SYM_E(name)` | `MRB_SYM_E(name)` | `:name=` |
| `MRB_OPSYM(op)` | `MRB_OPSYM(add)` | `:+` |
| `MRB_IVSYM(name)` | `MRB_IVSYM(name)` | `:@name` |
| `MRB_CVSYM(name)` | `MRB_CVSYM(count)` | `:@@count` |
| `MRB_GVSYM(name)` | `MRB_GVSYM(stdout)` | `:$stdout` |
## Binary Format (.mrb)
@@ -299,25 +299,25 @@ mrbc -Boutput source.rb # C array format
## Compilation Limits
| Limit | Value |
| ----- | ----- |
| Max nesting depth | 256 (`MRB_CODEGEN_LEVEL_MAX`) |
| Max local variables | 255 (uint16 `nlocals`) |
| Max symbols per irep | 65535 |
| Max operand (standard) | 255 (8-bit) |
| Max operand (extended) | 65535 (16-bit) |
| Limit | Value |
| ---------------------- | ----------------------------- |
| Max nesting depth | 256 (`MRB_CODEGEN_LEVEL_MAX`) |
| Max local variables | 255 (uint16 `nlocals`) |
| Max symbols per irep | 65535 |
| Max operand (standard) | 255 (8-bit) |
| Max operand (extended) | 65535 (16-bit) |
## Source Files
| File | Contents |
| ---- | -------- |
| `mrbgems/mruby-compiler/core/parse.y` | Lrama/Bison grammar |
| `mrbgems/mruby-compiler/core/y.tab.c` | Generated parser |
| `mrbgems/mruby-compiler/core/codegen.c` | Code generator |
| `mrbgems/mruby-compiler/core/node.h` | AST node types |
| `include/mruby/irep.h` | IRep structure definition |
| `include/mruby/compile.h` | Compiler context API |
| `include/mruby/ops.h` | Opcode definitions |
| `src/load.c` | Binary format loader |
| `src/dump.c` | Binary format writer |
| `lib/mruby/presym.rb` | Presym table generator |
| File | Contents |
| --------------------------------------- | ------------------------- |
| `mrbgems/mruby-compiler/core/parse.y` | Lrama/Bison grammar |
| `mrbgems/mruby-compiler/core/y.tab.c` | Generated parser |
| `mrbgems/mruby-compiler/core/codegen.c` | Code generator |
| `mrbgems/mruby-compiler/core/node.h` | AST node types |
| `include/mruby/irep.h` | IRep structure definition |
| `include/mruby/compile.h` | Compiler context API |
| `include/mruby/ops.h` | Opcode definitions |
| `src/load.c` | Binary format loader |
| `src/dump.c` | Binary format writer |
| `lib/mruby/presym.rb` | Presym table generator |
+22 -22
View File
@@ -26,12 +26,12 @@ pauses.
Every heap-allocated object has a color stored in
`RBasic::gc_color` (3 bits):
| Color | Value | Meaning |
| ----- | ----- | ------- |
| White (A or B) | 1 or 2 | Unmarked, candidate for collection |
| Gray | 0 | Marked, but children not yet scanned |
| Black | 4 | Fully marked and scanned |
| Red | 7 | Static/ROM object, never collected |
| Color | Value | Meaning |
| -------------- | ------ | ------------------------------------ |
| White (A or B) | 1 or 2 | Unmarked, candidate for collection |
| Gray | 0 | Marked, but children not yet scanned |
| Black | 4 | Fully marked and scanned |
| Red | 7 | Static/ROM object, never collected |
The GC uses two white types (A and B) in a flip-flop scheme. At the
start of each GC cycle, the meaning of "current white" is flipped by
@@ -107,7 +107,7 @@ The GC operates as a three-state machine:
GC_STATE_ROOT --> GC_STATE_MARK --> GC_STATE_SWEEP --> GC_STATE_ROOT
```
### Root Scan (GC\_STATE\_ROOT)
### Root Scan (GC_STATE_ROOT)
Marks objects directly reachable from the VM:
@@ -121,7 +121,7 @@ Marks objects directly reachable from the VM:
After root scanning, the white color is flipped.
### Incremental Marking (GC\_STATE\_MARK)
### Incremental Marking (GC_STATE_MARK)
Gray objects are popped from the gray stack and their children
marked. Each step processes a limited number of objects:
@@ -137,7 +137,7 @@ When the gray stack is exhausted, the final marking phase re-marks
the arena and global variables to catch objects created during
marking, then transitions to sweep.
### Sweep (GC\_STATE\_SWEEP)
### Sweep (GC_STATE_SWEEP)
Iterates through heap pages. For each object:
@@ -325,15 +325,15 @@ From Ruby: `GC.start`.
### Compile-Time
| Macro | Default | Description |
| ----- | ------- | ----------- |
| `MRB_HEAP_PAGE_SIZE` | 1024 | Objects per heap page |
| `MRB_GRAY_STACK_SIZE` | 1024 | Gray stack capacity |
| `MRB_GC_ARENA_SIZE` | 100 | Arena size (fixed mode) or initial size |
| `MRB_GC_FIXED_ARENA` | off | Use fixed-size arena |
| `MRB_GC_TURN_OFF_GENERATIONAL` | off | Disable generational mode |
| `MRB_GC_STRESS` | off | Full GC on every allocation (debug) |
| `MRB_USE_MALLOC_TRIM` | off | Call `malloc_trim()` after full GC |
| Macro | Default | Description |
| ------------------------------ | ------- | --------------------------------------- |
| `MRB_HEAP_PAGE_SIZE` | 1024 | Objects per heap page |
| `MRB_GRAY_STACK_SIZE` | 1024 | Gray stack capacity |
| `MRB_GC_ARENA_SIZE` | 100 | Arena size (fixed mode) or initial size |
| `MRB_GC_FIXED_ARENA` | off | Use fixed-size arena |
| `MRB_GC_TURN_OFF_GENERATIONAL` | off | Disable generational mode |
| `MRB_GC_STRESS` | off | Full GC on every allocation (debug) |
| `MRB_USE_MALLOC_TRIM` | off | Call `malloc_trim()` after full GC |
### Runtime
@@ -350,8 +350,8 @@ GC.disable # disable GC
## Source Files
| File | Contents |
| ---- | -------- |
| `src/gc.c` | GC implementation (~1400 lines) |
| File | Contents |
| -------------------- | --------------------------------- |
| `src/gc.c` | GC implementation (~1400 lines) |
| `include/mruby/gc.h` | `mrb_gc` structure, public GC API |
| `include/mruby.h` | Arena save/restore macros |
| `include/mruby.h` | Arena save/restore macros |
+19 -19
View File
@@ -97,12 +97,12 @@ return n + 1 (skip self)
### Call Context Info (cci)
| Value | Name | Meaning |
| ----- | ---- | ------- |
| 0 | `CINFO_NONE` | Normal VM-to-VM call |
| 1 | `CINFO_DIRECT` | Explicit VM call (block, lambda.call) |
| 2 | `CINFO_SKIP` | Skip frame in stack traces |
| 3 | `CINFO_RESUMED` | Fiber resumed (stop execution) |
| Value | Name | Meaning |
| ----- | --------------- | ------------------------------------- |
| 0 | `CINFO_NONE` | Normal VM-to-VM call |
| 1 | `CINFO_DIRECT` | Explicit VM call (block, lambda.call) |
| 2 | `CINFO_SKIP` | Skip frame in stack traces |
| 3 | `CINFO_RESUMED` | Fiber resumed (stop execution) |
## Dispatch Loop
@@ -241,13 +241,13 @@ correctness.
### Proc Types
| Flag | Meaning |
| ---- | ------- |
| `MRB_PROC_CFUNC_FL` | C function (not irep-based) |
| `MRB_PROC_STRICT` | Lambda (strict argument check) |
| `MRB_PROC_ORPHAN` | No environment attachment |
| `MRB_PROC_ENVSET` | Has captured environment |
| `MRB_PROC_SCOPE` | Defines a new variable scope |
| Flag | Meaning |
| ------------------- | ------------------------------ |
| `MRB_PROC_CFUNC_FL` | C function (not irep-based) |
| `MRB_PROC_STRICT` | Lambda (strict argument check) |
| `MRB_PROC_ORPHAN` | No environment attachment |
| `MRB_PROC_ENVSET` | Has captured environment |
| `MRB_PROC_SCOPE` | Defines a new variable scope |
## Fiber Switching
@@ -317,9 +317,9 @@ ensuring the incremental GC correctly tracks live references.
## Source Files
| File | Contents |
| ---- | -------- |
| `src/vm.c` | Dispatch loop, method invocation (~1900 lines) |
| `include/mruby.h` | `mrb_state`, `mrb_callinfo`, `mrb_context` |
| `include/mruby/proc.h` | `RProc`, `REnv` structures |
| `include/mruby/throw.h` | `MRB_TRY`/`MRB_CATCH` macros |
| File | Contents |
| ----------------------- | ---------------------------------------------- |
| `src/vm.c` | Dispatch loop, method invocation (~1900 lines) |
| `include/mruby.h` | `mrb_state`, `mrb_callinfo`, `mrb_context` |
| `include/mruby/proc.h` | `RProc`, `REnv` structures |
| `include/mruby/throw.h` | `MRB_TRY`/`MRB_CATCH` macros |
+3 -3
View File
@@ -274,11 +274,11 @@ enabled with the `MRB_UTF8_STRING` compile flag.
Integer size depends on the value boxing configuration:
| Configuration | Integer range |
| ------------- | ------------- |
| Configuration | Integer range |
| ----------------------------- | ---------------- |
| Word boxing, 64-bit (default) | roughly +/- 2^62 |
| Word boxing, 32-bit (default) | roughly +/- 2^30 |
| NaN boxing (64-bit only) | -2^31 to 2^31-1 |
| NaN boxing (64-bit only) | -2^31 to 2^31-1 |
Code relying on 64-bit integer precision may behave differently
across configurations. The `mruby-bigint` gem provides
+4 -4
View File
@@ -138,8 +138,8 @@
/* turn off generational GC by default */
//#define MRB_GC_TURN_OFF_GENERATIONAL
/* default size of khash table bucket */
//#define KHASH_DEFAULT_SIZE 32
/* initial size of khash table bucket */
//#define KHASH_INITIAL_SIZE 32
/* allocated memory address alignment */
//#define POOL_ALIGNMENT 4
@@ -208,8 +208,8 @@
# define MRB_NO_METHOD_CACHE
# endif
# ifndef KHASH_DEFAULT_SIZE
# define KHASH_DEFAULT_SIZE 16
# ifndef KHASH_INITIAL_SIZE
# define KHASH_INITIAL_SIZE 16
# endif
# ifndef MRB_HEAP_PAGE_SIZE
+1 -1
View File
@@ -21,7 +21,7 @@ typedef uint32_t khint_t;
typedef khint_t khiter_t;
#ifndef KHASH_INITIAL_SIZE
# define KHASH_INITIAL_SIZE 8
# define KHASH_INITIAL_SIZE 32
#endif
#define KHASH_MIN_SIZE 8
#define KHASH_SMALL_LIMIT 4
+4
View File
@@ -316,7 +316,11 @@ struct RCptr {
#endif
#define mrb_test(o) mrb_bool(o)
#ifndef mrb_bigint_p
#ifdef MRB_USE_BIGINT
#define mrb_bigint_p(o) (mrb_type(o) == MRB_TT_BIGINT)
#else
#define mrb_bigint_p(o) FALSE
#endif
#endif
/**
+3 -3
View File
@@ -57,7 +57,7 @@ MRB_BEGIN_DECL
/*
* Patch level string. (optional)
*/
#define MRUBY_PATCHLEVEL_STR ""
#define MRUBY_PATCHLEVEL_STR "RC4"
#ifndef MRUBY_PATCHLEVEL_STR
# if MRUBY_PATCHLEVEL < 0
@@ -80,7 +80,7 @@ MRB_BEGIN_DECL
/*
* Release year.
*/
#define MRUBY_RELEASE_YEAR 2025
#define MRUBY_RELEASE_YEAR 2026
/*
* Release month.
@@ -90,7 +90,7 @@ MRB_BEGIN_DECL
/*
* Release day.
*/
#define MRUBY_RELEASE_DAY 20
#define MRUBY_RELEASE_DAY 13
/*
* Release date as a string.
+2 -1
View File
@@ -109,7 +109,8 @@ module MRuby
if build.kind_of?(MRuby::CrossBuild)
return %w(x86_64-w64-mingw32 i686-w64-mingw32).include?(build.host_target)
elsif build.kind_of?(MRuby::Build)
return ('A'..'Z').to_a.any? { |vol| Dir.exist?("#{vol}:") }
return ('A'..'Z').to_a.any? { |vol| Dir.exist?("#{vol}:") } ||
('a'..'z').to_a.any? { |vol| Dir.exist?("/#{vol}/") }
end
return false
end
+7 -29
View File
@@ -611,37 +611,15 @@ class Array
# ary.product(*arys) -> array
# ary.product(*arys) { |item| ... } -> self
def product(*arys, &block)
size = arys.size
i = size
while i > 0
i -= 1
unless arys[i].kind_of?(Array)
raise TypeError, "no implicit conversion into Array"
gen = __product_generate(arys, &block)
return gen unless block
if gen
while group = __product_next(arys, gen)
yield group
end
end
i = size
total = self.size
total *= arys[i -= 1].size while i > 0
if block
i = 0
while i < total
group = self.__product_group(arys, i, size + 1)
block.call(group)
i += 1
end
return self
else
result = [nil] * total
i = 0
while i < total
group = self.__product_group(arys, i, size + 1)
result[i] = group
i += 1
end
return result
end
self
end
##
+147 -41
View File
@@ -6,6 +6,7 @@
#include <mruby/hash.h>
#include <mruby/data.h>
#include <mruby/class.h>
#include <mruby/numeric.h>
#include <mruby/internal.h>
#include <mruby/khash.h>
#include <mruby/error.h>
@@ -78,11 +79,14 @@ ary_assoc(mrb_state *mrb, mrb_value ary)
{
mrb_value k = mrb_get_arg1(mrb);
int ai = mrb_gc_arena_save(mrb);
for (mrb_int i = 0; i < RARRAY_LEN(ary); i++) {
mrb_value v = mrb_check_array_type(mrb, RARRAY_PTR(ary)[i]);
mrb_gc_protect(mrb, v); // v may be removed from ary by mrb_equal()
if (!mrb_nil_p(v) && RARRAY_LEN(v) > 0 &&
mrb_equal(mrb, RARRAY_PTR(v)[0], k))
return v;
mrb_gc_arena_restore(mrb, ai);
}
return mrb_nil_value();
}
@@ -106,12 +110,15 @@ ary_rassoc(mrb_state *mrb, mrb_value ary)
{
mrb_value value = mrb_get_arg1(mrb);
int ai = mrb_gc_arena_save(mrb);
for (mrb_int i = 0; i < RARRAY_LEN(ary); i++) {
mrb_value v = RARRAY_PTR(ary)[i];
mrb_gc_protect(mrb, v); // v may be removed from ary by mrb_equal()
if (mrb_array_p(v) &&
RARRAY_LEN(v) > 1 &&
mrb_equal(mrb, RARRAY_PTR(v)[1], value))
return v;
mrb_gc_arena_restore(mrb, ai);
}
return mrb_nil_value();
}
@@ -409,9 +416,12 @@ ary_init_temp_set(mrb_state *mrb, ary_set_t *set, mrb_int capacity)
static void
ary_populate_temp_set(mrb_state *mrb, ary_set_t *set, mrb_value ary)
{
mrb_int len = RARRAY_LEN(ary);
for (mrb_int i = 0; i < len; i++) {
kh_put(ary_set, mrb, set, RARRAY_PTR(ary)[i]);
int ai = mrb_gc_arena_save(mrb);
for (mrb_int i = 0; i < RARRAY_LEN(ary); i++) {
mrb_value p = RARRAY_PTR(ary)[i];
mrb_gc_protect(mrb, p); // p may be removed from ary by kh_put(ary_set, ...)
kh_put(ary_set, mrb, set, p);
mrb_gc_arena_restore(mrb, ai);
}
}
@@ -460,12 +470,15 @@ ary_subtract_body(mrb_state *mrb, void *data)
ary_populate_temp_set(mrb, ctx->set, ctx->argv[i]);
}
int ai = mrb_gc_arena_save(mrb);
for (mrb_int i = 0; i < RARRAY_LEN(ctx->self); i++) {
mrb_value p = RARRAY_PTR(ctx->self)[i];
mrb_gc_protect(mrb, p); // p may be removed from self by kh_get(ary_set, ...)
khiter_t k = kh_get(ary_set, mrb, ctx->set, p);
if (kh_is_end(ctx->set, k)) { /* key doesn't exist in any ary */
mrb_ary_push(mrb, ctx->result, p);
}
mrb_gc_arena_restore(mrb, ai);
}
return ctx->result;
@@ -499,13 +512,13 @@ ary_subtract_internal(mrb_state *mrb, mrb_value self, mrb_int argc, const mrb_va
}
}
else {
mrb_int self_len = RARRAY_LEN(self);
for (mrb_int i = 0; i < self_len; i++) {
int ai = mrb_gc_arena_save(mrb);
for (mrb_int i = 0; i < RARRAY_LEN(self); i++) {
mrb_value p = RARRAY_PTR(self)[i];
mrb_gc_protect(mrb, p); // p may be removed from self by mrb_equal()
mrb_bool found = FALSE;
for (mrb_int j = 0; j < argc; j++) {
mrb_int len = RARRAY_LEN(argv[j]);
for (mrb_int k = 0; k < len; k++) {
for (mrb_int k = 0; k < RARRAY_LEN(argv[j]); k++) {
if (mrb_equal(mrb, p, RARRAY_PTR(argv[j])[k])) {
found = TRUE;
break;
@@ -516,6 +529,7 @@ ary_subtract_internal(mrb_state *mrb, mrb_value self, mrb_int argc, const mrb_va
if (!found) {
mrb_ary_push(mrb, result, p);
}
mrb_gc_arena_restore(mrb, ai);
}
}
@@ -559,12 +573,10 @@ ary_difference(mrb_state *mrb, mrb_value self)
return ary_subtract_internal(mrb, self, argc, argv);
}
static void
add_uniq(mrb_state *mrb, mrb_value item, mrb_value result)
{
const mrb_int len = RARRAY_LEN(result);
for (mrb_int i = 0; i < len; i++) {
for (mrb_int i = 0; i < RARRAY_LEN(result); i++) {
if (mrb_eql(mrb, item, RARRAY_PTR(result)[i])) {
return;
}
@@ -584,15 +596,18 @@ static mrb_value
ary_union_body(mrb_state *mrb, void *data)
{
struct ary_union_ctx *ctx = (struct ary_union_ctx *)data;
int ai = mrb_gc_arena_save(mrb);
/* Add unique elements from self */
for (mrb_int i = 0; i < RARRAY_LEN(ctx->self_copy); i++) {
mrb_value elem = RARRAY_PTR(ctx->self_copy)[i];
mrb_gc_protect(mrb, elem); // elem may be removed from self_copy by kh_get(ary_set, ...)
khiter_t k = kh_get(ary_set, mrb, ctx->set, elem);
if (kh_is_end(ctx->set, k)) {
kh_put(ary_set, mrb, ctx->set, elem);
mrb_ary_push(mrb, ctx->result, elem);
}
mrb_gc_arena_restore(mrb, ai);
}
/* Add unique elements from others */
@@ -600,11 +615,13 @@ ary_union_body(mrb_state *mrb, void *data)
mrb_value other = ctx->argv[i];
for (mrb_int j = 0; j < RARRAY_LEN(other); j++) {
mrb_value elem = RARRAY_PTR(other)[j];
mrb_gc_protect(mrb, elem); // elem may be removed from other by kh_get(ary_set, ...)
khiter_t k = kh_get(ary_set, mrb, ctx->set, elem);
if (kh_is_end(ctx->set, k)) {
kh_put(ary_set, mrb, ctx->set, elem);
mrb_ary_push(mrb, ctx->result, elem);
}
mrb_gc_arena_restore(mrb, ai);
}
}
@@ -636,19 +653,25 @@ ary_union_internal(mrb_state *mrb, mrb_value self, mrb_int argc, const mrb_value
}
}
else {
int ai = mrb_gc_arena_save(mrb);
/* Use linear search for small arrays */
/* Add unique elements from self */
mrb_int alen = RARRAY_LEN(self);
for (mrb_int i = 0; i < alen; i++) {
add_uniq(mrb, RARRAY_PTR(self)[i], result);
for (mrb_int i = 0; i < RARRAY_LEN(self); i++) {
mrb_value p = RARRAY_PTR(self)[i];
mrb_gc_protect(mrb, p); // p may be removed from self by add_uniq()
add_uniq(mrb, p, result);
mrb_gc_arena_restore(mrb, ai);
}
/* Add unique elements from others */
for (mrb_int i = 0; i < argc; i++) {
mrb_value other = argv[i];
mrb_int olen = RARRAY_LEN(other);
for (mrb_int j = 0; j < olen; j++) {
add_uniq(mrb, RARRAY_PTR(other)[j], result);
for (mrb_int j = 0; j < RARRAY_LEN(other); j++) {
mrb_value p = RARRAY_PTR(other)[j];
mrb_gc_protect(mrb, p); // p may be removed from other by add_uniq()
add_uniq(mrb, p, result);
mrb_gc_arena_restore(mrb, ai);
}
}
}
@@ -711,13 +734,16 @@ ary_intersection_body(mrb_state *mrb, void *data)
ary_populate_temp_set(mrb, ctx->set, ctx->argv[i]);
}
int ai = mrb_gc_arena_save(mrb);
for (mrb_int i = 0; i < RARRAY_LEN(ctx->self); i++) {
mrb_value p = RARRAY_PTR(ctx->self)[i];
mrb_gc_protect(mrb, p); // p may be removed from self by kh_get(ary_set, ...)
khiter_t k = kh_get(ary_set, mrb, ctx->set, p);
if (!kh_is_end(ctx->set, k)) {
mrb_ary_push(mrb, ctx->result, p);
kh_del(ary_set, mrb, ctx->set, k);
}
mrb_gc_arena_restore(mrb, ai);
}
return ctx->result;
@@ -751,15 +777,15 @@ ary_intersection_internal(mrb_state *mrb, mrb_value self, mrb_int argc, const mr
}
}
else {
mrb_int self_len = RARRAY_LEN(self);
for (mrb_int i = 0; i < self_len; i++) {
int ai = mrb_gc_arena_save(mrb);
for (mrb_int i = 0; i < RARRAY_LEN(self); i++) {
mrb_value p = RARRAY_PTR(self)[i];
mrb_gc_protect(mrb, p); // p may be removed from self by mrb_equal()
mrb_bool found_in_all = TRUE;
for (mrb_int j = 0; j < argc; j++) {
mrb_bool found_in_current_other = FALSE;
mrb_int len = RARRAY_LEN(argv[j]);
for (mrb_int k = 0; k < len; k++) {
for (mrb_int k = 0; k < RARRAY_LEN(argv[j]); k++) {
if (mrb_equal(mrb, p, RARRAY_PTR(argv[j])[k])) {
found_in_current_other = TRUE;
break;
@@ -773,8 +799,7 @@ ary_intersection_internal(mrb_state *mrb, mrb_value self, mrb_int argc, const mr
if (found_in_all) {
mrb_bool already_added = FALSE;
mrb_int result_len = RARRAY_LEN(result);
for (mrb_int j = 0; j < result_len; j++) {
for (mrb_int j = 0; j < RARRAY_LEN(result); j++) {
if (mrb_equal(mrb, p, RARRAY_PTR(result)[j])) {
already_added = TRUE;
break;
@@ -784,6 +809,7 @@ ary_intersection_internal(mrb_state *mrb, mrb_value self, mrb_int argc, const mr
mrb_ary_push(mrb, result, p);
}
}
mrb_gc_arena_restore(mrb, ai);
}
}
return result;
@@ -854,8 +880,12 @@ ary_intersect_p_body(mrb_state *mrb, void *data)
ary_populate_temp_set(mrb, ctx->set, ctx->shorter_ary_copy);
int ai = mrb_gc_arena_save(mrb);
for (mrb_int i = 0; i < RARRAY_LEN(ctx->longer_ary); i++) {
khiter_t k = kh_get(ary_set, mrb, ctx->set, RARRAY_PTR(ctx->longer_ary)[i]);
mrb_value p = RARRAY_PTR(ctx->longer_ary)[i];
mrb_gc_protect(mrb, p); // p may be removed from longer_ary by kh_get(ary_set, ...)
khiter_t k = kh_get(ary_set, mrb, ctx->set, p);
mrb_gc_arena_restore(mrb, ai);
if (!kh_is_end(ctx->set, k)) {
*ctx->found = TRUE;
break;
@@ -905,12 +935,16 @@ ary_intersect_p(mrb_state *mrb, mrb_value self)
}
}
else {
int ai = mrb_gc_arena_save(mrb);
for (mrb_int i = 0; i < RARRAY_LEN(longer_ary); i++) {
mrb_value p = RARRAY_PTR(longer_ary)[i];
mrb_gc_protect(mrb, p); // p may be removed from longer_ary by mrb_equal()
for (mrb_int j = 0; j < RARRAY_LEN(shorter_ary); j++) {
if (mrb_equal(mrb, RARRAY_PTR(longer_ary)[i], RARRAY_PTR(shorter_ary)[j])) {
if (mrb_equal(mrb, p, RARRAY_PTR(shorter_ary)[j])) {
return mrb_true_value();
}
}
mrb_gc_arena_restore(mrb, ai);
}
}
@@ -1041,6 +1075,7 @@ ary_fill_exec(mrb_state *mrb, mrb_value self)
}
/* Fill the array */
mrb_ary_modify(mrb, ary);
mrb_value *ptr = ARY_PTR(ary) + start;
for (mrb_int i = 0; i < length; i++) {
ptr[i] = obj;
@@ -1058,7 +1093,6 @@ struct ary_uniq_bang_ctx {
mrb_value self_copy;
mrb_value self;
mrb_int *write_pos;
mrb_int len;
};
static mrb_value
@@ -1068,15 +1102,19 @@ ary_uniq_bang_body(mrb_state *mrb, void *data)
ary_populate_temp_set(mrb, ctx->set, ctx->self_copy);
for (mrb_int read_pos = 0; read_pos < ctx->len; read_pos++) {
int ai = mrb_gc_arena_save(mrb);
for (mrb_int read_pos = 0; read_pos < RARRAY_LEN(ctx->self); read_pos++) {
mrb_value elem = RARRAY_PTR(ctx->self)[read_pos];
mrb_gc_protect(mrb, elem); // elem may be removed from self by kh_get(ary_set, ...)
khiter_t k = kh_get(ary_set, mrb, ctx->set, elem);
if (!kh_is_end(ctx->set, k)) {
if (*ctx->write_pos != read_pos) {
if (*ctx->write_pos != read_pos && *ctx->write_pos < RARRAY_LEN(ctx->self)) {
mrb_ary_modify(mrb, mrb_ary_ptr(ctx->self));
RARRAY_PTR(ctx->self)[*ctx->write_pos] = elem;
}
(*ctx->write_pos)++;
kh_del(ary_set, mrb, ctx->set, k);
mrb_gc_arena_restore(mrb, ai);
}
}
@@ -1103,28 +1141,32 @@ ary_uniq_bang(mrb_state *mrb, mrb_value self)
ary_set_t *set = &set_struct;
ary_init_temp_set(mrb, set, len);
struct ary_uniq_bang_ctx ctx = { set, self_copy, self, &write_pos, len };
struct ary_uniq_bang_ctx ctx = { set, self_copy, self, &write_pos };
mrb_value result;
MRB_ENSURE(mrb, result, ary_uniq_bang_body, &ctx) {
ary_destroy_temp_set(mrb, set);
}
}
else {
for (mrb_int read_pos = 0; read_pos < len; read_pos++) {
int ai = mrb_gc_arena_save(mrb);
for (mrb_int read_pos = 0; read_pos < RARRAY_LEN(self); read_pos++) {
mrb_value elem = RARRAY_PTR(self)[read_pos];
mrb_gc_protect(mrb, elem); // elem may be removed from self by mrb_equal()
mrb_bool found = FALSE;
for (mrb_int j = 0; j < write_pos; j++) {
for (mrb_int j = 0; j < write_pos && j < RARRAY_LEN(self); j++) {
if (mrb_equal(mrb, elem, RARRAY_PTR(self)[j])) {
found = TRUE;
break;
}
}
if (!found) {
if (write_pos != read_pos) {
if (write_pos != read_pos && write_pos < RARRAY_LEN(self)) {
mrb_ary_modify(mrb, mrb_ary_ptr(self));
RARRAY_PTR(self)[write_pos] = elem;
}
write_pos++;
}
mrb_gc_arena_restore(mrb, ai);
}
}
@@ -1373,6 +1415,13 @@ ary_insert(mrb_state *mrb, mrb_value self)
return self;
}
struct ary_product_generator {
mrb_int total;
mrb_int cursor;
};
static struct mrb_data_type ary_product_generator_type = { "ary_product_generator", mrb_free };
/*
* Internal helper for Array#product to construct a group array.
* Takes the base array (self), the array of other arrays (arys),
@@ -1380,29 +1429,85 @@ ary_insert(mrb_state *mrb, mrb_value self)
* of the group array (group_len).
*/
static mrb_value
ary_product_group(mrb_state *mrb, mrb_value self_ary)
ary_product_fetch(mrb_state *mrb, mrb_value self_ary, mrb_value arys_ary, mrb_int n)
{
mrb_value arys_ary;
mrb_int current_i, group_len;
mrb_get_args(mrb, "Aii", &arys_ary, &current_i, &group_len);
mrb_value group = mrb_ary_new_capa(mrb, group_len);
mrb_int j = RARRAY_LEN(arys_ary); // Corresponds to 'size' in Ruby
mrb_int n = current_i;
mrb_value group = mrb_ary_new_capa(mrb, j + 1 /* self_ary */);
while (j > 0) {
j -= 1;
while (j-- > 0) {
mrb_value a = RARRAY_PTR(arys_ary)[j]; // arys[j]
mrb_check_type(mrb, a, MRB_TT_ARRAY);
mrb_int b = RARRAY_LEN(a); // a.size
if (b <= 0) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "cannot compute product with an empty array");
}
mrb_ary_set(mrb, group, j + 1, RARRAY_PTR(a)[n % b]);
n /= b;
}
if (n >= RARRAY_LEN(self_ary)) {
mrb_raise(mrb, E_INDEX_ERROR, "index out of range");
}
mrb_ary_set(mrb, group, 0, RARRAY_PTR(self_ary)[n]);
return group;
}
static mrb_value
ary_product_generate(mrb_state *mrb, mrb_value self)
{
mrb_value arys_ary, block;
mrb_get_args(mrb, "A&", &arys_ary, &block);
mrb_int total = RARRAY_LEN(self);
for (mrb_int i = 0; i < RARRAY_LEN(arys_ary); i++) {
mrb_value a = RARRAY_PTR(arys_ary)[i];
mrb_check_type(mrb, a, MRB_TT_ARRAY);
mrb_int n = RARRAY_LEN(a);
if (n == 0) {
total = 0;
break;
}
if (mrb_int_mul_overflow(total, n, &total)) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "result too big");
}
}
if (mrb_nil_p(block)) {
mrb_value result = mrb_ary_new_capa(mrb, total);
for (mrb_int i = 0; i < total; i++) {
mrb_value group = ary_product_fetch(mrb, self, arys_ary, i);
mrb_ary_push(mrb, result, group);
}
return result;
}
else if (total > 0) {
struct RData *d;
struct ary_product_generator *g;
Data_Make_Struct(mrb, mrb->object_class, struct ary_product_generator,
&ary_product_generator_type, g, d);
g->total = total;
g->cursor = 0;
return mrb_obj_value(d);
}
else {
return mrb_nil_value();
}
}
static mrb_value
ary_product_next(mrb_state *mrb, mrb_value self)
{
mrb_value arys;
struct ary_product_generator *g;
mrb_get_args(mrb, "Ad", &arys, &g, &ary_product_generator_type);
if (g->cursor >= g->total) {
return mrb_nil_value();
}
return ary_product_fetch(mrb, self, arys, g->cursor++);
}
/*
* call-seq:
* ary.deconstruct -> ary
@@ -1563,7 +1668,8 @@ static const mrb_mt_entry array_ext_rom_entries[] = {
MRB_MT_ENTRY(ary_fetch, MRB_SYM(__fetch), MRB_ARGS_REQ(3)),
MRB_MT_ENTRY(ary_insert, MRB_SYM(insert), MRB_ARGS_ARG(1,-1)),
MRB_MT_ENTRY(ary_deconstruct, MRB_SYM(deconstruct), MRB_ARGS_NONE()),
MRB_MT_ENTRY(ary_product_group, MRB_SYM(__product_group), MRB_ARGS_REQ(3)),
MRB_MT_ENTRY(ary_product_generate, MRB_SYM(__product_generate), MRB_ARGS_REQ(1)),
MRB_MT_ENTRY(ary_product_next, MRB_SYM(__product_next), MRB_ARGS_REQ(2)),
MRB_MT_ENTRY(ary_combination_init, MRB_SYM(__combination_init), MRB_ARGS_REQ(2)),
MRB_MT_ENTRY(ary_combination_next, MRB_SYM(__combination_next), MRB_ARGS_REQ(1)),
};
+33 -6
View File
@@ -3175,8 +3175,12 @@ gen_literal_array(codegen_scope *s, node *tree, mrb_bool sym, int val)
{
if (val) {
int array_size = 0;
int first = 1;
int slimit = GEN_LIT_ARY_MAX;
node *current = tree;
if (cursp() >= slimit) slimit = GEN_VAL_STACK_MAX;
/* Process each segment separated by NODE_LITERAL_DELIM */
while (current) {
/* Find the segment boundaries without allocating */
@@ -3213,6 +3217,24 @@ gen_literal_array(codegen_scope *s, node *tree, mrb_bool sym, int val)
/* Only process non-empty segments */
if (!is_empty_segment) {
/* Flush accumulated elements when stack is full */
if (cursp() >= slimit) {
if (array_size > 0) {
pop_n(array_size);
if (first) {
genop_2(s, OP_ARRAY, cursp(), array_size);
push();
first = 0;
}
else {
pop();
genop_2(s, OP_ARYPUSH, cursp(), array_size);
push();
}
array_size = 0;
}
}
/* Temporarily terminate the segment by saving and clearing the cdr */
node *saved_cdr = NULL;
if (segment_prev) {
@@ -3243,8 +3265,14 @@ gen_literal_array(codegen_scope *s, node *tree, mrb_bool sym, int val)
}
}
/* Generate the array from pushed elements */
if (array_size > 0) {
/* Handle remaining elements */
if (!first) {
if (array_size > 0) {
pop_n(array_size + 1);
genop_2(s, OP_ARYPUSH, cursp(), array_size);
}
}
else if (array_size > 0) {
pop_n(array_size);
genop_2(s, OP_ARRAY, cursp(), array_size);
}
@@ -4427,10 +4455,9 @@ codegen_case_match(codegen_scope *s, node *varnode, int val)
current_in = current_in->cdr;
}
/* No pattern matched - generate nil or error */
if (val) {
genop_1(s, OP_LOADNIL, cursp());
}
/* No pattern matched - raise NoMatchingPatternError */
genop_1(s, OP_LOADFALSE, cursp());
genop_1(s, OP_MATCHERR, cursp());
/* Dispatch all end jumps */
if (case_end_jumps != JMPLINK_START) {
+3 -3
View File
@@ -3260,7 +3260,7 @@ primary : literal
$<stack>$ = p->cmdarg_stack;
p->cmdarg_stack = 0;
}
stmt {p->lstate = EXPR_ENDARG;} rparen
compstmt {p->lstate = EXPR_ENDARG;} rparen
{
p->cmdarg_stack = $<stack>2;
$$ = $3;
@@ -4563,13 +4563,13 @@ f_label : tIDENTIFIER tLABEL_TAG
{
$$ = $1;
local_nest(p);
p->lstate = EXPR_ARG; /* make newlines significant after label */
p->lstate = EXPR_MID; /* make newlines significant after label */
}
| tNUMPARAM tLABEL_TAG
{
$$ = intern_numparam($1);
local_nest(p);
p->lstate = EXPR_ARG; /* make newlines significant after label */
p->lstate = EXPR_MID; /* make newlines significant after label */
}
;
File diff suppressed because it is too large Load Diff
+6 -5
View File
@@ -291,11 +291,12 @@ class Enumerator
#
def flat_map(&block)
Lazy.new(self){|yielder, val|
ary = block.call(val)
# TODO: check ary is an Array
ary.each {|x|
yielder << x
}
result = block.call(val)
if result.respond_to?(:each)
result.each {|x| yielder << x }
else
yielder << result
end
}
end
alias collect_concat flat_map
+12
View File
@@ -46,6 +46,18 @@ assert("Enumerator::Lazy#to_enum") do
assert_equal [0*1, 2*3, 4*5, 6*7], lazy_enum.map { |a| a.first * a.last }.first(4)
end
assert("Enumerator::Lazy#flat_map with array from block") do
assert_equal [1, 10, 2, 20, 3, 30], [1, 2, 3].lazy.flat_map {|x| [x, x*10]}.force
end
assert("Enumerator::Lazy#flat_map with non-enumerable from block") do
assert_equal [1, 2, 3], [1, 2, 3].lazy.flat_map {|x| x}.force
end
assert("Enumerator::Lazy#flat_map with nested array from block") do
assert_equal [[1, 2], [3, 4]], [1, 3].lazy.flat_map {|x| [[x, x+1]]}.force
end
assert("Enumerator::Lazy#grep_v") do
lazy_grep_v = (0..).lazy.grep_v(2..4)
assert_kind_of Enumerator::Lazy, lazy_grep_v
+1 -5
View File
@@ -190,11 +190,7 @@ int_powm(mrb_state *mrb, mrb_value x)
return mrb_int_pow(mrb, x, mrb_get_arg1(mrb));
}
mrb_get_args(mrb, "oo", &e, &m);
if (!mrb_integer_p(e)
#ifdef MRB_USE_BIGINT
&& !mrb_bigint_p(e)
#endif
) {
if (!mrb_integer_p(e) && !mrb_bigint_p(e)) {
mrb_raise(mrb, E_TYPE_ERROR, "int.pow(n,m): 2nd argument not allowed unless 1st argument is an integer");
}
#ifdef MRB_USE_BIGINT
+1 -3
View File
@@ -701,13 +701,11 @@ class UDPSocket < IPSocket
# data, addr = sock.recvfrom_nonblock(1024)
#
def recvfrom_nonblock(*args)
s = self
begin
self._setnonblock(true)
self.recvfrom(*args)
ensure
# XXX: self is a SystemcallException here! (should be bug)
s._setnonblock(false)
self._setnonblock(false)
end
end
+7
View File
@@ -380,6 +380,13 @@ mrb_str_format(mrb_state *mrb, mrb_int argc, const mrb_value *argv, mrb_value fm
argc++;
argv--;
mrb_ensure_string_type(mrb, fmt);
/* Duplicate the format string so that to_s/inspect callbacks invoked
during the loop cannot invalidate p/end by mutating the original
via String#replace or similar. mrb_str_dup shares the underlying
buffer, so this is O(1); String#replace on the original goes
through str_replace which decrements the shared refcount, leaving
our copy's buffer intact. */
fmt = mrb_str_dup(mrb, fmt);
p = RSTRING_PTR(fmt);
end = p + RSTRING_LEN(fmt);
blen = 0;
+16
View File
@@ -90,3 +90,19 @@ assert("String#% invalid format") do
"%?" % ""
end
end
assert("sprintf with to_s mutating format string") do
# The to_s callback must not be able to invalidate sprintf's internal
# iteration pointers by mutating the format string.
fmt = "%s" + "B" * 200
mutator = Object.new
$sprintf_test_fmt = fmt
def mutator.to_s
$sprintf_test_fmt.replace("Z")
"ok"
end
result = sprintf(fmt, mutator)
assert_equal 202, result.length
assert_equal "ok", result[0, 2]
assert_equal "B" * 200, result[2..]
end
+15 -3
View File
@@ -2201,15 +2201,27 @@ str_prepend(mrb_state *mrb, mrb_value self)
char *p = RSTRING_PTR(self);
/* Move original content to the end */
/* Move original content to the end. The original self data now lives
at p + total_prepend_len, which we use as the source for any
self-referencing arguments (e.g., s.prepend(s, s)) to avoid reading
data that has already been overwritten by earlier copies. */
memmove(p + total_prepend_len, p, self_len);
/* Copy prepended strings in order */
mrb_int offset = 0;
for (mrb_int i = 0; i < argc; i++) {
mrb_int arg_len = RSTRING_LEN(argv[i]);
const char *src;
mrb_int arg_len;
if (mrb_obj_eq(mrb, self, argv[i])) {
src = p + total_prepend_len;
arg_len = self_len;
}
else {
src = RSTRING_PTR(argv[i]);
arg_len = RSTRING_LEN(argv[i]);
}
if (arg_len > 0) {
memcpy(p + offset, RSTRING_PTR(argv[i]), arg_len);
memcpy(p + offset, src, arg_len);
offset += arg_len;
}
}
+15
View File
@@ -492,6 +492,21 @@ assert('String#prepend') do
g = "world"
assert_equal "hello world", g.prepend("", "hello ", "")
assert_equal "hello world", g
# Self-referencing arguments (GHSA-3hgj-g76g-878c)
h = "A" * 100
h.prepend(h, h)
assert_equal 300, h.length
assert_equal "A" * 300, h
# Mixed self-reference and literal
i = "AB"
i.prepend("XYZ", i)
assert_equal "XYZABAB", i
j = "AB"
j.prepend(j, "X", j)
assert_equal "ABXABAB", j
end
assert('String#ljust') do
+9 -3
View File
@@ -19,9 +19,11 @@ MRuby::Gem::Specification.new('mruby-test') do |spec|
file assert_c => [assert_rb, build.mrbcfile] do |t|
_pp "GEN", t.name.relative_path
mkdir_p File.dirname(t.name)
open(t.name, 'w') do |f|
tmpfile = t.name + ".tmp"
open(tmpfile, 'w') do |f|
mrbc.run f, assert_rb, 'mrbtest_assert_irep', cdump: false
end
File.rename(tmpfile, t.name)
end
gem_table = build.gems.generate_gem_table build
@@ -36,7 +38,8 @@ MRuby::Gem::Specification.new('mruby-test') do |spec|
file g.test_rbireps => [g.test_rbfiles, build.mrbcfile].flatten do |t|
_pp "GEN", t.name.relative_path
mkdir_p File.dirname(t.name)
open(t.name, 'w') do |f|
tmpfile = t.name + ".tmp"
open(tmpfile, 'w') do |f|
g.print_gem_test_header(f)
test_preload = g.test_preload and [g.dir, MRUBY_ROOT].map {|dir|
File.expand_path(g.test_preload, dir)
@@ -117,6 +120,7 @@ MRuby::Gem::Specification.new('mruby-test') do |spec|
end
f.puts %Q[}]
end
File.rename(tmpfile, t.name)
end
end
@@ -134,7 +138,8 @@ MRuby::Gem::Specification.new('mruby-test') do |spec|
file clib => ["#{build.build_dir}/mrbgems/active_gems.txt", build.mrbcfile, __FILE__] do |_t|
_pp "GEN", clib.relative_path
mkdir_p File.dirname(clib)
open(clib, 'w') do |f|
tmpfile = clib + ".tmp"
open(tmpfile, 'w') do |f|
f.puts %Q[/*]
f.puts %Q[ * This file contains a list of all]
f.puts %Q[ * test functions.]
@@ -169,5 +174,6 @@ MRuby::Gem::Specification.new('mruby-test') do |spec|
end
f.puts %Q[}]
end
File.rename(tmpfile, clib)
end
end
+4 -3
View File
@@ -2748,7 +2748,7 @@ prepare_writer_name(mrb_state *mrb, mrb_sym sym)
}
static mrb_value
mod_attr_define(mrb_state *mrb, mrb_value mod, mrb_value (*accessor)(mrb_state*, mrb_value), mrb_sym (*access_name)(mrb_state*, mrb_sym))
mod_attr_define(mrb_state *mrb, mrb_value mod, mrb_int aargc, mrb_value (*accessor)(mrb_state*, mrb_value), mrb_sym (*access_name)(mrb_state*, mrb_sym))
{
struct RClass *c = mrb_class_ptr(mod);
const mrb_value *argv;
@@ -2765,6 +2765,7 @@ mod_attr_define(mrb_state *mrb, mrb_value mod, mrb_value (*accessor)(mrb_state*,
}
struct RProc *p = mrb_proc_new_cfunc_with_env(mrb, accessor, 1, &name);
p->flags |= aargc == 0 ? MRB_PROC_NOARG : 0;
mrb_method_t m;
MRB_METHOD_FROM_PROC(m, p);
mrb_define_method_raw(mrb, c, method, m);
@@ -2783,7 +2784,7 @@ attr_reader(mrb_state *mrb, mrb_value obj)
static mrb_value
mrb_mod_attr_reader(mrb_state *mrb, mrb_value mod)
{
return mod_attr_define(mrb, mod, attr_reader, NULL);
return mod_attr_define(mrb, mod, 0, attr_reader, NULL);
}
static mrb_value
@@ -2799,7 +2800,7 @@ attr_writer(mrb_state *mrb, mrb_value obj)
static mrb_value
mrb_mod_attr_writer(mrb_state *mrb, mrb_value mod)
{
return mod_attr_define(mrb, mod, attr_writer, prepare_writer_name);
return mod_attr_define(mrb, mod, 1, attr_writer, prepare_writer_name);
}
static mrb_value
+6 -6
View File
@@ -541,15 +541,15 @@ mrb_gc_unregister(mrb_state *mrb, mrb_value obj)
if (!mrb_array_p(table)) return;
struct RArray *a = mrb_ary_ptr(table);
mrb_ary_modify(mrb, a);
mrb_int len = ARY_LEN(a)-1;
mrb_int len = ARY_LEN(a);
mrb_value *ptr = ARY_PTR(a);
for (mrb_int i = 0; i <= len; i++) {
if (mrb_ptr(ptr[i]) == mrb_ptr(obj)) {
ARY_SET_LEN(a, len);
memmove(&ptr[i], &ptr[i + 1], (len - i) * sizeof(mrb_value));
break;
mrb_int w = 0;
for (mrb_int r = 0; r < len; r++) {
if (mrb_ptr(ptr[r]) != mrb_ptr(obj)) {
ptr[w++] = ptr[r];
}
}
ARY_SET_LEN(a, w);
}
MRB_API struct RBasic*
+1 -5
View File
@@ -432,11 +432,7 @@ mrb_get_values_at(mrb_state *mrb, mrb_value obj, mrb_int olen, mrb_int argc, con
for (mrb_int i = 0; i < argc; i++) {
mrb_value v = argv[i];
if (mrb_integer_p(v)
#ifdef MRB_USE_BIGINT
|| mrb_bigint_p(v)
#endif
) {
if (mrb_integer_p(v) || mrb_bigint_p(v)) {
mrb_int i = mrb_as_int(mrb, v);
mrb_ary_push(mrb, result, func(mrb, obj, i));
}
+20 -9
View File
@@ -134,19 +134,29 @@ static inline void
envadjust(mrb_state *mrb, mrb_value *oldbase, mrb_value *newbase)
{
mrb_callinfo *ci = mrb->c->cibase;
ptrdiff_t delta = newbase - oldbase;
/*
* Byte-level calculation to avoid truncation when allocator alignment is
* smaller than sizeof(mrb_value).
* eg: MRB_NO_BOXING + MRB_INT64 with MRB_32BIT => sizeof(mrb_value)=16
* And when memory allocator's alignment is 8 bytes
* Pointer subtraction on mrb_value* would truncate (8/16 -> 0).
* So, we use char* for pointer calculation to get the correct offset in bytes,
* then apply that offset to mrb_value* pointers.
*/
ptrdiff_t off = (char *)newbase - (char *)oldbase;
if (delta == 0) return;
if (off == 0) return;
while (ci <= mrb->c->ci) {
struct REnv *e = mrb_vm_ci_env(ci);
mrb_value *new_stack = (mrb_value *)((char *)ci->stack + off);
if (e) {
mrb_assert(e->cxt == mrb->c && MRB_ENV_ONSTACK_P(e));
mrb_assert(e->stack == ci->stack);
e->stack += delta;
e->stack = new_stack;
}
ci->stack += delta;
ci->stack = new_stack;
ci++;
}
}
@@ -2703,11 +2713,10 @@ RETRY_TRY_BLOCK:
if (kidx < 0 || !mrb_hash_p(kdict=regs[kidx]) || !mrb_hash_key_p(mrb, kdict, k)) {
RAISE_FORMAT(mrb, E_ARGUMENT_ERROR, "missing keyword: %v", k);
}
v = mrb_hash_get(mrb, kdict, k);
v = mrb_hash_delete_key(mrb, kdict, k);
ci = mrb->c->ci;
regs[a] = v;
mrb_hash_delete_key(mrb, kdict, k);
ci = mrb->c->ci;
NEXT;
}
@@ -3113,7 +3122,9 @@ RETRY_TRY_BLOCK:
{ \
mrb_value arg = mrb_int_value(mrb, c); \
mrb_sym mid = MRB_OPSYM(op_name); \
regs[a] = mrb_funcall_argv(mrb, regs[a], mid, 1, &arg); \
mrb_value v = mrb_funcall_argv(mrb, regs[a], mid, 1, &arg); \
ci = mrb->c->ci; \
regs[a] = v; \
mrb_gc_arena_restore(mrb, ai); \
} \
break; \
+17 -2
View File
@@ -29,7 +29,7 @@ MRuby.each_target do |build|
end
end
file presym.list_path => ppps do
presym_task = file presym.list_path => ppps do
presyms = presym.scan(ppps)
current_presyms = presym.read_list if File.exist?(presym.list_path)
if presyms != current_presyms
@@ -41,6 +41,21 @@ MRuby.each_target do |build|
end
end
# Don't directly write dependency tasks in the "task" arguments.
# The rake system tracks dependencies recursively
# (see Rake::Task#all_prerequisite_tasks and #collect_prerequisites).
# Therefore, indirect dependencies from ".o" to ".pi" must be eliminated.
# ref. https://github.com/mruby/mruby/issues/6721
# This task acts a proxy-like for the "presym.list_path" task.
presym_proxy = task "gensym:update:#{build.name}" do
presym_task.invoke
end
# Override the "timestamp" method to reflect the presym file.
presym_proxy.define_singleton_method :timestamp do
presym_task.timestamp
end
# Ensure .o files depend on presym headers being generated.
# This is critical when a build's .o files are compiled during another
# build's presym scanning chain (before :gensym completes), e.g.:
@@ -50,7 +65,7 @@ MRuby.each_target do |build|
next unless File.extname(prereq) == build.exts.object
next unless prereq.start_with?(build_dir)
next if mrbc_build_dir && prereq.start_with?(mrbc_build_dir)
file prereq => presym.list_path
file prereq => presym_proxy
end
task gensym: presym.list_path
+3
View File
@@ -163,6 +163,9 @@ assert('Module#attr_reader', '15.2.2.4.13') do
AttrTestReader.cattr_val = 'test'
assert_equal 'test', AttrTestReader.cattr
assert_raise(ArgumentError) { attr_instance.iattr(1) }
assert_raise(ArgumentError) { attr_instance.iattr(1, 2, 3) }
end
assert('Module#attr_writer', '15.2.2.4.14') do
+20 -5
View File
@@ -680,6 +680,20 @@ assert 'keyword arguments' do
assert_equal [{a: 1}, {b: 2}], m({a: 1}, b: 2)
assert_raise(ArgumentError) { m({a: 1}, {b: 2}) }
def m1(a: {}) a end
assert_equal({}, m1)
assert_equal(:abc, m1(a: :abc))
def m2(a: +1) a end
assert_equal(1, m2)
assert_nothing_raised do
def m3 arg:
123
end
end
assert_equal(123, m3(arg: 456))
def m(*, a:) a end
assert_equal 1, m(a: 1)
assert_equal 3, m(1, 2, a: 3)
@@ -1246,12 +1260,13 @@ assert('pattern matching - NoMatchingPatternError') do
assert_true e.message.is_a?(String)
end
# case/in without else returns nil (no match)
result = case 5
in 1 then :one
in 2 then :two
# case/in without else raises NoMatchingPatternError
assert_raise(NoMatchingPatternError) do
case 5
in 1 then :one
in 2 then :two
end
end
assert_nil result
end
assert('pattern matching - complex patterns') do