Compare commits

..

58 Commits

Author SHA1 Message Date
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
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
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
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
45 changed files with 3303 additions and 3014 deletions
+16
View File
@@ -5,11 +5,27 @@ 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:
- "*"
+6
View File
@@ -32,6 +32,8 @@ jobs:
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@v6
with:
persist-credentials: false
- name: Ruby version
run: ruby -v
- name: Compiler version
@@ -45,6 +47,8 @@ jobs:
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@v6
with:
persist-credentials: false
- name: Ruby version
run: ruby -v
- name: Cache cosmocc
@@ -71,6 +75,8 @@ jobs:
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@v6
with:
persist-credentials: false
- name: Ruby version
run: ruby -v
- name: Build and test
+2
View File
@@ -19,6 +19,8 @@ jobs:
steps:
- name: Checkout repository
uses: actions/checkout@v6
with:
persist-credentials: false
- name: Initialize CodeQL
uses: github/codeql-action/init@v4
with:
+5
View File
@@ -2,6 +2,9 @@ name: Coverage
on: [push]
permissions:
contents: read
jobs:
coverage:
name: Coverage
@@ -17,6 +20,8 @@ jobs:
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@v6
with:
persist-credentials: false
- name: Ruby version
run: ruby -v
- name: Compiler version
+2
View File
@@ -12,6 +12,8 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
- uses: ls-lint/action@v2.3.1
with:
config: .github/linters/.ls-lint.yml
+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@v6
with:
persist-credentials: false
- uses: j178/prek-action@v2
with:
install-only: true
- name: Run manual pre-commit hooks
run: prek run --color=always --all-files --hook-stage manual
+3 -3
View File
@@ -13,8 +13,8 @@ jobs:
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@v6
- uses: j178/prek-action@v1
with:
persist-credentials: false
- uses: j178/prek-action@v2
with:
extra-args: --all-files
- name: Run manual pre-commit hooks
run: prek run --color=always --all-files --hook-stage manual
+2
View File
@@ -20,6 +20,8 @@ jobs:
steps:
- name: "Checkout ${{ github.ref_name }} ( ${{ github.sha }} )"
uses: actions/checkout@v6
with:
persist-credentials: false
- name: Builds
id: builds
run: |
+1
View File
@@ -19,6 +19,7 @@ jobs:
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
env:
+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.11
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
+39
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,30 @@ 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
# Security Fixes
@@ -320,4 +357,6 @@ 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))
- Multiple memory leak fixes in bigint, Set, Array, and Task gems
+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
+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
/**
+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
+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));
}
+5 -4
View File
@@ -2703,11 +2703,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 +3112,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