Compare commits

..

1 Commits

Author SHA1 Message Date
google-labs-jules[bot] 71e8dbcd97 Refactor str_succ_bang for in-place modification
This commit refactors the str_succ_bang function in mrbgems/mruby-string-ext/src/string.c to perform string succession in-place.

The previous implementation used an intermediate temporary string, which incurred memory and performance overhead.
This change aims to reduce memory usage and improve performance by modifying the string buffer directly, using mrb_str_resize and memmove when prepending characters is necessary (e.g., '9' -> '10').

Formatting changes (newlines before 'else') have also been applied as per user request.
2025-06-23 09:23:54 +00:00
340 changed files with 18583 additions and 59164 deletions
-22
View File
@@ -5,29 +5,7 @@ updates:
directory: "/"
schedule:
interval: "daily"
groups:
bundler-dependencies:
patterns:
- "*"
cooldown:
default-days: 7
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
groups:
github-actions-dependencies:
patterns:
- "*"
cooldown:
default-days: 7
- package-ecosystem: "pre-commit"
directory: "/"
schedule:
interval: "daily"
groups:
pre-commit-hooks:
patterns:
- "*"
cooldown:
default-days: 7
-13
View File
@@ -1,13 +0,0 @@
AllCops:
DisabledByDefault: true
TargetRubyVersion: 3.4
Layout/AssignmentIndentation:
Enabled: true
Layout/BlockEndNewline:
Enabled: true
Layout/IndentationStyle:
Enabled: true
EnforcedStyle: spaces
+10
View File
@@ -0,0 +1,10 @@
{
"ignorePatterns": [
{
"pattern": "^https://github.com/mruby/mruby/commit/"
},
{
"pattern": "^mailto:"
}
]
}
+2 -32
View File
@@ -31,9 +31,7 @@ jobs:
LD: ${{ matrix.cc }}
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
uses: actions/checkout@v4
- name: Ruby version
run: ruby -v
- name: Compiler version
@@ -41,32 +39,6 @@ jobs:
- name: Build and test
run: rake -m test:run:serial
Cosmopolitan:
runs-on: ubuntu-24.04
timeout-minutes: 15
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Ruby version
run: ruby -v
- name: Cache cosmocc
uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4
id: cache-cosmocc
with:
path: ~/cosmo
key: cosmocc-${{ runner.os }}-20260104
- name: Install cosmocc
if: steps.cache-cosmocc.outputs.cache-hit != 'true'
run: |
mkdir -p ~/cosmo && cd ~/cosmo
wget https://cosmo.zip/pub/cosmocc/cosmocc.zip
unzip cosmocc.zip
- name: Build and test
run: |
COSMO_ROOT=~/cosmo rake -m test:run:serial MRUBY_CONFIG=cosmopolitan
Windows-VC:
runs-on: windows-2022
timeout-minutes: 10
@@ -74,9 +46,7 @@ jobs:
MRUBY_CONFIG: ci/msvc
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
uses: actions/checkout@v4
- name: Ruby version
run: ruby -v
- name: Build and test
-33
View File
@@ -1,33 +0,0 @@
name: CodeQL Analysis
on:
push:
branches: [master]
pull_request:
branches: [master]
permissions:
actions: read
contents: read
security-events: write
jobs:
codeql:
name: CodeQL
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
language: ["actions"]
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- name: Initialize CodeQL
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
- name: Autobuild
uses: github/codeql-action/autobuild@v4
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@v4
with:
category: "Security"
+2 -7
View File
@@ -2,9 +2,6 @@ name: Coverage
on: [push]
permissions:
contents: read
jobs:
coverage:
name: Coverage
@@ -19,9 +16,7 @@ jobs:
LDFLAGS: --coverage
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
uses: actions/checkout@v4
- name: Ruby version
run: ruby -v
- name: Compiler version
@@ -39,7 +34,7 @@ jobs:
echo \`\`\`
} > "$GITHUB_STEP_SUMMARY"
- name: Upload coverage report
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@v4
with:
name: coverage-${{ github.sha }}
path: coverage/
+1 -1
View File
@@ -9,7 +9,7 @@ jobs:
pull-requests: write
runs-on: ubuntu-latest
steps:
- uses: actions/labeler@634933edcd8ababfe52f92936142cc22ac488b1b # v6.0.1
- uses: actions/labeler@v5
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
sync-labels: true
+35
View File
@@ -0,0 +1,35 @@
# https://pre-commit.com/
name: Lint
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@v4
- name: Install
run: |
python -m pip install --upgrade pip
pip install pre-commit
- name: Set PY
run: echo "PY=$(python -VV | sha256sum | cut -d' ' -f1)" >> "$GITHUB_ENV"
- uses: actions/cache@v4
with:
path: ~/.cache/pre-commit
key: pre-commit|${{ env.PY }}|${{ hashFiles('.pre-commit-config.yaml') }}
- name: Run pre-commit
run: pre-commit run --all-files
ls-lint:
name: Run ls-lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ls-lint/action@v2.3.1
with:
config: .github/linters/.ls-lint.yml
-19
View File
@@ -1,19 +0,0 @@
# https://github.com/loeffel-io/ls-lint
name: Lint
on: [pull_request]
permissions:
contents: read
jobs:
ls-lint:
name: Run ls-lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: ls-lint/action@02e380fe8733d499cbfc9e22276de5085508a5bd # v2.3.1
with:
config: .github/linters/.ls-lint.yml
+1 -1
View File
@@ -20,7 +20,7 @@ jobs:
fuzz-seconds: 600
dry-run: false
- name: Upload Crash
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
uses: actions/upload-artifact@v4
if: failure()
with:
name: artifacts
-22
View File
@@ -1,22 +0,0 @@
# https://github.com/j178/prek
name: Manual hooks
on: [pull_request]
permissions:
contents: read
jobs:
pre-commit:
name: Run pre-commit
runs-on: ubuntu-latest
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: j178/prek-action@53276d8b0d10f8b6672aa85b4588c6921d0370cc # v2.0.1
with:
install-only: true
- name: Run manual pre-commit hooks
run: prek run --color=always --all-files --hook-stage manual
-20
View File
@@ -1,20 +0,0 @@
# https://github.com/j178/prek
name: pre-commit
on: [pull_request]
permissions:
contents: read
jobs:
pre-commit:
name: Run pre-commit
runs-on: ubuntu-latest
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: j178/prek-action@53276d8b0d10f8b6672aa85b4588c6921d0370cc # v2.0.1
with:
extra-args: --all-files
+1 -3
View File
@@ -19,9 +19,7 @@ jobs:
fail-fast: false
steps:
- name: "Checkout ${{ github.ref_name }} ( ${{ github.sha }} )"
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
uses: actions/checkout@v4
- name: Builds
id: builds
run: |
+2 -3
View File
@@ -15,13 +15,12 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
uses: actions/checkout@v4
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@9e863354e3ff62e0727d37183162c4a88873df41 # v8.6.0
uses: super-linter/super-linter/slim@v7.4.0
env:
# VALIDATE_BASH_EXEC: true
VALIDATE_DOCKERFILE_HADOLINT: true
+1 -1
View File
@@ -24,7 +24,7 @@ benchmark/**/*.dat
benchmark/*.pdf
benchmark/*.png
bin
/build
build
doc/api
doc/capi
compile_commands.json
+36 -34
View File
@@ -1,9 +1,11 @@
---
# https://github.com/j178/prek
# https://pre-commit.com/
default_stages: [pre-commit, pre-push]
default_language_version:
# force all unspecified Python hooks to run python3
python: python3
node: 24.14.0
# force all unspecified Node hooks to run Node.js v22.15.0 LTS
node: 22.15.0
minimum_pre_commit_version: "3.2.0"
exclude: "^tools/lrama/"
repos:
@@ -20,42 +22,34 @@ repos:
- id: prettier
name: run prettier
description: format files with prettier
entry: prettier --write '**/*.md' '**/*.yaml' '**/*.yml'
files: \.(md|ya?ml)$
entry: prettier --write .
files: \.(json|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
description: Zip files are not allowed in the repository
language: fail
entry: |
Zip files are not allowed in the repository as they are hard to
track and have security implications. Please remove the zip file from the repository.
files: \.zip$
additional_dependencies: ["prettier@3.5.3"]
- repo: https://github.com/gitleaks/gitleaks
rev: v8.30.1
rev: v8.25.1
hooks:
- id: gitleaks
name: run gitleaks
description: detect hardcoded secrets with gitleaks
- repo: https://github.com/oxipng/oxipng
rev: v10.1.0
- repo: https://github.com/shssoichiro/oxipng
rev: v9.1.5
hooks:
- id: oxipng
name: run oxipng
description: use lossless compression to optimize PNG files
args: ["--fix", "-o", "4", "--strip", "safe", "--alpha"]
stages: [manual]
args: ["-o", "4", "--strip", "safe", "--alpha"]
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
rev: v5.0.0
hooks:
- id: check-added-large-files
- id: check-case-conflict
- id: check-executables-have-shebangs
exclude: ^test/t/lang\.rb$
- id: check-illegal-windows-names
- id: pretty-format-json
args: [--autofix, --no-sort-keys]
- id: check-json
- id: check-merge-conflict
- id: check-shebang-scripts-are-executable
- id: check-vcs-permalinks
@@ -73,7 +67,7 @@ repos:
- id: mixed-line-ending
- id: trailing-whitespace
- repo: https://github.com/Lucas-C/pre-commit-hooks
rev: v1.5.6
rev: v1.5.5
hooks:
- id: forbid-tabs
name: run no-tabs checker
@@ -85,19 +79,26 @@ repos:
args: [--whitespaces-count, "2"]
exclude: Makefile$
- repo: https://github.com/rhysd/actionlint
rev: v1.7.12
rev: v1.7.7
hooks:
- id: actionlint
name: run actionlint
description: lint GitHub Actions workflow files
- repo: https://github.com/codespell-project/codespell
rev: v2.4.2
rev: v2.4.1
hooks:
- id: codespell
name: run codespell
description: check spelling with codespell
- repo: https://github.com/jumanjihouse/pre-commit-hooks
rev: 3.0.0
hooks:
- id: script-must-not-have-extension
name: run script-must-not-have-extension
description: local policy is to exclude extension from all shell files
types: [shell]
- repo: https://github.com/igorshubovych/markdownlint-cli
rev: v0.48.0
rev: v0.44.0
hooks:
- id: markdownlint
name: run markdownlint
@@ -105,22 +106,23 @@ repos:
args: [--config=.github/linters/.markdown-lint.yml]
types: [markdown]
files: \.md$
- repo: https://github.com/rubocop/rubocop
rev: v1.86.0
- repo: https://github.com/tcort/markdown-link-check
rev: v3.13.7
hooks:
- id: rubocop
name: run rubocop
description: RuboCop is a Ruby code style checker (linter) and formatter based on the community-driven Ruby Style Guide
exclude: ^test/t/syntax\.rb$
args: [--config=.github/linters/.rubocop.yml]
- id: markdown-link-check
name: run markdown-link-check
description: checks hyperlinks in Markdown files
args: [--config=.github/linters/mlc_config.json, -q]
types: [markdown]
files: \.md$
- repo: https://github.com/shellcheck-py/shellcheck-py
rev: v0.11.0.1
rev: v0.10.0.1
hooks:
- id: shellcheck
name: run shellcheck
description: check shell scripts with a static analysis tool
- repo: https://github.com/adrienverge/yamllint
rev: v1.38.0
rev: v1.37.0
hooks:
- id: yamllint
name: run yamllint
-1
View File
@@ -3,4 +3,3 @@ build
coverage
doc/internal/opcode.md
tools/lrama
.venv
+11 -17
View File
@@ -1,15 +1,15 @@
# Authors of mruby (mruby developers)
## The List of Contributors sorted by number of commits (as of 2026-03-02 02877f0)
## The List of Contributors sorted by number of commits (as of 2025-06-03 a3e7280)
7532 Yukihiro "Matz" Matsumoto (@matz)*
712 dearblue (@dearblue)*
6258 Yukihiro "Matz" Matsumoto (@matz)*
681 dearblue (@dearblue)*
587 KOBAYASHI Shuji (@shuujii)
353 Daniel Bovensiepen (@bovi)*
345 Takeshi Watanabe (@take-cheeze)*
333 Masaki Muranaka (@monaka)
255 John Bampton (@jbampton)
234 Jun Hiroe (@suzukaze)
233 John Bampton (@jbampton)
228 Tomoyuki Sahara (@tsahara)*
220 Cremno (@cremno)*
209 Yuki Kurihara (@ksss)+
@@ -43,7 +43,6 @@
22 Zachary Scott (@zzak)*
21 Ryan Lopopolo (@lopopolo)
20 Ryan Scott (@ryan-scott-dev)*
20 google-labs-jules[bot] (@google-jules)
19 Bouke van der Bijl (@bouk)
19 Jared Breeden (@jbreeden)*
19 go kikuta (@gkta)*
@@ -53,7 +52,6 @@
18 leviongit (@leviongit)
18 mirichi (@mirichi)
17 Mitchell Blank Jr (@mitchblank)*
16 HASUMI Hitoshi (@hasumikin)
16 bggd (@bggd)
16 kano4 (@kano4)
15 Felix Jones (@felixjones)*
@@ -63,11 +61,11 @@
14 fn ⌃ ⌥ (@FnControlOption)
13 Jose Narvaez (@goyox86)
13 Patrick Hogan (@pbhogan)
13 google-labs-jules[bot] (@google-jules)
12 Akira Kuroda (@akuroda)
12 Kouki Ooyatsu (kaishuu0123)*
12 NAKAMURA Usaku (@unak)*
12 Ray Chason (@chasonr)*
12 SiZiOUS (@suzious)
12 Takashi Sawanaka (@sdottaka)*
12 Ukrainskiy Sergey (@ukrainskiysergey)
12 Xuejie "Rafael" Xiao (@xxuejie)*
@@ -76,7 +74,6 @@
11 RIZAL Reckordp (@Reckordp)+
11 Seeker (@SeekingMeaning)
11 takkaw (@takkaw)
10 Hendrik (@Asmod4n)
10 Miura Hideki (@miura1729)
10 Narihiro Nakamura (@authorNari)
10 YAMAMOTO Masaya (pandax381)
@@ -89,6 +86,7 @@
7 Bhargava Shastry (@bshastry)*
7 Kouichi Nakanishi (@keizo042)
7 Rubyist (@expeditiousRubyist)
7 SiZiOUS (@sizious)
7 Simon Génier (@simon-shopify)
7 Terence Lee (@hone)
7 roco (@rystyle)*
@@ -97,30 +95,28 @@
6 Beoran (@beoran)
6 David Siaw (@davidsiaw)*
6 Frederick John Milens III (@fjmilens3)
6 HASUMI Hitoshi (@hasumikin)
6 Hiro Asari (@BanzaiMan)
6 INOUE Yasuyuki (@yasuyuki)
6 Junji Sawada (@junjis0203)
6 Kenji Okimoto (@okkez)+
6 Paweł Świątkowski (@katafrakt)
6 Selman ULUG (@selman)
6 Yusuke Endoh (@mame)*
6 buty4649 (@buty4649)
6 masahino (@masahino)
5 Chris Reuter (@suetanvil)
5 Davide D'Agostino (@DAddYE)
5 Eric Hodel (@drbrain)
5 Hendrik (@Asmod4n)
5 Ichito Nagata (@i110)
5 Keita Obo (@ktaobo)*
5 Max Anselm (@silverhammermba)
5 Rodrigo Malizia (@rmalizia44)+
5 Ryan Davis (@zenspider)
5 Syohei YOSHIDA (@syohex)
5 TOMITA Masahiro (@tmtm)
5 Yurie Yamane (@yurie)+
5 dreamedge (@dreamedge)
5 nkshigeru (@nkshigeru)
5 xuejianqing (@joans321)
4 Chris Hasiński (@khasinski)
4 Dante Catalfamo (@dantecatalfamo)
4 Goro Kikuchi (@gorogit)
4 Herwin Weststrate (@herwinw)
@@ -135,7 +131,6 @@
4 NARUSE, Yui (@nurse)
4 Ravil Bayramgalin (@brainopia)*+
4 Satoshi Odawara (@SatoshiOdawara)
4 UENO, M. (@eunos-1128)
4 Yuhei Okazaki (@Yuuhei-Okazaki)*
4 Yuji Yamano (@yyamano)
4 kurodash (@kurodash)*
@@ -152,7 +147,6 @@
3 Jonas Minnberg (@sasq64)
3 Joseph McCullough (@joequery)
3 Mark McCurry (@fundamental)
3 Meder Kydyraliev (@meder)
3 Nobuhiro Iwamatsu (@iwamatsu)
3 Per Lundberg (@perlun)*
3 Rob Fors (@robfors)*
@@ -205,9 +199,9 @@
2 Tomás Pollak (@tomas)*
2 Yutaka HARA (@yhara)*+
2 Zhang Xiaohui (@hifoolno)
2 buty4649 (@buty4649)
2 icm7216 (@icm7216)
1 A-Sat (@asatou)+
1 AN Long (@aisk)
1 Abinoam Praxedes Marques Junior (@abinoam)
1 Alex Wang (@nanamiwang)+
1 AlexDenisov (@AlexDenisov)
@@ -270,15 +264,15 @@
1 Nicholas (@knf)
1 Nozomi SATO (@nozomiS)
1 Okumura Takahiro (@hfm)
1 Oliver Chang (@oliverchang)
1 Patrick Ellis (@pje)
1 Patrick Pokatilo (@SHyx0rmZ)
1 Pavel Evstigneev (@Paxa)+
1 Paweł Świątkowski (@katafrakt)
1 Pete Kinnecom (@petekinnecom)
1 Piotr Usewicz (@pusewicz)
1 Prayag Verma (@pra85)
1 Ranmocy (@ranmocy)
1 Robert McNally (@wolfmcnally)
1 Ryan Davis (@zenspider)
1 Ryan Scott Lewis (@RyanScottLewis)
1 Ryo Okubo (@syucream)
1 SAkira a.k.a. Akira Suzuki (@sakisakira)
+26 -34
View File
@@ -18,50 +18,41 @@ things in mind before submitting your pull request:
- Use mrbgem to provide non ISO features (classes, modules and methods) unless
you have a special reason to implement them in the core
## Security Issues
## pre-commit
If you discover a security vulnerability:
A framework for managing and maintaining multi-language `pre-commit` hooks.
`pre-commit` can be [installed](https://pre-commit.com/#installation) with `pip`, `curl`, `brew` or `conda`.
- **High priority security vulnerabilities** (RCE): Report via email to <matz@ruby.or.jp>
- **VM crashes from valid Ruby code**: Please report as regular bug reports on our issue tracker
You need to first install `pre-commit` and then install the `pre-commit` hooks with `pre-commit install`.
Now `pre-commit` will run automatically on git commit!
For detailed guidance on what qualifies as a security issue and what doesn't, see [SECURITY.md](SECURITY.md).
It's usually a good idea to run the hooks against all the files when adding new hooks (usually `pre-commit`
will only run on the changed files during git hooks). Use `pre-commit run --all-files` to check all files.
## prek
To run a single hook use `pre-commit run --all-files <hook_id>`
We use [prek](https://github.com/j178/prek), a fast Rust-based pre-commit hook manager.
It reads the standard `.pre-commit-config.yaml` format.
Install `prek` following the [installation guide](https://github.com/j178/prek#installation),
then install the hooks with `prek install`.
Now `prek` will run automatically on git commit!
It's usually a good idea to run the hooks against all the files when adding new hooks (usually `prek`
will only run on the changed files during git hooks). Use `prek run --all-files` to check all files.
To run a single hook use `prek run --all-files <hook_id>`
To update use `prek autoupdate`
To update use `pre-commit autoupdate`
Sometimes you might need to skip one or more hooks which can be done with the `SKIP` environment variable.
`$ SKIP=yamllint git commit -m "foo"`
For convenience, we have added `prek run --all-files`, `prek install` and `prek autoupdate`
For convenience, we have added `pre-commit run --all-files`, `pre-commit install` and `pre-commit autoupdate`
to both the Makefile and the Rakefile. Run them with:
- `make check` or `rake check`
- `make checkinstall` or `rake checkinstall`
- `make checkupdate` or `rake checkupdate`
To configure hooks you can modify the config file [.pre-commit-config.yaml](.pre-commit-config.yaml).
We use [GitHub Actions](.github/workflows/pre-commit.yml) to run `prek` on every pull request.
To configure `pre-commit` you can modify the config file [.pre-commit-config.yaml](.pre-commit-config.yaml).
We use [GitHub Actions](.github/workflows/lint.yml) to run `pre-commit` on every pull request.
### prek quick links
### pre-commit quick links
- [prek GitHub](https://github.com/j178/prek)
- [Installation](https://github.com/j178/prek#installation)
- [Usage](https://github.com/j178/prek#usage)
- [Quick start](https://pre-commit.com/#quick-start)
- [Usage](https://pre-commit.com/#usage)
- [pre-commit autoupdate](https://pre-commit.com/#pre-commit-autoupdate)
- [Temporarily disabling hooks](https://pre-commit.com/#temporarily-disabling-hooks)
## Docker
@@ -97,19 +88,20 @@ mruby-test latest ec60f9536948 29 seconds ago 1.29GB
```
You can also run any custom `docker-compose` command which will override
the default. For example to run `prek run --all-files` type:
the default. For example to run `pre-commit run --all-files` type:
`$ docker-compose -p mruby run test prek run --all-files`
`$ docker-compose -p mruby run test pre-commit run --all-files`
For convenience, you can also run `prek` with:
For convenience, you can also run `pre-commit` with:
- `make composecheck`
- `rake composecheck`
The bonus of running `prek` with `docker-compose` is that you won't need
to install `prek` and the hooks on your local machine.
The bonus of running `pre-commit` with `docker-compose` is that you won't need
to install `pre-commit` and the hooks on your local machine. And that also
means you won't need to install `brew`, `conda` or `pip`.
Note limitation: currently running `prek` with `docker-compose` we
Note limitation: currently running `pre-commit` with `docker-compose` we
skip the `check-executables-have-shebangs` hook.
Two more examples of custom `docker-compose` commands are:
@@ -127,7 +119,7 @@ can use the `-f` flag:
## Spell Checking
We are using `prek` to run [codespell](https://github.com/codespell-project/codespell)
We are using `pre-commit` to run [codespell](https://github.com/codespell-project/codespell)
to check code for common misspellings. We have a small custom dictionary file [codespell.txt](.github/linters/codespell.txt).
## Coding conventions
@@ -176,7 +168,7 @@ language itself. Please note the following hints for your Ruby code:
#### Comply with the Ruby standard (ISO/IEC 30170:2012)
mruby is currently targeting to execute Ruby code which complies to ISO/IEC
30170:2012 (<https://www.iso.org/standard/59579.html>),
30170:2012 (<https://www.iso.org/iso/iso_catalogue/catalogue_tc/catalogue_detail.htm?csnumber=59579>),
unless there's a clear reason, e.g. the latest Ruby has changed behavior from ISO.
## Building documentation
+1 -1
View File
@@ -48,7 +48,7 @@ PROJECT_NAME = mruby
# could be handy for archiving the generated documentation or if some version
# control system is used.
PROJECT_NUMBER = 4.0.0
PROJECT_NUMBER = 3.4.0
# Using the PROJECT_BRIEF tag one can provide an optional one line description
# for a project that appears at the top of each page and should give viewer a
+2 -2
View File
@@ -2,8 +2,8 @@ GEM
remote: https://rubygems.org/
specs:
coderay (1.1.3)
rake (13.3.1)
yard (0.9.38)
rake (13.3.0)
yard (0.9.37)
yard-coderay (0.1.0)
coderay
yard
+23 -30
View File
@@ -2,7 +2,7 @@
RAKE = rake
DOCKER_COMPOSE = docker-compose
PRE_COMMIT = prek
PRE_COMMIT = pre-commit
define check_command
@command -v $(1) >/dev/null 2>&1 || { \
@@ -11,53 +11,46 @@ define check_command
}
endef
# For colors
ifneq ($(shell tty -s),)
CYAN := $(shell tput setaf 6)
RESET := $(shell tput sgr0)
else
CYAN :=
RESET :=
endif
.PHONY: all test clean check checkinstall checkupdate composecheck composetest check_rake check_docker_compose check_pre_commit help
.DEFAULT_GOAL := all
all: check_rake ## build all targets, install (locally) in-repo
all : check_rake
$(RAKE)
.PHONY : all
test: check_rake all ## build and run all mruby tests
test : check_rake all
$(RAKE) test
.PHONY : test
clean: check_rake ## clean all built and in-repo installed artifacts
clean : check_rake
$(RAKE) clean
.PHONY : clean
check: check_pre_commit ## run all prek hooks against all files
check : check_pre_commit
$(PRE_COMMIT) run --all-files
.PHONY : check
checkinstall: check_pre_commit ## install the prek hooks
checkinstall : check_pre_commit
$(PRE_COMMIT) install
.PHONY : checkinstall
checkupdate: check_pre_commit ## check the prek hooks for updates
checkupdate : check_pre_commit
$(PRE_COMMIT) autoupdate
.PHONY : checkupdate
composecheck: check_docker_compose check_pre_commit ## run all prek hooks against all files with docker-compose
composecheck : check_docker_compose check_pre_commit
$(DOCKER_COMPOSE) -p mruby run test $(PRE_COMMIT) run --all-files
.PHONY : composecheck
composetest: check_docker_compose ## build and run all mruby tests with docker-compose
composetest : check_docker_compose
$(DOCKER_COMPOSE) -p mruby run test
.PHONY : composetest
check_rake: ## check if Rake is installed
check_rake:
$(call check_command, $(RAKE))
.PHONY : check_rake
check_docker_compose: ## check if docker-compose is installed
check_docker_compose:
$(call check_command, $(DOCKER_COMPOSE))
.PHONY : check_docker_compose
check_pre_commit: ## check if prek is installed
check_pre_commit:
$(call check_command, $(PRE_COMMIT))
help: ## display this help message
@echo "Usage: make <target>"
@echo
@echo "Available targets:"
@grep -E '^[a-z_-]+:.*##' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*## *"}; {printf " $(CYAN)%-20s$(RESET) %s\n", $$1, $$2}'
.PHONY : check_pre_commit
+254
View File
@@ -0,0 +1,254 @@
NEWS
----
# User visible changes in `mruby3.4` from `mruby3.3`
"**_NOTE_**:" are changes to be aware of.
# The language
- mruby now supports `private` and `protected` visibility ([b0db0bd](https://github.com/mruby/mruby/commit/b0db0bd))
- Maximum length of inlined symbols reduced from 5 to 4 characters to provide space for visibility flags ([6442a01](https://github.com/mruby/mruby/commit/6442a01))
- Many methods are made private according to CRuby visibility ([4a0e806](https://github.com/mruby/mruby/commit/4a0e806))
- Generate OP_SSEND for `self.method` type calls ([111fe4b](https://github.com/mruby/mruby/commit/111fe4b))
- `initialize` method will be always private ([eb8b412](https://github.com/mruby/mruby/commit/eb8b412))
- Add new hooks `method_removed`, `method_undefined` ([9c74f6e](https://github.com/mruby/mruby/commit/9c74f6e))
- Add new hooks `singleton_method_removed`, `singleton_method_undefined` ([0863c08](https://github.com/mruby/mruby/commit/0863c08))
- Updated `OP_DEF` output from codedump ([3a3e877](https://github.com/mruby/mruby/commit/3a3e877))
- Better handling of binary strings, e.g. String#b ([b0127f0](https://github.com/mruby/mruby/commit/b0127f0))
- Hash `to_s` format has changed ([baeeb5e](https://github.com/mruby/mruby/commit/baeeb5e))
- Some encoding related method such as `#force_encoding` ([e47b4ca](https://github.com/mruby/mruby/commit/e47b4ca)), `#b` ([b0127f0](https://github.com/mruby/mruby/commit/b0127f0))
- Constant folding for `String#+` ([6687bdd](https://github.com/mruby/mruby/commit/6687bdd))
- Remove Float bit-operation ([db8368f](https://github.com/mruby/mruby/commit/db8368f))
- use SWAR technique for strlen performance ([cbb31e6](https://github.com/mruby/mruby/commit/cbb31e6))
- use merge sort for `Array#sort` ([5bd63d6](https://github.com/mruby/mruby/commit/5bd63d6))
# Changes in C API
- pool.c renamed to mempool.c (and mrb_pool to mrb_mempool) ([49525fa](https://github.com/mruby/mruby/commit/49525fa))
- mrb_pool_value renamed to mrb_irep_pool to reduce confusion ([62ef5db](https://github.com/mruby/mruby/commit/62ef5db))
- rename BOXNIX_SET_VALUE to BOXNO_SET_VALUE ([#6397](https://github.com/mruby/mruby/pull/6397))
- `MRB_FROZEN_P()` is replaced by `mrb_frozen_p()` ([c11d18e](https://github.com/mruby/mruby/commit/c11d18e))
- rename `color` to `gc_color` ([0e79f6b](https://github.com/mruby/mruby/commit/0e79f6b), [1e36d76](https://github.com/mruby/mruby/commit/1e36d76))
- add `obj->frozen` instead of flags `MRB_SET_FROZEN_FLAG`/`MRB_UNSET_FROZEN_FLAG` ([8276143](https://github.com/mruby/mruby/commit/8276143))
# Build & Configuration
- New Build Target: `test:run:serial`, `test:run:serial:lib`, `test:run:serial:bin` ([#6423](https://github.com/mruby/mruby/pull/6423))
- New Platform: PlayStation Portable ([#6022](https://github.com/mruby/mruby/pull/6465))
- New Platform: emscripten ([#6487](https://github.com/mruby/mruby/pull/6487))
- New Config: no-float (with MRB_NO_FLOAT) ([32200f1](https://github.com/mruby/mruby/commit/32200f1))
# Changes in mrbgems
- **mruby-print**: removed; if you do not use `mruby-io`, mruby use `#print` etc. in the core ([8c8bbd9](https://github.com/mruby/mruby/commit/8c8bbd9))
- **mruby-enum-lazy**: Add Enumerable::Lazy#grep_v to mruby-enum-lazy ([#6171](https://github.com/mruby/mruby/pull/6171))
- **mruby-io**: Add `level` argument to `File.dirname` ([#6463](https://github.com/mruby/mruby/pull/6463))
- **mruby-io**: File.absolute_path? ([#6482](https://github.com/mruby/mruby/pull/6482))
- **mruby-io**: File.absolute_path ([96113a2](https://github.com/mruby/mruby/commit/96113a2))
- **mruby-toplevel-ext**: top-level public/private/protected moved to the core ([2a876d2](https://github.com/mruby/mruby/commit/2a876d2))
- **mruby-metaprog**: method list methods now works according to the visibility ([9229da1](https://github.com/mruby/mruby/commit/9229da1))
- **mruby-metaprog**: `public_instance_methods`, `private_instance_methods`, `protected_instance_methods` ([9e3e7b2](https://github.com/mruby/mruby/commit/9e3e7b2))
- **mruby-encoding**: MRB_UTF8_STRING turned on automatically with this gem ([74bdae9](https://github.com/mruby/mruby/commit/74bdae9))
# Fixed GitHub Issues
- [#6173](https://github.com/mruby/mruby/issues/6173) Fails to build with tcc(Tiny C Compiler)
- [#6156](https://github.com/mruby/mruby/issues/6156) '/LIBPATH' issue
- [#6183](https://github.com/mruby/mruby/issues/6183) ".e".to_f returns NAN
- [#6182](https://github.com/mruby/mruby/issues/6182) mrb_read_float() converts "0.3" with a small error compared to strtod()
- [#6210](https://github.com/mruby/mruby/issues/6210) Unary minus seems broken
- [#6255](https://github.com/mruby/mruby/issues/6255) Wrong number of characters in broken UTF-8 string
- [#4038](https://github.com/mruby/mruby/issues/4038) Heap buffer overflow in OP_ENTER
- [#6262](https://github.com/mruby/mruby/issues/6262) Unable to define == for objects when using Array#delete
- [#6267](https://github.com/mruby/mruby/issues/6267) When MRB_UTF8_STRING is enabled, giving byte characters for String#index and String#split gives wrong results
- [#6277](https://github.com/mruby/mruby/issues/6277) MSVC: can't use malloc() in string.c with WIN32_LEAN_AND_MEAN
- [#6240](https://github.com/mruby/mruby/issues/6240) Differentiate between lib and lib64 in the build settings.
- [#6304](https://github.com/mruby/mruby/issues/6304) Calling method_missing with only Kwargs passes arguments incorrectly
- [#6317](https://github.com/mruby/mruby/issues/6317) mrb_gc_register() may cause GC and collect the object being protected
- [#6307](https://github.com/mruby/mruby/issues/6307) Planned change patch for mrb_vm_exec()
- [#6298](https://github.com/mruby/mruby/issues/6298) foo :bar {} is legal in mruby?
- [#6326](https://github.com/mruby/mruby/issues/6326) Detect “Use-after-free” with address sanitizer
- [#5358](https://github.com/mruby/mruby/issues/5358) static warning from getpwnam
- [#6339](https://github.com/mruby/mruby/issues/6339) mrb_ary_delete() may refer to an invalid address (use-after-free)
- [#6346](https://github.com/mruby/mruby/issues/6346) Block kwargs are passed as last positional arg when using yield
- [#6365](https://github.com/mruby/mruby/issues/6365) powl() not available when compiling for Dreamcast
- [#6369](https://github.com/mruby/mruby/issues/6369) 100x Performance Regression from 3.1
- [#6270](https://github.com/mruby/mruby/issues/6270) NODE_ZSUPER from deeply nested blocks will result in a truncated digits for block index in OP_ARGARY
- [#6297](https://github.com/mruby/mruby/issues/6297) Assigning to a block variable changes the actual block (thus affecting block_given? and yield)
- [#6389](https://github.com/mruby/mruby/issues/6389) instance_exec named block args don't work properly
- [#6388](https://github.com/mruby/mruby/issues/6388) Recent commit broke my windows build
- [#6411](https://github.com/mruby/mruby/issues/6411) Wrong function unwinding when using return in a block
- [#6439](https://github.com/mruby/mruby/issues/6439) OP_JMPUW does not call the ensure block when it jumps to the beginning of the begin block
- [#6441](https://github.com/mruby/mruby/issues/6441) break inside while loop will execute the ensure block outside of the while loop
- [#6453](https://github.com/mruby/mruby/issues/6453) Bigint: incorrect behavior of ^ operator
- [#6452](https://github.com/mruby/mruby/issues/6452) Bigint: weird mod behavior
- [#6451](https://github.com/mruby/mruby/issues/6451) Bigint: incorrect division behavior
- [#6456](https://github.com/mruby/mruby/issues/6456) bigint: bug with division of a small number by a bigint
- [#6466](https://github.com/mruby/mruby/issues/6466) mruby-bin-mruby using Kernel#p and Kernel#print in bintest fails test
- [#6467](https://github.com/mruby/mruby/issues/6467) Heap-Use-After-Free due to Recursive group_by Calls
- [#6471](https://github.com/mruby/mruby/issues/6471) Discrepancy in codegen for binary operations between master branch and 3.3.0
- [#6477](https://github.com/mruby/mruby/issues/6477) heap-buffer-overflow in mrb_vm_exec
- [#6485](https://github.com/mruby/mruby/issues/6485) Hash#rehash does not check if the hash is frozen
- [#6483](https://github.com/mruby/mruby/issues/6483) Hash#default_proc= accepts arbitrary objects that are not callable
- [#6491](https://github.com/mruby/mruby/issues/6491) Destroy existing string literals when composing string literals
# Merged Pull Requests (User Visible Ones)
- [#6171](https://github.com/mruby/mruby/pull/6171) Add Enumerable::Lazy#grep_v to mruby-enum-lazy
- [#6174](https://github.com/mruby/mruby/pull/6174) Fix MRUBY_PACKAGE_DIR in mruby-config.bat
- [#6175](https://github.com/mruby/mruby/pull/6175) Corrected strange conditional in mrb_vm_run()
- [#6176](https://github.com/mruby/mruby/pull/6176) Stop assuming alias proc in CI_PROC_SET()
- [#6177](https://github.com/mruby/mruby/pull/6177) gha: add macOS 14 to the build
- [#6184](https://github.com/mruby/mruby/pull/6184) Remove the L_STOP label
- [#6185](https://github.com/mruby/mruby/pull/6185) Added mrb_callinfo::u.keep_context for clarity
- [#6186](https://github.com/mruby/mruby/pull/6186) Omit NULL check of e->cxt in OP_RETURN_BLK
- [#6191](https://github.com/mruby/mruby/pull/6191) Speed up symbol equality comparison
- [#6192](https://github.com/mruby/mruby/pull/6192) Fix `OP_STOP` with exception
- [#6193](https://github.com/mruby/mruby/pull/6193) Fix wrong assertion in `OP_SENDB`.
- [#6194](https://github.com/mruby/mruby/pull/6194) Simplify the calculation of the number of closures in `MRB_TT_FIBER`
- [#6197](https://github.com/mruby/mruby/pull/6197) Fix int_xor to call flo_xor.
- [#6201](https://github.com/mruby/mruby/pull/6201) tasks/doc.rake: standardize the `rake doc` error messages
- [#6202](https://github.com/mruby/mruby/pull/6202) Remove the `.yardoc` folder with `rake doc:clean:api`
- [#6204](https://github.com/mruby/mruby/pull/6204) Clean up the `.editorconfig` file
- [#6209](https://github.com/mruby/mruby/pull/6209) Minor `.gitignore` clean up
- [#6211](https://github.com/mruby/mruby/pull/6211) Minor `.gitignore` clean up; order entries
- [#6216](https://github.com/mruby/mruby/pull/6216) Shared empty `iv_tbl` of module
- [#6217](https://github.com/mruby/mruby/pull/6217) Strict declaration for `mrb_istruct_size()`
- [#6219](https://github.com/mruby/mruby/pull/6219) Avoid assigning a fixed value in the loop
- [#6220](https://github.com/mruby/mruby/pull/6220) Reorganize `mrb_cache_entry` and `mrb_method_t` types
- [#6221](https://github.com/mruby/mruby/pull/6221) Arranging `each_backtrace()`
- [#6222](https://github.com/mruby/mruby/pull/6222) Need to synchronize `dbg->regs` after VM call in `mrdb`
- [#6224](https://github.com/mruby/mruby/pull/6224) `mrb_env_unshare()` to break the link to fiber
- [#6225](https://github.com/mruby/mruby/pull/6225) Revert "Adjust environment when `mrb_exec_irep` happened."
- [#6227](https://github.com/mruby/mruby/pull/6227) fix: `Array#shuffle(!)` result distribution
- [#6228](https://github.com/mruby/mruby/pull/6228) Revert "`env` referred from top-level callinfo should not be unshared; fix #4019"
- [#6230](https://github.com/mruby/mruby/pull/6230) Fix status of fiber after switched by exception raised
- [#6231](https://github.com/mruby/mruby/pull/6231) Add a way to let other gems handle closing of fds in mruby-io
- [#6232](https://github.com/mruby/mruby/pull/6232) Fold the code for freeing `env`
- [#6233](https://github.com/mruby/mruby/pull/6233) Free stack memory at end of fiber
- [#6235](https://github.com/mruby/mruby/pull/6235) fix `Array#delete` always firing the block when deleting `nil`
- [#6236](https://github.com/mruby/mruby/pull/6236) unify the code for filter methods (and speed up `#reject!`)
- [#6237](https://github.com/mruby/mruby/pull/6237) Stricter env objects to attach to ci
- [#6238](https://github.com/mruby/mruby/pull/6238) Minimize zero initialization of the stack
- [#6243](https://github.com/mruby/mruby/pull/6243) Fixed base64 decoding in `mruby-pack`
- [#6244](https://github.com/mruby/mruby/pull/6244) Revise scope of role of `mrb_vm_run()`
- [#6246](https://github.com/mruby/mruby/pull/6246) Fix typo in `test/t/hash.rb`
- [#6249](https://github.com/mruby/mruby/pull/6249) Fix spelling in `src/vm.c`
- [#6250](https://github.com/mruby/mruby/pull/6250) Fix spelling
- [#6251](https://github.com/mruby/mruby/pull/6251) Clean up root move `CODEOWNERS` to `.github` directory
- [#6253](https://github.com/mruby/mruby/pull/6253) Allow recycling fibers by GC if not referenced directly
- [#6256](https://github.com/mruby/mruby/pull/6256) Update documentation for `mrb_top_run()`
- [#6257](https://github.com/mruby/mruby/pull/6257) fix some mrbconf.md typos
- [#6260](https://github.com/mruby/mruby/pull/6260) Remove `exc_caught` from `mrb_vm_exec()`
- [#6261](https://github.com/mruby/mruby/pull/6261) fix: `to_a` integer ranges with `begin > end` failing
- [#6263](https://github.com/mruby/mruby/pull/6263) fix: `Array#delete` mistakenly calling block even if not passed
- [#6264](https://github.com/mruby/mruby/pull/6264) Must pass keyword arguments for `Kernel#to_enum`
- [#6265](https://github.com/mruby/mruby/pull/6265) Fixes `Dir.children` and `Dir.each_child`
- [#6266](https://github.com/mruby/mruby/pull/6266) Passes the nonexistent key as a block argument in `Array#delete`
- [#6273](https://github.com/mruby/mruby/pull/6273) Improvements to `mrb_protect_atexit()`
- [#6275](https://github.com/mruby/mruby/pull/6275) Fixed `Binding#eval` that failed to assign to the same variable
- [#6276](https://github.com/mruby/mruby/pull/6276) Always run `atexit` on the top-level call frame
- [#6279](https://github.com/mruby/mruby/pull/6279) Include headers for malloc() explicitly; fix #6277
- [#6280](https://github.com/mruby/mruby/pull/6280) Remove `MRB_ENV_CLOSED` flag
- [#6281](https://github.com/mruby/mruby/pull/6281) Fixes local variables in `mruby-binding`.
- [#6283](https://github.com/mruby/mruby/pull/6283) Simplify `uvenv()`
- [#6288](https://github.com/mruby/mruby/pull/6288) Detach `env` of ci explicitly on atexit
- [#6289](https://github.com/mruby/mruby/pull/6289) Simplify `OP_RETURN_BLK` and `OP_BREAK`
- [#6290](https://github.com/mruby/mruby/pull/6290) Allow to change the output directory name of the `libmruby` file
- [#6293](https://github.com/mruby/mruby/pull/6293) Changed the instruction table in `opcode.md`
- [#6294](https://github.com/mruby/mruby/pull/6294) Optimise `mrb_iv_get`
- [#6302](https://github.com/mruby/mruby/pull/6302) Minor cleanup in mrb_str_init
- [#6303](https://github.com/mruby/mruby/pull/6303) mrb_str_aset_m() should return replace instead of str
- [#6305](https://github.com/mruby/mruby/pull/6305) Protect keyword arguments in `prepare_missing()`
- [#6308](https://github.com/mruby/mruby/pull/6308) Assume that `MRB_CATCH()` has `mrb->exc` set
- [#6310](https://github.com/mruby/mruby/pull/6310) Doubling the call stack when extending it
- [#6311](https://github.com/mruby/mruby/pull/6311) Added fast-path for positional arguments less than 15 in `OP_SEND`
- [#6312](https://github.com/mruby/mruby/pull/6312) Omit error checking at `OP_RETURN`, `OP_RETURN_BLK` and `OP_BREAK`
- [#6313](https://github.com/mruby/mruby/pull/6313) Fix wrong column number in opcode.md
- [#6314](https://github.com/mruby/mruby/pull/6314) Optimize even?/odd? for big integers
- [#6318](https://github.com/mruby/mruby/pull/6318) Shrink variables in `mrb_vm_exec()`
- [#6320](https://github.com/mruby/mruby/pull/6320) Shrinking the code in `OP_BREAK` and `OP_RETURN_BLK`
- [#6321](https://github.com/mruby/mruby/pull/6321) Avoid warnings in `lib/**/*.rb`
- [#6322](https://github.com/mruby/mruby/pull/6322) Fix mrb_ro_data_p on Intel Mac
- [#6324](https://github.com/mruby/mruby/pull/6324) Remove `localjump_error()`
- [#6327](https://github.com/mruby/mruby/pull/6327) fix ncurses linking issues
- [#6328](https://github.com/mruby/mruby/pull/6328) Fix use-after-free in `obj_free()` for env objects
- [#6329](https://github.com/mruby/mruby/pull/6329) Fix use-after-free in `mrb_obj_alloc()`
- [#6330](https://github.com/mruby/mruby/pull/6330) Add a precondition to call `mrb_env_unshare()`.
- [#6331](https://github.com/mruby/mruby/pull/6331) Restore the GC arena with tests
- [#6332](https://github.com/mruby/mruby/pull/6332) Must not depend on the “host” build to generate `mruby-compiler/core/y.tab.c`
- [#6333](https://github.com/mruby/mruby/pull/6333) Reduce the number of branch instructions in the `heap_p()`
- [#6335](https://github.com/mruby/mruby/pull/6335) Add `return_ci` in `CHECKPOINT_MAIN()` of `OP_RETURN`
- [#6338](https://github.com/mruby/mruby/pull/6338) Need to place static proc objects into 8-byte alignments
- [#6340](https://github.com/mruby/mruby/pull/6340) Fix use-after-free for `Array#<=>`
- [#6341](https://github.com/mruby/mruby/pull/6341) Need to restore the GC arena after some function calls
- [#6344](https://github.com/mruby/mruby/pull/6344) prefer using `mrb_yield` to call block arguments
- [#6347](https://github.com/mruby/mruby/pull/6347) codegen.c,parse.y: remove flattening of `yield` arguments; fix #6346
- [#6348](https://github.com/mruby/mruby/pull/6348) Cancel the warning disablement
- [#6349](https://github.com/mruby/mruby/pull/6349) Perform GC before deleting directories
- [#6350](https://github.com/mruby/mruby/pull/6350) Fixed character encoding conversion function mismatch
- [#6351](https://github.com/mruby/mruby/pull/6351) Remove unnecessary `mrb_gc_arena_restore()`
- [#6353](https://github.com/mruby/mruby/pull/6353) Fix use-after-free in `mrb_ary_delete()`
- [#6356](https://github.com/mruby/mruby/pull/6356) Making splat argument objects invisible from Ruby side
- [#6373](https://github.com/mruby/mruby/pull/6373) Add build config for Milk-V Duo (RISC-V Linux) board
- [#6382](https://github.com/mruby/mruby/pull/6382) Make array objects invisible in `mrb_gc_register()`
- [#6385](https://github.com/mruby/mruby/pull/6385) Small improvements for `mrb_gc_register()`
- [#6386](https://github.com/mruby/mruby/pull/6386) Avoid calling `mrb_gv_set()` from `mrb_gc_unregister()`
- [#6387](https://github.com/mruby/mruby/pull/6387) Small improvements for `mrb_gc_unregister()`
- [#6390](https://github.com/mruby/mruby/pull/6390) Fix use-after-free by `mrb_gc_unregistor()`
- [#6391](https://github.com/mruby/mruby/pull/6391) Fixed argument forwarding in `instance_exec`
- [#6392](https://github.com/mruby/mruby/pull/6392) Fix argument forwarding in `mrb_exec_irep()`
- [#6393](https://github.com/mruby/mruby/pull/6393) Follow-up to #6391
- [#6395](https://github.com/mruby/mruby/pull/6395) Storing method-id inside Symbol#to_proc
- [#6396](https://github.com/mruby/mruby/pull/6396) Milk-V Build Config: update GPIO gem URL
- [#6397](https://github.com/mruby/mruby/pull/6397) boxing_no.h: rename BOXNIX_SET_VALUE -> BOXNO_SET_VALUE
- [#6399](https://github.com/mruby/mruby/pull/6399) Add macOS 15 to the build
- [#6405](https://github.com/mruby/mruby/pull/6405) `io_read`: use `%i` instead of `%d` in call to `mrb_raisef`
- [#6407](https://github.com/mruby/mruby/pull/6407) Allow to exclude specific files in `rake install`
- [#6408](https://github.com/mruby/mruby/pull/6408) Improve compliance with C++ standards
- [#6410](https://github.com/mruby/mruby/pull/6410) Put `#include <stdlib.h>` in `parse.y`
- [#6412](https://github.com/mruby/mruby/pull/6412) Distinguish the call frame of the generator with `OP_RETURN_BLK`
- [#6413](https://github.com/mruby/mruby/pull/6413) Add links to documentation in `README.md`
- [#6415](https://github.com/mruby/mruby/pull/6415) Fix numbered parameters when used as a singleton
- [#6416](https://github.com/mruby/mruby/pull/6416) Optimize the "new" method's iseq
- [#6419](https://github.com/mruby/mruby/pull/6419) Follow Ruby's behavior for numbered parameters in -> {}
- [#6420](https://github.com/mruby/mruby/pull/6420) Update `labeler.yml`: add label for the `tools` directory
- [#6422](https://github.com/mruby/mruby/pull/6422) Add annotations for function names defined in the preprocessor
- [#6423](https://github.com/mruby/mruby/pull/6423) Adding a serialized test task
- [#6427](https://github.com/mruby/mruby/pull/6427) Need to update `ci` variable after re-entry to VM
- [#6428](https://github.com/mruby/mruby/pull/6428) Change the limits of OP_ADDI and OP_SUBI from 0-127 to 0-255.
- [#6429](https://github.com/mruby/mruby/pull/6429) Fix numbered parameters when used as hash keys
- [#6432](https://github.com/mruby/mruby/pull/6432) Moving code in macro arguments out of macros
- [#6434](https://github.com/mruby/mruby/pull/6434) Added document "Layout of the mruby filesystem"
- [#6436](https://github.com/mruby/mruby/pull/6436) Make `rake doc:update-index` prettier friendly
- [#6437](https://github.com/mruby/mruby/pull/6437) Add more details to the pre-commit config
- [#6438](https://github.com/mruby/mruby/pull/6438) Remove unused `MRuby::Build#list_install_excludes` method
- [#6440](https://github.com/mruby/mruby/pull/6440) Fix `redo` keyword
- [#6442](https://github.com/mruby/mruby/pull/6442) Fixed wrong range condition in `OP_JMPUW`
- [#6443](https://github.com/mruby/mruby/pull/6443) Fix NODE_NEGATE for bigints
- [#6444](https://github.com/mruby/mruby/pull/6444) Add test cases for bigints
- [#6446](https://github.com/mruby/mruby/pull/6446) Omit the `_WIN64` definition check
- [#6447](https://github.com/mruby/mruby/pull/6447) Fixed `File.expand_path`
- [#6448](https://github.com/mruby/mruby/pull/6448) Suppress presym in `mruby/ext/io.h` file
- [#6449](https://github.com/mruby/mruby/pull/6449) Using presym in the `mruby-io/src/file_test.c` file
- [#6450](https://github.com/mruby/mruby/pull/6450) Change `MRB_WITH_IO_PREAD_PWRITE` configuration name
- [#6454](https://github.com/mruby/mruby/pull/6454) mruby-bigint: handle rhs bigint in int_mod and int_divmod functions
- [#6455](https://github.com/mruby/mruby/pull/6455) mruby-bigint: fix rounding behavior in mpz_mdiv and mpz_mdivmod functions
- [#6457](https://github.com/mruby/mruby/pull/6457) bigint: fix bug with division of a small number by a bigint
- [#6459](https://github.com/mruby/mruby/pull/6459) `FileTest` is a module
- [#6461](https://github.com/mruby/mruby/pull/6461) To create a release package file in draft
- [#6462](https://github.com/mruby/mruby/pull/6462) Properly cast the return value of `memchr()`
- [#6463](https://github.com/mruby/mruby/pull/6463) Add `level` argument to `File.dirname`
- [#6465](https://github.com/mruby/mruby/pull/6465) Add initial PlayStation portable crossbuild support
- [#6468](https://github.com/mruby/mruby/pull/6468) Fixed missing changes to `IB_FIND_BY_KEY()` parameter names
- [#6469](https://github.com/mruby/mruby/pull/6469) Add more `const` qualifier for `RProc`
- [#6472](https://github.com/mruby/mruby/pull/6472) Moved tests for `Integer#quo`
- [#6473](https://github.com/mruby/mruby/pull/6473) Hide `mpz_and()` symbol
- [#6473](https://github.com/mruby/mruby/pull/6473) Hide mpz_and() symbol
- [#6474](https://github.com/mruby/mruby/pull/6474) Avoid array object creation with “unknown keyword” error
- [#6475](https://github.com/mruby/mruby/pull/6475) Don't include deleted mruby-print
- [#6478](https://github.com/mruby/mruby/pull/6478) Fixed buffer overrun in function `chars2bytes()`
- [#6479](https://github.com/mruby/mruby/pull/6479) Reimplementation of `File.expand_path` method
- [#6482](https://github.com/mruby/mruby/pull/6482) Add `File.absolute_path?` method
- [#6487](https://github.com/mruby/mruby/pull/6487) Add Emscripten toolchain & build_config
-367
View File
@@ -1,367 +0,0 @@
# User visible changes in `mruby4.0` from `mruby3.4`
"**_NOTE_**:" are changes to be aware of.
# The language
## Pattern Matching
mruby now supports pattern matching (case/in) syntax:
- Basic pattern matching with `case`/`in` syntax ([dadfac6](https://github.com/mruby/mruby/commit/dadfac6))
- Array pattern matching ([ec67fd9](https://github.com/mruby/mruby/commit/ec67fd9))
- Hash pattern matching ([2147263](https://github.com/mruby/mruby/commit/2147263))
- Find pattern matching (`[*pre, target, *post]`) ([6c4d98b](https://github.com/mruby/mruby/commit/6c4d98b))
- Pin operator (`^variable`) ([1de6340](https://github.com/mruby/mruby/commit/1de6340))
- Guard clauses (`if`/`unless` conditions) ([07ac110](https://github.com/mruby/mruby/commit/07ac110))
- One-line pattern matching (`expr in pattern`) ([e76ce24](https://github.com/mruby/mruby/commit/e76ce24))
- Brace-less hash pattern support ([e8096bf](https://github.com/mruby/mruby/commit/e8096bf))
## Other Language Changes
- `&nil` in formal parameters to explicitly opt out of block arguments ([b07518e](https://github.com/mruby/mruby/commit/b07518e))
- 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
- **_NOTE_**: `mrb_alloca()` renamed to `mrb_temp_alloc()` ([7fe5c2e](https://github.com/mruby/mruby/commit/7fe5c2e))
- **_NOTE_**: `mruby/ext/io.h` renamed to `mruby/io.h` ([2813f79](https://github.com/mruby/mruby/commit/2813f79))
- `mrb_gc_add_region()` for contiguous heap region support ([072855a](https://github.com/mruby/mruby/commit/072855a))
- `mrb_class_outer()` to get the outer class/module ([3a1b771](https://github.com/mruby/mruby/commit/3a1b771))
- `MRB_ENSURE()` macro for exception-safe cleanup ([3ac682b](https://github.com/mruby/mruby/commit/3ac682b))
- `mrb_time_get_tm()` for accessing struct tm ([daaaafe](https://github.com/mruby/mruby/commit/daaaafe))
- `MRB_OPEN_FAILURE()` macro for checking mrb_open result ([40b0cb9](https://github.com/mruby/mruby/commit/40b0cb9))
- `mrb_print_error()` now handles NULL gracefully ([8e50a45](https://github.com/mruby/mruby/commit/8e50a45))
- `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
All built-in classes and most extension gems now use read-only method
tables stored in `.rodata` instead of heap-allocated hash tables. Method
definitions no longer consume heap memory, significantly reducing memory
footprint for embedded use.
Core classes converted: BasicObject, Object, Module, Class, Kernel,
String, Array, Hash, Numeric, Integer, Float, NilClass, TrueClass,
FalseClass, Range, Symbol, Exception, Proc.
Extension gems converted: mruby-string-ext, mruby-array-ext, mruby-set,
mruby-struct, mruby-class-ext, mruby-numeric-ext, mruby-random,
mruby-kernel-ext, mruby-complex, mruby-rational, mruby-io, mruby-socket,
mruby-method, mruby-metaprog, mruby-time, mruby-hash-ext, mruby-proc-ext,
mruby-symbol-ext, mruby-range-ext, mruby-object-ext.
# GC and Memory
- **_NOTE_**: `MRB_NO_PRESYM` removed; presym is now always enabled ([81689045](https://github.com/mruby/mruby/commit/81689045))
- Replace `gcnext` gray linked list with fixed-size gray stack, reducing per-object overhead ([31fea170](https://github.com/mruby/mruby/commit/31fea170))
- `mrb_gc_add_region()` for providing contiguous memory buffers as GC heap pages ([072855a](https://github.com/mruby/mruby/commit/072855a))
- Chunk-based pool for symbol string allocation ([e05bd8f](https://github.com/mruby/mruby/commit/e05bd8f))
- Reduce `IV_INITIAL_SIZE` from 4 to 2 ([6bd1f51](https://github.com/mruby/mruby/commit/6bd1f51))
- Lossless float encoding using rotation in word boxing ([b6148c8](https://github.com/mruby/mruby/commit/b6148c8))
- Lossless rotation encoding for 32-bit float32 word boxing ([14a5cfb](https://github.com/mruby/mruby/commit/14a5cfb))
- Consolidated irep allocation for .mrb loading ([74fb045](https://github.com/mruby/mruby/commit/74fb045))
- Object shapes (hidden classes) for `MRB_TT_OBJECT` IV storage, sharing key layouts across objects with the same instance variable assignment order ([8d10056](https://github.com/mruby/mruby/commit/8d10056))
# Build & Configuration
- **_NOTE_**: `MRB_WORDBOX_NO_FLOAT_TRUNCATE` renamed to `MRB_WORDBOX_NO_INLINE_FLOAT` (old name still works) ([59e1fe2](https://github.com/mruby/mruby/commit/59e1fe2))
- **_NOTE_**: `MRB_INT64` on 32-bit now requires `MRB_NO_BOXING` (other boxing modes cannot guarantee alignment for heap-allocated 64-bit integers) ([eaaa66b](https://github.com/mruby/mruby/commit/eaaa66b))
- Amalgamation support via `rake amalgam` task ([d995ca2](https://github.com/mruby/mruby/commit/d995ca2))
- New Platform: Cosmopolitan Libc ([#6681](https://github.com/mruby/mruby/pull/6681))
- 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
## New Gems
- **mruby-task**: Cooperative multitasking with preemptive scheduling ([ae0d7a0](https://github.com/mruby/mruby/commit/ae0d7a0))
- **mruby-benchmark**: Benchmarking gem ([2f40f3d](https://github.com/mruby/mruby/commit/2f40f3d))
- **mruby-strftime**: Time#strftime implementation ([b31e22f](https://github.com/mruby/mruby/commit/b31e22f))
## mruby-bin-mirb Improvements
- Custom multi-line editor replacing readline ([527018c](https://github.com/mruby/mruby/commit/527018c))
- Syntax highlighting for keywords, strings, result values, hash key symbols ([624272b](https://github.com/mruby/mruby/commit/624272b), [1713d4a](https://github.com/mruby/mruby/commit/1713d4a))
- Automatic light/dark theme detection via OSC 11 ([db4c8d9](https://github.com/mruby/mruby/commit/db4c8d9))
- Tab completion support ([2f15282](https://github.com/mruby/mruby/commit/2f15282))
- Colored output for prompts and errors ([b36e0b4](https://github.com/mruby/mruby/commit/b36e0b4))
- Auto-indentation and auto-dedent ([d52f318](https://github.com/mruby/mruby/commit/d52f318), [e901b6d](https://github.com/mruby/mruby/commit/e901b6d))
- Command history with Up/Down navigation ([5f85c1b](https://github.com/mruby/mruby/commit/5f85c1b))
- Line numbers in multi-line prompts ([5a3f0e2](https://github.com/mruby/mruby/commit/5a3f0e2))
- UTF-8 multibyte character support ([4a97da3](https://github.com/mruby/mruby/commit/4a97da3))
## mruby-bigint Improvements
- Toom-3 multiplication for large numbers ([99620804](https://github.com/mruby/mruby/commit/99620804))
- Karatsuba multiplication for medium-sized numbers ([85e81072](https://github.com/mruby/mruby/commit/85e81072))
- Balance multiplication for asymmetric operands ([0220ec2b](https://github.com/mruby/mruby/commit/0220ec2b))
- Divide-and-conquer optimization for `to_s` ([990ff90f](https://github.com/mruby/mruby/commit/990ff90f))
- Consolidated mpn layer for low-level limb operations ([9ef3362f](https://github.com/mruby/mruby/commit/9ef3362f))
- Always use 32-bit limbs by default ([c747c77f](https://github.com/mruby/mruby/commit/c747c77f))
## 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))
- **mruby-compiler**: Variable-sized AST nodes for reduced memory usage
- **mruby-compiler**: `no_return_value` context flag for script optimization ([613b03a](https://github.com/mruby/mruby/commit/613b03a))
- `initialize_copy` and `respond_to_missing?` defined as private ([#6708](https://github.com/mruby/mruby/pull/6708))
- Struct keyword argument initialization ([#6574](https://github.com/mruby/mruby/pull/6574))
# Compiler Improvements
- Variable-sized AST nodes for reduced memory consumption ([821b989](https://github.com/mruby/mruby/commit/821b989))
- Pattern matching bytecode optimizations ([21d4135](https://github.com/mruby/mruby/commit/21d4135))
- 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
New super-instructions that fuse common opcode sequences to reduce bytecode size and improve performance:
- `OP_SEND0`/`OP_SSEND0`: Zero-argument method call, avoiding argument count setup ([9123ef4](https://github.com/mruby/mruby/commit/9123ef4))
- `OP_TDEF`/`OP_SDEF`: Fused method definition combining TCLASS/SCLASS+METHOD+DEF into single instruction, saving 4 bytes per method ([8d4f47e](https://github.com/mruby/mruby/commit/8d4f47e))
- `OP_GETIDX0`: Fast path for `array[0]` and `Array#first` access ([680f7ec](https://github.com/mruby/mruby/commit/680f7ec))
- `OP_ADDILV`/`OP_SUBILV`: Local variable increment/decrement fusion for `i += n` patterns ([43f64b9](https://github.com/mruby/mruby/commit/43f64b9))
- `OP_RETSELF`: Single-byte instruction for `return self` pattern ([a71db8c](https://github.com/mruby/mruby/commit/a71db8c))
- `OP_RETNIL`: Single-byte instruction for `return nil` pattern ([64e30bf](https://github.com/mruby/mruby/commit/64e30bf))
- `OP_RETTRUE`/`OP_RETFALSE`: Single-byte instructions for `return true`/`return false` patterns ([0b15727](https://github.com/mruby/mruby/commit/0b15727))
- `OP_MATCHERR`: Pattern matching error with conditional execution ([944168a](https://github.com/mruby/mruby/commit/944168a))
- `OP_BLKCALL`: Direct block call for `yield`, bypassing method dispatch (13-17% faster) ([3aa2872](https://github.com/mruby/mruby/commit/3aa2872))
Other optimizations:
- 1.5x stack growth instead of linear growth for reduced reallocations ([f7988c93](https://github.com/mruby/mruby/commit/f7988c93))
- Skip keyword argument hash duplication ([5970e350](https://github.com/mruby/mruby/commit/5970e350))
# Fixed GitHub Issues
- [#5531](https://github.com/mruby/mruby/issues/5531) Hash recursion detection
- [#6506](https://github.com/mruby/mruby/issues/6506) Constant lookup in singleton class
- [#6507](https://github.com/mruby/mruby/issues/6507) tally multi-values
- [#6508](https://github.com/mruby/mruby/issues/6508) Enumerable#sum index
- [#6509](https://github.com/mruby/mruby/issues/6509) scope_new nregs initialization
- [#6515](https://github.com/mruby/mruby/issues/6515) y.tab.c in repository
- [#6516](https://github.com/mruby/mruby/issues/6516) Private backquote
- [#6554](https://github.com/mruby/mruby/issues/6554) Socket private #initialize
- [#6570](https://github.com/mruby/mruby/issues/6570) instance_eval crash
- [#6613](https://github.com/mruby/mruby/issues/6613) const_added hook during bootstrapping
- [#6635](https://github.com/mruby/mruby/issues/6635), [#6636](https://github.com/mruby/mruby/issues/6636) Colon3 constant lookup
- [#6637](https://github.com/mruby/mruby/issues/6637) arm64 mingw64 builtin setjmp/longjmp
- [#6642](https://github.com/mruby/mruby/issues/6642) Task segfault when sleep called from C
- [#6645](https://github.com/mruby/mruby/issues/6645) Set memory leak from double initialization
- [#6646](https://github.com/mruby/mruby/issues/6646) IO#gets negative length
- [#6647](https://github.com/mruby/mruby/issues/6647) IO#ungetc buffer overflow
- [#6648](https://github.com/mruby/mruby/issues/6648) sprintf buffer overread
- [#6649](https://github.com/mruby/mruby/issues/6649) Array#sort! use-after-realloc
- [#6650](https://github.com/mruby/mruby/issues/6650) Array#fill validation
- [#6652](https://github.com/mruby/mruby/issues/6652) Array comparison use-after-realloc
- [#6657](https://github.com/mruby/mruby/issues/6657) Exception handling for ||= on class variables
- [#6659](https://github.com/mruby/mruby/issues/6659) Super with keyword arguments
- [#6660](https://github.com/mruby/mruby/issues/6660) Regression on struct/array/hash == override with super
- [#6662](https://github.com/mruby/mruby/issues/6662) Array set operations use-after-free
- [#6664](https://github.com/mruby/mruby/issues/6664) Set#flatten memory leak
- [#6666](https://github.com/mruby/mruby/issues/6666) Regexp literal with encoding
- [#6668](https://github.com/mruby/mruby/issues/6668) Method#== for aliased methods and comparison bug
- [#6671](https://github.com/mruby/mruby/issues/6671) Separate inter-gem headers from external API headers
- [#6674](https://github.com/mruby/mruby/issues/6674) Document pattern matching limitations
- [#6675](https://github.com/mruby/mruby/issues/6675) Allow Hash#[] to be aliased again
- [#6687](https://github.com/mruby/mruby/issues/6687) Expand MRB_SYM/MRB_GVSYM support for symbols with special characters
- [#6698](https://github.com/mruby/mruby/issues/6698) Bigint tests fail on architectures other than x86_64 and i386
- [#6701](https://github.com/mruby/mruby/issues/6701) Heap-use-after-free in mrb_vm_exec involving mruby-rational / mruby-bigint
- [#6702](https://github.com/mruby/mruby/issues/6702) mruby-bigint doesn't compile in C++ project
- [#6704](https://github.com/mruby/mruby/issues/6704) Heap-buffer-overflow in mrb_vm_exec via malformed source code
- [#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
- [#6418](https://github.com/mruby/mruby/pull/6418) Add `ls-lint` with GitHub Actions
- [#6492](https://github.com/mruby/mruby/pull/6492) fix a typo, update specs
- [#6493](https://github.com/mruby/mruby/pull/6493) Fix TYPO in memory.md
- [#6495](https://github.com/mruby/mruby/pull/6495) Remove `MRB_ENDIAN_LOHI()` that is no longer in use
- [#6497](https://github.com/mruby/mruby/pull/6497) gha: update `build.yml` try `windows-2025` image
- [#6498](https://github.com/mruby/mruby/pull/6498) Clean up and standardize the pre-commit config
- [#6501](https://github.com/mruby/mruby/pull/6501) Update pre-commit Node.js version to `v22.14.0 LTS`
- [#6502](https://github.com/mruby/mruby/pull/6502) pre-commit: update prettier to the latest version
- [#6503](https://github.com/mruby/mruby/pull/6503) misc: fix typos
- [#6505](https://github.com/mruby/mruby/pull/6505) mrbgems: fix spelling
- [#6510](https://github.com/mruby/mruby/pull/6510) Fixed class method visibility via `module_function`
- [#6511](https://github.com/mruby/mruby/pull/6511) Exclude the external project "lrama" from pre-commit
- [#6513](https://github.com/mruby/mruby/pull/6513) mruby 3.4.0 released
- [#6517](https://github.com/mruby/mruby/pull/6517) core/codegen.c: remove unneeded duplicate semicolon
- [#6518](https://github.com/mruby/mruby/pull/6518) Change mrbc_args.flags bit width from 2 to 3
- [#6519](https://github.com/mruby/mruby/pull/6519) Add `tools/lrama` to `.prettierignore`
- [#6520](https://github.com/mruby/mruby/pull/6520) pre-commit: autoupdate and update node LTS version
- [#6521](https://github.com/mruby/mruby/pull/6521) Add codespell config file `.codespellrc`
- [#6522](https://github.com/mruby/mruby/pull/6522) gha: label more files
- [#6523](https://github.com/mruby/mruby/pull/6523) add `rand(Range)` and unify implementations of `Random#rand` and `Kernel#rand`
- [#6524](https://github.com/mruby/mruby/pull/6524) Fix Kernel#p when no argument
- [#6525](https://github.com/mruby/mruby/pull/6525) Skip adding empty input to mirb history
- [#6526](https://github.com/mruby/mruby/pull/6526) Add build config for Luckfox Pico embedded SBC
- [#6528](https://github.com/mruby/mruby/pull/6528) misc: fix spelling
- [#6530](https://github.com/mruby/mruby/pull/6530) Revert "class.c (find_visibility_scope): when callinfo returns, \*ep == NULL; #6512"
- [#6531](https://github.com/mruby/mruby/pull/6531) Improve method table performance by rehashing at 75% load factor
- [#6532](https://github.com/mruby/mruby/pull/6532) Reverted method table optimizations to prioritize memory savings
- [#6533](https://github.com/mruby/mruby/pull/6533) Fix calling `extended` callback
- [#6534](https://github.com/mruby/mruby/pull/6534) Add descriptive comment to mrb_read_float function
- [#6535](https://github.com/mruby/mruby/pull/6535) Added descriptive comments for functions/macros in src/mempool.c
- [#6536](https://github.com/mruby/mruby/pull/6536) Add descriptive comments to public functions in src/debug.c
- [#6537](https://github.com/mruby/mruby/pull/6537) Updated comments in `cdump.c` to remove the `@brief` tag
- [#6539](https://github.com/mruby/mruby/pull/6539) Add descriptive comments for functions in src/load.c
- [#6540](https://github.com/mruby/mruby/pull/6540) Add descriptive comments to MRB_API functions in object.c
- [#6541](https://github.com/mruby/mruby/pull/6541) Add descriptive comments for MRB_API functions in src/array.c
- [#6542](https://github.com/mruby/mruby/pull/6542) Add descriptive comments to MRB_API functions in src/symbol.c
- [#6543](https://github.com/mruby/mruby/pull/6543) Add descriptive comments to several functions in src/dump.c
- [#6544](https://github.com/mruby/mruby/pull/6544) Fix build strings that must be mutable
- [#6545](https://github.com/mruby/mruby/pull/6545) Add descriptive comments for MRB_API functions in src/class.c
- [#6548](https://github.com/mruby/mruby/pull/6548) Add descriptive comments to MRB_API functions in src/etc.c
- [#6549](https://github.com/mruby/mruby/pull/6549) Add descriptive comments to kernel functions
- [#6550](https://github.com/mruby/mruby/pull/6550) Add descriptive comments for MRB_API functions in src/proc.c
- [#6551](https://github.com/mruby/mruby/pull/6551) Add descriptive comments for MRB_API functions in src/state.c
- [#6552](https://github.com/mruby/mruby/pull/6552) Fix: Correct placement of comments in src/variable.c
- [#6553](https://github.com/mruby/mruby/pull/6553) Add descriptive comments for MRB_API functions in src/vm.c
- [#6555](https://github.com/mruby/mruby/pull/6555) `mrb_mt_foreach()` needs to update the pointer at each loop
- [#6556](https://github.com/mruby/mruby/pull/6556) `iv_foreach()` needs to update the pointer at each loop
- [#6560](https://github.com/mruby/mruby/pull/6560) Refactor: Improve Set GC marking and freeing
- [#6561](https://github.com/mruby/mruby/pull/6561) pre-commit updates and fix prettier entrypoint
- [#6562](https://github.com/mruby/mruby/pull/6562) misc: fix spelling word case
- [#6563](https://github.com/mruby/mruby/pull/6563) pre-commit add rubocop with one rule spaces for indentation
- [#6564](https://github.com/mruby/mruby/pull/6564) Remove jumanjihouse pre-commit hooks no longer maintained
- [#6565](https://github.com/mruby/mruby/pull/6565) Rubocop: fix target Ruby version; add two more cops; fix lint error
- [#6566](https://github.com/mruby/mruby/pull/6566) Removed unreferenced variables in `CrossBuild#run_bintest`
- [#6567](https://github.com/mruby/mruby/pull/6567) Avoid array object creation in `cmd_bin` method in bintest
- [#6568](https://github.com/mruby/mruby/pull/6568) mruby-bin-debugger depends on mruby-bin-mrbc in bintest
- [#6569](https://github.com/mruby/mruby/pull/6569) sed s/Mruby/MRuby/g
- [#6571](https://github.com/mruby/mruby/pull/6571) Update limitations.md to add behavior on small hash
- [#6572](https://github.com/mruby/mruby/pull/6572) Add Claude Code GitHub Workflow
- [#6573](https://github.com/mruby/mruby/pull/6573) pre-commit fixes and updates
- [#6574](https://github.com/mruby/mruby/pull/6574) Support initializing structs via keyword arguments
- [#6575](https://github.com/mruby/mruby/pull/6575) Fix typo in file time methods
- [#6581](https://github.com/mruby/mruby/pull/6581) Merge `mrb_obj_iv_inspect()` into `mrb_obj_inspect()`
- [#6582](https://github.com/mruby/mruby/pull/6582) Stricter type tag in `mrb_obj_alloc()`
- [#6583](https://github.com/mruby/mruby/pull/6583) Add fallback to local build_config.rb before using default configuration
- [#6585](https://github.com/mruby/mruby/pull/6585) Fix typo in mruby3.2 docs
- [#6586](https://github.com/mruby/mruby/pull/6586) Makefile: refactor add docs and add command line `help` target
- [#6587](https://github.com/mruby/mruby/pull/6587) Add Set#hash tests
- [#6588](https://github.com/mruby/mruby/pull/6588) Add CodeQL Analysis for GitHub Actions
- [#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
- [#6602](https://github.com/mruby/mruby/pull/6602) Improved iseq annotations for `new` and `!=`
- [#6604](https://github.com/mruby/mruby/pull/6604) pre-commit config updates
- [#6607](https://github.com/mruby/mruby/pull/6607) fix bigint on raspberry pi
- [#6610](https://github.com/mruby/mruby/pull/6610) Extract golden ratio prime into constant
- [#6614](https://github.com/mruby/mruby/pull/6614) Fix uninitialized variable in io_gets causing segmentation fault
- [#6617](https://github.com/mruby/mruby/pull/6617) Fix various minor problems and speed up build
- [#6618](https://github.com/mruby/mruby/pull/6618) Stop generating unnecessary C++ files in mruby-bin-mruby
- [#6621](https://github.com/mruby/mruby/pull/6621) Set up all GEMS before mruby core tasks definition
- [#6624](https://github.com/mruby/mruby/pull/6624) Fixed wrong `MRuby::Build.current` at the top level of `mrbgem.rake`
- [#6628](https://github.com/mruby/mruby/pull/6628) Revert `File.absolute_path` logic
- [#6629](https://github.com/mruby/mruby/pull/6629) pre-commit update
- [#6631](https://github.com/mruby/mruby/pull/6631) Revert "Rakefile: make the whole thing parallel unless SERIAL=1"
- [#6633](https://github.com/mruby/mruby/pull/6633) Fix a heap-buffer-overflow in str strip! methods
- [#6643](https://github.com/mruby/mruby/pull/6643) Fix crash caused by an incorrect node type check in `codegen_masgn`
- [#6651](https://github.com/mruby/mruby/pull/6651) Address stack-use-after-return in the mruby bigint implementation
- [#6653](https://github.com/mruby/mruby/pull/6653) Improve HAL-related components for MinGW
- [#6655](https://github.com/mruby/mruby/pull/6655) Preventing Memory Leaks in `Array#__combination_init`
- [#6656](https://github.com/mruby/mruby/pull/6656) Fix integer overflow in allocation size calculation
- [#6663](https://github.com/mruby/mruby/pull/6663) Added the `kh_is_end()` macro function
- [#6665](https://github.com/mruby/mruby/pull/6665) Fixed use-after-free with `Set#join`
- [#6670](https://github.com/mruby/mruby/pull/6670) Arranging VM dispatch macros
- [#6673](https://github.com/mruby/mruby/pull/6673) Adjust broken license links; clean up Markdown
- [#6677](https://github.com/mruby/mruby/pull/6677) gha: run pre-commit with `--color=always`
- [#6678](https://github.com/mruby/mruby/pull/6678) Put ls-lint and pre-commit in separate workflow files
- [#6679](https://github.com/mruby/mruby/pull/6679) pre-commit autoupdate; update node and prettier
- [#6681](https://github.com/mruby/mruby/pull/6681) Add Cosmopolitan Libc build configuration
- [#6689](https://github.com/mruby/mruby/pull/6689) docs: fix pre-commit manual hooks; fix link
- [#6694](https://github.com/mruby/mruby/pull/6694) Fix mirb build under Cosmopolitan
- [#6695](https://github.com/mruby/mruby/pull/6695) Dependabot: add a cooldown period for new releases
- [#6696](https://github.com/mruby/mruby/pull/6696) Fix parse error with required kwargs and omitted parens
- [#6699](https://github.com/mruby/mruby/pull/6699) Fix mruby-task for PicoRuby Integration
- [#6700](https://github.com/mruby/mruby/pull/6700) Fix float/double pack/unpack on s390x
- [#6706](https://github.com/mruby/mruby/pull/6706) Refactor task class to use symbol IDs
- [#6708](https://github.com/mruby/mruby/pull/6708) `initialize_copy` and `respond_to_missing?` defined as private
- [#6709](https://github.com/mruby/mruby/pull/6709) Add the `MRB_ENSURE()` macro
- [#6711](https://github.com/mruby/mruby/pull/6711) Fix out of bounds read and write in IO.select
- [#6714](https://github.com/mruby/mruby/pull/6714) Fix OP_DEBUG operand type and add NULL check for debug_op_hook
- [#6716](https://github.com/mruby/mruby/pull/6716) Fixes identity for proc object
- [#6717](https://github.com/mruby/mruby/pull/6717) Fix mruby-task: wrapping by critical section and setting initial task receiver
- [#6718](https://github.com/mruby/mruby/pull/6718) Add installation instructions for conda and Homebrew
- [#6723](https://github.com/mruby/mruby/pull/6723) Add `RInteger` and `RFloat` to `RVALUE`
- [#6727](https://github.com/mruby/mruby/pull/6727) Language documentation: update wording of "overloading" section
- [#6729](https://github.com/mruby/mruby/pull/6729) Simplifying dependency addition for gensym task
- [#6730](https://github.com/mruby/mruby/pull/6730) Simplifying presym file generation actions
- [#6733](https://github.com/mruby/mruby/pull/6733) Include `mruby/presym.h` for all source files
- [#6734](https://github.com/mruby/mruby/pull/6734) Chunk array literals at 64 elements to reduce register pressure
- [#6735](https://github.com/mruby/mruby/pull/6735) Prevent full recompilation without changes to presym file
- [#6739](https://github.com/mruby/mruby/pull/6739) Fix MSYS2 build error with drive letters
- [#6743](https://github.com/mruby/mruby/pull/6743) Chunk `%w()` and `%i()` literals to reduce register pressure
- [#6744](https://github.com/mruby/mruby/pull/6744) Raise `NoMatchingPatternError` in `case`/`in` without `else`
- [#6747](https://github.com/mruby/mruby/pull/6747) Correctly handle empty hash as default named argument
- [#6749](https://github.com/mruby/mruby/pull/6749) Fix microcontroller profile
- [#6750](https://github.com/mruby/mruby/pull/6750) Fix out-of-bounds read and divide-by-zero in `Array#product`
- [#6752](https://github.com/mruby/mruby/pull/6752) Fix `attr_reader`-generated methods accepting extra arguments
- [#6753](https://github.com/mruby/mruby/pull/6753) Further optimize `Array#product`
- [#6754](https://github.com/mruby/mruby/pull/6754) Mark `attr_reader` procs as noarg
- [#6755](https://github.com/mruby/mruby/pull/6755) Reload `ci` after `mrb_hash_delete_key()` in keyword argument handling
- [#6756](https://github.com/mruby/mruby/pull/6756) Avoid impact of object modifications caused by `mrb_vm_exec()` calls
- [#6758](https://github.com/mruby/mruby/pull/6758) Don't assign result of `mrb_funcall()` directly to `regs`
- [#6759](https://github.com/mruby/mruby/pull/6759) Define `mrb_bigint_p()` always
- [#6761](https://github.com/mruby/mruby/pull/6761) Fix `mrb_gc_unregister()` to remove all matching entries
- [#6762](https://github.com/mruby/mruby/pull/6762) Write generated test C files atomically to avoid build race condition
- [#6765](https://github.com/mruby/mruby/pull/6765) Fix `Lazy#flat_map` to handle non-enumerable block return values
- [#6767](https://github.com/mruby/mruby/pull/6767) Allow compound statement in parenthesized argument context
- [#6780](https://github.com/mruby/mruby/pull/6780) Fix `String#prepend` with self-referencing arguments
- [#6781](https://github.com/mruby/mruby/pull/6781) Protect `sprintf` format string from mutation during callbacks
- [#6783](https://github.com/mruby/mruby/pull/6783) Pin GitHub Actions workflows to commit hashes
# Security Fixes
- Buffer overflow in bigint uadd ([3f2611e](https://github.com/mruby/mruby/commit/3f2611e))
- Stack buffer overflow in Montgomery reduction ([edce0a3](https://github.com/mruby/mruby/commit/edce0a3))
- Buffer overflow in pack_uu encoding ([2993302](https://github.com/mruby/mruby/commit/2993302))
- Buffer overflow in IO#ungetc ([01ab2ff](https://github.com/mruby/mruby/commit/01ab2ff))
- Heap-buffer-overflow in pattern alternation codegen ([eea9e30](https://github.com/mruby/mruby/commit/eea9e30))
- Out of bounds read and write in IO.select ([44831711](https://github.com/mruby/mruby/commit/44831711))
- Off-by-one in bounds check for symbol names and pool strings in load.c ([b3b8c01](https://github.com/mruby/mruby/commit/b3b8c01))
- Use-after-free in Set operations ([a6b55e7](https://github.com/mruby/mruby/commit/a6b55e7))
- Use-after-free in Array set operations ([729b84c](https://github.com/mruby/mruby/commit/729b84c))
- Use-after-free in Set#join ([0e653eb](https://github.com/mruby/mruby/commit/0e653eb))
- Use-after-realloc in Array#sort! ([eb39897](https://github.com/mruby/mruby/commit/eb39897))
- Heap-use-after-free in insertion_sort ([099d2c47](https://github.com/mruby/mruby/commit/099d2c47))
- Integer overflow in str_check_length ([6afff1c3](https://github.com/mruby/mruby/commit/6afff1c3))
- Integer overflow in Integer#lcm ([070bef24](https://github.com/mruby/mruby/commit/070bef24))
- Heap buffer overflow in `#method_missing` ([550d10a](https://github.com/mruby/mruby/commit/550d10a))
- Out-of-bounds read and divide-by-zero in `Array#product` ([8441eaf](https://github.com/mruby/mruby/commit/8441eaf))
- Heap buffer overflow in `String#prepend` with self-referencing arguments ([18ba026](https://github.com/mruby/mruby/commit/18ba026))
- Use-after-free in `sprintf` via `to_s` callback mutating format string ([48fc422](https://github.com/mruby/mruby/commit/48fc422))
- Multiple memory leak fixes in bigint, Set, Array, and Task gems
+6 -53
View File
@@ -12,28 +12,11 @@
</a>
</div>
### Table of contents
- [What is mruby](#what-is-mruby)
- [How to get mruby](#how-to-get-mruby)
- [mruby homepage](#mruby-homepage)
- [Mailing list](#mailing-list)
- [How to compile, test, and install (mruby and gems)](#how-to-compile-test-and-install-mruby-and-gems)
- [Amalgamation (single-file build)](#amalgamation-single-file-build)
- [Building documentation](#building-documentation)
- [How to customize mruby (mrbgems)](#how-to-customize-mruby-mrbgems)
- [Index of Document](#index-of-document)
- [License](#license)
- [Note for License](#note-for-license)
- [How to Contribute](#how-to-contribute)
- [Star History](#star-history)
- [Contributors](#contributors)
## What is mruby
mruby is the lightweight implementation of the Ruby language complying to (part
of) the [ISO standard][ISO-standard] with more recent features provided by Ruby 4.x.
Also, its syntax is Ruby 4.x compatible.
of) the [ISO standard][ISO-standard] with more recent features provided by Ruby 3.x.
Also, its syntax is Ruby 3.x compatible except for pattern matching.
You can link and embed mruby within your application. The "mruby" interpreter
program and the interactive "mirb" shell are provided as examples. You can also
@@ -47,11 +30,9 @@ of the Ministry of Economy, Trade and Industry of Japan.
## How to get mruby
To get mruby, you can download the stable version 4.0.0 from the official mruby
To get mruby, you can download the stable version 3.4.0 from the official mruby
GitHub repository or clone the trunk of the mruby source tree with the "git
clone" command. You can also install and compile mruby using [ruby-install](https://github.com/postmodern/ruby-install), [ruby-build](https://github.com/rbenv/ruby-build), [rvm](https://github.com/rvm/rvm), [conda](https://anaconda.org/channels/conda-forge/packages/mruby/overview) or [Homebrew](https://formulae.brew.sh/formula/mruby).
The release candidate version 4.0.0 of mruby can be downloaded via the following URL: [https://github.com/mruby/mruby/archive/4.0.0-rc3.zip](https://github.com/mruby/mruby/archive/4.0.0-rc3.zip)
clone" command. You can also install and compile mruby using [ruby-install](https://github.com/postmodern/ruby-install), [ruby-build](https://github.com/rbenv/ruby-build) or [rvm](https://github.com/rvm/rvm).
The latest development version of mruby can be downloaded via the following URL: [https://github.com/mruby/mruby/zipball/master](https://github.com/mruby/mruby/zipball/master)
@@ -80,21 +61,6 @@ rake all test
See the [compile.md](doc/guides/compile.md) file for the detail.
## Amalgamation (single-file build)
mruby supports amalgamation, which combines all source files into a single
`mruby.c` and `mruby.h` for easy embedding (similar to SQLite).
```console
rake amalgam
```
Output files are generated in `build/host/amalgam/`. To use:
```console
gcc -I./build/host/amalgam your_app.c ./build/host/amalgam/mruby.c -o your_app -lm
```
## Building documentation
There are two sets of documentation in mruby: the mruby API (generated by YARD) and C API (Doxygen and Graphviz)
@@ -129,20 +95,15 @@ extensions in C and/or Ruby. For a guide on how to use mrbgems, consult the
<!-- BEGIN OF MRUBY DOCUMENT INDEX -->
- [About the Limitations of mruby](doc/limitations.md)
- [About Amalgamation (Single-File Build)](doc/guides/amalgamation.md)
- [C API Reference](doc/guides/capi.md)
- [About the Compile](doc/guides/compile.md)
- [About the Debugger with the `mrdb` Command](doc/guides/debugger.md)
- [About GC Arena](doc/guides/gc-arena-howto.md)
- [Getting Started with mruby](doc/guides/getting-started.md)
- [About the mruby directory structure](doc/guides/hier.md)
- [About Linking with `libmruby`](doc/guides/link.md)
- [About Memory Allocator Customization and Heap Regions](doc/guides/memory.md)
- [About Memory Allocator Customization](doc/guides/memory.md)
- [About Build-time Configurations](doc/guides/mrbconf.md)
- [About the Build-time Library Manager](doc/guides/mrbgems.md)
- [ROM Method Tables for Memory-Efficient Method Registration](doc/guides/rom-method-table.md)
- [About the Symbols](doc/guides/symbol.md)
- [Internal Implementation / About mruby Architecture](doc/internal/architecture.md)
- [Internal Implementation / About Value Boxing](doc/internal/boxing.md)
- [Internal Implementation / About mruby Virtual Machine Instructions](doc/internal/opcode.md)
@@ -174,13 +135,5 @@ Please ask us if you want to distribute your code under another license.
To contribute to mruby, please refer to the [contribution guidelines][contribution-guidelines] and send a pull request to the [mruby GitHub repository](https://github.com/mruby/mruby).
By contributing, you grant non-exclusive rights to your code under the MIT License.
## Star History
[![mruby Star History](https://api.star-history.com/svg?repos=mruby/mruby&type=Date)](https://www.star-history.com/#mruby/mruby&Date)
## Contributors
[![mruby Contributors](https://contrib.rocks/image?repo=mruby/mruby&anon=1&max=500)](https://github.com/mruby/mruby/graphs/contributors)
[ISO-standard]: https://www.iso.org/standard/59579.html
[ISO-standard]: https://www.iso.org/iso/iso_catalogue/catalogue_tc/catalogue_detail.htm?csnumber=59579
[contribution-guidelines]: CONTRIBUTING.md
+4 -10
View File
@@ -16,11 +16,6 @@ require "mruby/build"
MRUBY_CONFIG = MRuby::Build.mruby_config_path
load MRUBY_CONFIG
# set up all gems
MRuby.each_target do
gems.setup(self) if enable_gems?
end
# load basic rules
MRuby.each_target do |build|
build.define_rules
@@ -37,7 +32,6 @@ load "#{MRUBY_ROOT}/tasks/test.rake"
load "#{MRUBY_ROOT}/tasks/benchmark.rake"
load "#{MRUBY_ROOT}/tasks/doc.rake"
load "#{MRUBY_ROOT}/tasks/install.rake"
load "#{MRUBY_ROOT}/tasks/amalgam.rake"
##############################
# generic build targets, rules
@@ -78,22 +72,22 @@ end
desc "run all pre-commit hooks against all files"
task :check do
sh "prek run --all-files"
sh "pre-commit run --all-files"
end
desc "install the pre-commit hooks"
task :checkinstall do
sh "prek install"
sh "pre-commit install"
end
desc "check the pre-commit hooks for updates"
task :checkupdate do
sh "prek autoupdate"
sh "pre-commit autoupdate"
end
desc "run all pre-commit hooks against all files with docker-compose"
task :composecheck do
sh "docker-compose -p mruby run test prek run --all-files"
sh "docker-compose -p mruby run test pre-commit run --all-files"
end
desc "build and run all mruby tests with docker-compose"
+10 -44
View File
@@ -2,53 +2,19 @@
## Reporting a Vulnerability
To report a security vulnerability, please email the mruby team at <matz@ruby.or.jp>. We appreciate your efforts to disclose your findings responsibly.
If you have any security concern, contact <matz@ruby.or.jp>.
## Scope
mruby is an embeddable Ruby implementation. Its security model is designed for integration into a host application, which is responsible for sandboxing and resource management. This policy defines what we consider a security vulnerability within the mruby interpreter itself.
We consider the following issues as vulnerabilities:
### High Priority Security Vulnerabilities
- Remote code execution
- Crash caused by a valid Ruby script
We consider the following issues to be **high priority security vulnerabilities**:
We _don't_ consider the following issues as vulnerabilities:
- **Remote Code Execution (RCE)**: The ability to execute arbitrary machine code or shell commands from within a Ruby script, beyond the intended execution scope of the script itself.
### Lower Priority: Crashes (Preferably Report as Bugs)
We **accept but deprioritize** the following issues. We recommend reporting them as **bug reports** on our issue tracker rather than security reports:
- **VM Crash on Valid Ruby Code**: Segmentation faults, assertion failures, or other interpreter crashes triggered by syntactically and semantically valid Ruby scripts.
- _Recommendation_: Please report these as bugs on our issue tracker.
- _Rationale_: While we will fix these issues, they typically only result in denial of service (DoS), not arbitrary code execution. They are lower priority than RCE vulnerabilities.
- _Note_: This does not include standard Ruby exceptions like `TypeError` or `ZeroDivisionError`, which are expected behavior.
- _Example_: A segmentation fault when running `[1, 2, 3].map { |x| x * 2 }` is best reported as a bug.
### Out of Scope: Not Considered Security Vulnerabilities
We do **not** consider the following issues to be security vulnerabilities:
- **Resource Exhaustion**: Infinite loops, excessive memory allocation, or high CPU usage originating from a Ruby script.
- _Rationale_: The host application is responsible for implementing resource limits, sandboxing, and execution timeouts. mruby provides the execution engine; the host provides the constraints.
- _Example_: `loop {}` or `"a" * (2**30)` are not vulnerabilities, even if they lead to memory or CPU exhaustion.
- **Crashes from Malformed Bytecode**: Crashes resulting from loading or executing corrupted or intentionally malformed `.mrb` files.
- _Rationale_: mruby's bytecode format is not a security boundary. Applications should only execute bytecode from trusted sources.
- _Example_: A crash discovered by fuzzing `.mrb` files is not considered a vulnerability.
- **Crashes from C API Misuse**: Crashes caused by incorrect usage of mruby's C API from the embedding application.
- _Rationale_: The C API is a trusted interface for developers. The caller is responsible for adhering to the API contract (e.g., not passing `NULL` pointers, managing object lifetimes correctly).
- _Example_: Calling `mrb_funcall()` with an invalid `mrb_state*` pointer is not a vulnerability.
- **Theoretical Undefined Behavior (UB)**: Issues reported by tools like ASAN, UBSan, or Valgrind that do not lead to a demonstrable crash or exploitable behavior in practice.
- _Rationale_: While we strive for clean, well-defined code, our focus is on practical security impact. We prioritize fixing UB that is exploitable over issues that are purely theoretical.
- _Example_: An integer overflow in an intermediate calculation that gets handled correctly before affecting program output or control flow.
- **Warnings on Large Memory Allocations**: Tooling warnings related to large memory allocations that do not result in a crash.
- _Rationale_: mruby is designed to handle `malloc(3)` returning `NULL` on large allocation requests. This is considered graceful error handling, not a vulnerability.
### Summary
- **High Priority Security Reports**: Remote code execution vulnerabilities.
- **Accepted (but preferably as bug reports)**: VM crashes from valid Ruby code.
- **Not Accepted as Security Issues**: Resource exhaustion, malformed bytecode, C API misuse, theoretical undefined behavior, or allocation warnings.
- Runtime C undefined behavior (including integer overflow)
- Crash caused by misused API
- Crash caused by modified compiled binary
- ASAN/Valgrind warning for too big memory allocation
mruby assumes `malloc(3)` returns `NULL` for too big allocations
+3 -7
View File
@@ -1,17 +1,13 @@
# Things to Do in the future
# After mruby 3.4
# After mruby 3.1
- parser and code generator independent from `mrb_state` (picoruby?)
- variable sized AST node
- iv/hash entry cache
- method inline caching improvements (cache method lookup results)
- more peephole optimization (if possible)
- built-in profiler (method call tracing, stack profiling, detailed memory analysis)
- improved REPL (mirb) features (syntax highlighting)
- configurable memory pools (per-object-type, memory-constrained devices)
- suspend/resume VM state (serialize/deserialize for power cycling)
- CMake build support (better IDE integration, standard C tooling)
# Things to do (Things we need to consider)
- `begin ... end while cond` to behave as CRuby
- special variables ($1,$2..)
-34
View File
@@ -1,34 +0,0 @@
def mandelbrot(c_r, c_i)
limit=95
iterations=0
cr = (c_r * 100).to_i
ci = (c_i * 100).to_i
zr = zi = 0
# Avoid sqrt by squaring the threshold: sqrt(x) < 1000 => x < 1000000
while iterations<limit
zr2 = zr*zr
zi2 = zi*zi
break if zr2+zi2 >= 1000000
zr, zi = (zr2-zi2)/100+cr, (zr*zi*2)/100+ci
iterations+=1
end
return iterations
end
def mandel_calc(min_r, min_i, max_r, max_i, res)
cur_i = min_i
while cur_i > max_i
putc "|"
cur_r = min_r
while cur_r < max_r
ch = 127 - mandelbrot(cur_r, cur_i)
putc ch # Use putc with integer - no string allocation!
cur_r += res
end
putc "|"
putc "\n"
cur_i -= res
end
end
mandel_calc(-2, 1, 1, -1, 0.04)
+11 -19
View File
@@ -3,45 +3,39 @@
#
# contributed by Karl von Laudermann
# modified by Jeremy Echols
# optimized: while loops instead of for..in to avoid closure overhead
size = 600 # ARGV[0].to_i
puts "P4\n#{size} #{size}"
# Cache constants in local variables to avoid repeated constant lookup
iter = 49
limit_squared = 4.0
ITER = 49 # Iterations - 1 for easy for..in looping
LIMIT_SQUARED = 4.0 # Presquared limit
byte_acc = 0
bit_num = 0
count_size = size - 1
count_size = size - 1 # Precomputed size for easy for..in looping
# Use while loops instead of for..in to avoid closure/upvalue overhead
y = 0
while y <= count_size
x = 0
while x <= count_size
# For..in loops are faster than .upto, .downto, .times, etc.
for y in 0..count_size
for x in 0..count_size
zr = 0.0
zi = 0.0
cr = (2.0*x/size)-1.5
ci = (2.0*y/size)-1.0
escape = false
# Use while instead of for..in to avoid closure overhead
i = 0
while i <= iter
# To make use of the for..in code, we use a dummy variable,
# like one would in C
for dummy in 0..ITER
tr = zr*zr - zi*zi + cr
ti = 2*zr*zi + ci
zr = tr
zi = ti
zr, zi = tr, ti
if (zr*zr+zi*zi) > limit_squared
if (zr*zr+zi*zi) > LIMIT_SQUARED
escape = true
break
end
i += 1
end
byte_acc = (byte_acc << 1) | (escape ? 0b0 : 0b1)
@@ -59,7 +53,5 @@ while y <= count_size
byte_acc = 0
bit_num = 0
end
x += 1
end
y += 1
end
-197
View File
@@ -1,197 +0,0 @@
/*
* VM Dispatch Micro-benchmark
*
* This benchmark measures the raw dispatch overhead of the mruby VM
* by executing minimal bytecode sequences.
*
* Compile:
* cc -O2 -I include -I build/host/include \
* benchmark/vm_dispatch_bench.c \
* build/host/lib/libmruby.a -lm -o vm_dispatch_bench
*
* Run:
* ./vm_dispatch_bench
*/
#include <mruby.h>
#include <mruby/compile.h>
#include <mruby/string.h>
#include <mruby/proc.h>
#include <stdio.h>
#include <time.h>
#define ITERATIONS 10
static double
get_time_ms(void)
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return ts.tv_sec * 1000.0 + ts.tv_nsec / 1000000.0;
}
static void
run_benchmark(mrb_state *mrb, const char *name, const char *code, int iterations)
{
double times[ITERATIONS];
double total = 0.0;
double min_time = 1e9;
double max_time = 0.0;
/* Compile once */
mrbc_context *cxt = mrbc_context_new(mrb);
struct mrb_parser_state *p = mrb_parse_string(mrb, code, cxt);
if (!p || p->nerr > 0) {
fprintf(stderr, "Failed to parse: %s\n", name);
if (p) mrb_parser_free(p);
mrbc_context_free(mrb, cxt);
return;
}
struct RProc *proc = mrb_generate_code(mrb, p);
mrb_parser_free(p);
mrbc_context_free(mrb, cxt);
if (!proc) {
fprintf(stderr, "Failed to compile: %s\n", name);
return;
}
/* Warm up */
for (int i = 0; i < 3; i++) {
mrb_top_run(mrb, proc, mrb_top_self(mrb), 0);
mrb->exc = NULL;
}
/* Measure */
for (int i = 0; i < iterations; i++) {
mrb_gc_arena_save(mrb);
mrb_full_gc(mrb);
double t0 = get_time_ms();
mrb_top_run(mrb, proc, mrb_top_self(mrb), 0);
double t1 = get_time_ms();
times[i] = t1 - t0;
total += times[i];
if (times[i] < min_time) min_time = times[i];
if (times[i] > max_time) max_time = times[i];
mrb->exc = NULL;
mrb_gc_arena_restore(mrb, 0);
}
double avg = total / iterations;
printf("%-30s avg: %8.2f ms min: %8.2f ms max: %8.2f ms\n",
name, avg, min_time, max_time);
}
int
main(int argc, char **argv)
{
mrb_state *mrb = mrb_open();
if (!mrb) {
fprintf(stderr, "Failed to create mrb_state\n");
return 1;
}
printf("========================================\n");
printf("mruby VM Dispatch Micro-benchmarks\n");
printf("========================================\n\n");
/* 1. Pure dispatch overhead */
printf("--- Dispatch Overhead ---\n");
run_benchmark(mrb, "empty_loop_1M",
"i = 0; while i < 1000000; i += 1; end", ITERATIONS);
run_benchmark(mrb, "empty_loop_10M",
"i = 0; while i < 10000000; i += 1; end", ITERATIONS);
/* 2. Arithmetic operations */
printf("\n--- Arithmetic ---\n");
run_benchmark(mrb, "int_add_1M",
"x = 0; i = 0; while i < 1000000; x = x + 1; i += 1; end", ITERATIONS);
run_benchmark(mrb, "int_mul_1M",
"x = 1; i = 0; while i < 1000000; x = x * 1; i += 1; end", ITERATIONS);
run_benchmark(mrb, "float_add_1M",
"x = 0.0; i = 0; while i < 1000000; x = x + 1.0; i += 1; end", ITERATIONS);
/* 3. Method calls */
printf("\n--- Method Calls ---\n");
run_benchmark(mrb, "empty_method_100K",
"class X; def m; end; end; "
"o = X.new; i = 0; while i < 100000; o.m; i += 1; end", ITERATIONS);
run_benchmark(mrb, "method_1arg_100K",
"class Y; def m(a); a; end; end; "
"o = Y.new; i = 0; while i < 100000; o.m(1); i += 1; end", ITERATIONS);
run_benchmark(mrb, "method_2arg_100K",
"class Z; def m(a,b); a+b; end; end; "
"o = Z.new; i = 0; while i < 100000; o.m(1,2); i += 1; end", ITERATIONS);
/* 4. Array access */
printf("\n--- Array/Hash ---\n");
run_benchmark(mrb, "array_read_1M",
"a = [0,1,2,3,4,5,6,7,8,9]; "
"i = 0; s = 0; while i < 1000000; s += a[i % 10]; i += 1; end", ITERATIONS);
run_benchmark(mrb, "array_write_1M",
"a = [0,0,0,0,0,0,0,0,0,0]; "
"i = 0; while i < 1000000; a[i % 10] = i; i += 1; end", ITERATIONS);
run_benchmark(mrb, "hash_read_100K",
"h = {0=>0,1=>1,2=>2,3=>3,4=>4,5=>5,6=>6,7=>7,8=>8,9=>9}; "
"i = 0; s = 0; while i < 100000; s += h[i % 10]; i += 1; end", ITERATIONS);
/* 5. Comparison and branching */
printf("\n--- Comparison/Branch ---\n");
run_benchmark(mrb, "lt_compare_1M",
"i = 0; c = 0; while i < 1000000; c += 1 if i < 500000; i += 1; end", ITERATIONS);
run_benchmark(mrb, "eq_compare_1M",
"i = 0; c = 0; while i < 1000000; c += 1 if i == 500000; i += 1; end", ITERATIONS);
/* 6. Block calls */
printf("\n--- Blocks ---\n");
run_benchmark(mrb, "times_100K",
"s = 0; 100000.times { |i| s += i }", ITERATIONS);
run_benchmark(mrb, "each_100K",
"a = (0...1000).to_a; s = 0; 100.times { a.each { |x| s += x } }", ITERATIONS);
/* 7. Recursion */
printf("\n--- Recursion ---\n");
run_benchmark(mrb, "fib_25",
"def fib(n); n < 2 ? n : fib(n-1) + fib(n-2); end; fib(25)", ITERATIONS);
run_benchmark(mrb, "fib_30",
"def fib(n); n < 2 ? n : fib(n-1) + fib(n-2); end; fib(30)", ITERATIONS);
/* 8. Local variable access */
printf("\n--- Local Variables ---\n");
run_benchmark(mrb, "few_vars_1M",
"i = 0; a = 0; b = 0; "
"while i < 1000000; a += 1; b += 1; i += 1; end", ITERATIONS);
run_benchmark(mrb, "many_vars_1M",
"i = 0; a = 0; b = 0; c = 0; d = 0; e = 0; f = 0; g = 0; h = 0; "
"while i < 1000000; a += 1; b += 1; c += 1; d += 1; e += 1; "
"f += 1; g += 1; h += 1; i += 1; end", ITERATIONS);
printf("\n========================================\n");
printf("Benchmark complete\n");
printf("========================================\n");
mrb_close(mrb);
return 0;
}
-513
View File
@@ -1,513 +0,0 @@
# VM Optimization Benchmarks for mruby
# Usage: ./bin/mruby benchmark/vm_optimization_bench.rb
#
# Each benchmark is designed to isolate specific VM behaviors:
# - Dispatch overhead
# - Arithmetic operations
# - Method calls
# - Array/Hash access
# - Loop performance
# Benchmark infrastructure
def measure(name, iterations = 1)
# Warm up
3.times { yield }
# Force GC before measurement
GC.start
t0 = Time.now
iterations.times { yield }
elapsed = Time.now - t0
puts "#{name}: #{elapsed * 1000 / iterations} ms"
elapsed
end
N = 1_000_000
M = 100_000
puts "=" * 60
puts "mruby VM Optimization Benchmarks"
puts "=" * 60
puts
#=============================================================================
# 1. DISPATCH OVERHEAD BENCHMARKS
# Target: Tail-call threading, computed goto efficiency
#=============================================================================
puts "--- Dispatch Overhead ---"
# 1a. Empty loop (pure dispatch cost)
measure("empty_loop", 10) do
i = 0
while i < N
i += 1
end
end
# 1b. NOP-heavy (many instructions, minimal work)
measure("nop_sequence", 10) do
i = 0
while i < M
a = 1; b = 2; c = 3; d = 4; e = 5
a = 1; b = 2; c = 3; d = 4; e = 5
a = 1; b = 2; c = 3; d = 4; e = 5
a = 1; b = 2; c = 3; d = 4; e = 5
i += 1
end
end
#=============================================================================
# 2. ARITHMETIC BENCHMARKS
# Target: Type specialization, register variables, ADDI fusion
#=============================================================================
puts
puts "--- Arithmetic Operations ---"
# 2a. Integer addition (tests OP_ADD fast path)
measure("int_add", 10) do
x = 0
i = 0
while i < N
x = x + 1
i += 1
end
x
end
# 2b. Integer increment (tests potential OP_INCI fusion)
measure("int_increment", 10) do
x = 0
i = 0
while i < N
x += 1
i += 1
end
x
end
# 2c. Mixed arithmetic (tests type checking overhead)
measure("mixed_arith", 10) do
x = 0
y = 1.5
i = 0
while i < M
x = x + 1
y = y + 0.5
i += 1
end
x
end
# 2d. Comparison in loop (tests OP_LT + JMPNOT fusion potential)
measure("comparison_loop", 10) do
x = 0
while x < N
x += 1
end
x
end
# 2e. Multiple comparisons (branch prediction)
measure("multi_compare", 10) do
i = 0
count = 0
while i < M
count += 1 if i > 100
count += 1 if i < 50000
count += 1 if i == 25000
i += 1
end
count
end
#=============================================================================
# 3. METHOD CALL BENCHMARKS
# Target: Inline caching, method dispatch optimization
#=============================================================================
puts
puts "--- Method Calls ---"
class BenchClass
def empty_method
end
def simple_add(a, b)
a + b
end
def self.class_method
end
end
$obj = BenchClass.new
# 3a. Empty method call (pure dispatch overhead)
measure("empty_method_call", 10) do
obj = $obj
i = 0
while i < M
obj.empty_method
i += 1
end
end
# 3b. Method with arguments
measure("method_with_args", 10) do
obj = $obj
i = 0
while i < M
obj.simple_add(1, 2)
i += 1
end
end
# 3c. Self method call (tests OP_SENDSELF potential)
class SelfCallBench
def run
i = 0
while i < M
helper
i += 1
end
end
def helper
end
end
measure("self_method_call", 10) do
SelfCallBench.new.run
end
# 3d. Polymorphic call site (tests inline cache invalidation)
class Duck1
def quack; 1; end
end
class Duck2
def quack; 2; end
end
$duck1 = Duck1.new
$duck2 = Duck2.new
measure("polymorphic_call", 10) do
d1, d2 = $duck1, $duck2
i = 0
sum = 0
while i < M
sum += d1.quack
sum += d2.quack
i += 1
end
sum
end
#=============================================================================
# 4. ARRAY/HASH BENCHMARKS
# Target: GETIDX/SETIDX fast path, bounds checking
#=============================================================================
puts
puts "--- Array/Hash Access ---"
$ary = Array.new(1000) { |i| i }
$hash = {}
1000.times { |i| $hash[i] = i }
# 4a. Array read (sequential)
measure("array_read_seq", 10) do
ary = $ary
i = 0
sum = 0
while i < M
sum += ary[i % 1000]
i += 1
end
sum
end
# 4b. Array read (constant index - tests constant propagation)
measure("array_read_const", 10) do
ary = $ary
i = 0
sum = 0
while i < M
sum += ary[500]
i += 1
end
sum
end
# 4c. Array write
measure("array_write", 10) do
ary = Array.new(1000, 0)
i = 0
while i < M
ary[i % 1000] = i
i += 1
end
end
# 4d. Hash read
measure("hash_read", 10) do
h = $hash
i = 0
sum = 0
while i < M
sum += h[i % 1000]
i += 1
end
sum
end
#=============================================================================
# 5. LOOP PATTERN BENCHMARKS
# Target: Loop optimization, branch prediction
#=============================================================================
puts
puts "--- Loop Patterns ---"
# 5a. Simple while loop
measure("while_loop", 10) do
i = 0
while i < N
i += 1
end
end
# 5b. times iterator (block overhead)
measure("times_iterator", 10) do
sum = 0
M.times do |i|
sum += i
end
sum
end
# 5c. each iterator on array
$small_ary = (0...1000).to_a
measure("each_iterator", 10) do
ary = $small_ary
total = 0
1000.times do
ary.each { |x| total += x }
end
total
end
# 5d. Nested loops
measure("nested_loop", 10) do
sum = 0
i = 0
while i < 1000
j = 0
while j < 1000
sum += 1
j += 1
end
i += 1
end
sum
end
#=============================================================================
# 6. CONSTANT LOADING BENCHMARKS
# Target: Constant pre-computation, pool access
#=============================================================================
puts
puts "--- Constant Loading ---"
# 6a. Integer literals (tests LOADI optimization)
measure("int_literals", 10) do
i = 0
sum = 0
while i < M
sum += 1
sum += 2
sum += 3
sum += 42
sum += 100
i += 1
end
sum
end
# 6b. Large integer literals (tests LOADL from pool)
measure("large_int_literals", 10) do
i = 0
sum = 0
while i < M
sum += 1000000
sum += 2000000
sum += 3000000
i += 1
end
sum
end
# 6c. Float literals
measure("float_literals", 10) do
i = 0
sum = 0.0
while i < M
sum += 1.5
sum += 2.5
sum += 3.5
i += 1
end
sum
end
# 6d. String literals (allocation vs interning)
measure("string_literals", 5) do
i = 0
while i < 100000
s = "hello"
s = "world"
s = "test"
i += 1
end
end
#=============================================================================
# 7. BRANCH PREDICTION BENCHMARKS
# Target: mrb_likely/mrb_unlikely effectiveness
#=============================================================================
puts
puts "--- Branch Prediction ---"
# 7a. Predictable branch (always true)
measure("predictable_true", 10) do
i = 0
count = 0
while i < N
count += 1 if true
i += 1
end
count
end
# 7b. Predictable branch (always false)
measure("predictable_false", 10) do
i = 0
count = 0
while i < N
count += 1 if false
i += 1
end
count
end
# 7c. Unpredictable branch (50/50)
measure("unpredictable_50", 10) do
i = 0
count = 0
while i < M
count += 1 if i & 1 == 0
i += 1
end
count
end
# 7d. Rare branch (error path simulation)
measure("rare_branch", 10) do
i = 0
count = 0
while i < N
count += 1 if i == -1 # Never true
i += 1
end
count
end
#=============================================================================
# 8. REGISTER PRESSURE BENCHMARKS
# Target: Register variable optimization
#=============================================================================
puts
puts "--- Register Pressure ---"
# 8a. Few local variables (should fit in registers)
measure("few_locals", 10) do
i = 0
a = 0
while i < N
a += 1
i += 1
end
a
end
# 8b. Many local variables (register spilling)
measure("many_locals", 10) do
i = 0
a = 0; b = 0; c = 0; d = 0; e = 0
f = 0; g = 0; h = 0; j = 0; k = 0
l = 0; m = 0; n = 0; o = 0; p = 0
while i < M
a += 1; b += 1; c += 1; d += 1; e += 1
f += 1; g += 1; h += 1; j += 1; k += 1
l += 1; m += 1; n += 1; o += 1; p += 1
i += 1
end
a + b + c + d + e + f + g + h + j + k + l + m + n + o + p
end
#=============================================================================
# 9. COMPOSITE BENCHMARKS (Real-world-ish)
#=============================================================================
puts
puts "--- Composite Benchmarks ---"
# 9a. Fibonacci (recursion + arithmetic)
def fib(n)
return n if n < 2
fib(n - 1) + fib(n - 2)
end
measure("fibonacci_30", 3) do
fib(30)
end
# 9b. Tak function (heavy recursion)
def tak(x, y, z)
if y < x
tak(tak(x - 1, y, z), tak(y - 1, z, x), tak(z - 1, x, y))
else
z
end
end
measure("tak_18_12_6", 3) do
tak(18, 12, 6)
end
# 9c. Array manipulation
measure("array_manipulation", 5) do
ary = []
10000.times { |i| ary << i }
ary.map! { |x| x * 2 }
ary.select { |x| x % 3 == 0 }.size
end
# 9d. String operations
measure("string_ops", 5) do
s = ""
10000.times { |i| s = s + i.to_s }
s.size
end
# 9e. Hash operations
measure("hash_ops", 5) do
h = {}
50000.times { |i| h[i.to_s] = i }
sum = 0
h.each { |k, v| sum += v }
sum
end
puts
puts "=" * 60
puts "Benchmark complete"
puts "=" * 60
+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_INITIAL_SIZE=8)
cc.defines << %w(KHASH_DEFAULT_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_INITIAL_SIZE=8)
cc.defines << %w(KHASH_DEFAULT_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_INITIAL_SIZE=8)
cc.defines << %w(KHASH_DEFAULT_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
-86
View File
@@ -1,86 +0,0 @@
# Cosmopolitan Libc build configuration for mruby
# https://github.com/jart/cosmopolitan
#
# Produces Actually Portable Executables (APE) - single binaries that run on:
# - Linux (x86_64, ARM64)
# - macOS (x86_64, ARM64)
# - Windows (x86_64)
# - FreeBSD (x86_64)
# - OpenBSD (x86_64)
# - NetBSD (x86_64)
#
# Requirements:
# Download cosmocc toolchain from https://cosmo.zip/pub/cosmocc/
#
# Usage:
# COSMO_ROOT=/path/to/cosmocc rake MRUBY_CONFIG=cosmopolitan
#
# The resulting binaries in bin/ will have .com extension and run on all
# supported platforms without recompilation.
COSMO_ROOT = ENV['COSMO_ROOT']
unless COSMO_ROOT && File.directory?(COSMO_ROOT)
msg = <<~MSG
Cosmopolitan toolchain not found.
Please set COSMO_ROOT environment variable to the cosmocc directory:
mkdir -p ~/cosmo && cd ~/cosmo
wget https://cosmo.zip/pub/cosmocc/cosmocc.zip
unzip cosmocc.zip
export COSMO_ROOT=~/cosmo
Then run:
COSMO_ROOT=~/cosmo rake MRUBY_CONFIG=cosmopolitan
MSG
raise msg
end
MRuby::Build.new do |conf|
# C compiler
conf.cc do |cc|
cc.command = "#{COSMO_ROOT}/bin/cosmocc"
cc.flags = %w[-Os -fno-omit-frame-pointer]
end
# C++ compiler
conf.cxx do |cxx|
cxx.command = "#{COSMO_ROOT}/bin/cosmoc++"
cxx.flags = conf.cc.flags.dup
end
# Linker
conf.linker do |linker|
linker.command = "#{COSMO_ROOT}/bin/cosmocc"
linker.flags = %w[-static]
end
# Archiver
conf.archiver do |archiver|
archiver.command = "#{COSMO_ROOT}/bin/cosmoar"
end
# APE binaries use .com extension
conf.exts.executable = '.com'
# Cosmopolitan provides POSIX compatibility, explicitly select POSIX HALs
conf.gem core: 'hal-posix-io'
conf.gem core: 'hal-posix-socket'
conf.gem core: 'hal-posix-dir'
# Standard library
conf.gembox 'stdlib'
conf.gembox 'stdlib-ext'
conf.gembox 'stdlib-io' # Includes mruby-io, mruby-socket, mruby-dir
conf.gembox 'math'
conf.gembox 'metaprog'
# Binary tools
# Note: mruby-bin-config is a shell script and incompatible with .com extension
conf.gem core: 'mruby-bin-mrbc'
conf.gem core: 'mruby-bin-mruby'
conf.gem core: 'mruby-bin-strip'
conf.gem core: 'mruby-bin-mirb'
conf.gem core: 'mruby-bin-debugger'
end
+22 -21
View File
@@ -5,16 +5,12 @@
# http://gamedev.allusion.net/softprj/kos/
#
# This configuration has been improved to be used as KallistiOS Port (kos-ports)
# Updated: 2025-07-31
# Updated: 2023-12-24
#
# Tested on GNU/Linux, macOS and Windows (MinGW-w64/MSYS2, Cygwin, DreamSDK)
# DreamSDK is based on both MinGW/MSYS and MinGW-w64/MSYS2: https://dreamsdk.org/
# DreamSDK is based on MinGW/MSYS: https://dreamsdk.org/
#
# Install mruby for Sega Dreamcast using the "mruby" kos-port.
# See: https://github.com/kallistios/kallistios
#
# If you want to see examples, check the /examples/dreamcast/mruby directory
# in the KallistiOS repository.
#
MRuby::CrossBuild.new("dreamcast") do |conf|
toolchain :gcc
@@ -23,37 +19,42 @@ MRuby::CrossBuild.new("dreamcast") do |conf|
KOS_BASE = ENV["KOS_BASE"]
KOS_CC_BASE = ENV["KOS_CC_BASE"]
# Check environment variables
if KOS_BASE.to_s.empty?
raise "Error: KallistiOS is required; KOS_BASE need to be declared; Stop."
if (KOS_BASE.nil? || KOS_BASE.empty? || KOS_CC_BASE.nil? || KOS_CC_BASE.empty?)
raise "Error: KallistiOS is required; KOS_BASE/KOS_CC_BASE needs to be declared; Stop."
end
# Root directory for KallistiOS wrappers
# This will handle specific DreamSDK wrappers if needed
KOS_WRAPPERS_BASE = if ENV["ENVIRONMENT_NAME"] == "DreamSDK" && ENV["RAKE_AVAILABLE"] == "0"
"#{KOS_CC_BASE}/bin"
else
"#{KOS_BASE}/utils/build_wrappers"
end
# C compiler
# All flags and settings below were extracted from KallistiOS environment files
conf.cc do |cc|
cc.command = "#{KOS_WRAPPERS_BASE}/kos-cc"
cc.command = "#{KOS_CC_BASE}/bin/sh-elf-gcc"
cc.include_paths << ["#{KOS_BASE}/include", "#{KOS_BASE}/kernel/arch/dreamcast/include", "#{KOS_BASE}/addons/include", "#{KOS_BASE}/../kos-ports/include"]
cc.flags << ["-O2", "-fomit-frame-pointer", "-fno-builtin", "-ml", "-m4-single-only", "-ffunction-sections", "-fdata-sections", "-matomic-model=soft-imask", "-ftls-model=local-exec", "-Wall", "-g"]
cc.compile_options = %Q[%{flags} -o "%{outfile}" -c "%{infile}"]
cc.defines << %w(_arch_dreamcast)
cc.defines << %w(_arch_sub_pristine)
end
# C++ compiler
conf.cxx do |cxx|
cxx.command = "#{KOS_WRAPPERS_BASE}/kos-c++"
cxx.command = conf.cc.command.dup
cxx.include_paths = conf.cc.include_paths.dup
cxx.flags = conf.cc.flags.dup
cxx.flags << %w(-fno-operator-names)
cxx.defines = conf.cc.defines.dup
cxx.compile_options = conf.cc.compile_options.dup
end
# Linker
conf.linker do |linker|
linker.command = "#{KOS_WRAPPERS_BASE}/kos-ld"
linker.command = "#{KOS_CC_BASE}/bin/sh-elf-gcc"
linker.flags << ["-Wl,-Ttext=0x8c010000", "-Wl,--gc-sections", "-T#{KOS_BASE}/utils/ldscripts/shlelf.xc", "-nodefaultlibs", "-Wl,--start-group -lkallisti -lc -lgcc -Wl,--end-group"]
linker.library_paths << ["#{KOS_BASE}/lib/dreamcast", "#{KOS_BASE}/addons/lib/dreamcast", "#{KOS_BASE}/../kos-ports/lib"]
end
# Archiver
conf.archiver do |archiver|
archiver.command = "#{KOS_WRAPPERS_BASE}/kos-ar"
archiver.command = "#{KOS_CC_BASE}/bin/sh-elf-ar"
archiver.archive_options = 'rcs "%{outfile}" %{objs}'
end
# No executables needed for KallistiOS
-16
View File
@@ -1,16 +0,0 @@
MRuby::Build.new do |conf|
# load specific toolchain settings
toolchain :gcc
# include the GEM box
conf.gembox 'full-core'
conf.cc.flags << '-m32'
conf.cc.defines << 'MRB_USE_FLOAT32'
conf.linker.flags << '-m32'
# Turn on `enable_debug` for better debugging
conf.enable_debug
conf.enable_test
conf.enable_bintest
end
+1
View File
@@ -7,4 +7,5 @@ MRuby::Build.new do |conf|
conf.build_mrbc_exec
conf.disable_libmruby
conf.disable_presym
end
-67
View File
@@ -1,67 +0,0 @@
# mruby Documentation
## Getting Started
New to mruby? Start here:
| 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 |
## 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 |
### 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 |
### Tools
| 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 |
| ------------------------------------- | ------------------ |
| [Directory Structure](guides/hier.md) | Source tree layout |
## Internals (for mruby contributors)
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 |
## Release Notes
- [mruby 3.4](mruby3.4.md)
- [mruby 3.3](mruby3.3.md)
- [mruby 3.2](mruby3.2.md)
- [mruby 3.1](mruby3.1.md)
- [mruby 3.0](mruby3.0.md)
-153
View File
@@ -1,153 +0,0 @@
<!-- summary: About Amalgamation (Single-File Build) -->
# Amalgamation
Amalgamation combines all mruby source files into a single `mruby.c` and
`mruby.h` for easy embedding, similar to SQLite's distribution model.
## Benefits
- **Simple integration**: Just two files to add to your project
- **Single compilation unit**: Enables better compiler optimization
- **No build system required**: Compile directly with any C compiler
- **Portable**: No external dependencies beyond standard C library
(but see [Platform-Dependent Gems](#platform-dependent-gems) below)
## Generating Amalgamation
```console
rake amalgam
```
Output files are generated in `build/<target>/amalgam/`:
- `mruby.h` - All headers concatenated in dependency order
- `mruby.c` - All sources concatenated (core + gems + mrblib)
### With Custom Configuration
The amalgamation includes gems specified in your build configuration:
```console
MRUBY_CONFIG=build_config/minimal.rb rake amalgam
```
## Using the Amalgamation
### Basic Usage
```c
#include "mruby.h"
int main(void) {
mrb_state *mrb = mrb_open();
mrb_load_string(mrb, "puts 'Hello from mruby!'");
mrb_close(mrb);
return 0;
}
```
### Compiling
```console
gcc -I./build/host/amalgam your_app.c ./build/host/amalgam/mruby.c -o your_app -lm
```
For optimized builds:
```console
gcc -O2 -DNDEBUG -I./build/host/amalgam your_app.c ./build/host/amalgam/mruby.c -o your_app -lm
```
## Gem Compatibility
### Known Working Gems
The following gems work with amalgamation:
- `mruby-compiler` - Required for `mrb_load_string`
- `mruby-eval` - `eval`, `Binding`
- `mruby-array-ext`, `mruby-string-ext`, `mruby-hash-ext`
- `mruby-numeric-ext`, `mruby-range-ext`, `mruby-symbol-ext`
- `mruby-proc-ext`, `mruby-kernel-ext`, `mruby-object-ext`, `mruby-class-ext`
- `mruby-enum-ext`, `mruby-compar-ext`
- `mruby-error`, `mruby-math`, `mruby-struct`
- `mruby-bigint`, `mruby-rational`, `mruby-complex`
- `mruby-io` (with `hal-posix-io`)
- `mruby-task` (with `hal-posix-task`)
### Platform-Dependent Gems
Gems that use a HAL (Hardware Abstraction Layer) include
platform-specific code in the amalgamation. For example, if
`mruby-io` selects `hal-posix-io` on Linux, the generated `mruby.c`
contains POSIX-specific code and cannot be compiled on Windows.
If you need amalgamated files for multiple platforms, generate them
separately for each target platform (or cross-build configuration).
### Excluded Gems
Binary gems (`mruby-bin-*`) are automatically excluded as they contain
their own `main()` function. The amalgamation produces a library, not
an executable.
## Example Configuration
A minimal configuration for amalgamation:
```ruby
# build_config/amalgam.rb
MRuby::Build.new do |conf|
conf.toolchain :gcc
conf.gem core: 'mruby-compiler'
conf.gem core: 'mruby-error'
conf.gem core: 'mruby-eval'
conf.gem core: 'mruby-array-ext'
conf.gem core: 'mruby-string-ext'
conf.gem core: 'mruby-hash-ext'
conf.gem core: 'mruby-io'
end
```
Generate with:
```console
MRUBY_CONFIG=build_config/amalgam.rb rake amalgam
```
## Output Sizes
Typical sizes depend on included gems:
- `mruby.h`: 200-500 KB
- `mruby.c`: 2-4 MB
## Technical Details
### Header Processing
- Include guards are stripped to allow concatenation
- Headers are ordered by dependency (foundation types first)
- Internal includes are commented out (already in `mruby.h`)
### Source Processing
- Sources are concatenated in proper initialization order
- X-macro headers (like `mruby/ops.h`) are inlined at each use
- Local includes (`.cstub` files) are automatically inlined
- Generated files (`mrblib.c`, `gem_init.c`) are included
### Gem Defines
Gems that add preprocessor defines affecting core structures are
automatically detected and included at the top of `mruby.h`.
Supported patterns: `MRB_USE_*`, `MRB_UTF8_*`, `HAVE_MRUBY_*`.
### Build Order
1. Core sources (`src/*.c`)
2. Gem sources (`mrbgems/*/src/*.c` or `core/*.c`)
3. Generated mrblib (`build/*/mrblib/mrblib.c`)
4. Gem initialization (`build/*/mrbgems/gem_init.c`)
-856
View File
@@ -1,856 +0,0 @@
<!-- summary: C API Reference -->
# C API Reference
This document covers the mruby C API for embedding and extending mruby.
**Contents:**
[Headers](#headers) |
[State Management](#state-management) |
[Values](#values) |
[Defining Classes and Modules](#defining-classes-and-modules) |
[Defining Methods](#defining-methods) |
[Parsing Arguments](#parsing-arguments) |
[Calling Ruby Methods from C](#calling-ruby-methods-from-c) |
[String Operations](#string-operations) |
[Array Operations](#array-operations) |
[Hash Operations](#hash-operations) |
[Wrapping C Structures](#wrapping-c-structures) |
[Exception Handling](#exception-handling) |
[Method Visibility](#method-visibility) |
[Proc and Block Handling](#proc-and-block-handling) |
[Fiber API](#fiber-api) |
[Compilation Contexts](#compilation-contexts) |
[Precompiled Bytecode](#precompiled-bytecode) |
[GC Arena](#gc-arena) |
[Memory Allocation](#memory-allocation)
## Headers
```c
#include <mruby.h> /* core types, state, class/method definition */
#include <mruby/compile.h> /* mrb_load_string, mrb_load_file */
#include <mruby/string.h> /* string operations */
#include <mruby/array.h> /* array operations */
#include <mruby/hash.h> /* hash operations */
#include <mruby/data.h> /* wrapping C structs */
#include <mruby/class.h> /* class inspection */
#include <mruby/value.h> /* value type macros */
#include <mruby/irep.h> /* loading precompiled bytecode */
#include <mruby/error.h> /* error handling (mrb_protect etc.) */
#include <mruby/variable.h> /* instance/class/global variables */
```
## State Management
```c
mrb_state *mrb = mrb_open(); /* create state with all gems */
mrb_state *mrb = mrb_open_core(); /* create state without gems */
mrb_close(mrb); /* close and free state */
```
`mrb_open()` returns `NULL` on allocation failure. Always check the
return value.
## Values
All Ruby values are represented as `mrb_value` in C.
### Creating Values
```c
mrb_nil_value() /* nil */
mrb_true_value() /* true */
mrb_false_value() /* false */
mrb_bool_value(mrb_bool b) /* true or false */
mrb_fixnum_value(mrb_int i) /* Integer */
mrb_float_value(mrb_state *mrb, mrb_float f) /* Float */
mrb_symbol_value(mrb_sym sym) /* Symbol */
mrb_obj_value(void *p) /* object pointer to value */
mrb_cptr_value(mrb_state *mrb, void *p) /* C pointer */
```
### Type Checking
```c
mrb_type(v) /* returns enum mrb_vtype */
mrb_nil_p(v) /* true if nil */
mrb_integer_p(v) /* true if Integer */
mrb_float_p(v) /* true if Float */
mrb_symbol_p(v) /* true if Symbol */
mrb_string_p(v) /* true if String */
mrb_array_p(v) /* true if Array */
mrb_hash_p(v) /* true if Hash */
mrb_true_p(v) /* true if true */
mrb_false_p(v) /* true if false */
mrb_undef_p(v) /* true if undefined */
mrb_immediate_p(v) /* true if not a heap object */
```
### Extracting C Values
```c
mrb_integer(v) /* mrb_int from Integer value */
mrb_float(v) /* mrb_float from Float value */
mrb_symbol(v) /* mrb_sym from Symbol value */
mrb_ptr(v) /* void* from object value */
mrb_str_to_cstr(mrb, v) /* const char* from String value */
```
### Value Types
| `mrb_vtype` | Ruby Class | Notes |
| ------------------ | ------------------- | ------------------------ |
| `MRB_TT_FALSE` | FalseClass/NilClass | `nil` has `MRB_TT_FALSE` |
| `MRB_TT_TRUE` | TrueClass | |
| `MRB_TT_INTEGER` | Integer | Immediate value |
| `MRB_TT_FLOAT` | Float | May be immediate |
| `MRB_TT_SYMBOL` | Symbol | Immediate value |
| `MRB_TT_STRING` | String | Heap object |
| `MRB_TT_ARRAY` | Array | Heap object |
| `MRB_TT_HASH` | Hash | Heap object |
| `MRB_TT_OBJECT` | Object | User-defined classes |
| `MRB_TT_CLASS` | Class | |
| `MRB_TT_MODULE` | Module | |
| `MRB_TT_PROC` | Proc | |
| `MRB_TT_CDATA` | (C data) | Wrapped C structs |
| `MRB_TT_EXCEPTION` | Exception | |
| `MRB_TT_FIBER` | Fiber | |
## Defining Classes and Modules
```c
/* Define a class under Object */
struct RClass *my_class = mrb_define_class(mrb, "MyClass", mrb->object_class);
/* Define a class under another class/module */
struct RClass *inner = mrb_define_class_under(mrb, outer, "Inner", mrb->object_class);
/* Define a module */
struct RClass *my_mod = mrb_define_module(mrb, "MyModule");
struct RClass *inner_mod = mrb_define_module_under(mrb, outer, "InnerMod");
/* Include/prepend a module */
mrb_include_module(mrb, my_class, my_mod);
mrb_prepend_module(mrb, my_class, my_mod);
/* Look up existing class/module */
struct RClass *c = mrb_class_get(mrb, "String");
struct RClass *m = mrb_module_get(mrb, "Kernel");
/* Define a constant */
mrb_define_const(mrb, my_class, "VERSION", mrb_str_new_lit(mrb, "1.0"));
```
## Defining Methods
All C methods have the same signature:
```c
static mrb_value
my_method(mrb_state *mrb, mrb_value self)
{
/* self is the receiver */
return mrb_nil_value();
}
```
Register with:
```c
mrb_define_method(mrb, klass, "name", my_method, MRB_ARGS_NONE());
mrb_define_class_method(mrb, klass, "name", my_method, MRB_ARGS_REQ(1));
mrb_define_module_function(mrb, mod, "name", my_method, MRB_ARGS_ANY());
```
### Argument Specifiers
| Macro | Meaning |
| ---------------------- | ------------------------------------- |
| `MRB_ARGS_NONE()` | No arguments |
| `MRB_ARGS_REQ(n)` | `n` required arguments |
| `MRB_ARGS_OPT(n)` | `n` optional arguments |
| `MRB_ARGS_ARG(r,o)` | `r` required + `o` optional |
| `MRB_ARGS_REST()` | Splat (`*args`) |
| `MRB_ARGS_BLOCK()` | Block (`&block`) |
| `MRB_ARGS_ANY()` | Any number (same as REST) |
| `MRB_ARGS_KEY(n,rest)` | `n` keyword args, `rest`=1 for `**kw` |
These can be combined with `|`:
```c
MRB_ARGS_REQ(1) | MRB_ARGS_OPT(2) | MRB_ARGS_BLOCK()
```
## Parsing Arguments
`mrb_get_args()` extracts arguments from the Ruby call stack:
```c
mrb_int mrb_get_args(mrb_state *mrb, const char *format, ...);
```
### Format Specifiers
| Spec | Ruby Type | C Type(s) | Notes |
| ---- | ------------- | --------------------------- | -------------------------------- |
| `o` | any | `mrb_value` | No type check |
| `i` | Numeric | `mrb_int` | Coerces to integer |
| `f` | Numeric | `mrb_float` | Coerces to float |
| `b` | any | `mrb_bool` | Truthiness |
| `n` | String/Symbol | `mrb_sym` | Converts to symbol |
| `s` | String | `const char*, mrb_int` | Pointer + length |
| `z` | String | `const char*` | Null-terminated |
| `S` | String | `mrb_value` | String value |
| `A` | Array | `mrb_value` | Array value |
| `H` | Hash | `mrb_value` | Hash value |
| `C` | Class | `mrb_value` | Class/Module value |
| `c` | Class | `struct RClass*` | Class pointer |
| `a` | Array | `const mrb_value*, mrb_int` | Array pointer + length |
| `d` | C Data | `void*` | Requires `mrb_data_type*` |
| `&` | Block | `mrb_value` | Block argument |
| `*` | rest | `const mrb_value*, mrb_int` | Rest arguments |
| `\|` | — | — | Following args are optional |
| `?` | — | `mrb_bool` | Was previous optional arg given? |
| `:` | keywords | `mrb_kwargs` | Keyword arguments |
Adding `!` to `S`, `A`, `H`, `C`, `c`, `s`, `z`, `a`, `d` allows `nil`
(returns NULL/zero for nil).
### Examples
```c
/* def method(name, count) */
const char *name; mrb_int len, count;
mrb_get_args(mrb, "si", &name, &len, &count);
/* def method(required, optional=nil) */
mrb_value req, opt = mrb_nil_value();
mrb_get_args(mrb, "o|o", &req, &opt);
/* def method(*args) */
const mrb_value *args; mrb_int argc;
mrb_get_args(mrb, "*", &args, &argc);
/* def method(&block) */
mrb_value block;
mrb_get_args(mrb, "&", &block);
/* def method(name:, age: 0) */
mrb_sym kw_names[] = { mrb_intern_lit(mrb, "name"), mrb_intern_lit(mrb, "age") };
mrb_value kw_values[2];
mrb_kwargs kw = { 2, 1, kw_names, kw_values, NULL };
mrb_get_args(mrb, ":", &kw);
/* kw_values[0] = name (required), kw_values[1] = age (optional, undef if not given) */
```
## Calling Ruby Methods from C
```c
/* Call obj.method(arg1, arg2) */
mrb_funcall(mrb, obj, "method", 2, arg1, arg2);
/* Call with symbol (faster, no string lookup) */
mrb_funcall_id(mrb, obj, mrb_intern_lit(mrb, "method"), 2, arg1, arg2);
/* Call with argv array */
mrb_value argv[] = { arg1, arg2 };
mrb_funcall_argv(mrb, obj, mrb_intern_lit(mrb, "method"), 2, argv);
/* Call with block */
mrb_funcall_with_block(mrb, obj, mid, argc, argv, block);
/* Yield to block */
mrb_yield(mrb, block, arg);
mrb_yield_argv(mrb, block, argc, argv);
```
## String Operations
```c
/* Creation */
mrb_str_new_lit(mrb, "hello") /* from string literal */
mrb_str_new(mrb, ptr, len) /* from pointer + length */
mrb_str_new_cstr(mrb, cstr) /* from null-terminated C string */
mrb_str_new_static(mrb, ptr, len) /* from static data (no copy) */
/* Access */
RSTRING_PTR(str) /* char* pointer */
RSTRING_LEN(str) /* length */
mrb_str_to_cstr(mrb, str) /* null-terminated (may copy) */
/* Modification */
mrb_str_cat(mrb, str, ptr, len) /* append bytes */
mrb_str_cat_cstr(mrb, str, cstr) /* append C string */
mrb_str_cat_str(mrb, str, str2) /* append String */
/* Comparison */
mrb_str_equal(mrb, str1, str2) /* equality */
mrb_str_cmp(mrb, str1, str2) /* comparison (-1, 0, 1) */
```
## Array Operations
```c
/* Creation */
mrb_ary_new(mrb) /* empty array */
mrb_ary_new_capa(mrb, capa) /* preallocated */
mrb_ary_new_from_values(mrb, n, vals) /* from C array */
/* Access */
RARRAY_PTR(ary) /* mrb_value* pointer */
RARRAY_LEN(ary) /* length */
mrb_ary_entry(ary, idx) /* get element (no mrb needed) */
/* Modification */
mrb_ary_push(mrb, ary, val) /* append */
mrb_ary_pop(mrb, ary) /* remove last */
mrb_ary_shift(mrb, ary) /* remove first */
mrb_ary_unshift(mrb, ary, val) /* prepend */
mrb_ary_set(mrb, ary, idx, val) /* set element */
mrb_ary_splice(mrb, ary, pos, len, rpl) /* splice */
mrb_ary_concat(mrb, ary, other) /* extend */
```
## Hash Operations
```c
/* Creation */
mrb_hash_new(mrb) /* empty hash */
/* Access */
mrb_hash_get(mrb, hash, key) /* get value */
mrb_hash_fetch(mrb, hash, key, def) /* get with default */
mrb_hash_key_p(mrb, hash, key) /* key exists? */
mrb_hash_empty_p(mrb, hash) /* empty? */
mrb_hash_size(mrb, hash) /* number of entries */
/* Modification */
mrb_hash_set(mrb, hash, key, val) /* set key-value */
mrb_hash_delete_key(mrb, hash, key) /* delete key */
mrb_hash_merge(mrb, hash1, hash2) /* merge hash2 into hash1 */
/* Iteration */
mrb_hash_keys(mrb, hash) /* Array of keys */
mrb_hash_values(mrb, hash) /* Array of values */
```
## Wrapping C Structures
To expose a C struct to Ruby, use `mrb_data_type` and `Data_Wrap_Struct`:
```c
/* 1. Define the data type with a name and destructor */
static void point_free(mrb_state *mrb, void *p) {
mrb_free(mrb, p);
}
static const mrb_data_type point_type = {
"Point", point_free
};
/* 2. Allocate and initialize */
static mrb_value
point_init(mrb_state *mrb, mrb_value self)
{
mrb_float x, y;
mrb_get_args(mrb, "ff", &x, &y);
double *data = (double*)mrb_malloc(mrb, sizeof(double) * 2);
data[0] = x;
data[1] = y;
DATA_PTR(self) = data;
DATA_TYPE(self) = &point_type;
return self;
}
/* 3. Access the wrapped data */
static mrb_value
point_x(mrb_state *mrb, mrb_value self)
{
double *data = (double*)mrb_data_get_ptr(mrb, self, &point_type);
return mrb_float_value(mrb, data[0]);
}
/* 4. Register the class */
struct RClass *point = mrb_define_class(mrb, "Point", mrb->object_class);
MRB_SET_INSTANCE_TT(point, MRB_TT_CDATA);
mrb_define_method(mrb, point, "initialize", point_init, MRB_ARGS_REQ(2));
mrb_define_method(mrb, point, "x", point_x, MRB_ARGS_NONE());
```
## Exception Handling
### Raising Exceptions
```c
mrb_raise(mrb, E_RUNTIME_ERROR, "something went wrong");
mrb_raisef(mrb, E_ARGUMENT_ERROR, "expected %d, got %d", expected, actual);
mrb_raise(mrb, E_TYPE_ERROR, "wrong type");
```
Common exception classes: `E_RUNTIME_ERROR`, `E_TYPE_ERROR`,
`E_ARGUMENT_ERROR`, `E_RANGE_ERROR`, `E_NAME_ERROR`,
`E_NOMETHOD_ERROR`, `E_NOTIMP_ERROR`, `E_KEY_ERROR`.
### Catching Exceptions
```c
/* Check after mrb_load_string or mrb_funcall */
mrb_value result = mrb_load_string(mrb, code);
if (mrb->exc) {
mrb_print_error(mrb);
mrb->exc = NULL; /* clear exception */
}
```
### Protected Execution
`mrb_protect()` executes a function under protection. If an
exception is raised, it is captured as a return value instead of
propagating:
```c
static mrb_value
safe_operation(mrb_state *mrb, mrb_value data)
{
/* This function might raise an exception */
return mrb_funcall(mrb, data, "do_something", 0);
}
mrb_bool error;
mrb_value result = mrb_protect(mrb, safe_operation, data, &error);
if (error) {
/* result contains the exception object; mrb->exc is cleared */
mrb_value inspect = mrb_inspect(mrb, result);
fprintf(stderr, "Error: %s\n", mrb_str_to_cstr(mrb, inspect));
}
```
**Note:** `mrb_protect` clears `mrb->exc` after catching the
exception. The exception is returned as `result`. Do not use
`mrb_print_error()` after `mrb_protect` — it reads `mrb->exc`
which is already `NULL`.
For lower-level protection with a `void*` callback:
```c
static mrb_value
body(mrb_state *mrb, void *userdata)
{
/* ... */
}
mrb_bool error;
mrb_value result = mrb_protect_error(mrb, body, userdata, &error);
```
### Rescue
`mrb_rescue()` catches `StandardError` (like Ruby's `rescue`):
```c
static mrb_value
body_func(mrb_state *mrb, mrb_value body_data)
{
return mrb_funcall(mrb, body_data, "risky_method", 0);
}
static mrb_value
rescue_func(mrb_state *mrb, mrb_value rescue_data)
{
/* handle error, rescue_data is the data passed in */
return mrb_nil_value();
}
mrb_value result = mrb_rescue(mrb, body_func, body_data,
rescue_func, rescue_data);
```
To rescue specific exception classes:
```c
struct RClass *classes[] = {
E_ARGUMENT_ERROR,
mrb_class_get(mrb, "IOError")
};
mrb_value result = mrb_rescue_exceptions(mrb, body_func, body_data,
rescue_func, rescue_data,
2, classes);
```
### Ensure
`mrb_ensure()` guarantees cleanup runs regardless of exceptions
(like Ruby's `ensure`):
```c
static mrb_value
body_func(mrb_state *mrb, mrb_value data)
{
return mrb_funcall(mrb, data, "process", 0);
}
static mrb_value
cleanup_func(mrb_state *mrb, mrb_value data)
{
mrb_funcall(mrb, data, "close", 0);
return mrb_nil_value();
}
mrb_value result = mrb_ensure(mrb, body_func, body_data,
cleanup_func, cleanup_data);
```
The ensure function always executes. If the body raises an
exception, the ensure runs and then the exception is re-raised.
### Error State Management
```c
mrb_bool mrb_check_error(mrb_state *mrb); /* check and clear mrb->exc */
void mrb_clear_error(mrb_state *mrb); /* clear mrb->exc */
```
## Method Visibility
```c
/* Public (default) */
mrb_define_method(mrb, klass, "name", func, MRB_ARGS_NONE());
/* Private - only callable without explicit receiver */
mrb_define_private_method(mrb, klass, "name", func, MRB_ARGS_NONE());
/* Class method (singleton method on the class object) */
mrb_define_class_method(mrb, klass, "name", func, MRB_ARGS_NONE());
/* Module function (both module method and private instance method) */
mrb_define_module_function(mrb, mod, "name", func, MRB_ARGS_NONE());
/* Singleton method on a specific object */
mrb_define_singleton_method(mrb, obj, "name", func, MRB_ARGS_NONE());
/* Method alias: alias new_name old_name */
mrb_define_alias(mrb, klass, "new_name", "old_name");
/* Remove a method */
mrb_undef_method(mrb, klass, "name");
mrb_undef_class_method(mrb, klass, "name");
```
All `_method` variants have `_id` counterparts that accept
`mrb_sym` instead of `const char*` for better performance.
## Proc and Block Handling
### Creating Procs from C Functions
```c
/* Simple C function proc */
struct RProc *proc = mrb_proc_new_cfunc(mrb, my_func);
/* C closure with captured local variables */
struct RProc *proc = mrb_closure_new_cfunc(mrb, my_func, nlocals);
```
### C Functions with Environment (requires mruby-proc-ext)
Store values in a proc's environment, accessible from the C
function:
```c
mrb_value env_values[] = { mrb_fixnum_value(42) };
struct RProc *proc = mrb_proc_new_cfunc_with_env(mrb, my_func, 1, env_values);
/* Inside my_func, retrieve environment values */
static mrb_value my_func(mrb_state *mrb, mrb_value self)
{
mrb_value val = mrb_proc_cfunc_env_get(mrb, 0); /* index 0 */
return val;
}
```
## Fiber API
```c
#include <mruby.h> /* fiber types and functions */
```
### Creating and Using Fibers
```c
/* Create a fiber from a proc */
mrb_value fiber = mrb_fiber_new(mrb, proc);
/* Resume the fiber with arguments */
mrb_value args[] = { mrb_fixnum_value(1) };
mrb_value result = mrb_fiber_resume(mrb, fiber, 1, args);
/* Check if fiber is still alive */
mrb_bool alive = mrb_test(mrb_fiber_alive_p(mrb, fiber));
```
### Yielding from C
`mrb_fiber_yield()` can only be used as the return value of a C
function — no code may execute after it:
```c
static mrb_value
my_yield_method(mrb_state *mrb, mrb_value self)
{
mrb_value yield_args[] = { mrb_str_new_lit(mrb, "yielded") };
return mrb_fiber_yield(mrb, 1, yield_args); /* must be returned directly */
}
```
### Fiber States
| State | Meaning |
| ----------------------- | -------------------------------- |
| `MRB_FIBER_CREATED` | Created but not yet resumed |
| `MRB_FIBER_RUNNING` | Currently executing |
| `MRB_FIBER_RESUMED` | Resumed another fiber |
| `MRB_FIBER_SUSPENDED` | Yielded, waiting to resume |
| `MRB_FIBER_TRANSFERRED` | Transferred via `Fiber#transfer` |
| `MRB_FIBER_TERMINATED` | Finished execution |
**Limitation:** fibers cannot yield across C function boundaries.
You cannot call `mrb_fiber_yield` from within a C-implemented
method, except via `mrb_fiber_yield` at function return.
## Compilation Contexts
For advanced compilation control, use `mrb_ccontext`:
```c
#include <mruby/compile.h>
mrb_ccontext *cxt = mrb_ccontext_new(mrb);
/* Set source filename for error messages and debug info */
mrb_ccontext_filename(mrb, cxt, "my_script.rb");
/* Compile and execute with context */
mrb_value result = mrb_load_string_cxt(mrb, "1 + 2", cxt);
/* Clean up */
mrb_ccontext_free(mrb, cxt);
```
### Context Options
The `mrb_ccontext` structure provides several flags:
| Field | Purpose |
| ---------------- | --------------------------------------- |
| `capture_errors` | Collect parse errors instead of raising |
| `no_exec` | Compile without executing (get RProc) |
| `no_optimize` | Disable peephole optimizations |
| `no_ext_ops` | Disable extended operand instructions |
| `keep_lv` | Preserve local variables across loads |
### Loading with Context
```c
mrb_load_string_cxt(mrb, code, cxt); /* string + context */
mrb_load_nstring_cxt(mrb, code, len, cxt); /* with explicit length */
mrb_load_file_cxt(mrb, fp, cxt); /* file + context */
mrb_load_detect_file_cxt(mrb, fp, cxt); /* auto-detect .mrb or .rb */
```
## Precompiled Bytecode
Load `.mrb` files compiled by `mrbc`:
```c
#include <mruby/irep.h>
/* From byte array (generated by mrbc -B) */
mrb_value result = mrb_load_irep(mrb, bytecode);
/* From buffer with explicit size (safer, bounds-checked) */
mrb_value result = mrb_load_irep_buf(mrb, buf, size);
/* From file */
FILE *fp = fopen("script.mrb", "rb");
mrb_value result = mrb_load_irep_file(mrb, fp);
fclose(fp);
/* Load without executing (returns irep for inspection) */
mrb_irep *irep = mrb_read_irep(mrb, bytecode);
```
All `_irep` loading functions have `_cxt` variants that accept
a compilation context.
### Deployment Pattern
Ahead-of-time compilation eliminates the need for the compiler gem
at runtime:
```shell
# Compile to C array
mrbc -Bscript_bytecode script.rb
# This generates a C header with:
# const uint8_t script_bytecode[];
```
```c
#include "script.mrb.h"
mrb_state *mrb = mrb_open_core(); /* no compiler needed */
mrb_load_irep(mrb, script_bytecode);
```
**Important:** wrap bytecode loading in arena save/restore when
loading multiple scripts:
```c
int ai = mrb_gc_arena_save(mrb);
mrb_load_irep(mrb, script1);
mrb_gc_arena_restore(mrb, ai);
ai = mrb_gc_arena_save(mrb);
mrb_load_irep(mrb, script2);
mrb_gc_arena_restore(mrb, ai);
```
## Symbols
```c
/* Create symbol from string */
mrb_sym sym = mrb_intern_lit(mrb, "name"); /* from literal */
mrb_sym sym = mrb_intern_cstr(mrb, cstr); /* from C string */
mrb_sym sym = mrb_intern(mrb, ptr, len); /* from pointer + length */
/* Symbol to string */
const char *name = mrb_sym_name(mrb, sym);
mrb_int len;
const char *name = mrb_sym_name_len(mrb, sym, &len);
```
## Instance Variables
```c
/* Get/set instance variables on an object */
mrb_iv_get(mrb, obj, mrb_intern_lit(mrb, "@x"));
mrb_iv_set(mrb, obj, mrb_intern_lit(mrb, "@x"), val);
mrb_iv_defined(mrb, obj, mrb_intern_lit(mrb, "@x"));
mrb_iv_remove(mrb, obj, mrb_intern_lit(mrb, "@x"));
```
## Global Variables
```c
mrb_gv_get(mrb, mrb_intern_lit(mrb, "$verbose"));
mrb_gv_set(mrb, mrb_intern_lit(mrb, "$verbose"), mrb_true_value());
```
## Class Variables
```c
mrb_cv_get(mrb, klass, mrb_intern_lit(mrb, "@@count"));
mrb_cv_set(mrb, klass, mrb_intern_lit(mrb, "@@count"), mrb_fixnum_value(0));
```
## Loading and Executing Code
```c
/* Load and execute a string (requires mruby-compiler gem) */
mrb_value result = mrb_load_string(mrb, "1 + 2");
/* Load and execute a file */
FILE *f = fopen("script.rb", "r");
mrb_value result = mrb_load_file(mrb, f);
fclose(f);
/* Load precompiled bytecode (no compiler needed) */
mrb_value result = mrb_load_irep(mrb, bytecode_array);
```
## GC Arena
When creating many temporary Ruby objects in C, use the GC arena to
prevent them from being collected prematurely:
```c
int ai = mrb_gc_arena_save(mrb);
/* create temporary objects here */
mrb_gc_arena_restore(mrb, ai);
```
See [gc-arena-howto.md](gc-arena-howto.md) for details.
## Memory Allocation
```c
void *p = mrb_malloc(mrb, size); /* raises on failure */
void *p = mrb_calloc(mrb, nmemb, size); /* zero-initialized */
void *p = mrb_realloc(mrb, ptr, size); /* resize */
mrb_free(mrb, p); /* free */
/* NULL-returning variants (for custom error handling) */
void *p = mrb_malloc_simple(mrb, size);
void *p = mrb_realloc_simple(mrb, ptr, size);
```
## Type Conversion
```c
mrb_obj_as_string(mrb, val) /* to_s */
mrb_inspect(mrb, val) /* inspect */
mrb_any_to_s(mrb, val) /* default to_s */
mrb_str_to_integer(mrb, str, base, badcheck) /* String to Integer */
mrb_str_to_dbl(mrb, str, badcheck) /* String to Float */
mrb_ensure_float_type(mrb, val) /* ensure Float */
```
## Object Comparison
```c
mrb_equal(mrb, a, b) /* Ruby == */
mrb_eql(mrb, a, b) /* Ruby eql? */
mrb_obj_eq(mrb, a, b) /* Ruby equal? (identity) */
mrb_cmp(mrb, a, b) /* Ruby <=> (returns mrb_int) */
```
## Object Inspection
```c
mrb_obj_classname(mrb, obj) /* class name as C string */
mrb_obj_class(mrb, obj) /* class as RClass* */
mrb_obj_is_kind_of(mrb, obj, klass) /* is_a? / kind_of? */
mrb_obj_respond_to(mrb, klass, mid) /* respond_to? */
mrb_obj_id(obj) /* object_id */
mrb_obj_freeze(mrb, obj) /* freeze */
mrb_obj_dup(mrb, obj) /* dup */
```
## Compile-Time Flags
When compiling C code that uses mruby, you must use the same flags as
the library was built with. Use `mruby-config` to get them:
```console
$ build/host/bin/mruby-config --cflags # compiler flags
$ build/host/bin/mruby-config --ldflags # linker flags
$ build/host/bin/mruby-config --libs # libraries
```
Key macros that affect ABI:
| Macro | Effect |
| ----------------- | ---------------------------------------- |
| `MRB_NO_BOXING` | Struct-based values (larger, debuggable) |
| `MRB_WORD_BOXING` | Single-word values (fast, 32-bit safe) |
| `MRB_NAN_BOXING` | NaN-tagged values (default on 32-bit) |
| `MRB_NO_FLOAT` | Disable Float support |
| `MRB_INT64` | 64-bit integers |
| `MRB_USE_FLOAT32` | 32-bit floats |
Mismatching these between library and application causes silent
data corruption.
+6 -3
View File
@@ -261,9 +261,12 @@ end
### Preallocated Symbols
Preallocated symbols are always enabled. Symbol IDs used in C source code
(via `MRB_SYM()` etc.) are resolved to compile-time constants during the
build process.
By far, preallocated symbols are highly compatible with the previous versions, so
we expect you won't see any problem with them. But just in case you face any
issue, you can disable preallocated symbols by specifying `conf.disable_presym`.
In the build process, `mrbc` under cross compiling environment will be compiled
with this configuration.
### Mrbgems
+1 -1
View File
@@ -40,7 +40,7 @@ To confirm mrdb was installed properly, run mrdb with the `--version` option:
```bash
$ mrdb --version
mruby 4.0.0 (2026-04-20)
mruby 3.4.0 (2025-04-20)
```
## 2.2 Basic Operation
+39 -16
View File
@@ -96,31 +96,54 @@ In mruby, C function calls are surrounded by this save/restore, but we
can further optimize memory usage by surrounding save/restore, and can
avoid creating arena overflow bugs.
Let's take a real example. Here is the source code of `Array#inspect`
(from `src/array.c`):
Let's take a real example. Here is the source code of `Array#inspect`:
```c
static mrb_value
mrb_ary_to_s(mrb_state *mrb, mrb_value self)
inspect_ary(mrb_state *mrb, mrb_value ary, mrb_value list)
{
mrb->c->ci->mid = MRB_SYM(inspect);
mrb_value ret = mrb_str_new_lit(mrb, "[");
int ai = mrb_gc_arena_save(mrb);
if (MRB_RECURSIVE_UNARY_P(mrb, MRB_SYM(inspect), self)) {
mrb_str_cat_lit(mrb, ret, "...]");
return ret;
mrb_int i;
mrb_value s, arystr;
char head[] = { '[' };
char sep[] = { ',', ' ' };
char tail[] = { ']' };
/* check recursive */
for (i=0; i<RARRAY_LEN(list); i++) {
if (mrb_obj_equal(mrb, ary, RARRAY_PTR(list)[i])) {
return mrb_str_new(mrb, "[...]", 5);
}
}
for (mrb_int i=0; i<RARRAY_LEN(self); i++) {
if (i>0) mrb_str_cat_lit(mrb, ret, ", ");
mrb_str_cat_str(mrb, ret, mrb_inspect(mrb, RARRAY_PTR(self)[i]));
mrb_ary_push(mrb, list, ary);
arystr = mrb_str_new_capa(mrb, 64);
mrb_str_cat(mrb, arystr, head, sizeof(head));
for (i=0; i<RARRAY_LEN(ary); i++) {
int ai = mrb_gc_arena_save(mrb);
if (i > 0) {
mrb_str_cat(mrb, arystr, sep, sizeof(sep));
}
if (mrb_array_p(RARRAY_PTR(ary)[i])) {
s = inspect_ary(mrb, RARRAY_PTR(ary)[i], list);
}
else {
s = mrb_inspect(mrb, RARRAY_PTR(ary)[i]);
}
mrb_str_cat(mrb, arystr, RSTRING_PTR(s), RSTRING_LEN(s));
mrb_gc_arena_restore(mrb, ai);
}
mrb_str_cat_lit(mrb, ret, "]");
return ret;
mrb_str_cat(mrb, arystr, tail, sizeof(tail));
mrb_ary_pop(mrb, list);
return arystr;
}
```
This is a real example, so slightly complicated, but bear with me.
The essence of `Array#inspect` is that after stringifying each element
of array using `inspect` method, we join them together so that we can
get `inspect` representation of the entire array.
@@ -129,7 +152,7 @@ After the `inspect` representation is created, we no longer require the
individual string representation. This means that we don't have to register
these temporal objects into GC arena.
Therefore, in order to keep the arena size small; the function
Therefore, in order to keep the arena size small; the `ary_inspect()` function
will do the following:
- save the position of the stack top using `mrb_gc_arena_save()`.
@@ -143,7 +166,7 @@ required temporal object may be deleted by GC.
We may have an usecase where after creating many temporal objects, we'd
like to keep some of them. In this case, we cannot use the same idea
in `mrb_ary_to_s()` like appending objects to existing one.
in `ary_inspect()` like appending objects to existing one.
Instead, after `mrb_gc_arena_restore()`, we must re-register the objects we
want to keep in the arena using `mrb_gc_protect(mrb, obj)`.
Use `mrb_gc_protect()` with caution because it could also lead to an "arena
-283
View File
@@ -1,283 +0,0 @@
<!-- summary: Getting Started with mruby -->
# Getting Started with mruby
This guide walks you through building mruby, running your first Ruby program,
and embedding mruby in a C application.
## Prerequisites
You need:
- C compiler (`gcc` or `clang`)
- Ruby 2.5 or later (for the build system)
- `rake` (bundled with Ruby)
- `git` (optional, for cloning the source)
## Building mruby
Clone the repository and build:
```console
$ git clone https://github.com/mruby/mruby.git
$ cd mruby
$ rake
```
This compiles the default configuration and produces:
- `bin/mruby` — Ruby script interpreter
- `bin/mirb` — interactive Ruby shell
- `bin/mrbc` — bytecode compiler
- `build/host/lib/libmruby.a` — library for embedding
## Running Ruby Code
### Interactive shell
```console
$ bin/mirb
mirb - Pair interactive mruby
> puts "Hello, mruby!"
Hello, mruby!
=> nil
> 1 + 2
=> 3
```
### Running a script file
Create `hello.rb`:
```ruby
puts "Hello from mruby!"
```
Run it:
```console
$ bin/mruby hello.rb
Hello from mruby!
```
### One-liner
```console
$ bin/mruby -e 'puts "Hello!"'
Hello!
```
## Compiling to Bytecode
mruby can compile Ruby scripts to bytecode (`.mrb` files) for faster
loading and deployment without source code:
```console
$ bin/mrbc hello.rb # produces hello.mrb
$ bin/mruby -b hello.mrb # run bytecode
Hello from mruby!
```
You can also generate C source from Ruby scripts:
```console
$ bin/mrbc -Bhello_code hello.rb # produces hello.c with byte array
```
This generates a C file with a `const uint8_t hello_code[]` array that
can be loaded with `mrb_load_irep()` in your C application.
## Embedding mruby in C
The primary use case of mruby is embedding in C/C++ applications.
### Minimal example
Create `embed.c`:
```c
#include <mruby.h>
#include <mruby/compile.h>
int main(void)
{
mrb_state *mrb = mrb_open();
if (!mrb) return 1;
mrb_load_string(mrb, "puts 'Hello from embedded mruby!'");
if (mrb->exc) {
mrb_print_error(mrb);
}
mrb_close(mrb);
return 0;
}
```
### Compile and link
Use `mruby-config` to get the correct compiler and linker flags:
```console
$ gcc -I include `build/host/bin/mruby-config --cflags` embed.c \
`build/host/bin/mruby-config --ldflags --libs` -o embed
$ ./embed
Hello from embedded mruby!
```
**Important**: Always use `mruby-config --cflags` when compiling code
that uses mruby. The build configuration may define macros (such as
`MRB_NO_BOXING` or `MRB_USE_BIGINT`) that change the internal data
layout. Compiling without these flags causes silent data corruption.
### Calling Ruby from C
```c
#include <stdio.h>
#include <mruby.h>
#include <mruby/compile.h>
#include <mruby/string.h>
int main(void)
{
mrb_state *mrb = mrb_open();
/* Define a Ruby method */
mrb_load_string(mrb, "def greet(name) \"Hello, #{name}!\" end");
/* Call it from C */
mrb_value result = mrb_funcall(mrb, mrb_top_self(mrb),
"greet", 1, mrb_str_new_lit(mrb, "World"));
printf("%s\n", mrb_str_to_cstr(mrb, result));
mrb_close(mrb);
return 0;
}
```
### Defining C functions callable from Ruby
```c
#include <mruby.h>
#include <mruby/compile.h>
static mrb_value
my_add(mrb_state *mrb, mrb_value self)
{
mrb_int a, b;
mrb_get_args(mrb, "ii", &a, &b);
return mrb_fixnum_value(a + b);
}
int main(void)
{
mrb_state *mrb = mrb_open();
/* Define method on Kernel (available everywhere) */
mrb_define_method(mrb, mrb->kernel_module, "my_add",
my_add, MRB_ARGS_REQ(2));
mrb_load_string(mrb, "puts my_add(3, 4)"); /* prints 7 */
mrb_close(mrb);
return 0;
}
```
## Loading Precompiled Bytecode
For deployment without the compiler gem, precompile your Ruby code:
```console
$ bin/mrbc -Bruby_code app.rb
```
Then load in C:
```c
#include <mruby.h>
#include <mruby/irep.h>
#include "app.c" /* contains ruby_code[] */
int main(void)
{
mrb_state *mrb = mrb_open();
mrb_load_irep(mrb, ruby_code);
if (mrb->exc) {
mrb_print_error(mrb);
}
mrb_close(mrb);
return 0;
}
```
This approach does not require the `mruby-compiler` gem, resulting in
a smaller binary.
## Customizing the Build
mruby's functionality is controlled by the build configuration file.
The default is `build_config/default.rb`.
### Using a custom configuration
```console
$ MRUBY_CONFIG=build_config/minimal.rb rake
```
### Selecting gems
Gems add features to mruby. A minimal configuration:
```ruby
MRuby::Build.new do |conf|
conf.toolchain :gcc
# Core language extensions
conf.gem core: 'mruby-array-ext'
conf.gem core: 'mruby-string-ext'
conf.gem core: 'mruby-hash-ext'
# Tools
conf.gem core: 'mruby-bin-mruby' # mruby command
conf.gem core: 'mruby-bin-mirb' # interactive shell
conf.gem core: 'mruby-bin-mrbc' # bytecode compiler
# Compiler (needed for mrb_load_string)
conf.gem core: 'mruby-compiler'
end
```
### Using a gembox
Gemboxes are predefined collections of gems:
```ruby
MRuby::Build.new do |conf|
conf.toolchain :gcc
conf.gembox 'default' # standard set of gems
end
```
## Amalgamation (Single-File Build)
For the simplest integration, use amalgamation to combine all mruby
source into a single `mruby.c` and `mruby.h`:
```console
$ rake amalgam
$ gcc -I build/host/amalgam your_app.c build/host/amalgam/mruby.c -o your_app -lm
```
See [amalgamation.md](amalgamation.md) for details.
## What's Next
- [Language Features](language.md) — Ruby subset supported by mruby
- [C API Reference](capi.md) — values, classes, methods, error handling
- [Compile](compile.md) — full build system reference
- [mrbgems](mrbgems.md) — creating and using gems
- [Linking](link.md) — linking `libmruby` to applications
- [Build-time Configurations](mrbconf.md) — compile-time options
- [GC Arena](gc-arena-howto.md) — managing GC arena in C extensions
- [Limitations](../limitations.md) — differences from CRuby
-430
View File
@@ -1,430 +0,0 @@
<!-- summary: mruby Language Features and Ruby Compatibility -->
# mruby Language Features
This guide describes the Ruby language features supported by mruby 4.0.
mruby implements a subset of the Ruby language, optimized for embedded use.
For a list of specific behavioral differences, see
[limitations.md](../limitations.md).
**If you are coming from CRuby**, note these major differences upfront:
- No `require` or `load` — all code is linked at build time
- No `defined?` keyword — use `respond_to?`, `const_defined?`, etc.
- No refinements (`refine`, `using`)
- No `Encoding` class — UTF-8 opt-in via `MRB_UTF8_STRING`
- Fibers cannot yield across C function boundaries
- Integer size varies by platform and boxing mode
- Operators cannot be overridden by user code
See [Key Differences from CRuby](#key-differences-from-cruby) for
the full list.
## Syntax
### Keywords
mruby supports the following keywords:
`BEGIN`, `END`, `alias`, `and`, `begin`, `break`, `case`, `class`,
`def`, `do`, `else`, `elsif`, `end`, `ensure`, `false`, `for`, `if`,
`in`, `module`, `next`, `nil`, `not`, `or`, `redo`, `rescue`,
`retry`, `return`, `self`, `super`, `then`, `true`, `undef`,
`unless`, `until`, `when`, `while`, `yield`
Magic variables: `__FILE__`, `__LINE__`, `__ENCODING__`, `__method__`
**Not supported:** `defined?` (use `respond_to?`, `const_defined?`,
etc. instead), `refinements` (`using`, `refine`).
### Classes and Modules
```ruby
class Animal
attr_accessor :name
def initialize(name)
@name = name
end
def speak
"..."
end
end
class Dog < Animal
def speak
"Woof!"
end
end
module Greetable
def greet
"Hello, I'm #{name}"
end
end
class Dog
include Greetable
end
```
All standard class and module features are supported: inheritance,
`include`, `prepend`, `extend`, `attr_reader`/`attr_writer`/
`attr_accessor`, `public`/`private`/`protected` visibility, class
variables (`@@var`), class methods, and `super`.
### Methods
```ruby
# Required, optional, rest, post-rest, keyword, and block arguments
def example(a, b = 1, *rest, last, key:, opt_key: nil, **kwargs, &block)
end
# Endless method definition
def double(x) = x * 2
```
### Blocks and Procs
```ruby
[1, 2, 3].each { |n| puts n }
[1, 2, 3].each do |n|
puts n
end
square = Proc.new { |x| x * x }
square = proc { |x| x * x }
double = lambda { |x| x * 2 }
double = ->(x) { x * 2 }
```
### Control Flow
```ruby
# if/unless (both statement and modifier forms)
if condition
# ...
elsif other
# ...
else
# ...
end
result = value if condition
result = value unless condition
# case/when
case obj
when String then "string"
when Integer then "integer"
else "other"
end
# Loops
while condition
# ...
end
until condition
# ...
end
for item in collection
# ...
end
# Loop control
break # exit loop
next # skip to next iteration
redo # restart current iteration
retry # restart begin/rescue block
```
### Exception Handling
```ruby
begin
risky_operation
rescue ArgumentError => e
handle_arg_error(e)
rescue StandardError => e
handle_error(e)
ensure
cleanup
end
raise "something went wrong"
raise ArgumentError, "bad argument"
```
**Note:** `raise` without arguments in a `rescue` clause does not
re-raise the current exception. Capture and re-raise explicitly:
```ruby
begin
risky_operation
rescue => e
log(e)
raise e # explicit re-raise required
end
```
### Strings
```ruby
"double-quoted with #{interpolation}"
'single-quoted literal'
heredoc = <<~HEREDOC
indented heredoc
with #{interpolation}
HEREDOC
```
### Regular Expressions
Regular expressions require an external gem such as `mruby-regexp-pcre`
or `mruby-onig-regexp`. Without a regexp gem, `Regexp` literals
(`/pattern/`) are not available.
### Pattern Matching (Limited)
Only rightward assignment with simple variable binding is supported:
```ruby
expr => var # assigns expr to var
```
`case/in` syntax, array/hash patterns, guard clauses, pin operator,
find patterns, and alternative patterns are **not** supported.
## Numeric Types
mruby's numeric type sizes depend on the boxing mode and platform.
### Integer
| 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 |
Integer overflow raises a `RangeError` unless the `mruby-bigint` gem
is included, in which case integers automatically promote to
arbitrary precision.
### Float
By default, `Float` uses 64-bit `double`. Compile-time options:
- `MRB_USE_FLOAT32`: use 32-bit `float` instead
- `MRB_NO_FLOAT`: disable floating-point entirely
With word boxing on 64-bit, many float values are stored inline
(without heap allocation) using a rotation encoding.
### Additional Numeric Types (via gems)
- **Rational** (`mruby-rational`): exact rational arithmetic
- **Complex** (`mruby-complex`): complex number support
- **Bigint** (`mruby-bigint`): arbitrary-precision integers
## Core Classes
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 |
### Core Modules
| Module | Notes |
| ---------- | ----------------------------------------- |
| Kernel | Core methods (`puts`, `p`, `raise`, etc.) |
| Comparable | Comparison operators via `<=>` |
| Enumerable | Collection iteration methods |
## Standard Library (via gemboxes)
mruby's standard library is organized into gemboxes. The `default`
gembox includes all of the below. Use this table to find which
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 |
### 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 |
### 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` |
### 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 |
## Key Differences from CRuby
### No Runtime Loading
mruby has no `require` or `load`. All code (gems, libraries) is
linked at build time. To add functionality, include the appropriate
gem in your build configuration:
```ruby
MRuby::Build.new do |conf|
conf.gem :core => "mruby-time"
end
```
### No `defined?` Keyword
The `defined?` keyword raises `NameError` instead of returning a
type string or `nil`. Use alternatives:
```ruby
# Instead of: defined?(Foo)
Object.const_defined?(:Foo)
# Instead of: defined?(@var)
instance_variable_defined?(:@var)
# Instead of: defined?(method_name)
respond_to?(:method_name)
```
### Fiber Limitations
Fibers cannot cross C function boundaries. You cannot yield from a
fiber inside a C-implemented method. Only `mrb_fiber_yield` at
function return is supported.
### Array and String Subclasses
`Array` and `String` do not support instance variables to reduce memory.
This means subclassing `Array` or `String` and adding `@fields` will raise an error.
### Operator Overriding
Operators of primitive classes cannot be overridden by user code.
Redefining `String#+` has no effect on the behavior of the `+` operator.
### Module Loading Hooks
`include`/`prepend`/`extend` do not call `append_features`/
`prepend_features`/`extend_object` hooks. The module is included
directly.
### Small Hash Optimization
For small hashes, `#hash` is not called on keys. Custom `#hash`
methods may not execute for small hash tables.
### No Refinements
Module refinements (`refine`, `using`) are not supported.
### No Encoding Class
There is no `Encoding` class. String encoding is either pure bytes
or UTF-8 (opt-in via `MRB_UTF8_STRING` compile flag).
### `nil?` in Conditionals
Redefining `nil?` has no effect on conditional expressions.
The VM uses direct nil checks for performance.
### Integer Precision
Integer size varies by boxing mode (see [Numeric Types](#numeric-types)
above). Code relying on 64-bit integer precision may behave
differently on 32-bit or NaN boxing configurations.
## Build-Time Configuration
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) |
See [mrbconf.md](mrbconf.md) for the complete list of configuration
macros.
+5 -96
View File
@@ -1,4 +1,4 @@
<!-- summary: About Memory Allocator Customization and Heap Regions -->
<!-- summary: About Memory Allocator Customization -->
# Memory Allocation
@@ -50,6 +50,7 @@ Implement mrb_basic_alloc_func() in your code, and mruby will invoke it for ever
If you are moving from the old API:
1. **Removal of `mrb_open_allocf()`**
- \_Old:
```c
@@ -64,9 +65,11 @@ If you are moving from the old API:
```
2. **`mrb_open_core()` takes no arguments**
- Simply drop any allocf or user-data arguments, and redefine `mrb_basic_alloc_func` as you need.
3. **No more `mrb_allocf` type**
- Definitions using the `mrb_allocf` typedef can be removed; implement `mrb_basic_alloc_func()` with the signature below:
```c
@@ -74,6 +77,7 @@ If you are moving from the old API:
```
4. **`mrb_basic_alloc_func` signature change**
- _Old:_
```c
@@ -114,98 +118,3 @@ If you are moving from the old API:
mrb_state *mrb = mrb_open_core();
```
---
## 3. Heap Regions: Contiguous Memory for GC
By default, mruby allocates GC heap pages individually via `malloc()`.
On embedded targets with multiple memory banks (e.g., STM32 CCM+SRAM,
ESP32 PSRAM+IRAM), you may want to place heap pages in a specific
memory region. `mrb_gc_add_region()` lets you provide a contiguous
buffer that mruby carves into heap pages.
### API
```c
#include <mruby/gc.h>
int mrb_gc_add_region(mrb_state *mrb, void *start, size_t size);
```
- **`start`**: pointer to a contiguous memory buffer.
- **`size`**: size of the buffer in bytes.
- **Returns**: number of heap pages carved from the buffer, or 0 if
the buffer is too small.
The buffer is aligned internally to pointer size. Each page is
approximately 40 KB on 64-bit systems (24 KB on 32-bit). The caller
retains ownership of the buffer and must keep it valid for the
lifetime of the `mrb_state`.
### Example: Static buffer
```c
#include <mruby.h>
#include <mruby/gc.h>
/* 256 KB static buffer -- about 6 pages on 64-bit */
static char heap_buf[256 * 1024];
int main(void)
{
mrb_state *mrb = mrb_open();
int pages = mrb_gc_add_region(mrb, heap_buf, sizeof(heap_buf));
/* pages are immediately available for object allocation */
/* ... use mrb ... */
mrb_close(mrb); /* region pages are cleaned up; buffer is not freed */
return 0;
}
```
### Example: MCU with multiple RAM banks
```c
/* STM32 with 64 KB CCM and 128 KB SRAM */
extern char __ccm_start[], __ccm_end[]; /* linker symbols */
extern char __sram_start[], __sram_end[];
mrb_state *mrb = mrb_open();
mrb_gc_add_region(mrb, __ccm_start, __ccm_end - __ccm_start);
mrb_gc_add_region(mrb, __sram_start, __sram_end - __sram_start);
```
### How it works
When `mrb_gc_add_region()` is called, mruby:
1. Aligns the buffer start to pointer size.
2. Divides the buffer into `mrb_heap_page`-sized chunks.
3. Initializes each page's freelist and links it into the GC heap.
4. Records the region in a descriptor for O(1) pointer-to-page mapping.
Region pages participate in the normal GC cycle (mark-and-sweep) like
any other heap page. The only differences are:
- **Never freed**: the GC will not call `free()` on region pages, even
if all objects on a page are dead. The page stays in the heap with an
empty freelist, ready for reuse.
- **Fallback**: when all region pages are full, mruby falls back to
`malloc()` for new pages as usual.
- **Cleanup**: `mrb_close()` frees the internal region descriptor but
does not free the buffer itself.
### Sizing
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 |
To estimate pages for a given buffer: `pages = buffer_size / sizeof(mrb_heap_page)`.
Each page provides `MRB_HEAP_PAGE_SIZE` object slots.
+21 -65
View File
@@ -61,12 +61,12 @@ end
`MRB_STACK_EXTEND_DOUBLING`
- If defined doubles the stack size when extending it.
- Otherwise extends stack with 1.5x growth (minimum `MRB_STACK_GROWTH`).
- Otherwise extends stack with `MRB_STACK_GROWTH`.
`MRB_STACK_GROWTH`
- Default value is `128`.
- Minimum stack growth size when extending.
- Used in stack extending.
- Ignored when `MRB_STACK_EXTEND_DOUBLING` is defined.
`MRB_STACK_MAX`
@@ -95,7 +95,6 @@ end
- When defined, or `MRB_INT32` are not defined on 64-bit CPU mode, `mrb_int` will be defined as `int64_t`.
- Conflicts with `MRB_INT32`.
- On 32-bit platforms, `MRB_INT64` requires `MRB_NO_BOXING` because heap-allocated `RInteger` needs 8-byte alignment that the GC heap may not guarantee with word or NaN boxing.
## Garbage collector configuration
@@ -126,6 +125,11 @@ end
- Default value is `1024`.
- Specifies number of `RBasic` per each heap page.
- To calculate the number of bytes per heap page, it is "(size of management data per heap page) + (size per object) \* `MRB_HEAP_PAGE_SIZE`".
In mruby 3.1.0, the "size of management data per heap page" is 6 words, also "size per object" is 6 words.
For a 32-bit CPU, `(6 * 4) + (6 * 4) * MRB_HEAP_PAGE_SIZE` gives the bytes of size per heap page.
Conversely, for example, to keep the size per heap page to 4 Ki bytes,
calculate `(4096 - (6 * 4)) / (6 * 4)` to specify `MRB_HEAP_PAGE_SIZE=169`.
## Memory pool configuration
@@ -169,24 +173,8 @@ end
`MRB_WORD_BOXING`
- If defined represent `mrb_value` as a word (natural unit of data for the processor).
- Default boxing mode when none is specified.
- On 64-bit platforms, floats are inlined using rotation encoding.
- On 32-bit platforms, floats are heap-allocated as `RFloat` objects.
`MRB_NO_BOXING`
- If defined represent `mrb_value` as a C struct (occupies 2 words).
- Most portable but least memory-efficient representation.
- Required for `MRB_INT64` on 32-bit platforms.
- Default for `host-debug` configuration.
`MRB_WORDBOX_NO_INLINE_FLOAT`
- If defined disables inline float values in word boxing.
- All floats are heap-allocated as `RFloat` objects.
- Automatically defined on 32-bit platforms (64-bit `double` cannot fit in a 32-bit word).
- Only meaningful with `MRB_WORD_BOXING`.
- If defined represent `mrb_value` as a word.
- If defined `Float` will be a mruby object with `RBasic`.
## Reduce heap memory configuration
@@ -194,7 +182,7 @@ end
- Use `etext` and `edata` section addresses defined by the linker to detect read-only data.
- Those addresses are widely available, but not portable, nor standardized.
- Defined by default on User-mode Linux.
- This macro is defined by default on User-mode Linux.
`MRB_NO_DEFAULT_RO_DATA_P`
@@ -202,15 +190,16 @@ end
`MRB_USE_CUSTOM_RO_DATA_P`
- Define to provide your own `mrb_ro_data_p()` implementation.
- Please try if `MRB_USE_LINK_TIME_RO_DATA_P` is not available.
- The `mrb_ro_data_p()` function is implemented by the user in an arbitrary file.
- The prototype declaration is `mrb_bool mrb_ro_data_p(const char *ptr)`.
- Return `TRUE` if `ptr` is in the read-only section, otherwise return `FALSE`.
## Other configuration
`MRB_USE_MALLOC_TRIM`
`MRB_MALLOC_TRIM`
- Call `malloc_trim(0)` for each `mrb_full_gc()` call.
- call `malloc_trim(0)` for each `mrb_full_gc()` call
`MRB_UTF8_STRING`
@@ -219,13 +208,13 @@ end
`MRB_STR_LENGTH_MAX`
- The maximum length of strings (default 1048576).
- Set this value to zero to skip the check.
- The maximum length of strings (default 1MB)
- set this value to zero to skip the check
`MRB_ARY_LENGTH_MAX`
- The maximum length of arrays (default 131072).
- Set this value to zero to skip the check.
- The maximum length of arrays (default 1MB)
- set this value to zero to skip the check
`MRB_FUNCALL_ARGC_MAX`
@@ -233,10 +222,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_INITIAL_SIZE`
`KHASH_DEFAULT_SIZE`
- Default value is `32`.
- Specifies initial size of khash table bucket.
- Specifies default size of khash table bucket.
- Used in `kh_init_ ## name` function.
`MRB_NO_METHOD_CACHE`
@@ -256,37 +245,4 @@ end
`MRB_USE_VM_SWITCH_DISPATCH`
- Turn on switch dispatch in VM loop.
- Otherwise, computed goto (direct threading) is used when supported by the compiler.
`MRB_SYMBOL_LINEAR_THRESHOLD`
- Default value is `256`.
- Threshold for switching symbol table from linear search to hash table.
## Tuning profiles
Predefined profiles adjust several macros together for specific
deployment targets. Define one of the following:
`MRB_CONSTRAINED_BASELINE_PROFILE`
- For micro controllers.
- Enables `MRB_NO_METHOD_CACHE`, reduces `KHASH_INITIAL_SIZE` to `16`,
and `MRB_HEAP_PAGE_SIZE` to `256`.
`MRB_BASELINE_PROFILE`
- Default mruby profile. No additional changes.
`MRB_MAIN_PROFILE`
- For desktop computers or workstations.
- Increases `MRB_METHOD_CACHE_SIZE` to `1024` and `MRB_HEAP_PAGE_SIZE`
to `4096`.
`MRB_HIGH_PROFILE`
- For long-lived server processes.
- Increases `MRB_METHOD_CACHE_SIZE` to `4096` and `MRB_HEAP_PAGE_SIZE`
to `4096`.
- Turn on switch dispatch in VM loop
+3 -25
View File
@@ -256,7 +256,7 @@ When more than one version requirements is passed, the dependency must satisfy a
You can have default gem to use as dependency when it's not defined in your build configuration.
When the last argument of `add_dependency` call is `Hash`, it will be treated as default gem information.
Its format is same as argument of method `MRuby::Build#gem`, except that it can't be treated as path gem location.
Its format is same as argument of method `MRuby::Build#gem`, expect that it can't be treated as path gem location.
When a special version of dependency is required,
use `MRuby::Build#gem` in the build configuration to override default gem.
@@ -304,34 +304,12 @@ Your GEM can export include paths to another GEMs that depends on your GEM.
By default, `/...absolute path.../{GEM_NAME}/include` will be exported.
So it is recommended not to put GEM's local header files on include/.
These exports are transitive.
These exports are retroactive.
For example: when B depends on C and A depends on B, A will get include paths exported by C.
Exported `include_paths` are automatically appended to GEM local `include_paths` by rake.
Exported include_paths are automatically appended to GEM local include_paths by rake.
You can use `spec.export_include_paths` accessor if you want more complex build.
### Settings for GEM build commands/tasks
When the block argument passed to `MRuby::Gem::Specification.new` is executed,
the GEM build commands/tasks for the `MRuby::Build` instance may not yet be finalized.
In most cases, modifying the GEM build commands/tasks within the block passed to
`MRuby::Gem::Specification.new` is not a problem.
However, you may need to perform GEM build commands/tasks after the GEM build
commands/tasks for the `MRuby::Build` instance have been finalized.
In such cases, you can achieve this by passing a block argument to
`MRuby::Gem::Specification#build_settings` within the block passed to
`MRuby::Gem::Specification.new`.
```ruby
spec.build_settings do
spec.cc.flags << "-any_flags"
end
```
**NOTE**: Using the `build_settings` method will cause GEM's all build command settings
directly written in the block passed to `MRuby::Gem::Specification.new` to be ignored.
## C Extension
mruby can be extended with C. This is possible by using the C API to
-413
View File
@@ -1,413 +0,0 @@
<!-- summary: ROM Method Tables for Memory-Efficient Method Registration -->
# ROM Method Tables
ROM method tables allow C methods to be registered using static data
stored in ROM (read-only memory) rather than heap-allocated RAM. This
saves significant memory on embedded systems where RAM is scarce.
## Motivation
In a default mruby build, `mrb_open()` builds ~40 classes with ~700+
method entries at startup. Each method entry is heap-allocated via
individual `mrb_define_method_id()` calls. On a constrained MCU, this
consumes ~14KB of RAM for method table metadata alone.
ROM method tables eliminate this cost by placing method metadata in
static `const` data at compile time. Only runtime mutations (e.g.,
reopening a class to add methods) trigger heap allocation.
## Architecture
### Chained Layers
Each class has a method table (`mt`) pointer to a linked list of
`mrb_mt_tbl` layers:
```
String.mt -> [mutable layer] -> [string_ext ROM] -> [string_core ROM] -> NULL
```
**Lookup** walks the chain front-to-back, returning the first match.
The method cache makes repeated lookups O(1), so the chain walk
only occurs on cache misses.
**Mutation** uses copy-on-write (COW): if the top layer is read-only,
a new mutable layer is created in front of it. The ROM data is never
modified.
```
Before: String.mt -> [string_ext ROM] -> [string_core ROM] -> NULL
After String.define_method(:foo):
String.mt -> [mutable: foo] -> [string_ext ROM] -> [string_core ROM] -> NULL
```
### Memory Layout
Each `mrb_mt_tbl` stores method entries as an array of `mrb_mt_entry`
structs, each combining a function pointer, a symbol key, and flags:
```
ptr -> [ entry[0] | entry[1] | ... | entry[N-1] ]
|<-- mrb_mt_entry: { val, key, flags } -->|
```
Values are `union mrb_mt_ptr` (function pointer or proc pointer). Keys
are pure `mrb_sym` (no flag encoding). Flags are a separate `uint32_t`
field that stores visibility, func/proc type, and argument spec.
Entries are searched linearly, so source code order does not matter.
The method cache makes repeated lookups O(1), so the linear scan
only occurs on cache misses.
### Per-State Wrappers
The `const mrb_mt_entry[]` arrays are truly static and shared across
the process. However, the `mrb_mt_tbl` wrapper (which carries the
`next` pointer for chaining) is heap-allocated per `mrb_state` by
`MRB_MT_INIT_ROM()`. This allows multiple `mrb_state` instances in
the same process to each have independent method table chains, even
when linking to the same const entries.
## How to Define a ROM Method Table
### Step 1: Define the Static Data
Include `<mruby/class.h>` (which provides `mrb_mt_entry`,
`MRB_MT_ENTRY()`, and flag constants) and define the ROM entries:
```c
#include <mruby/class.h>
static const mrb_mt_entry my_rom_entries[] = {
MRB_MT_ENTRY(my_method_a, MRB_SYM(method_a), MRB_ARGS_REQ(1)),
MRB_MT_ENTRY(my_method_b, MRB_SYM(method_b), MRB_ARGS_NONE()),
MRB_MT_ENTRY(my_method_eq, MRB_OPSYM(eq), MRB_ARGS_REQ(1)),
};
```
### Step 2: Register in the Init Function
Replace `mrb_define_method_id()` calls with a single
`MRB_MT_INIT_ROM()` call:
```c
void
mrb_mruby_mygem_gem_init(mrb_state *mrb)
{
struct RClass *c = mrb_define_class_id(mrb, MRB_SYM(MyClass), mrb->object_class);
MRB_MT_INIT_ROM(mrb, c, my_rom_entries);
}
```
`MRB_MT_INIT_ROM()` allocates a per-state wrapper and pushes the ROM
layer onto the class's method table chain.
### Step 3: Verify
Build and run the test suite. ROM tables are semantically transparent
to Ruby code.
## Reference
### Data Types
Defined in `include/mruby/class.h`:
```c
union mrb_mt_ptr {
const struct RProc *proc;
mrb_func_t func;
};
typedef struct mrb_mt_entry {
union mrb_mt_ptr val;
mrb_sym key; /* pure symbol ID (no flags packed) */
uint32_t flags; /* method flags + aspec */
} mrb_mt_entry;
typedef struct mrb_mt_tbl {
int size;
int alloc; /* bit 30: MRB_MT_READONLY_BIT */
mrb_mt_entry *ptr;
struct mrb_mt_tbl *next; /* next (lower-priority) layer, or NULL */
} mrb_mt_tbl;
```
### Macros
```c
/* ROM table entry: 3rd param is MRB_ARGS_*() optionally OR'd with
MRB_MT_PRIVATE. The macro OR's in MRB_MT_FUNC automatically. */
#define MRB_MT_ENTRY(fn, sym, flags) \
{ { .func = (fn) }, (sym), (flags) | MRB_MT_FUNC }
/* Extract aspec from combined flags */
#define MRB_MT_ASPEC(flags) ((mrb_aspec)((flags) & 0xffffff))
/* Allocate a per-state ROM wrapper and push onto class method chain */
#define MRB_MT_INIT_ROM(mrb, cls, entries) \
mrb_mt_init_rom(mrb, cls, entries, \
(int)(sizeof(entries)/sizeof(entries[0])))
```
### Flags
| Flag | Value | Description |
| ---------------- | ------- | ----------------------------------- |
| `MRB_MT_FUNC` | (1<<24) | C function (auto-set by macro) |
| `MRB_MT_PUBLIC` | 0 | Public visibility (default) |
| `MRB_MT_PRIVATE` | (1<<25) | Private visibility (in entry param) |
The third parameter to `MRB_MT_ENTRY()` is an `MRB_ARGS_*()`
expression optionally OR'd with `MRB_MT_PRIVATE`. The aspec value
occupies bits 0-23 and the visibility flag occupies bit 25; these
ranges do not overlap, so the values are simply OR'd together.
`MRB_MT_FUNC` is set automatically. The no-arg optimization is
derived at runtime from `aspec == 0` (`MRB_ARGS_NONE()`).
**How to write entries:**
- **`MRB_MT_ENTRY(fn, sym, MRB_ARGS_*(...))`**: Public method.
- **`MRB_MT_ENTRY(fn, sym, MRB_ARGS_*(...) | MRB_MT_PRIVATE)`**:
Private method.
- Use the same `MRB_ARGS_*()` macros as `mrb_define_method_id()`.
### Symbol Macros
Use the presym macros for keys. See `doc/guides/symbol.md` for the
full list:
```c
MRB_SYM(size) /* size */
MRB_SYM_B(chomp) /* chomp! */
MRB_SYM_Q(frozen) /* frozen? */
MRB_SYM_E(name) /* name= */
MRB_OPSYM(add) /* + */
MRB_OPSYM(eq) /* == */
MRB_OPSYM(aref) /* [] */
MRB_OPSYM(aset) /* []= */
MRB_OPSYM(cmp) /* <=> */
MRB_IVSYM(name) /* @name */
```
### API
```c
void mrb_mt_init_rom(mrb_state *mrb, struct RClass *c,
const mrb_mt_entry *entries, int size);
```
Allocates a per-state `mrb_mt_tbl` wrapper for the const entries and
pushes it onto the class's method table chain. The wrapper is tracked
in `mrb->rom_mt` and freed at `mrb_close()`. Use the `MRB_MT_INIT_ROM`
macro to auto-compute the size. Multiple calls push additional layers,
which is how extension gems add methods to core classes.
## Entry Correspondence
Each `MRB_MT_ENTRY()` bundles a function pointer with its method name
and flags in a single line. Their order in the source code does not
matter, but keeping related methods
together improves readability.
**Method aliases** (two names for the same function) are expressed as
separate entries sharing the same function pointer:
```c
static const mrb_mt_entry str_rom_entries[] = {
MRB_MT_ENTRY(mrb_str_size, MRB_SYM(size), MRB_ARGS_NONE()),
MRB_MT_ENTRY(mrb_str_size, MRB_SYM(length), MRB_ARGS_NONE()),
};
```
## Conditional Methods
Methods that depend on build configuration (e.g., `MRB_NO_FLOAT`) can
use `#ifdef` directly inside the ROM entries array. The `sizeof` in
`MRB_MT_INIT_ROM()` automatically adjusts to the number of entries
that survive preprocessing:
```c
static const mrb_mt_entry integer_rom_entries[] = {
MRB_MT_ENTRY(int_to_s, MRB_SYM(to_s), MRB_ARGS_OPT(1)),
MRB_MT_ENTRY(int_add, MRB_OPSYM(add), MRB_ARGS_REQ(1)),
#ifndef MRB_NO_FLOAT
MRB_MT_ENTRY(int_to_f, MRB_SYM(to_f), MRB_ARGS_NONE()),
#endif
};
```
For conditional methods on a **different class**, use a separate ROM
table wrapped in the `#ifdef`:
```c
#ifndef MRB_NO_FLOAT
static const mrb_mt_entry float_rom_entries[] = { ... };
#endif
void mrb_init_numeric(mrb_state *mrb) {
MRB_MT_INIT_ROM(mrb, integer, integer_rom_entries);
#ifndef MRB_NO_FLOAT
MRB_MT_INIT_ROM(mrb, fl, float_rom_entries);
#endif
}
```
## Extension Gems
Extension gems use exactly the same pattern. Since gems are
initialized after core, calling `MRB_MT_INIT_ROM()` pushes the gem's
ROM layer in front of the core ROM layer:
```c
/* mrbgems/mruby-string-ext/src/string.c */
static const mrb_mt_entry string_ext_rom_entries[] = { ... };
void mrb_mruby_string_ext_gem_init(mrb_state *mrb)
{
struct RClass *s = mrb->string_class;
MRB_MT_INIT_ROM(mrb, s, string_ext_rom_entries);
}
```
After initialization, String's method table chain looks like:
```
String.mt -> [string_ext ROM, 53 methods]
-> [string_core ROM, 46 methods]
-> NULL
```
A gem may also define ROM tables for multiple classes:
```c
void mrb_mruby_mygem_gem_init(mrb_state *mrb)
{
MRB_MT_INIT_ROM(mrb, mrb->string_class, string_mygem_rom_entries);
MRB_MT_INIT_ROM(mrb, mrb->integer_class, integer_mygem_rom_entries);
}
```
## Methods That Cannot Use ROM Tables
Some methods must remain as `mrb_define_method_id()` calls:
- **Class methods** (`mrb_define_class_method_id()`): ROM tables
register instance methods only.
- **Module functions** (`mrb_define_module_function_id()`): Same
reason.
- **Methods requiring `mrb_state*` at definition time**: For example,
methods that create frozen RProc objects during init.
- **Methods on dynamically created classes**: Classes created at
init time (not stored in `mrb->xxx_class`) that require
`mrb_define_class()` to obtain the class pointer.
- **Cross-class methods** (methods on a class the gem does not own):
Each ROM table adds a 16-byte `mrb_mt_tbl` layer to the target
class's chain. For 1-2 methods, this overhead exceeds the savings.
Use `mrb_define_method_id()` instead -- cross-class methods share
the target class's existing mutable layer.
These methods are added after `MRB_MT_INIT_ROM()` and go into the
mutable layer that sits in front of the ROM chain.
## Runtime Behavior
### Open Classes (COW)
Ruby's open classes work transparently. When a Ruby program or C code
adds a method to a class with a ROM table, the COW mechanism creates a
mutable layer:
```ruby
class String
def my_custom_method
42
end
end
"hello".my_custom_method #=> 42
"hello".size #=> 5 (still found in ROM layer)
```
### Method Removal
`remove_method` works on ROM methods using a tombstone marker. When a
method in a ROM layer is removed, a special entry (`MRB_MT_FUNC` flag with
`func=NULL`) is inserted into the mutable layer. The `mt_get()` lookup
treats this marker as "not found" and stops searching the chain,
effectively hiding the ROM entry. Unlike `undef_method` (which blocks
superclass lookup), `remove_method`'s tombstone allows the superclass
method to be found.
`undef_method` uses a different tombstone (`proc=NULL` without
`MRB_MT_FUNC`), which is returned by `mt_get()` so the caller raises
NoMethodError without searching the superclass.
### Class Duplication
`Class.dup` shares the ROM chain. The duplicated class gets an empty
mutable layer pointing to the same ROM layers as the original. No ROM
data is copied.
### Garbage Collection
ROM layers are skipped during GC mark and sweep phases. Only mutable
layers are scanned for live RProc references and freed when the class
is collected. ROM wrappers are freed at `mrb_close()` via the
`mrb->rom_mt` tracking list.
### Memory Measurement
`mrb_class_mt_memsize()` reports only mutable layer memory. ROM
wrappers are tracked separately and not counted per-class.
## Converting Existing Code
To convert existing `mrb_define_method_id()` calls to a ROM table:
1. **Count** the number of method definitions that can be converted.
2. **Create** the ROM entries array using `MRB_MT_ENTRY()`.
3. **Move** each `mrb_define_method_id()` call into the entries:
- `MRB_MT_ENTRY(func, sym, aspec)` where:
- `func` is the function pointer
- `sym` is the symbol macro (e.g., `MRB_SYM(name)`)
- `aspec` is the original `MRB_ARGS_*()` macro
- For private methods, OR `MRB_MT_PRIVATE` into the aspec:
`MRB_MT_ENTRY(func, sym, aspec | MRB_MT_PRIVATE)`
4. **Replace** the calls with `MRB_MT_INIT_ROM(mrb, c, entries)`.
5. **Keep** any methods that cannot be converted (see above) as
individual `mrb_define_method_id()` calls after the ROM init.
6. **Build and test**: `rake CONFIG=host-debug -j24 all test:run:serial`
### Before
```c
void mrb_mruby_foo_gem_init(mrb_state *mrb) {
struct RClass *foo = mrb_define_class_id(mrb, MRB_SYM(Foo), mrb->object_class);
mrb_define_method_id(mrb, foo, MRB_SYM(bar), foo_bar, MRB_ARGS_REQ(1));
mrb_define_method_id(mrb, foo, MRB_SYM(baz), foo_baz, MRB_ARGS_NONE());
mrb_define_method_id(mrb, foo, MRB_OPSYM(eq), foo_eq, MRB_ARGS_REQ(1));
}
```
### After
```c
static const mrb_mt_entry foo_rom_entries[] = {
MRB_MT_ENTRY(foo_bar, MRB_SYM(bar), MRB_ARGS_REQ(1)),
MRB_MT_ENTRY(foo_baz, MRB_SYM(baz), MRB_ARGS_NONE()),
MRB_MT_ENTRY(foo_eq, MRB_OPSYM(eq), MRB_ARGS_REQ(1)),
};
void mrb_mruby_foo_gem_init(mrb_state *mrb) {
struct RClass *foo = mrb_define_class_id(mrb, MRB_SYM(Foo), mrb->object_class);
MRB_MT_INIT_ROM(mrb, foo, foo_rom_entries);
}
```
+11 -4
View File
@@ -73,7 +73,14 @@ For `MRB_OPSYM()`, specify the names corresponding to operators (see
can be specified for it). Other than that, describe only word characters
excluding leading and ending punctuation.
These macros are converted to static symbol IDs at compile time.
The `_2` suffix variants (e.g., `MRB_SYM_2`) are kept for backward
compatibility only; they accept an explicit `mrb_state*` parameter
but ignore it. New code should use the standard macros above.
These macros are converted to static symbol IDs at compile time, unless
preallocate symbols are disabled by `conf.disable_presym`. In that case,
these macros are expanded to `mrb_intern_lit` calls, therefore the mruby state
variable is required. The above macros assume the variable name is `mrb`. If
its name is not `mrb`, you need to use macros with `_2` suffix, such as
`MRB_SYM_2` to specify `mrb_state*` variable.
### Disabling Preallocated Symbols
You can disable preallocated symbols by specifying `conf.disable_presym` in the
configuration file.
-182
View File
@@ -1,182 +0,0 @@
<!-- summary: About mruby Architecture -->
# mruby Architecture
This document provides a map of mruby's internals for developers who
want to understand, debug, or contribute to the codebase.
## Overview
mruby's execution pipeline:
```text
Ruby source → Parser → AST → Code Generator → Bytecode (irep)
VM → Result
```
The design priority is **memory > performance > readability**.
## Object Model
All heap-allocated Ruby objects share a common header (`MRB_OBJECT_HEADER`):
```text
struct RBasic (8 bytes on 64-bit)
┌──────────────┬─────┬──────────┬────────┬───────┐
│ RClass *c │ tt │ gc_color │ frozen │ flags │
│ (class ptr) │ 8b │ 3b │ 1b │ 20b │
└──────────────┴─────┴──────────┴────────┴───────┘
```
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` |
Immediate values (Integer, Symbol, `true`, `false`, `nil`) are encoded
directly in `mrb_value` without heap allocation. The encoding depends on
the boxing mode (see [boxing.md](boxing.md)).
Objects must fit within 5 words (`mrb_static_assert_object_size`).
## Virtual Machine
The VM is register-based, using two stacks: a **value stack** for
registers (locals, temporaries, arguments) and a **call info stack**
for tracking method/block call frames. Each method call pushes a
`mrb_callinfo` frame with the method symbol, proc, PC, and argument
counts.
The dispatch loop in `mrb_vm_run()` decodes opcodes and operates on
registers. Method dispatch looks up the receiver's class method table
(with a per-state method cache), then either calls a C function
directly or pushes a new call frame for Ruby methods.
Exception handling uses `setjmp`/`longjmp` (or C++ exceptions if
configured). Rescue/ensure handler tables are stored in each irep
and searched during stack unwinding.
See [vm.md](vm.md) for detailed VM internals, [opcode.md](opcode.md)
for the full instruction set.
## Garbage Collector
The GC uses **tri-color incremental mark-and-sweep** with an optional
**generational mode**. Objects are colored white (unmarked), gray
(marked, children pending), black (fully marked), or red (static/ROM).
The three-phase cycle (root scan, incremental marking, sweep) runs
in small steps between VM instructions to avoid long pauses. Write
barriers (`mrb_field_write_barrier`, `mrb_write_barrier`) maintain
correctness during incremental marking.
The GC arena protects newly created objects in C code. Heap regions
(`mrb_gc_add_region`) support embedded systems with fixed memory banks.
See [gc.md](gc.md) for detailed GC internals,
[../guides/gc-arena-howto.md](../guides/gc-arena-howto.md) for arena
usage patterns, [../guides/memory.md](../guides/memory.md) for memory
management.
## Compiler Pipeline
The compiler transforms Ruby source code through three stages:
1. **Parser** (`parse.y`): Lrama/Bison grammar produces an AST of
`mrb_ast_node` structures, tracking lexer state and local scopes.
2. **Code Generator** (`codegen.c`): walks the AST and emits bytecode
into `mrb_irep` structures (instruction sequence, literal pool,
symbol table, child ireps).
3. **Execution**: the irep is wrapped in an `RProc` and executed by
the VM, or serialized to `.mrb` binary format.
Alternative loading paths include `mrb_load_string()` (compile and
run), `mrb_load_irep()` (load precompiled bytecode), and `mrbc`
(ahead-of-time compilation).
See [compiler.md](compiler.md) for detailed compiler internals,
[opcode.md](opcode.md) for the instruction set.
## Source File Map
### 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 |
### 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 |
### 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 |
## mrbgems System
Gems are the module system for mruby. Each gem lives in
`mrbgems/mruby-*/` and contains:
```text
mruby-example/
├── mrbgem.rake gem specification (name, deps, bins)
├── src/ C source files
├── mrblib/ Ruby source files (compiled to bytecode)
├── include/ C headers
├── test/ mrbtest test files
└── bintest/ binary test files (CRuby)
```
At build time, gem Ruby files are compiled with `mrbc` and linked into
`libmruby.a`. Gem initialization runs in dependency order via
`gem_init.c` (auto-generated).
GemBoxes (`mrbgems/*.gembox`) define named collections of gems
(e.g., `default.gembox` includes `stdlib`, `stdlib-ext`, `stdlib-io`,
`math`, `metaprog`, and binary tools).
+2 -44
View File
@@ -26,30 +26,8 @@ The Word boxing packing bit patterns are like following:
| undef | `00000000 00000000 00000000 00010100` |
| symbol | `xxxxxxxx xxxxxxxx xxxxxxxx xxxxxx10` |
### Inline Float (64-bit)
On 64-bit platforms, `double` values are packed into the word using
rotation encoding. The IEEE 754 exponent field is rotated so that
common exponent values (those not colliding with the pointer/tag
patterns above) fit directly in a word. This encoding is lossless
for most float values; only a small set of exotic exponents require
heap allocation as `RFloat`.
To disable inline float and heap-allocate all floats, define
`MRB_WORDBOX_NO_INLINE_FLOAT`.
### 32-bit Considerations
On 32-bit platforms with 64-bit `double` (the common case),
`MRB_WORDBOX_NO_INLINE_FLOAT` is automatically defined because a
64-bit double cannot fit in a 32-bit word. All floats are
heap-allocated as `RFloat` objects.
The `RFloat` struct uses a `char[]` buffer instead of a `double`
field to avoid alignment issues, since GC heap slots (RVALUE) on
32-bit have only 4-byte alignment but `double` requires 8-byte.
Accessor functions `mrb_rfloat_value()` and `mrb_rfloat_set()` use
`memcpy` for safe access.
On 64-bit platforms (unless `MRB_WORDBOX_NO_FLOAT_TRUNCATE`), float values are also packed in the `mrb_value`. In that case, we drop least significant 2 bits from mantissa.
If you need full precision for floating-point numbers, define `MRB_WORDBOX_NO_FLOAT_TRUNCATE`.
## NaN Boxing
@@ -74,23 +52,3 @@ The object values appear far more frequently than floating-point numbers, so we
## No Boxing
No boxing represents `mrb_value` by the C struct with `type` and the value union. This is the most portable (but inefficient) representation. No boxing can be specified by `MRB_NO_BOXING`, and it's default for debugging configuration (e.g. `host-debug`).
## 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 |
## ABI Compatibility
The boxing mode changes the layout of `mrb_value`. Code compiled with
one boxing mode **cannot** be linked against a library built with a
different mode. Always use `mruby-config --cflags` to get the correct
compiler flags.
-323
View File
@@ -1,323 +0,0 @@
<!-- summary: Compiler Pipeline Internals -->
# Compiler Pipeline Internals
This document describes mruby's compilation pipeline for developers
working on the parser, code generator, or bytecode format.
**Read this if you are:** adding new syntax or modifying the parser,
debugging codegen issues (wrong registers, missing opcodes),
working with the `.mrb` binary format, or understanding how Ruby
constructs map to bytecode.
## Pipeline Overview
```text
Ruby source
|
v
Lexer/Parser (parse.y)
|
v
AST (mrb_ast_node)
|
v
Code Generator (codegen.c)
|
v
Bytecode (mrb_irep)
|
v
VM execution -or- .mrb binary file
```
## Stage 1: Lexer and Parser
The lexer and parser are combined in a single Lrama/Bison grammar
file: `mrbgems/mruby-compiler/core/parse.y`.
### Parser State
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 `+`/`-`
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
interpolation.
- **cond_stack**, **cmdarg_stack**: bit stacks tracking
conditional and command argument contexts.
- **tree**: root AST node after successful parse.
- **error_buffer**: accumulated parse errors.
### AST Nodes
The parser produces an AST using two node types:
- **Cons-list nodes**: traditional binary tree pairs (car/cdr)
- **Variable-sized nodes**: have a header with `node_type`, `lineno`,
and `filename_index`
Key node types include `NODE_SCOPE` (new variable scope),
`NODE_STMTS` (statement sequence), `NODE_IF`, `NODE_WHILE`,
`NODE_CALL` (method call), `NODE_DEF` (method definition),
`NODE_CLASS`, `NODE_RESCUE`, `NODE_ENSURE`, etc. See
`mrbgems/mruby-compiler/core/node.h` for the complete list.
### Local Variable Tracking
Local variables are tracked per-scope during parsing:
- `local_add(sym)`: register a new local variable in current scope
- `local_var_p(sym)`: check if a symbol is a local variable (affects
whether an identifier is parsed as a method call or variable
reference)
## Stage 2: Code Generator
The code generator (`mrbgems/mruby-compiler/core/codegen.c`) walks
the AST and emits bytecode into `mrb_irep` structures.
### Codegen Scope
Each lexical scope (method, block, class body) has its own
`codegen_scope`:
```text
codegen_scope
+-- sp current register index (stack pointer)
+-- pc current instruction count
+-- nlocals number of local variables
+-- nregs maximum register index used
+-- lv local variable list
+-- iseq[] instruction sequence (grows dynamically)
+-- pool[] literal pool (strings, numbers)
+-- syms[] symbol table (method/variable names)
+-- reps[] child ireps (nested methods/blocks)
+-- catch_table[] exception handler entries
+-- loop current loop context stack
+-- prev parent scope
+-- mscope true if method/module/class scope
```
Scopes nest for blocks, method definitions, and class/module bodies.
Each scope produces one `mrb_irep`.
### Register Allocation
The code generator uses a simple stack-based register allocator:
- Register 0 is always `self`
- Registers 1..nlocals-1 are local variables (in declaration order)
- Registers nlocals..nregs-1 are temporaries
`push()` increments `sp` and tracks the high-water mark in `nregs`.
`pop()` decrements `sp`. The allocator is linear - it does not
reuse temporaries within an expression.
### Instruction Emission
Instructions are emitted via helper functions:
- `genop_0(opcode)`: no operands
- `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)
- `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
### Peephole Optimization
The code generator performs limited peephole optimizations, such as
removing redundant `OP_MOVE` instructions and combining consecutive
literal loads. Optimization is disabled at jump targets and when
`no_optimize` is set in the compilation context.
### Loop Context
Loop constructs (`while`, `until`, `for`, blocks) push a
`loopinfo` structure that tracks jump destinations:
- `pc0`: destination for `next`
- `pc1`: destination for `redo`
- `pc2`: destination for `break`
Loop types (`LOOP_NORMAL`, `LOOP_BLOCK`, `LOOP_FOR`, `LOOP_BEGIN`,
`LOOP_RESCUE`) determine how `break`/`next`/`redo` behave.
## IRep Structure
The compiled bytecode is stored in `mrb_irep` (Instruction
REPresentation):
```text
mrb_irep
+-- iseq[] instruction sequence (mrb_code array)
+-- pool[] literal pool (mrb_irep_pool entries)
+-- syms[] symbol table (mrb_sym array)
+-- reps[] child ireps (nested scopes)
+-- lv[] local variable names (for debugging)
+-- nlocals local variable count
+-- nregs register count (locals + temporaries)
+-- ilen instruction count
+-- plen pool entry count
+-- slen symbol count
+-- rlen child irep count
+-- clen catch handler count
+-- debug_info source file/line mapping
```
### Literal Pool
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 |
The code generator deduplicates pool entries: identical strings
and equal numeric values share the same pool index.
### Catch Handler Table
Exception handler entries are appended after the instruction
sequence in memory:
```text
mrb_irep_catch_handler
+-- type MRB_CATCH_RESCUE (0) or MRB_CATCH_ENSURE (1)
+-- begin[4] start PC of protected range
+-- end[4] end PC of protected range
+-- target[4] jump target when handler fires
```
During exception unwinding, handlers are searched in reverse order
(last to first) for the current PC.
## Operand Encoding
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 |
| `OP_EXT2` | Second operand (b) becomes 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 |
See [opcode.md](opcode.md) for the full instruction table.
## 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:
```text
Bits 23 no-block flag
Bits 18-22 required argument count (5 bits, 0-31)
Bits 13-17 optional argument count (5 bits, 0-31)
Bit 12 rest argument flag (*args)
Bits 7-11 post-rest argument count (5 bits, 0-31)
Bits 2-6 keyword argument count (5 bits, 0-31)
Bit 1 keyword rest flag (**kwargs)
Bit 0 block argument flag (&block)
```
Example: `def foo(a, b=1, *rest, &block)` produces an aspec with
1 required, 1 optional, rest flag set, and block flag set.
## Presym: Compile-Time Symbols
The presym system pre-allocates symbol IDs at build time for
frequently used method names and operators. This avoids runtime
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` |
## Binary Format (.mrb)
Precompiled bytecode is stored in the RITE binary format:
```text
Header: "RITE" magic + version ("0400") + CRC + size
Section IREP: instruction sequences, pools, symbols
Section DBG: debug info (optional, filename/line mapping)
Section LVAR: local variable names (optional)
Footer: "END\0"
```
Loading functions:
- `mrb_load_irep(mrb, bin)`: load and execute from byte array
- `mrb_load_irep_buf(mrb, buf, len)`: load with explicit size
(safer)
- `mrb_read_irep(mrb, bin)`: load without executing (returns
`mrb_irep*`)
- `mrb_load_irep_file(mrb, fp)`: load from file
The `mrbc` command-line tool performs ahead-of-time compilation:
```shell
mrbc -o output.mrb source.rb # binary format
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) |
## 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 |
-357
View File
@@ -1,357 +0,0 @@
<!-- summary: Garbage Collector Internals -->
# Garbage Collector Internals
This document describes the internals of mruby's garbage collector
for developers working on `src/gc.c` and related code.
**Read this if you are:** modifying core data structures that hold
object references (and need to add write barriers), debugging
memory leaks or GC-related crashes, tuning GC performance for an
embedded target, or working on the GC code itself.
**For user-facing GC docs**, see
[gc-arena-howto.md](../guides/gc-arena-howto.md) (arena usage in C
extensions) and [memory.md](../guides/memory.md) (heap regions).
## Overview
mruby uses a **tri-color incremental mark-and-sweep** garbage collector
with an optional **generational mode**. The collector runs in small
incremental steps between VM instruction execution, avoiding long
pauses.
## Tri-Color Model
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 |
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
XORing the white bits. This avoids recoloring all live objects at
cycle boundaries, which is an O(1) operation instead of O(n).
```c
#define is_dead(s, o) \
(((o)->gc_color & other_white_part(s) & GC_WHITES) || \
(o)->tt == MRB_TT_FREE)
```
An object is dead if it still carries the previous cycle's white color.
## Heap Structure
### Heap Pages
Objects are allocated from fixed-size heap pages:
```text
mrb_heap_page
+-- freelist linked list of free slots
+-- next next page in heap list
+-- free_next next page with free slots
+-- old old generation flag (generational mode)
+-- region true if carved from a contiguous region
+-- objects[MRB_HEAP_PAGE_SIZE] RVALUE array (default 1024)
```
Each page holds `MRB_HEAP_PAGE_SIZE` objects (default 1024). On
64-bit systems, a page is approximately 40 KB (40 bytes per slot).
### RVALUE Union
All mruby object types share the same slot size via a C union:
```text
RVALUE = union of {
RBasic, RObject, RClass, RString, RArray, RHash,
RRange, RData, RProc, REnv, RFiber, RException, ...
struct { RBasic header; RVALUE *next; } (free slot)
}
```
Free slots use the union space for a freelist pointer.
### Freelist
Each page maintains a singly-linked freelist of available slots.
Allocation pops from the freelist; deallocation during sweep
prepends to the freelist. The GC tracks pages with free slots in
`gc->free_heaps` for fast allocation.
### Heap Regions
For embedded systems with fixed memory banks, `mrb_gc_add_region()`
carves heap pages from a user-provided contiguous buffer:
```c
static uint8_t heap_buf[64 * 1024];
mrb_gc_add_region(mrb, heap_buf, sizeof(heap_buf));
```
Region pages are never freed by the GC (even if all objects die).
When region pages are exhausted, allocation falls back to `malloc()`.
## GC Phases
The GC operates as a three-state machine:
```text
GC_STATE_ROOT --> GC_STATE_MARK --> GC_STATE_SWEEP --> GC_STATE_ROOT
```
### Root Scan (GC_STATE_ROOT)
Marks objects directly reachable from the VM:
1. Global variables (`mrb_gc_mark_gv`)
2. GC arena (`gc->arena[0..arena_idx-1]`)
3. All built-in classes (Object, Class, Module, etc.)
4. Top-level self (`mrb->top_self`)
5. Current exception (`mrb->exc`)
6. Execution contexts (VM stacks, call info stacks, active fibers)
7. Task queues (if `MRB_USE_TASK_SCHEDULER` is defined)
After root scanning, the white color is flipped.
### 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:
```text
limit = (GC_STEP_SIZE / 100) * step_ratio
```
With default `step_ratio = 200` and `GC_STEP_SIZE = 1024`, the
limit is 2048 objects per step.
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)
Iterates through heap pages. For each object:
- If dead (previous cycle's white): call `obj_free()`, return
slot to freelist
- If alive: paint with current white for the next cycle
Sweep is also incremental: `gc->sweeps` tracks the current page
position between steps.
## Gray Stack
The gray stack is a fixed-size array of object pointers:
```c
struct RBasic *gray_stack[MRB_GRAY_STACK_SIZE]; /* default 1024 */
size_t gray_stack_top;
mrb_bool gray_overflow;
```
When the stack overflows, `gray_overflow` is set to `TRUE`. During
marking, `gc_gray_rescan()` scans the entire heap to find any gray
objects that could not be pushed. This guarantees correctness at the
cost of a full heap scan.
## Write Barriers
During incremental marking, a black (fully marked) object storing
a reference to a white (unmarked) object creates a dangerous edge
that could lead to premature collection. Write barriers prevent this.
### Field Write Barrier
Used when assigning a specific field:
```c
mrb_field_write_barrier(mrb, parent, child);
```
If `parent` is black and `child` is white:
- During marking or generational mode: paint `child` gray (add to
gray stack for scanning)
- During sweep: paint `parent` with current white (demote it for
next cycle)
### General Write Barrier
Used when an object has been modified but the specific child is
not known:
```c
mrb_write_barrier(mrb, obj);
```
Paints `obj` gray and pushes it onto the gray stack for re-scanning.
## GC Arena
The arena protects newly created objects from collection before
they are stored in a reachable location. Every `mrb_obj_alloc()`
automatically pushes the new object onto the arena.
C extensions must save and restore the arena index when creating
many temporary objects to prevent arena overflow:
```c
int ai = mrb_gc_arena_save(mrb);
/* create temporary objects */
mrb_gc_arena_restore(mrb, ai);
```
### Fixed vs Dynamic Arena
- **Dynamic** (default): arena grows by 1.5x when full. Risk of
unbounded growth if arena is not properly managed.
- **Fixed** (`MRB_GC_FIXED_ARENA`): raises an exception on overflow.
Arena size is `MRB_GC_ARENA_SIZE` (default 100).
### Permanent Registration
For long-lived C objects that must survive indefinitely:
```c
mrb_gc_register(mrb, obj); /* add to permanent root */
mrb_gc_unregister(mrb, obj); /* remove from root */
```
These store objects in a global array that is always marked as
part of the root set.
See [gc-arena-howto.md](../guides/gc-arena-howto.md) for detailed
usage patterns.
## Generational Mode
When enabled (default, unless `MRB_GC_TURN_OFF_GENERATIONAL` is
defined), the GC classifies objects into young and old generations.
### Minor GC
Only processes young objects. Pages where all objects are old are
marked with `page->old = TRUE` and skipped entirely during sweep.
Minor GC always runs to completion in a single step.
### Major GC
A full mark-and-sweep cycle that processes all objects. Triggered
when `gc->live > gc->oldgen_threshold`. Major GC runs
incrementally, like the non-generational collector.
After a major GC completes, the collector reverts to minor GC mode.
The old-generation threshold is recalculated:
```text
oldgen_threshold = live_after_mark * MAJOR_GC_INC_RATIO / 100
```
With `MAJOR_GC_INC_RATIO = 120`, a major GC triggers when live
objects exceed 120% of the last major GC's survivors.
### Transitioning Between Modes
```c
mrb_gc_generational_mode_set(mrb, TRUE); /* enable */
mrb_gc_generational_mode_set(mrb, FALSE); /* disable */
```
From Ruby: `GC.generational_mode = true/false`.
## Object Allocation
`mrb_obj_alloc()` is the core allocation function:
1. If `MRB_GC_STRESS` is defined, run a full GC
2. If `gc->live >= gc->threshold`, run `mrb_incremental_gc()`
3. Ensure arena has space (`gc_arena_keep`)
4. Pop an object from the freelist of `gc->free_heaps`
5. If no free pages, allocate a new page (`add_heap`)
6. Initialize the object (zero fill, set type and class)
7. Paint with current white color
8. Push onto arena (`gc_protect`)
9. Increment `gc->live`
## Object Freeing
`obj_free()` performs type-specific cleanup:
- **Objects/Exceptions**: free instance variable tables
- **Classes**: free method tables and instance variable tables
- **Arrays**: free heap buffer (if not embedded/shared)
- **Hashes**: free hash table
- **Strings**: free heap buffer (if not embedded/shared)
- **Data objects**: call user-provided `dfree` callback
- **Procs**: decrement irep reference count
- **Fibers**: free context (stacks)
The object's type is set to `MRB_TT_FREE` after freeing.
## Triggering GC
### Automatic
GC runs automatically when `gc->live >= gc->threshold` during
object allocation. After each cycle:
```text
threshold = (live_after_mark / 100) * interval_ratio
minimum: GC_STEP_SIZE (1024)
```
With default `interval_ratio = 200`, GC triggers when live objects
roughly double.
### Manual
```c
mrb_full_gc(mrb); /* force complete GC cycle */
mrb_garbage_collect(mrb); /* public API wrapper */
```
From Ruby: `GC.start`.
## Configuration
### 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 |
### Runtime
From Ruby code:
```ruby
GC.interval_ratio = 200 # threshold = live * ratio / 100
GC.step_ratio = 200 # objects per incremental step
GC.generational_mode = true
GC.start # force full GC
GC.enable # re-enable GC
GC.disable # disable GC
```
## Source Files
| 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 |
+127 -195
View File
@@ -1,206 +1,138 @@
<!-- summary: About mruby Virtual Machine Instructions -->
# mruby Bytecode
# The new bytecode
mruby uses 8-bit instruction opcodes. Each instruction is a single byte,
allowing up to 256 opcodes. Instructions can take 0 to 3 operands.
We will reimplement the VM to use 8bit instruction code. By
bytecode, we mean real byte code. The whole purpose is
reducing the memory consumption of mruby VM.
## Operands
# Instructions
The size of operands can be either 8 bits, 16 bits, or 24 bits.
In the instruction table below, the operand type field describes
the size of each operand.
Instructions are bytes. There can be 256 instructions. Currently, we
have 106 instructions. Instructions can take 0 to 3 operands.
- `Z`: no operand
- `B`: 8 bits
- `S`: 16 bits
- `W`: 24 bits
## operands
If the first and second operands are of type `B` (8 bits), they may be
extended to 16 bits by the operand extension instruction immediately
The size of operands can be either 8bits, 16bits or 24bits.
In the table.1 below, the third field describes the size
of operands.
- B: 8bit
- S: 16bit
- W: 24bit
If the first and second operands are of type `B` (8bits), they may be
extended to 16bits by the operand extension instruction immediately
preceding them.
See also `OP_EXT1`, `OP_EXT2` and `OP_EXT3`.
## Instruction Table
## table.1 Instruction Table
| No. | Instruction Name | Operand type | Semantics |
| --: | ---------------- | ------------ | ----------------------------------------------------------------- |
| 0 | `OP_NOP` | `Z` | no operation |
| 1 | `OP_MOVE` | `BB` | `R[a] = R[b]` |
| 2 | `OP_LOADL` | `BB` | `R[a] = Pool[b]` |
| 3 | `OP_LOADI8` | `BB` | `R[a] = mrb_int(b)` |
| 4 | `OP_LOADINEG` | `BB` | `R[a] = mrb_int(-b)` |
| 5 | `OP_LOADI__1` | `B` | `R[a] = mrb_int(-1)` |
| 6 | `OP_LOADI_0` | `B` | `R[a] = mrb_int(0)` |
| 7 | `OP_LOADI_1` | `B` | `R[a] = mrb_int(1)` |
| 8 | `OP_LOADI_2` | `B` | `R[a] = mrb_int(2)` |
| 9 | `OP_LOADI_3` | `B` | `R[a] = mrb_int(3)` |
| 10 | `OP_LOADI_4` | `B` | `R[a] = mrb_int(4)` |
| 11 | `OP_LOADI_5` | `B` | `R[a] = mrb_int(5)` |
| 12 | `OP_LOADI_6` | `B` | `R[a] = mrb_int(6)` |
| 13 | `OP_LOADI_7` | `B` | `R[a] = mrb_int(7)` |
| 14 | `OP_LOADI16` | `BS` | `R[a] = mrb_int(b)` |
| 15 | `OP_LOADI32` | `BSS` | `R[a] = mrb_int((b<<16)+c)` |
| 16 | `OP_LOADSYM` | `BB` | `R[a] = Syms[b]` |
| 17 | `OP_LOADNIL` | `B` | `R[a] = nil` |
| 18 | `OP_LOADSELF` | `B` | `R[a] = self` |
| 19 | `OP_LOADTRUE` | `B` | `R[a] = true` |
| 20 | `OP_LOADFALSE` | `B` | `R[a] = false` |
| 21 | `OP_GETGV` | `BB` | `R[a] = getglobal(Syms[b])` |
| 22 | `OP_SETGV` | `BB` | `setglobal(Syms[b], R[a])` |
| 23 | `OP_GETSV` | `BB` | `R[a] = Special[Syms[b]]` |
| 24 | `OP_SETSV` | `BB` | `Special[Syms[b]] = R[a]` |
| 25 | `OP_GETIV` | `BB` | `R[a] = ivget(Syms[b])` |
| 26 | `OP_SETIV` | `BB` | `ivset(Syms[b],R[a])` |
| 27 | `OP_GETCV` | `BB` | `R[a] = cvget(Syms[b])` |
| 28 | `OP_SETCV` | `BB` | `cvset(Syms[b],R[a])` |
| 29 | `OP_GETCONST` | `BB` | `R[a] = constget(Syms[b])` |
| 30 | `OP_SETCONST` | `BB` | `constset(Syms[b],R[a])` |
| 31 | `OP_GETMCNST` | `BB` | `R[a] = R[a]::Syms[b]` |
| 32 | `OP_SETMCNST` | `BB` | `R[a+1]::Syms[b] = R[a]` |
| 33 | `OP_GETUPVAR` | `BBB` | `R[a] = uvget(b,c)` |
| 34 | `OP_SETUPVAR` | `BBB` | `uvset(b,c,R[a])` |
| 35 | `OP_GETIDX` | `B` | `R[a] = R[a][R[a+1]]` |
| 36 | `OP_GETIDX0` | `BB` | `R[a] = R[b][0]` |
| 37 | `OP_SETIDX` | `B` | `R[a][R[a+1]] = R[a+2]` |
| 38 | `OP_JMP` | `S` | `pc += a` |
| 39 | `OP_JMPIF` | `BS` | `if R[a] pc += b` |
| 40 | `OP_JMPNOT` | `BS` | `if !R[a] pc += b` |
| 41 | `OP_JMPNIL` | `BS` | `if R[a]==nil pc += b` |
| 42 | `OP_JMPUW` | `S` | `unwind_and_jump_to(a)` |
| 43 | `OP_EXCEPT` | `B` | `R[a] = exc` |
| 44 | `OP_RESCUE` | `BB` | `R[b] = R[a].isa?(R[b])` |
| 45 | `OP_RAISEIF` | `B` | `raise(R[a]) if R[a]` |
| 46 | `OP_MATCHERR` | `B` | `raise NoMatchingPatternError unless R[a]` |
| 47 | `OP_SSEND` | `BBB` | `R[a] = self.send(Syms[b],R[a+1]..,R[a+n+1]:R[a+n+2]..) (c=n\ |
| 48 | `OP_SSEND0` | `BB` | `R[a] = self.send(Syms[b])` (no args) |
| 49 | `OP_SSENDB` | `BBB` | `R[a] = self.send(Syms[b],R[a+1]..,&R[a+n+2k+1])` |
| 50 | `OP_SEND` | `BBB` | `R[a] = R[a].send(Syms[b],R[a+1]..,R[a+n+1]:R[a+n+2]..) (c=n\ |
| 51 | `OP_SEND0` | `BB` | `R[a] = R[a].send(Syms[b])` (no args) |
| 52 | `OP_SENDB` | `BBB` | `R[a] = R[a].send(Syms[b],R[a+1]..,&R[a+n+2k+1])` |
| 53 | `OP_CALL` | `Z` | `self.call(*, **, &)` (tailcall) |
| 54 | `OP_BLKCALL` | `BB` | `R[a] = R[a].call(R[a+1],...,R[a+b])` (direct block call) |
| 55 | `OP_SUPER` | `BB` | `R[a] = super(R[a+1],...,R[a+b+1])` |
| 56 | `OP_ARGARY` | `BS` | `R[a] = argument array (16=m5:r1:m5:d1:lv4)` |
| 57 | `OP_ENTER` | `W` | `arg setup according to flags (24=n1:m5:o5:r1:m5:k5:d1:b1)` |
| 58 | `OP_KEY_P` | `BB` | `R[a] = kdict.key?(Syms[b])` |
| 59 | `OP_KEYEND` | `Z` | `raise unless kdict.empty?` |
| 60 | `OP_KARG` | `BB` | `R[a] = kdict[Syms[b]]; kdict.delete(Syms[b])` |
| 61 | `OP_RETURN` | `B` | `return R[a]` (normal) |
| 62 | `OP_RETURN_BLK` | `B` | `return R[a]` (in-block return) |
| 63 | `OP_RETSELF` | `Z` | `return self` |
| 64 | `OP_RETNIL` | `Z` | `return nil` |
| 65 | `OP_RETTRUE` | `Z` | `return true` |
| 66 | `OP_RETFALSE` | `Z` | `return false` |
| 67 | `OP_BREAK` | `B` | `break R[a]` |
| 68 | `OP_BLKPUSH` | `BS` | `R[a] = block (16=m5:r1:m5:d1:lv4)` |
| 69 | `OP_ADD` | `B` | `R[a] = R[a] + R[a+1]` |
| 70 | `OP_ADDI` | `BB` | `R[a] = R[a] + mrb_int(b)` |
| 71 | `OP_SUB` | `B` | `R[a] = R[a] - R[a+1]` |
| 72 | `OP_SUBI` | `BB` | `R[a] = R[a] - mrb_int(b)` |
| 73 | `OP_ADDILV` | `BBB` | `R[a] = R[a] + mrb_int(c)` (with local variable fallback) |
| 74 | `OP_SUBILV` | `BBB` | `R[a] = R[a] - mrb_int(c)` (with local variable fallback) |
| 75 | `OP_MUL` | `B` | `R[a] = R[a] * R[a+1]` |
| 76 | `OP_DIV` | `B` | `R[a] = R[a] / R[a+1]` |
| 77 | `OP_EQ` | `B` | `R[a] = R[a] == R[a+1]` |
| 78 | `OP_LT` | `B` | `R[a] = R[a] < R[a+1]` |
| 79 | `OP_LE` | `B` | `R[a] = R[a] <= R[a+1]` |
| 80 | `OP_GT` | `B` | `R[a] = R[a] > R[a+1]` |
| 81 | `OP_GE` | `B` | `R[a] = R[a] >= R[a+1]` |
| 82 | `OP_ARRAY` | `BB` | `R[a] = ary_new(R[a],R[a+1]..R[a+b])` |
| 83 | `OP_ARRAY2` | `BBB` | `R[a] = ary_new(R[b],R[b+1]..R[b+c])` |
| 84 | `OP_ARYCAT` | `B` | `ary_cat(R[a],R[a+1])` |
| 85 | `OP_ARYPUSH` | `BB` | `ary_push(R[a],R[a+1]..R[a+b])` |
| 86 | `OP_ARYSPLAT` | `B` | `R[a] = ary_splat(R[a])` |
| 87 | `OP_AREF` | `BBB` | `R[a] = R[b][c]` |
| 88 | `OP_ASET` | `BBB` | `R[b][c] = R[a]` |
| 89 | `OP_APOST` | `BBB` | `*R[a],R[a+1]..R[a+c] = R[a][b..]` |
| 90 | `OP_INTERN` | `B` | `R[a] = intern(R[a])` |
| 91 | `OP_SYMBOL` | `BB` | `R[a] = intern(Pool[b])` |
| 92 | `OP_STRING` | `BB` | `R[a] = str_dup(Pool[b])` |
| 93 | `OP_STRCAT` | `B` | `str_cat(R[a],R[a+1])` |
| 94 | `OP_HASH` | `BB` | `R[a] = hash_new(R[a],R[a+1]..R[a+b*2-1])` |
| 95 | `OP_HASHADD` | `BB` | `hash_push(R[a],R[a+1]..R[a+b*2])` |
| 96 | `OP_HASHCAT` | `B` | `R[a] = hash_cat(R[a],R[a+1])` |
| 97 | `OP_LAMBDA` | `BB` | `R[a] = lambda(Irep[b],L_LAMBDA)` |
| 98 | `OP_BLOCK` | `BB` | `R[a] = lambda(Irep[b],L_BLOCK)` |
| 99 | `OP_METHOD` | `BB` | `R[a] = lambda(Irep[b],L_METHOD)` |
| 100 | `OP_RANGE_INC` | `B` | `R[a] = range_new(R[a],R[a+1],FALSE)` |
| 101 | `OP_RANGE_EXC` | `B` | `R[a] = range_new(R[a],R[a+1],TRUE)` |
| 102 | `OP_OCLASS` | `B` | `R[a] = ::Object` |
| 103 | `OP_CLASS` | `BB` | `R[a] = newclass(R[a],Syms[b],R[a+1])` |
| 104 | `OP_MODULE` | `BB` | `R[a] = newmodule(R[a],Syms[b])` |
| 105 | `OP_EXEC` | `BB` | `R[a] = blockexec(R[a],Irep[b])` |
| 106 | `OP_DEF` | `BB` | `R[a].newmethod(Syms[b],R[a+1]); R[a] = Syms[b]` |
| 107 | `OP_TDEF` | `BBB` | `target_class.newmethod(Syms[b],Irep[c]); R[a] = Syms[b]` |
| 108 | `OP_SDEF` | `BBB` | `R[a].singleton_class.newmethod(Syms[b],Irep[c]); R[a] = Syms[b]` |
| 109 | `OP_ALIAS` | `BB` | `alias_method(target_class,Syms[a],Syms[b])` |
| 110 | `OP_UNDEF` | `B` | `undef_method(target_class,Syms[a])` |
| 111 | `OP_SCLASS` | `B` | `R[a] = R[a].singleton_class` |
| 112 | `OP_TCLASS` | `B` | `R[a] = target_class` |
| 113 | `OP_DEBUG` | `BBB` | `print a,b,c` |
| 114 | `OP_ERR` | `B` | `raise(LocalJumpError, Pool[a])` |
| 115 | `OP_EXT1` | `Z` | make 1st operand (a) 16 bit |
| 116 | `OP_EXT2` | `Z` | make 2nd operand (b) 16 bit |
| 117 | `OP_EXT3` | `Z` | make 1st and 2nd operands 16 bit |
| 118 | `OP_STOP` | `Z` | stop VM |
## Notes
### OP_SEND0 / OP_SSEND0
These are optimized versions of `OP_SEND` / `OP_SSEND` for zero-argument
method calls (no operand `c` needed).
### OP_RETSELF / OP_RETNIL / OP_RETTRUE / OP_RETFALSE
These are optimized return instructions that avoid loading a value into
a register before returning. Common patterns like `attr_reader` methods
(`return self.@x`) and predicate methods (`return true`/`return false`)
benefit from these specialized opcodes.
### OP_BLKCALL
Direct block invocation that bypasses method dispatch. Used when calling
a block argument directly (e.g., `yield` or `block.call`).
### OP_ADDILV / OP_SUBILV
Optimized integer increment/decrement that keeps operands for method
call fallback when the receiver is not a Fixnum.
### OP_TDEF / OP_SDEF
Optimized method definition. `OP_TDEF` defines a method on the
`target_class` directly from an irep without creating an intermediate
`RProc`. `OP_SDEF` does the same for singleton methods.
### OP_MATCHERR
Raises `NoMatchingPatternError` when a pattern match fails. Used by
the `case`/`in` pattern matching syntax.
### OP_GETIDX / OP_GETIDX0 / OP_SETIDX Optimization
These instructions optimize `[]` and `[]=` access for Array, Hash, and String.
**OP_GETIDX** uses direct function calls:
- `Array`: `mrb_ary_entry()` (integer index only)
- `Hash`: `mrb_hash_get()`
- `String`: `mrb_str_aref()` (integer/string/range index)
**OP_GETIDX0** is a specialized variant for index 0 (e.g., `ary[0]`).
**OP_SETIDX** uses direct function calls:
- `Array`: `mrb_ary_set()` (integer index only)
- `Hash`: `mrb_hash_set()`
**Fallback to method dispatch** occurs when:
- Object is a subclass (e.g., `MyArray < Array`)
- Object has a singleton class (singleton methods defined)
- Index type is not supported (e.g., non-integer for Array)
This allows subclasses to override `[]`/`[]=` while base classes remain optimized.
| No. | Instruction Name | Operand type | Semantics
| --: | ---------------- | ------------ | ---------------
| 0 | `OP_NOP` | `-` | `no operation`
| 1 | `OP_MOVE` | `BB` | `R(a) = R(b)`
| 2 | `OP_LOADL` | `BB` | `R(a) = Pool(b)`
| 3 | `OP_LOADI8` | `BB` | `R(a) = mrb_int(b)`
| 4 | `OP_LOADINEG` | `BB` | `R(a) = mrb_int(-b)`
| 5 | `OP_LOADI__1` | `B` | `R(a) = mrb_int(-1)`
| 6 | `OP_LOADI_0` | `B` | `R(a) = mrb_int(0)`
| 7 | `OP_LOADI_1` | `B` | `R(a) = mrb_int(1)`
| 8 | `OP_LOADI_2` | `B` | `R(a) = mrb_int(2)`
| 9 | `OP_LOADI_3` | `B` | `R(a) = mrb_int(3)`
| 10 | `OP_LOADI_4` | `B` | `R(a) = mrb_int(4)`
| 11 | `OP_LOADI_5` | `B` | `R(a) = mrb_int(5)`
| 12 | `OP_LOADI_6` | `B` | `R(a) = mrb_int(6)`
| 13 | `OP_LOADI_7` | `B` | `R(a) = mrb_int(7)`
| 14 | `OP_LOADI16` | `BS` | `R(a) = mrb_int(b)`
| 15 | `OP_LOADI32` | `BSS` | `R(a) = mrb_int((b<<16)+c)`
| 16 | `OP_LOADSYM` | `BB` | `R(a) = Syms(b)`
| 17 | `OP_LOADNIL` | `B` | `R(a) = nil`
| 18 | `OP_LOADSELF` | `B` | `R(a) = self`
| 19 | `OP_LOADT` | `B` | `R(a) = true`
| 20 | `OP_LOADF` | `B` | `R(a) = false`
| 21 | `OP_GETGV` | `BB` | `R(a) = getglobal(Syms(b))`
| 22 | `OP_SETGV` | `BB` | `setglobal(Syms(b), R(a))`
| 23 | `OP_GETSV` | `BB` | `R(a) = Special[Syms(b)]`
| 24 | `OP_SETSV` | `BB` | `Special[Syms(b)] = R(a)`
| 25 | `OP_GETIV` | `BB` | `R(a) = ivget(Syms(b))`
| 26 | `OP_SETIV` | `BB` | `ivset(Syms(b),R(a))`
| 27 | `OP_GETCV` | `BB` | `R(a) = cvget(Syms(b))`
| 28 | `OP_SETCV` | `BB` | `cvset(Syms(b),R(a))`
| 29 | `OP_GETCONST` | `BB` | `R(a) = constget(Syms(b))`
| 30 | `OP_SETCONST` | `BB` | `constset(Syms(b),R(a))`
| 31 | `OP_GETMCNST` | `BB` | `R(a) = R(a)::Syms(b)`
| 32 | `OP_SETMCNST` | `BB` | `R(a+1)::Syms(b) = R(a)`
| 33 | `OP_GETUPVAR` | `BBB` | `R(a) = uvget(b,c)`
| 34 | `OP_SETUPVAR` | `BBB` | `uvset(b,c,R(a))`
| 35 | `OP_GETIDX` | `B` | `R(a) = R(a)[R(a+1)]`
| 36 | `OP_SETIDX` | `B` | `R(a)[R(a+1)] = R(a+2)`
| 37 | `OP_JMP` | `S` | `pc+=a`
| 38 | `OP_JMPIF` | `BS` | `if R(a) pc+=b`
| 39 | `OP_JMPNOT` | `BS` | `if !R(a) pc+=b`
| 40 | `OP_JMPNIL` | `BS` | `if R(a)==nil pc+=b`
| 41 | `OP_JMPUW` | `S` | `unwind_and_jump_to(a)`
| 42 | `OP_EXCEPT` | `B` | `R(a) = exc`
| 43 | `OP_RESCUE` | `BB` | `R(b) = R(a).isa?(R(b))`
| 44 | `OP_RAISEIF` | `B` | `raise(R(a)) if R(a)`
| 45 | `OP_SSEND` | `BBB` | `R(a) = self.send(Syms(b),R(a+1)..,R(a+n+1):R(a+n+2)..) (c=n\|k<<4)`
| 46 | `OP_SSENDB` | `BBB` | `R(a) = self.send(Syms(b),R(a+1)..,R(a+n+1):R(a+n+2)..,&R(a+n+2k+1))`
| 47 | `OP_SEND` | `BBB` | `R(a) = R(a).send(Syms(b),R(a+1)..,R(a+n+1):R(a+n+2)..) (c=n\|k<<4)`
| 48 | `OP_SENDB` | `BBB` | `R(a) = R(a).send(Syms(b),R(a+1)..,R(a+n+1):R(a+n+2)..,&R(a+n+2k+1))`
| 49 | `OP_CALL` | `-` | `self.call(*, **, &) (But overlay the current call frame; tailcall)`
| 50 | `OP_SUPER` | `BB` | `R(a) = super(R(a+1),... ,R(a+b+1))`
| 51 | `OP_ARGARY` | `BS` | `R(a) = argument array (16=m5:r1:m5:d1:lv4)`
| 52 | `OP_ENTER` | `W` | `arg setup according to flags (23=m5:o5:r1:m5:k5:d1:b1)`
| 53 | `OP_KEY_P` | `BB` | `R(a) = kdict.key?(Syms(b))`
| 54 | `OP_KEYEND` | `-` | `raise unless kdict.empty?`
| 55 | `OP_KARG` | `BB` | `R(a) = kdict[Syms(b)]; kdict.delete(Syms(b))`
| 56 | `OP_RETURN` | `B` | `return R(a) (normal)`
| 57 | `OP_RETURN_BLK` | `B` | `return R(a) (in-block return)`
| 58 | `OP_BREAK` | `B` | `break R(a)`
| 59 | `OP_BLKPUSH` | `BS` | `R(a) = block (16=m5:r1:m5:d1:lv4)`
| 60 | `OP_ADD` | `B` | `R(a) = R(a)+R(a+1)`
| 61 | `OP_ADDI` | `BB` | `R(a) = R(a)+mrb_int(b)`
| 62 | `OP_SUB` | `B` | `R(a) = R(a)-R(a+1)`
| 63 | `OP_SUBI` | `BB` | `R(a) = R(a)-mrb_int(b)`
| 64 | `OP_MUL` | `B` | `R(a) = R(a)*R(a+1)`
| 65 | `OP_DIV` | `B` | `R(a) = R(a)/R(a+1)`
| 66 | `OP_EQ` | `B` | `R(a) = R(a)==R(a+1)`
| 67 | `OP_LT` | `B` | `R(a) = R(a)<R(a+1)`
| 68 | `OP_LE` | `B` | `R(a) = R(a)<=R(a+1)`
| 69 | `OP_GT` | `B` | `R(a) = R(a)>R(a+1)`
| 70 | `OP_GE` | `B` | `R(a) = R(a)>=R(a+1)`
| 71 | `OP_ARRAY` | `BB` | `R(a) = ary_new(R(a),R(a+1)..R(a+b))`
| 72 | `OP_ARRAY2` | `BBB` | `R(a) = ary_new(R(b),R(b+1)..R(b+c))`
| 73 | `OP_ARYCAT` | `B` | `ary_cat(R(a),R(a+1))`
| 74 | `OP_ARYPUSH` | `BB` | `ary_push(R(a),R(a+1)..R(a+b))`
| 75 | `OP_ARYSPLAT` | `B` | `R(a) = ary_splat(R(a))`
| 76 | `OP_AREF` | `BBB` | `R(a) = R(b)[c]`
| 77 | `OP_ASET` | `BBB` | `R(b)[c] = R(a)`
| 78 | `OP_APOST` | `BBB` | `*R(a),R(a+1)..R(a+c) = R(a)[b..]`
| 79 | `OP_INTERN` | `B` | `R(a) = intern(R(a))`
| 80 | `OP_SYMBOL` | `BB` | `R(a) = intern(Pool(b))`
| 81 | `OP_STRING` | `BB` | `R(a) = str_dup(Pool(b))`
| 82 | `OP_STRCAT` | `B` | `str_cat(R(a),R(a+1))`
| 83 | `OP_HASH` | `BB` | `R(a) = hash_new(R(a),R(a+1)..R(a+b*2-1))`
| 84 | `OP_HASHADD` | `BB` | `hash_push(R(a),R(a+1)..R(a+b*2))`
| 85 | `OP_HASHCAT` | `B` | `R(a) = hash_cat(R(a),R(a+1))`
| 86 | `OP_LAMBDA` | `BB` | `R(a) = lambda(Irep(b),OP_L_LAMBDA)`
| 87 | `OP_BLOCK` | `BB` | `R(a) = lambda(Irep(b),OP_L_BLOCK)`
| 88 | `OP_METHOD` | `BB` | `R(a) = lambda(Irep(b),OP_L_METHOD)`
| 89 | `OP_RANGE_INC` | `B` | `R(a) = range_new(R(a),R(a+1),FALSE)`
| 90 | `OP_RANGE_EXC` | `B` | `R(a) = range_new(R(a),R(a+1),TRUE)`
| 91 | `OP_OCLASS` | `B` | `R(a) = ::Object`
| 92 | `OP_CLASS` | `BB` | `R(a) = newclass(R(a),Syms(b),R(a+1))`
| 93 | `OP_MODULE` | `BB` | `R(a) = newmodule(R(a),Syms(b))`
| 94 | `OP_EXEC` | `BB` | `R(a) = blockexec(R(a),Irep(b))`
| 95 | `OP_DEF` | `BB` | `R(a).newmethod(Syms(b),R(a+1)); R(a) = Syms(b)`
| 96 | `OP_ALIAS` | `BB` | `alias_method(target_class,Syms(a),Syms(b))`
| 97 | `OP_UNDEF` | `B` | `undef_method(target_class,Syms(a))`
| 98 | `OP_SCLASS` | `B` | `R(a) = R(a).singleton_class`
| 99 | `OP_TCLASS` | `B` | `R(a) = target_class`
| 100 | `OP_DEBUG` | `BBB` | `print a,b,c`
| 101 | `OP_ERR` | `B` | `raise(LocalJumpError, Pool(a))`
| 102 | `OP_EXT1` | `-` | `make 1st operand (a) 16bit`
| 103 | `OP_EXT2` | `-` | `make 2nd operand (b) 16bit`
| 104 | `OP_EXT3` | `-` | `make 1st and 2nd operands 16bit`
| 105 | `OP_STOP` | `-` | `stop VM`
-325
View File
@@ -1,325 +0,0 @@
<!-- summary: Virtual Machine Internals -->
# Virtual Machine Internals
This document describes mruby's virtual machine for developers
working on `src/vm.c` and related code.
**Read this if you are:** debugging method dispatch or call frame
issues, working on exception handling, implementing new opcodes,
modifying fiber/coroutine behavior, or optimizing the dispatch loop.
For the instruction set, see [opcode.md](opcode.md). For the
compiler that generates bytecode, see [compiler.md](compiler.md).
## Execution Model
mruby uses a **register-based VM**. Local variables and temporaries
occupy fixed register slots determined at compile time. Each method
call gets its own register window on a shared value stack.
## Execution Context
The VM state is stored in `mrb_context`:
```text
mrb_context
+-- stbase..stend value stack (mrb_value[])
+-- cibase..ciend call info stack (mrb_callinfo[])
+-- ci current call frame pointer
+-- status fiber state
+-- prev previous context (fiber chain)
+-- vmexec VM execution state flag
```
The value stack and call info stack grow independently. Each fiber
has its own `mrb_context`.
### Stack Sizing
- Initial value stack: 128 entries (`STACK_INIT_SIZE`)
- Initial call info stack: 32 entries (`CALLINFO_INIT_SIZE`)
- Growth factor: 1.5x (or 2x with `MRB_STACK_EXTEND_DOUBLING`)
- Minimum growth: 128 entries (`MRB_STACK_GROWTH`)
- Max stack depth: `MRB_STACK_MAX` (0x40000 - 128)
- Max call depth: `MRB_CALL_LEVEL_MAX` (512, or 128 with ASAN)
Exceeding either limit raises `SystemStackError`.
When the value stack is reallocated, all `REnv` objects and
`mrb_callinfo` stack pointers are adjusted by the delta
(`envadjust` function).
## Call Frames
Each method or block call pushes a `mrb_callinfo` frame:
```text
mrb_callinfo
+-- n:4 positional argument count (0-14, 15 = varargs)
+-- nk:4 keyword argument count (0-14, 15 = varargs)
+-- cci call context info (NONE, DIRECT, SKIP, RESUMED)
+-- vis visibility flags (public/private/protected)
+-- mid method symbol
+-- proc current RProc
+-- blk block argument (RProc*)
+-- stack pointer into value stack
+-- pc program counter (bytecode position)
+-- u.env closure environment (REnv*)
+-- u.target_class receiver's class
```
### Stack Layout Per Frame
```text
ci->stack:
[0] self (receiver)
[1..n] positional arguments
[n+1..] keyword argument pairs (key, value, key, value, ...)
[bidx] block argument
[bidx+1..] local variables and temporaries
```
### Argument Count Encoding
The `n` and `nk` fields are 4 bits each (0-15). When `n == 15`,
positional arguments are packed into a single Array in register 1.
When `nk == 15`, keyword arguments are packed into a single Hash.
The block index is calculated by `mrb_bidx(n, nk)`:
```text
if n == 15: n = 1 (array)
if nk == 15: n += 1 (hash)
else: n += nk * 2 (key-value pairs)
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) |
## Dispatch Loop
The main loop in `mrb_vm_run()` decodes and dispatches opcodes.
Two dispatch strategies are available:
- **Computed goto** (default on GCC/Clang): a jump table of label
addresses (`optable[]`) for direct dispatch. Faster due to
better branch prediction.
- **Switch-based** (`MRB_USE_VM_SWITCH_DISPATCH`): a standard
`switch(insn)` statement. Default on MSVC and other compilers.
The dispatch loop is wrapped in `MRB_TRY`/`MRB_CATCH` for exception
handling (see [Exception Handling](#exception-handling)).
## Method Dispatch
When `OP_SEND` (or `OP_SSEND`, `OP_SUPER`) executes:
### 1. Prepare Arguments
Determine argument layout. If argument count < 15, the fast path
uses inline registers. Otherwise, arguments are packed into an
Array (varargs mode).
### 2. Push Call Frame
```c
ci = cipush(mrb, a, CINFO_DIRECT, NULL, NULL, blk, mid, argc);
```
The new frame's stack starts at the previous frame's stack + `a`
(the receiver's register index).
### 3. Method Lookup
The lookup sequence:
1. **Method cache check**: hash table lookup by `(class, mid)`.
Default cache size: `MRB_METHOD_CACHE_SIZE` (256 entries).
2. **Method table walk**: if cache misses, search the receiver's
class method table (`mt`), then walk the superclass chain.
3. **Cache store**: on successful lookup, store in the cache.
The method cache is invalidated when classes are modified
(`mrb_mc_clear_by_class`).
### 4. Invoke
- **Ruby method** (irep-based): extend the stack to `irep->nregs`,
set `ci->pc` to `irep->iseq`, and jump to the new bytecode.
- **C function**: call `func(mrb, recv)` directly, then pop the
call frame and store the return value.
### 5. Visibility Check
Private methods are only callable without an explicit receiver.
Protected methods are callable from the same class hierarchy.
Violations raise `NoMethodError`.
## Exception Handling
### setjmp/longjmp
By default, mruby uses `setjmp`/`longjmp` for exception control
flow:
```c
MRB_TRY(&c_jmp) {
mrb->jmp = &c_jmp;
/* dispatch loop */
}
MRB_CATCH(&c_jmp) {
/* handle exception */
}
MRB_END_EXC(&c_jmp);
```
With `MRB_USE_CXX_EXCEPTION`, C++ `try`/`catch` is used instead.
### Handler Table
Each irep contains a catch handler table (appended after iseq in
memory) with entries for `rescue` and `ensure` blocks:
```text
mrb_irep_catch_handler
+-- type RESCUE (0) or ENSURE (1)
+-- begin[4] start PC of protected range
+-- end[4] end PC of protected range
+-- target[4] jump target when handler matches
```
### Unwinding Process
When an exception occurs:
1. Search the current irep's catch handler table (reverse order)
for a handler covering the current PC
2. If an `ensure` handler is found: execute it (may re-raise)
3. If a `rescue` handler is found: jump to handler code
4. If no handler: pop the call frame (`cipop`) and repeat with
the parent frame
5. `CINFO_DIRECT` frames are destroyed during propagation
## Block and Closure Handling
### REnv (Environment)
Closures capture their enclosing scope's variables through `REnv`:
```text
REnv
+-- stack pointer to captured variable values
+-- cxt owning context (NULL if detached from stack)
+-- mid method symbol
+-- flags length, block index, visibility
```
While the defining scope is active, `REnv::stack` points directly
into the VM value stack (shared). This avoids copying.
### Environment Unsharing
When a closure outlives its defining scope, `mrb_env_unshare()`
copies the captured variables from the stack to a heap-allocated
buffer:
```c
mrb_env_unshare(mrb, env, noraise);
```
After unsharing, `MRB_ENV_CLOSE(env)` sets `cxt = NULL` to indicate
the environment is detached. A write barrier is issued for GC
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 |
## Fiber Switching
Fibers are lightweight coroutines. Each fiber has its own
`mrb_context` with separate value and call info stacks.
### Fiber States
```text
CREATED --> RUNNING --> SUSPENDED --> TERMINATED
| ^
+-----------+
(yield/resume)
TRANSFERRED (via Fiber#transfer)
```
### Context Switch
On `Fiber#resume`:
1. Save current context state
2. Set `mrb->c` to the fiber's context
3. Push arguments onto the fiber's stack
4. Continue execution in the fiber
On `Fiber.yield`:
1. Save fiber context
2. Restore the parent context (`mrb->c = c->prev`)
3. Return yield values to the parent
### Fiber Termination
When a fiber completes (`fiber_terminate`):
1. Unshare any environments that reference the fiber's stack
2. Set status to `TERMINATED`
3. Free the fiber's stacks
4. Switch to the previous context
### C Function Boundary
Fibers cannot yield across C function boundaries. You cannot call
`Fiber.yield` from within a C-implemented method (except via
`mrb_fiber_yield` at return). This is because C call frames cannot
be suspended and resumed.
## GC Integration
The VM saves the arena index at the start of the dispatch loop:
```c
int ai = mrb_gc_arena_save(mrb);
```
After each C function call, the arena is shrunk back:
```c
mrb_gc_arena_shrink(mrb, ai);
```
This prevents temporary objects created by C functions from
accumulating in the arena.
Write barriers are issued when environments are detached or closed,
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 |
+22 -99
View File
@@ -28,11 +28,11 @@ rescue
end
```
#### CRuby
#### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)]
`ZeroDivisionError` is raised.
#### mruby
#### mruby [3.1.0 (2022-05-12)]
`RuntimeError` is raised instead of `ZeroDivisionError`. To re-raise the exception, you have to do:
@@ -64,11 +64,11 @@ end
p Liste.new "foobar"
```
#### CRuby
#### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)]
`[]`
#### mruby
#### mruby [3.1.0 (2022-05-12)]
`ArgumentError` is raised.
@@ -82,13 +82,13 @@ other reflection methods instead.
defined?(Foo)
```
#### CRuby
#### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)]
```
nil
```
#### mruby
#### mruby [3.1.0 (2022-05-12)]
`NameError` is raised.
@@ -101,18 +101,17 @@ of the ISO standard.
alias $a $__a__
```
#### CRuby
#### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)]
`nil`
#### mruby
#### mruby [3.1.0 (2022-05-12)]
Syntax error
## Operator modification
Operators on some of the primitive classes cannot be overridden, as they are
optimized in the VM.
An operator can't be overwritten by the user.
```ruby
class String
@@ -123,21 +122,26 @@ end
'a' + 'b'
```
#### CRuby
#### Ruby [ruby 2.0.0p645 (2015-04-13 revision 50299)]
`ArgumentError` is raised.
The re-defined `+` operator does not accept any arguments.
#### mruby
#### mruby [3.1.0 (2022-05-12)]
`'ab'`
Behavior of the operator wasn't changed.
## `Kernel#binding` is not supported without mruby-binding gem
## `Kernel#binding` is not supported until [3.0.0 (2021-03-05)]
`Kernel#binding` method requires the `mruby-binding` gem (included
in the `metaprog` gembox). Without this gem, `binding` is not
available.
`Kernel#binding` method is not supported.
#### Ruby [ruby 2.5.1p57 (2018-03-29 revision 63029)]
```shell
$ ruby -e 'puts Proc.new {}.binding'
#<Binding:0x00000e9deabb9950>
```
## `nil?` redefinition in conditional expressions
@@ -202,91 +206,10 @@ class C
end
```
#### CRuby
#### Ruby [ruby 3.5.0dev (2025-04-21 85bab61565))]
Prints `:append`.
#### mruby
#### mruby [3.5.0 (2025-04-28)]
Nothing printed (since `include` does not call `append_features` internally).
## No `#hash` call for small hashes
For performance reasons, mruby avoids calling the `#hash` method on keys when a hash table is small. This means that custom `#hash` methods on key objects may not be executed.
## Pattern Matching
Pattern matching is only partially supported in mruby. Currently, only the rightward assignment operator (`=>`) with simple variable binding is implemented.
```ruby
expr => var # Supported: assigns expr to var
```
#### CRuby
Full pattern matching with `case/in` syntax and various pattern types:
```ruby
case [1, 2, 3]
in [a, b, c]
puts "#{a}, #{b}, #{c}" # => "1, 2, 3"
end
case {name: "Alice", age: 30}
in {name:, age:}
puts "#{name} is #{age}" # => "Alice is 30"
end
```
#### mruby
Only rightward assignment with simple variable binding:
```ruby
[1, 2, 3] => x
puts x # => [1, 2, 3]
```
The following are **not supported**:
- `case/in` syntax
- Array patterns: `in [a, b, c]`
- Hash patterns: `in {name:, age:}`
- Guard clauses: `in pattern if condition`
- Pin operator: `in ^variable`
- Find patterns: `in [*, x, *]`
- Alternative patterns: `in pattern1 | pattern2`
- Boolean pattern check: `value in pattern`
Note: mruby does provide `Array#deconstruct` and `Hash#deconstruct_keys` methods for future pattern matching compatibility.
## No Refinements
Module refinements (`refine`, `using`) are not supported in mruby.
## No `Encoding` Class
mruby does not have an `Encoding` class. Strings are treated as
byte sequences by default. UTF-8 aware string operations can be
enabled with the `MRB_UTF8_STRING` compile flag.
## Integer Precision Varies by Boxing Mode
Integer size depends on the value boxing configuration:
| 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 |
Code relying on 64-bit integer precision may behave differently
across configurations. The `mruby-bigint` gem provides
arbitrary-precision integers when included.
## No `ObjectSpace.each_object` by Default
`ObjectSpace` is only available via the `mruby-objectspace` gem
(included in the `stdlib` gembox). Even with the gem,
`ObjectSpace.each_object` has limited functionality compared
to CRuby.
+2 -2
View File
@@ -20,7 +20,7 @@ Keyword arguments are basically separated from ordinal arguments.
Some configuration macros are available:
- `MRB_WORDBOX_NO_INLINE_FLOAT` (formerly `MRB_WORDBOX_NO_FLOAT_TRUNCATE`): by default, float values are packed in the word if possible, but define this macro to allocate float values in the heap.
- `MRB_WORDBOX_NO_FLOAT_TRUNCATE`: by default, float values are packed in the word if possible, but define this macro to allocate float values in the heap.
- `MRB_USE_RO_DATA_P_ETEXT`: define this macro if `_etext` is available on your platform.
- `MRB_NO_DEFAULT_RO_DATA_P`: define this macro to avoid using predefined `mrb_ro_data_p()` function
@@ -156,7 +156,7 @@ Now takes 2 operands and pushes multiple entries to an array.
### Word Boxing
`MRB_WORD_BOXING` now packs floating-point numbers in the word, if the size of `mrb_float` is equal or smaller than the size of `mrb_int` by default.
If the size of `mrb_float` and `mrb_int` are same, the last 2 bits in the `mrb_float` are trimmed and used as flags. If you need full precision, you need to define `MRB_WORDBOX_NO_INLINE_FLOAT` (formerly `MRB_WORDBOX_NO_FLOAT_TRUNCATE`) as described above.
If the size of `mrb_float` and `mrb_int` are same, the last 2 bits in the `mrb_float` are trimmed and used as flags. If you need full precision, you need to define `MRB_WORDBOX_NO_FLOAT_TRUNCATE` as described above.
### NaN Boxing
+1 -1
View File
@@ -17,7 +17,7 @@
## `mruby`
- `-b` only specifies the script is the binary. The files loaded by `-r` are not affected by the option.
- `mruby` now loads compiled binary if the suffix is `.mrb`.
- `mruby` now loads complied binary if the suffix is `.mrb`.
## `mrbc`
-367
View File
@@ -1,367 +0,0 @@
# User visible changes in `mruby4.0` from `mruby3.4`
"**_NOTE_**:" are changes to be aware of.
# The language
## Pattern Matching
mruby now supports pattern matching (case/in) syntax:
- Basic pattern matching with `case`/`in` syntax ([dadfac6](https://github.com/mruby/mruby/commit/dadfac6))
- Array pattern matching ([ec67fd9](https://github.com/mruby/mruby/commit/ec67fd9))
- Hash pattern matching ([2147263](https://github.com/mruby/mruby/commit/2147263))
- Find pattern matching (`[*pre, target, *post]`) ([6c4d98b](https://github.com/mruby/mruby/commit/6c4d98b))
- Pin operator (`^variable`) ([1de6340](https://github.com/mruby/mruby/commit/1de6340))
- Guard clauses (`if`/`unless` conditions) ([07ac110](https://github.com/mruby/mruby/commit/07ac110))
- One-line pattern matching (`expr in pattern`) ([e76ce24](https://github.com/mruby/mruby/commit/e76ce24))
- Brace-less hash pattern support ([e8096bf](https://github.com/mruby/mruby/commit/e8096bf))
## Other Language Changes
- `&nil` in formal parameters to explicitly opt out of block arguments ([b07518e](https://github.com/mruby/mruby/commit/b07518e))
- 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
- **_NOTE_**: `mrb_alloca()` renamed to `mrb_temp_alloc()` ([7fe5c2e](https://github.com/mruby/mruby/commit/7fe5c2e))
- **_NOTE_**: `mruby/ext/io.h` renamed to `mruby/io.h` ([2813f79](https://github.com/mruby/mruby/commit/2813f79))
- `mrb_gc_add_region()` for contiguous heap region support ([072855a](https://github.com/mruby/mruby/commit/072855a))
- `mrb_class_outer()` to get the outer class/module ([3a1b771](https://github.com/mruby/mruby/commit/3a1b771))
- `MRB_ENSURE()` macro for exception-safe cleanup ([3ac682b](https://github.com/mruby/mruby/commit/3ac682b))
- `mrb_time_get_tm()` for accessing struct tm ([daaaafe](https://github.com/mruby/mruby/commit/daaaafe))
- `MRB_OPEN_FAILURE()` macro for checking mrb_open result ([40b0cb9](https://github.com/mruby/mruby/commit/40b0cb9))
- `mrb_print_error()` now handles NULL gracefully ([8e50a45](https://github.com/mruby/mruby/commit/8e50a45))
- `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
All built-in classes and most extension gems now use read-only method
tables stored in `.rodata` instead of heap-allocated hash tables. Method
definitions no longer consume heap memory, significantly reducing memory
footprint for embedded use.
Core classes converted: BasicObject, Object, Module, Class, Kernel,
String, Array, Hash, Numeric, Integer, Float, NilClass, TrueClass,
FalseClass, Range, Symbol, Exception, Proc.
Extension gems converted: mruby-string-ext, mruby-array-ext, mruby-set,
mruby-struct, mruby-class-ext, mruby-numeric-ext, mruby-random,
mruby-kernel-ext, mruby-complex, mruby-rational, mruby-io, mruby-socket,
mruby-method, mruby-metaprog, mruby-time, mruby-hash-ext, mruby-proc-ext,
mruby-symbol-ext, mruby-range-ext, mruby-object-ext.
# GC and Memory
- **_NOTE_**: `MRB_NO_PRESYM` removed; presym is now always enabled ([81689045](https://github.com/mruby/mruby/commit/81689045))
- Replace `gcnext` gray linked list with fixed-size gray stack, reducing per-object overhead ([31fea170](https://github.com/mruby/mruby/commit/31fea170))
- `mrb_gc_add_region()` for providing contiguous memory buffers as GC heap pages ([072855a](https://github.com/mruby/mruby/commit/072855a))
- Chunk-based pool for symbol string allocation ([e05bd8f](https://github.com/mruby/mruby/commit/e05bd8f))
- Reduce `IV_INITIAL_SIZE` from 4 to 2 ([6bd1f51](https://github.com/mruby/mruby/commit/6bd1f51))
- Lossless float encoding using rotation in word boxing ([b6148c8](https://github.com/mruby/mruby/commit/b6148c8))
- Lossless rotation encoding for 32-bit float32 word boxing ([14a5cfb](https://github.com/mruby/mruby/commit/14a5cfb))
- Consolidated irep allocation for .mrb loading ([74fb045](https://github.com/mruby/mruby/commit/74fb045))
- Object shapes (hidden classes) for `MRB_TT_OBJECT` IV storage, sharing key layouts across objects with the same instance variable assignment order ([8d10056](https://github.com/mruby/mruby/commit/8d10056))
# Build & Configuration
- **_NOTE_**: `MRB_WORDBOX_NO_FLOAT_TRUNCATE` renamed to `MRB_WORDBOX_NO_INLINE_FLOAT` (old name still works) ([59e1fe2](https://github.com/mruby/mruby/commit/59e1fe2))
- **_NOTE_**: `MRB_INT64` on 32-bit now requires `MRB_NO_BOXING` (other boxing modes cannot guarantee alignment for heap-allocated 64-bit integers) ([eaaa66b](https://github.com/mruby/mruby/commit/eaaa66b))
- Amalgamation support via `rake amalgam` task ([d995ca2](https://github.com/mruby/mruby/commit/d995ca2))
- New Platform: Cosmopolitan Libc ([#6681](https://github.com/mruby/mruby/pull/6681))
- 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
## New Gems
- **mruby-task**: Cooperative multitasking with preemptive scheduling ([ae0d7a0](https://github.com/mruby/mruby/commit/ae0d7a0))
- **mruby-benchmark**: Benchmarking gem ([2f40f3d](https://github.com/mruby/mruby/commit/2f40f3d))
- **mruby-strftime**: Time#strftime implementation ([b31e22f](https://github.com/mruby/mruby/commit/b31e22f))
## mruby-bin-mirb Improvements
- Custom multi-line editor replacing readline ([527018c](https://github.com/mruby/mruby/commit/527018c))
- Syntax highlighting for keywords, strings, result values, hash key symbols ([624272b](https://github.com/mruby/mruby/commit/624272b), [1713d4a](https://github.com/mruby/mruby/commit/1713d4a))
- Automatic light/dark theme detection via OSC 11 ([db4c8d9](https://github.com/mruby/mruby/commit/db4c8d9))
- Tab completion support ([2f15282](https://github.com/mruby/mruby/commit/2f15282))
- Colored output for prompts and errors ([b36e0b4](https://github.com/mruby/mruby/commit/b36e0b4))
- Auto-indentation and auto-dedent ([d52f318](https://github.com/mruby/mruby/commit/d52f318), [e901b6d](https://github.com/mruby/mruby/commit/e901b6d))
- Command history with Up/Down navigation ([5f85c1b](https://github.com/mruby/mruby/commit/5f85c1b))
- Line numbers in multi-line prompts ([5a3f0e2](https://github.com/mruby/mruby/commit/5a3f0e2))
- UTF-8 multibyte character support ([4a97da3](https://github.com/mruby/mruby/commit/4a97da3))
## mruby-bigint Improvements
- Toom-3 multiplication for large numbers ([99620804](https://github.com/mruby/mruby/commit/99620804))
- Karatsuba multiplication for medium-sized numbers ([85e81072](https://github.com/mruby/mruby/commit/85e81072))
- Balance multiplication for asymmetric operands ([0220ec2b](https://github.com/mruby/mruby/commit/0220ec2b))
- Divide-and-conquer optimization for `to_s` ([990ff90f](https://github.com/mruby/mruby/commit/990ff90f))
- Consolidated mpn layer for low-level limb operations ([9ef3362f](https://github.com/mruby/mruby/commit/9ef3362f))
- Always use 32-bit limbs by default ([c747c77f](https://github.com/mruby/mruby/commit/c747c77f))
## 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))
- **mruby-compiler**: Variable-sized AST nodes for reduced memory usage
- **mruby-compiler**: `no_return_value` context flag for script optimization ([613b03a](https://github.com/mruby/mruby/commit/613b03a))
- `initialize_copy` and `respond_to_missing?` defined as private ([#6708](https://github.com/mruby/mruby/pull/6708))
- Struct keyword argument initialization ([#6574](https://github.com/mruby/mruby/pull/6574))
# Compiler Improvements
- Variable-sized AST nodes for reduced memory consumption ([821b989](https://github.com/mruby/mruby/commit/821b989))
- Pattern matching bytecode optimizations ([21d4135](https://github.com/mruby/mruby/commit/21d4135))
- 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
New super-instructions that fuse common opcode sequences to reduce bytecode size and improve performance:
- `OP_SEND0`/`OP_SSEND0`: Zero-argument method call, avoiding argument count setup ([9123ef4](https://github.com/mruby/mruby/commit/9123ef4))
- `OP_TDEF`/`OP_SDEF`: Fused method definition combining TCLASS/SCLASS+METHOD+DEF into single instruction, saving 4 bytes per method ([8d4f47e](https://github.com/mruby/mruby/commit/8d4f47e))
- `OP_GETIDX0`: Fast path for `array[0]` and `Array#first` access ([680f7ec](https://github.com/mruby/mruby/commit/680f7ec))
- `OP_ADDILV`/`OP_SUBILV`: Local variable increment/decrement fusion for `i += n` patterns ([43f64b9](https://github.com/mruby/mruby/commit/43f64b9))
- `OP_RETSELF`: Single-byte instruction for `return self` pattern ([a71db8c](https://github.com/mruby/mruby/commit/a71db8c))
- `OP_RETNIL`: Single-byte instruction for `return nil` pattern ([64e30bf](https://github.com/mruby/mruby/commit/64e30bf))
- `OP_RETTRUE`/`OP_RETFALSE`: Single-byte instructions for `return true`/`return false` patterns ([0b15727](https://github.com/mruby/mruby/commit/0b15727))
- `OP_MATCHERR`: Pattern matching error with conditional execution ([944168a](https://github.com/mruby/mruby/commit/944168a))
- `OP_BLKCALL`: Direct block call for `yield`, bypassing method dispatch (13-17% faster) ([3aa2872](https://github.com/mruby/mruby/commit/3aa2872))
Other optimizations:
- 1.5x stack growth instead of linear growth for reduced reallocations ([f7988c93](https://github.com/mruby/mruby/commit/f7988c93))
- Skip keyword argument hash duplication ([5970e350](https://github.com/mruby/mruby/commit/5970e350))
# Fixed GitHub Issues
- [#5531](https://github.com/mruby/mruby/issues/5531) Hash recursion detection
- [#6506](https://github.com/mruby/mruby/issues/6506) Constant lookup in singleton class
- [#6507](https://github.com/mruby/mruby/issues/6507) tally multi-values
- [#6508](https://github.com/mruby/mruby/issues/6508) Enumerable#sum index
- [#6509](https://github.com/mruby/mruby/issues/6509) scope_new nregs initialization
- [#6515](https://github.com/mruby/mruby/issues/6515) y.tab.c in repository
- [#6516](https://github.com/mruby/mruby/issues/6516) Private backquote
- [#6554](https://github.com/mruby/mruby/issues/6554) Socket private #initialize
- [#6570](https://github.com/mruby/mruby/issues/6570) instance_eval crash
- [#6613](https://github.com/mruby/mruby/issues/6613) const_added hook during bootstrapping
- [#6635](https://github.com/mruby/mruby/issues/6635), [#6636](https://github.com/mruby/mruby/issues/6636) Colon3 constant lookup
- [#6637](https://github.com/mruby/mruby/issues/6637) arm64 mingw64 builtin setjmp/longjmp
- [#6642](https://github.com/mruby/mruby/issues/6642) Task segfault when sleep called from C
- [#6645](https://github.com/mruby/mruby/issues/6645) Set memory leak from double initialization
- [#6646](https://github.com/mruby/mruby/issues/6646) IO#gets negative length
- [#6647](https://github.com/mruby/mruby/issues/6647) IO#ungetc buffer overflow
- [#6648](https://github.com/mruby/mruby/issues/6648) sprintf buffer overread
- [#6649](https://github.com/mruby/mruby/issues/6649) Array#sort! use-after-realloc
- [#6650](https://github.com/mruby/mruby/issues/6650) Array#fill validation
- [#6652](https://github.com/mruby/mruby/issues/6652) Array comparison use-after-realloc
- [#6657](https://github.com/mruby/mruby/issues/6657) Exception handling for ||= on class variables
- [#6659](https://github.com/mruby/mruby/issues/6659) Super with keyword arguments
- [#6660](https://github.com/mruby/mruby/issues/6660) Regression on struct/array/hash == override with super
- [#6662](https://github.com/mruby/mruby/issues/6662) Array set operations use-after-free
- [#6664](https://github.com/mruby/mruby/issues/6664) Set#flatten memory leak
- [#6666](https://github.com/mruby/mruby/issues/6666) Regexp literal with encoding
- [#6668](https://github.com/mruby/mruby/issues/6668) Method#== for aliased methods and comparison bug
- [#6671](https://github.com/mruby/mruby/issues/6671) Separate inter-gem headers from external API headers
- [#6674](https://github.com/mruby/mruby/issues/6674) Document pattern matching limitations
- [#6675](https://github.com/mruby/mruby/issues/6675) Allow Hash#[] to be aliased again
- [#6687](https://github.com/mruby/mruby/issues/6687) Expand MRB_SYM/MRB_GVSYM support for symbols with special characters
- [#6698](https://github.com/mruby/mruby/issues/6698) Bigint tests fail on architectures other than x86_64 and i386
- [#6701](https://github.com/mruby/mruby/issues/6701) Heap-use-after-free in mrb_vm_exec involving mruby-rational / mruby-bigint
- [#6702](https://github.com/mruby/mruby/issues/6702) mruby-bigint doesn't compile in C++ project
- [#6704](https://github.com/mruby/mruby/issues/6704) Heap-buffer-overflow in mrb_vm_exec via malformed source code
- [#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
- [#6418](https://github.com/mruby/mruby/pull/6418) Add `ls-lint` with GitHub Actions
- [#6492](https://github.com/mruby/mruby/pull/6492) fix a typo, update specs
- [#6493](https://github.com/mruby/mruby/pull/6493) Fix TYPO in memory.md
- [#6495](https://github.com/mruby/mruby/pull/6495) Remove `MRB_ENDIAN_LOHI()` that is no longer in use
- [#6497](https://github.com/mruby/mruby/pull/6497) gha: update `build.yml` try `windows-2025` image
- [#6498](https://github.com/mruby/mruby/pull/6498) Clean up and standardize the pre-commit config
- [#6501](https://github.com/mruby/mruby/pull/6501) Update pre-commit Node.js version to `v22.14.0 LTS`
- [#6502](https://github.com/mruby/mruby/pull/6502) pre-commit: update prettier to the latest version
- [#6503](https://github.com/mruby/mruby/pull/6503) misc: fix typos
- [#6505](https://github.com/mruby/mruby/pull/6505) mrbgems: fix spelling
- [#6510](https://github.com/mruby/mruby/pull/6510) Fixed class method visibility via `module_function`
- [#6511](https://github.com/mruby/mruby/pull/6511) Exclude the external project "lrama" from pre-commit
- [#6513](https://github.com/mruby/mruby/pull/6513) mruby 3.4.0 released
- [#6517](https://github.com/mruby/mruby/pull/6517) core/codegen.c: remove unneeded duplicate semicolon
- [#6518](https://github.com/mruby/mruby/pull/6518) Change mrbc_args.flags bit width from 2 to 3
- [#6519](https://github.com/mruby/mruby/pull/6519) Add `tools/lrama` to `.prettierignore`
- [#6520](https://github.com/mruby/mruby/pull/6520) pre-commit: autoupdate and update node LTS version
- [#6521](https://github.com/mruby/mruby/pull/6521) Add codespell config file `.codespellrc`
- [#6522](https://github.com/mruby/mruby/pull/6522) gha: label more files
- [#6523](https://github.com/mruby/mruby/pull/6523) add `rand(Range)` and unify implementations of `Random#rand` and `Kernel#rand`
- [#6524](https://github.com/mruby/mruby/pull/6524) Fix Kernel#p when no argument
- [#6525](https://github.com/mruby/mruby/pull/6525) Skip adding empty input to mirb history
- [#6526](https://github.com/mruby/mruby/pull/6526) Add build config for Luckfox Pico embedded SBC
- [#6528](https://github.com/mruby/mruby/pull/6528) misc: fix spelling
- [#6530](https://github.com/mruby/mruby/pull/6530) Revert "class.c (find_visibility_scope): when callinfo returns, \*ep == NULL; #6512"
- [#6531](https://github.com/mruby/mruby/pull/6531) Improve method table performance by rehashing at 75% load factor
- [#6532](https://github.com/mruby/mruby/pull/6532) Reverted method table optimizations to prioritize memory savings
- [#6533](https://github.com/mruby/mruby/pull/6533) Fix calling `extended` callback
- [#6534](https://github.com/mruby/mruby/pull/6534) Add descriptive comment to mrb_read_float function
- [#6535](https://github.com/mruby/mruby/pull/6535) Added descriptive comments for functions/macros in src/mempool.c
- [#6536](https://github.com/mruby/mruby/pull/6536) Add descriptive comments to public functions in src/debug.c
- [#6537](https://github.com/mruby/mruby/pull/6537) Updated comments in `cdump.c` to remove the `@brief` tag
- [#6539](https://github.com/mruby/mruby/pull/6539) Add descriptive comments for functions in src/load.c
- [#6540](https://github.com/mruby/mruby/pull/6540) Add descriptive comments to MRB_API functions in object.c
- [#6541](https://github.com/mruby/mruby/pull/6541) Add descriptive comments for MRB_API functions in src/array.c
- [#6542](https://github.com/mruby/mruby/pull/6542) Add descriptive comments to MRB_API functions in src/symbol.c
- [#6543](https://github.com/mruby/mruby/pull/6543) Add descriptive comments to several functions in src/dump.c
- [#6544](https://github.com/mruby/mruby/pull/6544) Fix build strings that must be mutable
- [#6545](https://github.com/mruby/mruby/pull/6545) Add descriptive comments for MRB_API functions in src/class.c
- [#6548](https://github.com/mruby/mruby/pull/6548) Add descriptive comments to MRB_API functions in src/etc.c
- [#6549](https://github.com/mruby/mruby/pull/6549) Add descriptive comments to kernel functions
- [#6550](https://github.com/mruby/mruby/pull/6550) Add descriptive comments for MRB_API functions in src/proc.c
- [#6551](https://github.com/mruby/mruby/pull/6551) Add descriptive comments for MRB_API functions in src/state.c
- [#6552](https://github.com/mruby/mruby/pull/6552) Fix: Correct placement of comments in src/variable.c
- [#6553](https://github.com/mruby/mruby/pull/6553) Add descriptive comments for MRB_API functions in src/vm.c
- [#6555](https://github.com/mruby/mruby/pull/6555) `mrb_mt_foreach()` needs to update the pointer at each loop
- [#6556](https://github.com/mruby/mruby/pull/6556) `iv_foreach()` needs to update the pointer at each loop
- [#6560](https://github.com/mruby/mruby/pull/6560) Refactor: Improve Set GC marking and freeing
- [#6561](https://github.com/mruby/mruby/pull/6561) pre-commit updates and fix prettier entrypoint
- [#6562](https://github.com/mruby/mruby/pull/6562) misc: fix spelling word case
- [#6563](https://github.com/mruby/mruby/pull/6563) pre-commit add rubocop with one rule spaces for indentation
- [#6564](https://github.com/mruby/mruby/pull/6564) Remove jumanjihouse pre-commit hooks no longer maintained
- [#6565](https://github.com/mruby/mruby/pull/6565) Rubocop: fix target Ruby version; add two more cops; fix lint error
- [#6566](https://github.com/mruby/mruby/pull/6566) Removed unreferenced variables in `CrossBuild#run_bintest`
- [#6567](https://github.com/mruby/mruby/pull/6567) Avoid array object creation in `cmd_bin` method in bintest
- [#6568](https://github.com/mruby/mruby/pull/6568) mruby-bin-debugger depends on mruby-bin-mrbc in bintest
- [#6569](https://github.com/mruby/mruby/pull/6569) sed s/Mruby/MRuby/g
- [#6571](https://github.com/mruby/mruby/pull/6571) Update limitations.md to add behavior on small hash
- [#6572](https://github.com/mruby/mruby/pull/6572) Add Claude Code GitHub Workflow
- [#6573](https://github.com/mruby/mruby/pull/6573) pre-commit fixes and updates
- [#6574](https://github.com/mruby/mruby/pull/6574) Support initializing structs via keyword arguments
- [#6575](https://github.com/mruby/mruby/pull/6575) Fix typo in file time methods
- [#6581](https://github.com/mruby/mruby/pull/6581) Merge `mrb_obj_iv_inspect()` into `mrb_obj_inspect()`
- [#6582](https://github.com/mruby/mruby/pull/6582) Stricter type tag in `mrb_obj_alloc()`
- [#6583](https://github.com/mruby/mruby/pull/6583) Add fallback to local build_config.rb before using default configuration
- [#6585](https://github.com/mruby/mruby/pull/6585) Fix typo in mruby3.2 docs
- [#6586](https://github.com/mruby/mruby/pull/6586) Makefile: refactor add docs and add command line `help` target
- [#6587](https://github.com/mruby/mruby/pull/6587) Add Set#hash tests
- [#6588](https://github.com/mruby/mruby/pull/6588) Add CodeQL Analysis for GitHub Actions
- [#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
- [#6602](https://github.com/mruby/mruby/pull/6602) Improved iseq annotations for `new` and `!=`
- [#6604](https://github.com/mruby/mruby/pull/6604) pre-commit config updates
- [#6607](https://github.com/mruby/mruby/pull/6607) fix bigint on raspberry pi
- [#6610](https://github.com/mruby/mruby/pull/6610) Extract golden ratio prime into constant
- [#6614](https://github.com/mruby/mruby/pull/6614) Fix uninitialized variable in io_gets causing segmentation fault
- [#6617](https://github.com/mruby/mruby/pull/6617) Fix various minor problems and speed up build
- [#6618](https://github.com/mruby/mruby/pull/6618) Stop generating unnecessary C++ files in mruby-bin-mruby
- [#6621](https://github.com/mruby/mruby/pull/6621) Set up all GEMS before mruby core tasks definition
- [#6624](https://github.com/mruby/mruby/pull/6624) Fixed wrong `MRuby::Build.current` at the top level of `mrbgem.rake`
- [#6628](https://github.com/mruby/mruby/pull/6628) Revert `File.absolute_path` logic
- [#6629](https://github.com/mruby/mruby/pull/6629) pre-commit update
- [#6631](https://github.com/mruby/mruby/pull/6631) Revert "Rakefile: make the whole thing parallel unless SERIAL=1"
- [#6633](https://github.com/mruby/mruby/pull/6633) Fix a heap-buffer-overflow in str strip! methods
- [#6643](https://github.com/mruby/mruby/pull/6643) Fix crash caused by an incorrect node type check in `codegen_masgn`
- [#6651](https://github.com/mruby/mruby/pull/6651) Address stack-use-after-return in the mruby bigint implementation
- [#6653](https://github.com/mruby/mruby/pull/6653) Improve HAL-related components for MinGW
- [#6655](https://github.com/mruby/mruby/pull/6655) Preventing Memory Leaks in `Array#__combination_init`
- [#6656](https://github.com/mruby/mruby/pull/6656) Fix integer overflow in allocation size calculation
- [#6663](https://github.com/mruby/mruby/pull/6663) Added the `kh_is_end()` macro function
- [#6665](https://github.com/mruby/mruby/pull/6665) Fixed use-after-free with `Set#join`
- [#6670](https://github.com/mruby/mruby/pull/6670) Arranging VM dispatch macros
- [#6673](https://github.com/mruby/mruby/pull/6673) Adjust broken license links; clean up Markdown
- [#6677](https://github.com/mruby/mruby/pull/6677) gha: run pre-commit with `--color=always`
- [#6678](https://github.com/mruby/mruby/pull/6678) Put ls-lint and pre-commit in separate workflow files
- [#6679](https://github.com/mruby/mruby/pull/6679) pre-commit autoupdate; update node and prettier
- [#6681](https://github.com/mruby/mruby/pull/6681) Add Cosmopolitan Libc build configuration
- [#6689](https://github.com/mruby/mruby/pull/6689) docs: fix pre-commit manual hooks; fix link
- [#6694](https://github.com/mruby/mruby/pull/6694) Fix mirb build under Cosmopolitan
- [#6695](https://github.com/mruby/mruby/pull/6695) Dependabot: add a cooldown period for new releases
- [#6696](https://github.com/mruby/mruby/pull/6696) Fix parse error with required kwargs and omitted parens
- [#6699](https://github.com/mruby/mruby/pull/6699) Fix mruby-task for PicoRuby Integration
- [#6700](https://github.com/mruby/mruby/pull/6700) Fix float/double pack/unpack on s390x
- [#6706](https://github.com/mruby/mruby/pull/6706) Refactor task class to use symbol IDs
- [#6708](https://github.com/mruby/mruby/pull/6708) `initialize_copy` and `respond_to_missing?` defined as private
- [#6709](https://github.com/mruby/mruby/pull/6709) Add the `MRB_ENSURE()` macro
- [#6711](https://github.com/mruby/mruby/pull/6711) Fix out of bounds read and write in IO.select
- [#6714](https://github.com/mruby/mruby/pull/6714) Fix OP_DEBUG operand type and add NULL check for debug_op_hook
- [#6716](https://github.com/mruby/mruby/pull/6716) Fixes identity for proc object
- [#6717](https://github.com/mruby/mruby/pull/6717) Fix mruby-task: wrapping by critical section and setting initial task receiver
- [#6718](https://github.com/mruby/mruby/pull/6718) Add installation instructions for conda and Homebrew
- [#6723](https://github.com/mruby/mruby/pull/6723) Add `RInteger` and `RFloat` to `RVALUE`
- [#6727](https://github.com/mruby/mruby/pull/6727) Language documentation: update wording of "overloading" section
- [#6729](https://github.com/mruby/mruby/pull/6729) Simplifying dependency addition for gensym task
- [#6730](https://github.com/mruby/mruby/pull/6730) Simplifying presym file generation actions
- [#6733](https://github.com/mruby/mruby/pull/6733) Include `mruby/presym.h` for all source files
- [#6734](https://github.com/mruby/mruby/pull/6734) Chunk array literals at 64 elements to reduce register pressure
- [#6735](https://github.com/mruby/mruby/pull/6735) Prevent full recompilation without changes to presym file
- [#6739](https://github.com/mruby/mruby/pull/6739) Fix MSYS2 build error with drive letters
- [#6743](https://github.com/mruby/mruby/pull/6743) Chunk `%w()` and `%i()` literals to reduce register pressure
- [#6744](https://github.com/mruby/mruby/pull/6744) Raise `NoMatchingPatternError` in `case`/`in` without `else`
- [#6747](https://github.com/mruby/mruby/pull/6747) Correctly handle empty hash as default named argument
- [#6749](https://github.com/mruby/mruby/pull/6749) Fix microcontroller profile
- [#6750](https://github.com/mruby/mruby/pull/6750) Fix out-of-bounds read and divide-by-zero in `Array#product`
- [#6752](https://github.com/mruby/mruby/pull/6752) Fix `attr_reader`-generated methods accepting extra arguments
- [#6753](https://github.com/mruby/mruby/pull/6753) Further optimize `Array#product`
- [#6754](https://github.com/mruby/mruby/pull/6754) Mark `attr_reader` procs as noarg
- [#6755](https://github.com/mruby/mruby/pull/6755) Reload `ci` after `mrb_hash_delete_key()` in keyword argument handling
- [#6756](https://github.com/mruby/mruby/pull/6756) Avoid impact of object modifications caused by `mrb_vm_exec()` calls
- [#6758](https://github.com/mruby/mruby/pull/6758) Don't assign result of `mrb_funcall()` directly to `regs`
- [#6759](https://github.com/mruby/mruby/pull/6759) Define `mrb_bigint_p()` always
- [#6761](https://github.com/mruby/mruby/pull/6761) Fix `mrb_gc_unregister()` to remove all matching entries
- [#6762](https://github.com/mruby/mruby/pull/6762) Write generated test C files atomically to avoid build race condition
- [#6765](https://github.com/mruby/mruby/pull/6765) Fix `Lazy#flat_map` to handle non-enumerable block return values
- [#6767](https://github.com/mruby/mruby/pull/6767) Allow compound statement in parenthesized argument context
- [#6780](https://github.com/mruby/mruby/pull/6780) Fix `String#prepend` with self-referencing arguments
- [#6781](https://github.com/mruby/mruby/pull/6781) Protect `sprintf` format string from mutation during callbacks
- [#6783](https://github.com/mruby/mruby/pull/6783) Pin GitHub Actions workflows to commit hashes
# Security Fixes
- Buffer overflow in bigint uadd ([3f2611e](https://github.com/mruby/mruby/commit/3f2611e))
- Stack buffer overflow in Montgomery reduction ([edce0a3](https://github.com/mruby/mruby/commit/edce0a3))
- Buffer overflow in pack_uu encoding ([2993302](https://github.com/mruby/mruby/commit/2993302))
- Buffer overflow in IO#ungetc ([01ab2ff](https://github.com/mruby/mruby/commit/01ab2ff))
- Heap-buffer-overflow in pattern alternation codegen ([eea9e30](https://github.com/mruby/mruby/commit/eea9e30))
- Out of bounds read and write in IO.select ([44831711](https://github.com/mruby/mruby/commit/44831711))
- Off-by-one in bounds check for symbol names and pool strings in load.c ([b3b8c01](https://github.com/mruby/mruby/commit/b3b8c01))
- Use-after-free in Set operations ([a6b55e7](https://github.com/mruby/mruby/commit/a6b55e7))
- Use-after-free in Array set operations ([729b84c](https://github.com/mruby/mruby/commit/729b84c))
- Use-after-free in Set#join ([0e653eb](https://github.com/mruby/mruby/commit/0e653eb))
- Use-after-realloc in Array#sort! ([eb39897](https://github.com/mruby/mruby/commit/eb39897))
- Heap-use-after-free in insertion_sort ([099d2c47](https://github.com/mruby/mruby/commit/099d2c47))
- Integer overflow in str_check_length ([6afff1c3](https://github.com/mruby/mruby/commit/6afff1c3))
- Integer overflow in Integer#lcm ([070bef24](https://github.com/mruby/mruby/commit/070bef24))
- Heap buffer overflow in `#method_missing` ([550d10a](https://github.com/mruby/mruby/commit/550d10a))
- Out-of-bounds read and divide-by-zero in `Array#product` ([8441eaf](https://github.com/mruby/mruby/commit/8441eaf))
- Heap buffer overflow in `String#prepend` with self-referencing arguments ([18ba026](https://github.com/mruby/mruby/commit/18ba026))
- Use-after-free in `sprintf` via `to_s` callback mutating format string ([48fc422](https://github.com/mruby/mruby/commit/48fc422))
- Multiple memory leak fixes in bigint, Set, Array, and Task gems
@@ -15,6 +15,11 @@
*/
#include <mruby/internal.h>
/*
* The "mruby/presym.h" file is placed at the end of the mruby header file.
*/
#include <mruby/presym.h>
/*
* Define your own struct RBigint.
*
+6 -25
View File
@@ -64,14 +64,8 @@
# define MRB_WORD_BOXING
#endif
/* if defined mruby does not inline float values in word boxing;
all floats are heap-allocated as RFloat objects */
//#define MRB_WORDBOX_NO_INLINE_FLOAT
/* obsolete configuration */
#if defined(MRB_WORDBOX_NO_FLOAT_TRUNCATE)
# define MRB_WORDBOX_NO_INLINE_FLOAT
#endif
/* if defined mruby allocates Float objects in the heap to keep full precision if needed */
//#define MRB_WORDBOX_NO_FLOAT_TRUNCATE
/* add -DMRB_INT32 to use 32-bit integer for mrb_int; conflict with MRB_INT64;
Default for 32-bit CPU mode. */
@@ -92,13 +86,6 @@
# endif
#endif
/* MRB_INT64 on 32-bit with word/NaN boxing causes alignment issues
for heap-allocated RInteger (int64_t needs 8-byte alignment but
GC heap slots may not guarantee it); use MRB_NO_BOXING instead */
#if defined(MRB_INT64) && defined(MRB_32BIT) && !defined(MRB_NO_BOXING)
#error "MRB_INT64 on 32-bit requires MRB_NO_BOXING"
#endif
/* call malloc_trim(0) from mrb_full_gc() */
//#define MRB_USE_MALLOC_TRIM
@@ -138,8 +125,8 @@
/* turn off generational GC by default */
//#define MRB_GC_TURN_OFF_GENERATIONAL
/* initial size of khash table bucket */
//#define KHASH_INITIAL_SIZE 32
/* default size of khash table bucket */
//#define KHASH_DEFAULT_SIZE 32
/* allocated memory address alignment */
//#define POOL_ALIGNMENT 4
@@ -166,12 +153,6 @@
//#define MRB_USE_DEBUG_HOOK /* hooks for debugger */
//#define MRB_USE_ALL_SYMBOLS /* Symbol.all_symbols */
/* Symbol table configuration */
/* Threshold for switching from linear search to hash table */
#ifndef MRB_SYMBOL_LINEAR_THRESHOLD
#define MRB_SYMBOL_LINEAR_THRESHOLD 256
#endif
/* obsolete configurations */
#if defined(DISABLE_STDIO) || defined(MRB_DISABLE_STDIO)
# define MRB_NO_STDIO
@@ -208,8 +189,8 @@
# define MRB_NO_METHOD_CACHE
# endif
# ifndef KHASH_INITIAL_SIZE
# define KHASH_INITIAL_SIZE 16
# ifndef KHASH_DEFAULT_SIZE
# define KHASH_DEFAULT_SIZE 16
# endif
# ifndef MRB_HEAP_PAGE_SIZE
+13 -109
View File
@@ -155,7 +155,6 @@ typedef uint8_t mrb_code;
typedef uint32_t mrb_aspec;
typedef struct mrb_irep mrb_irep;
struct mrb_state;
#ifndef MRB_FIXED_STATE_ATEXIT_STACK_SIZE
@@ -189,10 +188,6 @@ enum mrb_fiber_state {
MRB_FIBER_TERMINATED,
};
/* Task context status aliases */
#define MRB_TASK_CREATED MRB_FIBER_CREATED
#define MRB_TASK_STOPPED MRB_FIBER_TERMINATED
struct mrb_context {
struct mrb_context *prev;
@@ -227,7 +222,7 @@ mrb_static_assert_powerof2(MRB_METHOD_CACHE_SIZE);
typedef mrb_value (*mrb_func_t)(struct mrb_state *mrb, mrb_value self);
typedef struct {
uint32_t flags; /* method flags (no symbol packed) */
uint32_t flags; /* compatible with mt keys in class.c */
union {
const struct RProc *proc;
@@ -238,7 +233,7 @@ typedef struct {
#ifndef MRB_NO_METHOD_CACHE
struct mrb_cache_entry {
struct RClass *c, *c0;
mrb_sym mid;
/* mrb_sym mid; // mid is stored in mrb_method_t::flags */
mrb_method_t m;
};
#endif
@@ -247,19 +242,6 @@ struct mrb_jmpbuf;
typedef void (*mrb_atexit_func)(struct mrb_state*);
#ifdef MRB_USE_TASK_SCHEDULER
struct mrb_task;
typedef struct mrb_task_state {
struct mrb_task *queues[4]; /* Task queues (dormant, ready, waiting, suspended) */
volatile uint32_t tick; /* Current tick count */
volatile uint32_t wakeup_tick; /* Next wakeup tick */
volatile mrb_bool switching; /* Context switch pending flag */
struct mrb_task *main_task; /* Main task wrapper for root context */
uint8_t scheduler_lock; /* Lock counter for synchronous execution */
} mrb_task_state;
#endif
typedef struct mrb_state {
struct mrb_jmpbuf *jmp;
@@ -291,17 +273,16 @@ typedef struct mrb_state {
mrb_gc gc;
mrb_bool bootstrapping;
#ifndef MRB_NO_METHOD_CACHE
struct mrb_cache_entry cache[MRB_METHOD_CACHE_SIZE];
#endif
mrb_sym symidx;
const char **symtbl;
uint8_t *symlink;
uint8_t *symflags;
mrb_sym symhash[256];
size_t symcapa;
struct mrb_sym_hash_table *symhash;
void *sym_pool;
#ifndef MRB_USE_ALL_SYMBOLS
char symbuf[8]; /* buffer for small symbol names */
#endif
@@ -323,10 +304,6 @@ typedef struct mrb_state {
struct RObject *arena_err; /* pre-allocated arena overflow error */
#endif
struct mrb_mt_rom_list *rom_mt; /* heap-allocated ROM wrappers (freed at close) */
struct mrb_iv_shape *root_shape; /* root of IV shape tree */
void *ud; /* auxiliary data */
#ifdef MRB_FIXED_STATE_ATEXIT_STACK
@@ -335,10 +312,6 @@ typedef struct mrb_state {
mrb_atexit_func *atexit_stack;
#endif
uint16_t atexit_stack_len;
#ifdef MRB_USE_TASK_SCHEDULER
mrb_task_state task; /* Task scheduler state */
#endif
} mrb_state;
/**
@@ -416,7 +389,7 @@ MRB_API void mrb_include_module(mrb_state *mrb, struct RClass *cla, struct RClas
MRB_API void mrb_prepend_module(mrb_state *mrb, struct RClass *cla, struct RClass *prepended);
/**
* Defines a global function in Ruby.
* Defines a global function in ruby.
*
* If you're creating a gem it may look something like this
*
@@ -926,11 +899,6 @@ MRB_API struct RClass* mrb_define_module_under_id(mrb_state *mrb, struct RClass
*/
#define MRB_ARGS_BLOCK() ((mrb_aspec)1)
/**
* Function does not accept a block (&nil)
*/
#define MRB_ARGS_NOBLOCK() ((mrb_aspec)(1 << 23))
/**
* Function accepts any number of arguments
*/
@@ -965,7 +933,7 @@ MRB_API struct RClass* mrb_define_module_under_id(mrb_state *mrb, struct RClass
* | `I` | inline struct | void *, struct RClass | `I!` gives `NULL` for `nil` |
* | `&` | block | {mrb_value} | &! raises exception if no block given. |
* | `*` | rest arguments | const {mrb_value} *, {mrb_int} | Receive the rest of arguments as an array; `*!` avoid copy of the stack. |
* | `\|` | optional | | After this spec following specs would be optional. |
* | <code>\|</code> | optional | | After this spec following specs would be optional. |
* | `?` | optional given | {mrb_bool} | `TRUE` if preceding argument is given. Used to check optional argument is given. |
* | `:` | keyword args | {mrb_kwargs} const | Get keyword arguments. @see mrb_kwargs |
*
@@ -1015,7 +983,7 @@ typedef const char *mrb_args_format;
* mrb_value str, kw_rest;
* uint32_t kw_num = 3;
* uint32_t kw_required = 1;
* // `MRB_SYM()` is available via `mruby.h` (which includes `mruby/presym.h`).
* // Note that `#include <mruby/presym.h>` is required beforehand because `MRB_SYM()` is used.
* // If the usage of `MRB_SYM()` is not desired, replace it with `mrb_intern_lit()`.
* mrb_sym kw_names[] = { MRB_SYM(x), MRB_SYM(y), MRB_SYM(z) };
* mrb_value kw_values[kw_num];
@@ -1098,7 +1066,7 @@ MRB_API mrb_bool mrb_block_given_p(mrb_state *mrb);
#define mrb_strlen_lit(lit) (sizeof(lit "") - 1)
/**
* Call existing Ruby functions.
* Call existing ruby functions.
*
* Example:
*
@@ -1131,7 +1099,7 @@ MRB_API mrb_bool mrb_block_given_p(mrb_state *mrb);
MRB_API mrb_value mrb_funcall(mrb_state *mrb, mrb_value val, const char *name, mrb_int argc, ...);
MRB_API mrb_value mrb_funcall_id(mrb_state *mrb, mrb_value val, mrb_sym mid, mrb_int argc, ...);
/**
* Call existing Ruby functions. This is basically the type safe version of mrb_funcall.
* Call existing ruby functions. This is basically the type safe version of mrb_funcall.
*
* #include <stdio.h>
* #include <mruby.h>
@@ -1146,7 +1114,7 @@ MRB_API mrb_value mrb_funcall_id(mrb_state *mrb, mrb_value val, mrb_sym mid, mrb
*
* FILE *fp = fopen("test.rb","r");
* mrb_value obj = mrb_load_file(mrb,fp);
* mrb_funcall_argv(mrb, obj, MRB_SYM(method_name), 1, &obj); // Calling Ruby function from test.rb.
* mrb_funcall_argv(mrb, obj, MRB_SYM(method_name), 1, &obj); // Calling ruby function from test.rb.
* fclose(fp);
* mrb_close(mrb);
* }
@@ -1160,7 +1128,7 @@ MRB_API mrb_value mrb_funcall_id(mrb_state *mrb, mrb_value val, mrb_sym mid, mrb
*/
MRB_API mrb_value mrb_funcall_argv(mrb_state *mrb, mrb_value val, mrb_sym name, mrb_int argc, const mrb_value *argv);
/**
* Call existing Ruby functions with a block.
* Call existing ruby functions with a block.
*/
MRB_API mrb_value mrb_funcall_with_block(mrb_state *mrb, mrb_value val, mrb_sym name, mrb_int argc, const mrb_value *argv, mrb_value block);
/**
@@ -1279,44 +1247,6 @@ MRB_API mrb_state* mrb_open_core(void);
* Pointer to the mrb_state to be closed.
*/
MRB_API void mrb_close(mrb_state *mrb);
#ifndef MRB_NO_METHOD_CACHE
MRB_API void mrb_method_cache_clear(mrb_state *mrb);
#else
#define mrb_method_cache_clear(mrb) ((void)0)
#endif
/**
* Check if mrb_open() failed
*
* @param mrb
* Pointer returned from mrb_open() or mrb_open_core().
* @return
* Non-zero if initialization failed, 0 if succeeded.
* @note
* mrb_open() may return non-NULL even on failure (with mrb->exc set).
* Use this macro to check for failure:
* @code
* mrb_state *mrb = mrb_open();
* if (MRB_OPEN_FAILURE(mrb)) {
* if (mrb) {
* // Inspect mrb->exc for error details
* mrb_close(mrb);
* }
* return EXIT_FAILURE;
* }
* @endcode
*/
#define MRB_OPEN_FAILURE(mrb) (!(mrb) || (mrb)->exc)
/**
* Check if mrb_open() succeeded
*
* @param mrb
* Pointer returned from mrb_open() or mrb_open_core().
* @return
* Non-zero if initialization succeeded, 0 if failed.
*/
#define MRB_OPEN_SUCCESS(mrb) (!MRB_OPEN_FAILURE(mrb))
/**
* The memory allocation function. You can redefine this function for your own allocator.
@@ -1373,25 +1303,6 @@ MRB_API mrb_bool mrb_eql(mrb_state *mrb, mrb_value obj1, mrb_value obj2);
/* mrb_cmp(mrb, obj1, obj2): 1:0:-1; -2 for error */
MRB_API mrb_int mrb_cmp(mrb_state *mrb, mrb_value obj1, mrb_value obj2);
/* recursion detection */
MRB_API mrb_bool mrb_recursive_method_p(mrb_state *mrb, mrb_sym mid, mrb_value obj1, mrb_value obj2);
MRB_API mrb_bool mrb_recursive_func_p(mrb_state *mrb, mrb_sym mid, mrb_value obj1, mrb_value obj2);
#define MRB_RECURSIVE_P(mrb, mid, obj1, obj2) \
mrb_recursive_method_p(mrb, mid, obj1, obj2)
#define MRB_RECURSIVE_UNARY_P(mrb, mid, obj) \
mrb_recursive_method_p(mrb, mid, obj, mrb_nil_value())
#define MRB_RECURSIVE_BINARY_P(mrb, mid, obj1, obj2) \
mrb_recursive_method_p(mrb, mid, obj1, obj2)
#define MRB_RECURSIVE_FUNC_P(mrb, mid, obj) \
mrb_recursive_func_p(mrb, mid, obj, mrb_nil_value())
#define MRB_RECURSIVE_BINARY_FUNC_P(mrb, mid, obj1, obj2) \
mrb_recursive_func_p(mrb, mid, obj1, obj2)
#define mrb_gc_arena_save(mrb) ((mrb)->gc.arena_idx)
#define mrb_gc_arena_restore(mrb, idx) ((mrb)->gc.arena_idx = (idx))
@@ -1582,8 +1493,7 @@ MRB_API mrb_value mrb_fiber_alive_p(mrb_state *mrb, mrb_value fib);
MRB_API void mrb_stack_extend(mrb_state*, mrb_int);
/* temporary memory allocation, only effective while GC arena is kept */
MRB_API void* mrb_temp_alloc(mrb_state *mrb, size_t);
#define mrb_alloca(mrb, size) mrb_temp_alloc(mrb, size) /* for compatibility */
MRB_API void* mrb_alloca(mrb_state *mrb, size_t);
MRB_API void mrb_state_atexit(mrb_state *mrb, mrb_atexit_func func);
@@ -1594,8 +1504,6 @@ MRB_API mrb_value mrb_format(mrb_state *mrb, const char *format, ...);
#ifdef MRB_PRESYM_SCANNING
# include <mruby/presym/scanning.h>
#else
# include <mruby/presym.h>
#endif
#if 0
@@ -1627,10 +1535,6 @@ mrbmemset(void *s, int c, size_t n)
#define mrb_int_hash_func(mrb,key) (uint32_t)((key)^((key)<<2)^((key)>>2))
#define MRB_UNIQNAME(name) MRB_UNIQNAME_1(name, __LINE__)
#define MRB_UNIQNAME_1(name, line) MRB_UNIQNAME_2(name, line)
#define MRB_UNIQNAME_2(name, line) name##line
MRB_END_DECL
#endif /* MRUBY_H */
+1 -3
View File
@@ -20,7 +20,7 @@ typedef struct mrb_shared_array {
mrb_value *ptr;
} mrb_shared_array;
#if defined(MRB_32BIT) && defined(MRB_NO_BOXING) && (!defined(MRB_USE_FLOAT32) || defined(MRB_INT64)) && !defined(MRB_ARY_NO_EMBED)
#if defined(MRB_32BIT) && defined(MRB_NO_BOXING) && !defined(MRB_USE_FLOAT32) && !defined(MRB_ARY_NO_EMBED)
# define MRB_ARY_NO_EMBED
#endif
@@ -86,8 +86,6 @@ struct RArray {
#define ARY_UNSET_SHARED_FLAG(a) ((a)->flags &= ~MRB_ARY_SHARED)
MRB_API void mrb_ary_modify(mrb_state*, struct RArray*);
MRB_API mrb_value mrb_ary_dup(mrb_state*, mrb_value ary);
MRB_API mrb_value mrb_ary_make_shared_copy(mrb_state*, mrb_value ary);
MRB_API mrb_value mrb_ary_new_capa(mrb_state*, mrb_int);
/*
+23 -49
View File
@@ -7,44 +7,15 @@
#ifndef MRUBY_BOXING_WORD_H
#define MRUBY_BOXING_WORD_H
#if defined(MRB_32BIT) && !defined(MRB_USE_FLOAT32) && !defined(MRB_WORDBOX_NO_INLINE_FLOAT)
# define MRB_WORDBOX_NO_INLINE_FLOAT
#if defined(MRB_32BIT) && !defined(MRB_USE_FLOAT32) && !defined(MRB_WORDBOX_NO_FLOAT_TRUNCATE)
# define MRB_WORDBOX_NO_FLOAT_TRUNCATE
#endif
#ifndef MRB_NO_FLOAT
#if !defined(MRB_NO_FLOAT) && defined(MRB_WORDBOX_NO_FLOAT_TRUNCATE)
struct RFloat {
MRB_OBJECT_HEADER;
#ifdef MRB_WORDBOX_NO_INLINE_FLOAT
/* avoid 8-byte alignment on 32-bit; use memcpy-based accessors */
char f[sizeof(mrb_float)];
#else
mrb_float f;
#endif
};
#include <string.h>
static inline mrb_float
mrb_rfloat_value(const struct RFloat *p)
{
#ifdef MRB_WORDBOX_NO_INLINE_FLOAT
mrb_float f;
memcpy(&f, p->f, sizeof(mrb_float));
return f;
#else
return p->f;
#endif
}
static inline void
mrb_rfloat_set(struct RFloat *p, mrb_float f)
{
#ifdef MRB_WORDBOX_NO_INLINE_FLOAT
memcpy(p->f, &f, sizeof(mrb_float));
#else
p->f = f;
#endif
}
#endif
struct RInteger {
@@ -79,7 +50,7 @@ enum mrb_special_consts {
#define WORDBOX_FIXNUM_FLAG (1 << (WORDBOX_FIXNUM_BIT_POS - 1))
#define WORDBOX_FIXNUM_MASK ((1 << WORDBOX_FIXNUM_BIT_POS) - 1)
#if defined(MRB_WORDBOX_NO_INLINE_FLOAT) || defined(MRB_NO_FLOAT)
#if defined(MRB_WORDBOX_NO_FLOAT_TRUNCATE) || defined(MRB_NO_FLOAT)
/* floats are allocated in heaps */
#define WORDBOX_IMMEDIATE_MASK 0x03
#define WORDBOX_SYMBOL_BIT_POS 2
@@ -112,22 +83,27 @@ enum mrb_special_consts {
/*
* mrb_value representation:
*
* 64-bit word with inline float (rotation encoding, lossless):
* 64-bit word with inline float:
* nil : ...0000 0000 (all bits are 0)
* false : ...0000 0100 (mrb_fixnum(v) != 0)
* true : ...0000 1100
* undef : ...0001 0100
* symbol: ...0001 1100 (use only upper 32-bit as symbol value with MRB_64BIT)
* fixnum: ...IIII III1
* float : ...FFFF FF10 (rotl64(float64-ADDEND, 3); exponent [-255,+256])
* float : ...FFFF FF10 (51 bit significands; require MRB_64BIT)
* object: ...PPPP P000
* (floats outside inline range are heap-allocated as RFloat)
*
* 64-bit word with inline float32 (MRB_USE_FLOAT32):
* float : ...FFFF FF10 (float32 shifted left by 2)
* (other values same as above)
* 32-bit word with inline float:
* nil : ...0000 0000 (all bits are 0)
* false : ...0000 0100 (mrb_fixnum(v) != 0)
* true : ...0000 1100
* undef : ...0001 0100
* symbol: ...SSS1 0100 (symbol occupies 20bits)
* fixnum: ...IIII III1
* float : ...FFFF FF10 (22 bit significands; require MRB_64BIT)
* object: ...PPPP P000
*
* word boxing without inline float (MRB_WORDBOX_NO_INLINE_FLOAT):
* and word boxing without inline float (MRB_WORDBOX_NO_FLOAT_TRUNCATE):
* nil : ...0000 0000 (all bits are 0)
* false : ...0000 0100 (mrb_fixnum(v) != 0)
* true : ...0000 1100
@@ -144,9 +120,10 @@ union mrb_value_ {
void *p;
struct RBasic *bp;
#ifndef MRB_NO_FLOAT
struct RFloat *fp;
#if !defined(MRB_WORDBOX_NO_INLINE_FLOAT) && defined(MRB_USE_FLOAT32)
#ifndef MRB_WORDBOX_NO_FLOAT_TRUNCATE
mrb_float f;
#else
struct RFloat *fp;
#endif
#endif
struct RInteger *ip;
@@ -180,11 +157,11 @@ MRB_API mrb_value mrb_boxing_int_value(struct mrb_state*, mrb_int);
#define mrb_ptr(o) mrb_val_union(o).p
#define mrb_cptr(o) mrb_val_union(o).vp->p
#ifndef MRB_NO_FLOAT
#ifndef MRB_WORDBOX_NO_INLINE_FLOAT
#ifndef MRB_WORDBOX_NO_FLOAT_TRUNCATE
MRB_API mrb_float mrb_word_boxing_value_float(mrb_value v);
#define mrb_float(o) mrb_word_boxing_value_float(o)
#else
#define mrb_float(o) mrb_rfloat_value(mrb_val_union(o).fp)
#define mrb_float(o) mrb_val_union(o).fp->f
#endif
#endif
#define mrb_fixnum(o) (mrb_int)(((intptr_t)(o).w) >> WORDBOX_FIXNUM_SHIFT)
@@ -205,13 +182,10 @@ mrb_integer_func(mrb_value o) {
#define mrb_false_p(o) ((o).w == MRB_Qfalse)
#define mrb_true_p(o) ((o).w == MRB_Qtrue)
#ifndef MRB_NO_FLOAT
#ifdef MRB_WORDBOX_NO_INLINE_FLOAT
#define mrb_float_p(o) WORDBOX_OBJ_TYPE_P(o, FLOAT)
#elif defined(MRB_USE_FLOAT32) && defined(MRB_64BIT)
#ifndef MRB_WORDBOX_NO_FLOAT_TRUNCATE
#define mrb_float_p(o) WORDBOX_SHIFT_VALUE_P(o, FLOAT)
#else
/* rotation encoding: most floats inline, edge cases on heap */
#define mrb_float_p(o) (WORDBOX_SHIFT_VALUE_P(o, FLOAT) || WORDBOX_OBJ_TYPE_P(o, FLOAT))
#define mrb_float_p(o) WORDBOX_OBJ_TYPE_P(o, FLOAT)
#endif
#else
#define mrb_float_p(o) FALSE
+1 -54
View File
@@ -17,7 +17,7 @@ MRB_BEGIN_DECL
struct RClass {
MRB_OBJECT_HEADER;
struct iv_tbl *iv;
struct mrb_mt_tbl *mt;
struct mt_tbl *mt;
struct RClass *super;
};
@@ -88,7 +88,6 @@ MRB_API mrb_method_t mrb_method_search_vm(mrb_state*, struct RClass**, mrb_sym);
MRB_API mrb_method_t mrb_method_search(mrb_state*, struct RClass*, mrb_sym);
MRB_API struct RClass* mrb_class_real(struct RClass* cl);
MRB_API struct RClass* mrb_class_outer(mrb_state *mrb, struct RClass *c);
#ifndef MRB_NO_METHOD_CACHE
void mrb_mc_clear_by_class(mrb_state *mrb, struct RClass* c);
@@ -100,58 +99,6 @@ void mrb_mc_clear_by_class(mrb_state *mrb, struct RClass* c);
typedef int (mrb_mt_foreach_func)(mrb_state*,mrb_sym,mrb_method_t,void*);
MRB_API void mrb_mt_foreach(mrb_state*, struct RClass*, mrb_mt_foreach_func*, void*);
/* ROM method table types for static method registration */
union mrb_mt_ptr {
const struct RProc *proc;
mrb_func_t func;
};
/* entry combining function pointer, symbol key, and flags */
typedef struct mrb_mt_entry {
union mrb_mt_ptr val;
mrb_sym key; /* pure symbol ID (no flags packed) */
uint32_t flags; /* method flags + aspec */
} mrb_mt_entry;
typedef struct mrb_mt_tbl {
int size;
int alloc; /* bit 30: MRB_MT_READONLY_BIT, bit 29: MRB_MT_FROZEN_BIT */
mrb_mt_entry *ptr;
struct mrb_mt_tbl *next;
} mrb_mt_tbl;
#define MRB_MT_READONLY_BIT (1 << 30)
#define MRB_MT_FROZEN_BIT (1 << 29)
#define MRB_MT_FUNC (1 << 24) /* MRB_METHOD_FUNC_FL */
#define MRB_MT_PUBLIC 0
#define MRB_MT_PRIVATE (1 << 25) /* MRB_METHOD_PRIVATE_FL */
/* ROM table entry: 3rd param is MRB_ARGS_*() optionally OR'd with MRB_MT_PRIVATE. */
#define MRB_MT_ENTRY(fn, sym, flags) \
{ { .func = (fn) }, (sym), (flags) | MRB_MT_FUNC }
#define MRB_MT_ASPEC(flags) ((mrb_aspec)((flags) & 0xffffff))
/* "removed" tombstone: MRB_MT_FUNC flag set with NULL function pointer.
This combination never occurs naturally (C functions are never NULL).
Unlike undef (proc=NULL without MRB_MT_FUNC), a removed marker makes
mt_get() return 0 ("not found"), blocking ROM chain walk while
allowing superclass lookup. */
#define MRB_MT_REMOVED_P(e) (((e).flags&MRB_MT_FUNC) && (e).val.func==NULL)
/* Singly-linked list node for tracking heap-allocated ROM wrappers. */
struct mrb_mt_rom_list {
mrb_mt_tbl *tbl;
struct mrb_mt_rom_list *next;
};
/* Allocate a per-state ROM layer wrapping the const entries array,
and push it onto the class's method table chain. */
void mrb_mt_init_rom(mrb_state *mrb, struct RClass *c,
const mrb_mt_entry *entries, int size);
#define MRB_MT_INIT_ROM(mrb, cls, entries) \
mrb_mt_init_rom(mrb, cls, entries, \
(int)(sizeof(entries)/sizeof(entries[0])))
MRB_END_DECL
#endif /* MRUBY_CLASS_H */
-9
View File
@@ -59,15 +59,6 @@ MRB_BEGIN_DECL
# define mrb_deprecated
#endif
/** Branch prediction hints for optimization. */
#if defined(__GNUC__) || defined(__clang__)
# define mrb_likely(x) __builtin_expect(!!(x), 1)
# define mrb_unlikely(x) __builtin_expect(!!(x), 0)
#else
# define mrb_likely(x) (x)
# define mrb_unlikely(x) (x)
#endif
/** Declare a type or object as an alignment requirement. */
#ifndef mrb_alignas
# if defined(__cplusplus) && __cplusplus >= 201103L
+41 -5
View File
@@ -33,7 +33,6 @@ typedef struct mrb_ccontext {
mrb_bool keep_lv:1;
mrb_bool no_optimize:1;
mrb_bool no_ext_ops:1;
mrb_bool no_return_value:1;
const struct RProc *upper;
size_t parser_nerr;
@@ -54,7 +53,10 @@ MRB_API void mrb_ccontext_cleanup_local_variables(mrb_ccontext *c);
#define mrbc_cleanup_local_variables mrb_ccontext_cleanup_local_variables
/* AST node structure */
typedef struct mrb_ast_node mrb_ast_node;
typedef struct mrb_ast_node {
struct mrb_ast_node *car, *cdr;
uint16_t lineno, filename_index;
} mrb_ast_node;
/* lexer states */
enum mrb_lex_state_enum {
@@ -79,6 +81,42 @@ struct mrb_parser_message {
char* message;
};
#define STR_FUNC_PARSING 0x01
#define STR_FUNC_EXPAND 0x02
#define STR_FUNC_REGEXP 0x04
#define STR_FUNC_WORD 0x08
#define STR_FUNC_SYMBOL 0x10
#define STR_FUNC_ARRAY 0x20
#define STR_FUNC_HEREDOC 0x40
#define STR_FUNC_XQUOTE 0x80
enum mrb_string_type {
str_not_parsing = (0),
str_squote = (STR_FUNC_PARSING),
str_dquote = (STR_FUNC_PARSING|STR_FUNC_EXPAND),
str_regexp = (STR_FUNC_PARSING|STR_FUNC_REGEXP|STR_FUNC_EXPAND),
str_sword = (STR_FUNC_PARSING|STR_FUNC_WORD|STR_FUNC_ARRAY),
str_dword = (STR_FUNC_PARSING|STR_FUNC_WORD|STR_FUNC_ARRAY|STR_FUNC_EXPAND),
str_ssym = (STR_FUNC_PARSING|STR_FUNC_SYMBOL),
str_ssymbols = (STR_FUNC_PARSING|STR_FUNC_SYMBOL|STR_FUNC_ARRAY),
str_dsymbols = (STR_FUNC_PARSING|STR_FUNC_SYMBOL|STR_FUNC_ARRAY|STR_FUNC_EXPAND),
str_heredoc = (STR_FUNC_PARSING|STR_FUNC_HEREDOC),
str_xquote = (STR_FUNC_PARSING|STR_FUNC_XQUOTE|STR_FUNC_EXPAND),
};
/* heredoc structure */
struct mrb_parser_heredoc_info {
mrb_bool allow_indent:1;
mrb_bool remove_indent:1;
mrb_bool line_head:1;
size_t indent;
mrb_ast_node *indented;
enum mrb_string_type type;
const char *term;
int term_len;
mrb_ast_node *doc;
};
#define MRB_PARSER_TOKBUF_MAX (UINT16_MAX-1)
#define MRB_PARSER_TOKBUF_SIZE 256
@@ -104,7 +142,7 @@ struct mrb_parser_state {
unsigned int cmdarg_stack;
int paren_nest;
int lpar_beg;
int in_def, in_single, in_kwarg;
int in_def, in_single;
mrb_bool cmd_start:1;
mrb_ast_node *locals;
@@ -126,7 +164,6 @@ struct mrb_parser_state {
mrb_bool no_optimize:1;
mrb_bool capture_errors:1;
mrb_bool no_ext_ops:1;
mrb_bool no_return_value:1;
const struct RProc *upper;
struct mrb_parser_message error_buffer[10];
struct mrb_parser_message warn_buffer[10];
@@ -135,7 +172,6 @@ struct mrb_parser_state {
uint16_t filename_table_length;
uint16_t current_filename_index;
/* Variable-sized node management */
mrb_ast_node *nvars;
};
+2 -2
View File
@@ -52,13 +52,13 @@ MRB_API mrb_irep *mrb_read_irep_buf(mrb_state*, const void*, size_t);
/* Binary Format Version Major:Minor */
/* Major: Incompatible to prior versions */
/* Minor: Upper-compatible to prior versions */
#define RITE_BINARY_MAJOR_VER "04"
#define RITE_BINARY_MAJOR_VER "03"
#define RITE_BINARY_MINOR_VER "00"
#define RITE_BINARY_FORMAT_VER RITE_BINARY_MAJOR_VER RITE_BINARY_MINOR_VER
#define RITE_COMPILER_NAME "MATZ"
#define RITE_COMPILER_VERSION "0000"
#define RITE_VM_VER "0400"
#define RITE_VM_VER "0300"
#define RITE_BINARY_EOF "END\0"
#define RITE_SECTION_IREP_IDENT "IREP"
+4 -45
View File
@@ -8,7 +8,6 @@
#define MRUBY_ERROR_H
#include "common.h"
#include <string.h>
/**
* mruby error handling.
@@ -37,7 +36,7 @@ MRB_API mrb_value mrb_exc_new_str(mrb_state *mrb, struct RClass* c, mrb_value st
#define mrb_exc_new_lit(mrb, c, lit) mrb_exc_new_str(mrb, c, mrb_str_new_lit(mrb, lit))
MRB_API mrb_noreturn void mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_value args, const char *fmt, ...);
#if defined(MRB_NAN_BOXING) || defined(MRB_WORD_BOXING) || defined(MRB_64BIT)
#if defined(MRB_64BIT) || defined(MRB_USE_FLOAT32) || defined(MRB_NAN_BOXING) || defined(MRB_WORD_BOXING)
#undef MRB_USE_RBREAK_VALUE_UNION
#else
#define MRB_USE_RBREAK_VALUE_UNION 1
@@ -54,11 +53,7 @@ struct RBreak {
#ifndef MRB_USE_RBREAK_VALUE_UNION
mrb_value val;
#else
/* Store value as uint32_t words instead of union mrb_value_union
to avoid 8-byte alignment of int64_t/double on 32-bit platforms
(e.g., ARM, MIPS, PowerPC) which would inflate struct size beyond
the 5-word RVALUE limit due to padding. */
uint32_t value[sizeof(union mrb_value_union) / sizeof(uint32_t)];
union mrb_value_union value;
#endif
};
@@ -71,14 +66,14 @@ static inline mrb_value
mrb_break_value_get(struct RBreak *brk)
{
mrb_value val;
memcpy(&val.value, brk->value, sizeof(val.value));
val.value = brk->value;
val.tt = (enum mrb_vtype)(brk->flags & RBREAK_VALUE_TT_MASK);
return val;
}
static inline void
mrb_break_value_set(struct RBreak *brk, mrb_value val)
{
memcpy(brk->value, &val.value, sizeof(val.value));
brk->value = val.value;
brk->flags &= ~RBREAK_VALUE_TT_MASK;
brk->flags |= val.tt;
}
@@ -132,42 +127,6 @@ MRB_API mrb_value mrb_rescue_exceptions(mrb_state *mrb, mrb_func_t body, mrb_val
mrb_func_t rescue, mrb_value r_data,
mrb_int len, struct RClass **classes);
/**
* Calls `func` via `mrb_protect_error()` and then always executes the user block exactly once.
* Even if a global jump (similar to a Ruby exception) occurs within `func`, the block will be executed,
* and after the block's completion, the global jump will be re-thrown.
*
* By checking `mrb->exc != NULL` within the block, you can determine if a global jump occurred in `func`.
*
* If you want to suppress the global jump and continue processing, use `mrb_clear_error(mrb); break;`.
*
* - `mrb`: The mruby state reference
* - `result_var`: Pre-defined mrb_value type variable (to receive `func`'s return value)
* - `func`: Function to call (compatible with `mrb_protect_error_func`)
* - `data`: User data to pass to `func`
*
* Example:
*
* mrb_value result;
* MRB_ENSURE(mrb, result, body_func, userdata) {
* // This block is always executed (equivalent to Ruby's ensure)
*
* if (mrb->exc) {
* // Post-processing when an exception occurs
* }
*
* // To ignore the global jump, use `mrb_clear_error(mrb); break;` here
* }
*/
#define MRB_ENSURE(mrb, result_var, func, data) \
for (mrb_bool MRB_UNIQNAME(_break_) = FALSE; \
!MRB_UNIQNAME(_break_) && \
(((result_var) = mrb_protect_error(mrb, func, data, &MRB_UNIQNAME(_break_))), \
((mrb)->exc = (MRB_UNIQNAME(_break_) ? mrb_obj_ptr((result_var)) : NULL)), \
TRUE); \
(void)(MRB_UNIQNAME(_break_) && (mrb)->jmp && (mrb_exc_raise(mrb, result_var), TRUE)), \
MRB_UNIQNAME(_break_) = TRUE)
MRB_END_DECL
#endif /* MRUBY_ERROR_H */
+2 -9
View File
@@ -28,10 +28,6 @@ MRB_API void mrb_free_context(struct mrb_state *mrb, struct mrb_context *c);
#define MRB_GC_ARENA_SIZE 100
#endif
#ifndef MRB_GRAY_STACK_SIZE
#define MRB_GRAY_STACK_SIZE 1024
#endif
typedef enum {
MRB_GC_STATE_ROOT = 0,
MRB_GC_STATE_MARK,
@@ -42,10 +38,8 @@ typedef struct mrb_gc {
struct mrb_heap_page *heaps; /* all heaps pages */
struct mrb_heap_page *free_heaps;/* heaps for allocation */
struct mrb_heap_page *sweeps; /* page where sweep starts */
struct mrb_heap_region *regions; /* contiguous heap regions */
struct RBasic *gray_stack[MRB_GRAY_STACK_SIZE]; /* stack of gray objects */
size_t gray_stack_top; /* top index of gray stack */
mrb_bool gray_overflow:1; /* gray stack overflowed; needs heap rescan */
struct RBasic *gray_list; /* list of gray objects to be traversed incrementally */
struct RBasic *atomic_gray_list; /* list of objects to be traversed atomically */
size_t live; /* count of live objects */
size_t live_after_mark; /* old generation objects */
size_t threshold; /* threshold to start GC */
@@ -70,7 +64,6 @@ typedef struct mrb_gc {
} mrb_gc;
MRB_API mrb_bool mrb_object_dead_p(struct mrb_state *mrb, struct RBasic *object);
MRB_API int mrb_gc_add_region(struct mrb_state *mrb, void *start, size_t size);
#define MRB_GC_RED 7
+1 -1
View File
@@ -14,7 +14,7 @@
*/
MRB_BEGIN_DECL
/* offset of `iv` must match struct RObject */
/* offset of `iv` must be 3 words */
struct RHash {
MRB_OBJECT_HEADER;
#ifdef MRB_64BIT
+3 -24
View File
@@ -12,6 +12,8 @@ void mrb_ary_decref(mrb_state*, mrb_shared_array*);
mrb_value mrb_ary_subseq(mrb_state *mrb, mrb_value ary, mrb_int beg, mrb_int len);
#endif
mrb_bool mrb_inspect_recursive_p(mrb_state *mrb, mrb_value self);
#ifdef MRUBY_CLASS_H
struct RClass *mrb_vm_define_class(mrb_state*, mrb_value, mrb_value, mrb_sym);
struct RClass *mrb_vm_define_module(mrb_state*, mrb_value, mrb_sym);
@@ -131,11 +133,6 @@ mrb_value mrb_as_rational(mrb_state *mrb, mrb_value x);
void mrb_rational_copy(mrb_state *mrb, mrb_value x, mrb_value y);
int mrb_rational_mark(mrb_state *mrb, struct RBasic *rat);
#endif
#ifdef MRB_USE_SET
size_t mrb_gc_mark_set(mrb_state *mrb, struct RBasic *set);
void mrb_gc_free_set(mrb_state *mrb, struct RBasic *set);
size_t mrb_set_memsize(mrb_value);
#endif
#ifdef MRUBY_PROC_H
struct RProc *mrb_closure_new(mrb_state*, const mrb_irep*);
@@ -163,13 +160,9 @@ mrb_value mrb_str_inspect(mrb_state *mrb, mrb_value str);
mrb_bool mrb_str_beg_len(mrb_int str_len, mrb_int *begp, mrb_int *lenp);
mrb_value mrb_str_byte_subseq(mrb_state *mrb, mrb_value str, mrb_int beg, mrb_int len);
mrb_value mrb_str_aref(mrb_state *mrb, mrb_value str, mrb_value idx, mrb_value len);
mrb_bool mrb_strcasecmp_p(const char *s1, mrb_int len1, const char *s2, mrb_int len2);
#define MRB_STR_CASECMP_P(str, lit) \
mrb_strcasecmp_p(RSTRING_PTR(str), RSTRING_LEN(str), lit, sizeof(lit"")-1)
uint32_t mrb_byte_hash(const uint8_t*, mrb_int);
uint32_t mrb_byte_hash_step(const uint8_t*, mrb_int, uint32_t);
mrb_int mrb_utf8_to_buf(char *buf, uint32_t cp);
#ifdef MRB_UTF8_STRING
mrb_int mrb_utf8len(const char *str, const char *end);
mrb_int mrb_utf8_strlen(const char *str, mrb_int byte_len);
@@ -182,6 +175,7 @@ mrb_value mrb_vm_cv_get(mrb_state*, mrb_sym);
void mrb_vm_cv_set(mrb_state*, mrb_sym, mrb_value);
mrb_value mrb_vm_const_get(mrb_state*, mrb_sym);
size_t mrb_obj_iv_tbl_memsize(mrb_value);
mrb_value mrb_obj_iv_inspect(mrb_state*, struct RObject*);
void mrb_obj_iv_set_force(mrb_state *mrb, struct RObject *obj, mrb_sym sym, mrb_value v);
mrb_value mrb_mod_constants(mrb_state *mrb, mrb_value mod);
mrb_value mrb_mod_const_at(mrb_state *mrb, struct RClass *c, mrb_value ary);
@@ -199,10 +193,6 @@ void mrb_gc_free_gv(mrb_state*);
size_t mrb_gc_mark_iv(mrb_state*, struct RObject*);
void mrb_gc_free_iv(mrb_state*, struct RObject*);
/* IV shape tree */
void mrb_init_shape(mrb_state*);
void mrb_free_shape(mrb_state*);
/* VM */
#define MRB_CI_VISIBILITY(ci) MRB_FLAGS_GET((ci)->vis, 0, 2)
#define MRB_CI_SET_VISIBILITY(ci, visi) MRB_FLAGS_SET((ci)->vis, 0, 2, visi)
@@ -266,17 +256,6 @@ void mrb_bint_copy(mrb_state *mrb, mrb_value x, mrb_value y);
size_t mrb_bint_memsize(mrb_value x);
mrb_value mrb_bint_hash(mrb_state *mrb, mrb_value x);
mrb_value mrb_bint_sqrt(mrb_state *mrb, mrb_value x);
mrb_int mrb_bint_size(mrb_state *mrb, mrb_value bint);
mrb_value mrb_bint_from_bytes(mrb_state *mrb, const uint8_t *bytes, mrb_int len);
mrb_int mrb_bint_sign(mrb_state *mrb, mrb_value bint);
mrb_value mrb_bint_gcd(mrb_state *mrb, mrb_value x, mrb_value y);
mrb_value mrb_bint_lcm(mrb_state *mrb, mrb_value x, mrb_value y);
mrb_value mrb_bint_abs(mrb_state *mrb, mrb_value x);
#endif
#ifdef MRB_USE_TASK_SCHEDULER
/* GC marking for task scheduler */
void mrb_task_mark_all(mrb_state *mrb);
#endif
#endif /* MRUBY_INTERNAL_H */
-1
View File
@@ -82,7 +82,6 @@ struct mrb_irep {
#define MRB_ISEQ_NO_FREE 1
#define MRB_IREP_NO_FREE 2
#define MRB_IREP_STATIC (MRB_ISEQ_NO_FREE | MRB_IREP_NO_FREE)
#define MRB_IREP_CONSOLIDATED 4 /* pool/syms/reps packed with irep struct */
MRB_API mrb_irep *mrb_add_irep(mrb_state *mrb);
+111 -262
View File
@@ -20,13 +20,12 @@ MRB_BEGIN_DECL
typedef uint32_t khint_t;
typedef khint_t khiter_t;
#ifndef KHASH_INITIAL_SIZE
# define KHASH_INITIAL_SIZE 32
#ifndef KHASH_DEFAULT_SIZE
# define KHASH_DEFAULT_SIZE 8
#endif
#define KHASH_MIN_SIZE 8
#define KHASH_SMALL_LIMIT 4
#define KH_UPPER_BOUND(x) ((x) - ((x)>>3)) /* 87.5% load factor */
#define UPPER_BOUND(x) ((x)>>2|(x)>>1)
/* extern uint8_t __m[]; */
@@ -49,21 +48,7 @@ static const uint8_t __m_either[] = {0x03, 0x0c, 0x30, 0xc0};
v++;\
} while (0)
#define khash_mask(h) ((h)->n_buckets-1)
#define khash_upper_bound(h) (KH_UPPER_BOUND((h)->n_buckets))
/* BREAKING CHANGE: khash structure optimized for 50% memory reduction
*
* The structure now uses a single data pointer instead of separate keys,
* vals, and ed_flags pointers, reducing size from 32 to 16 bytes.
*
* MIGRATION REQUIRED for field access macros:
* - OLD: kh_key(h, x) NEW: kh_key(typename, h, x)
* - OLD: kh_val(h, x) NEW: kh_val(typename, h, x)
* - OLD: kh_exist(h, x) NEW: kh_exist(typename, h, x)
* - OLD: KHASH_FOREACH() NEW: KHASH_FOREACH(typename, ...)
*
* Function-style macros (kh_get, kh_put, etc.) remain unchanged.
*/
#define khash_upper_bound(h) (UPPER_BOUND((h)->n_buckets))
/* declare struct kh_xxx and kh_xxx_funcs
@@ -74,22 +59,13 @@ static const uint8_t __m_either[] = {0x03, 0x0c, 0x30, 0xc0};
*/
#define KHASH_DECLARE(name, khkey_t, khval_t, kh_is_map) \
typedef struct kh_##name { \
void *data; /* Single allocation: [keys][vals][flags] */ \
khint_t n_buckets; /* Number of buckets (power of 2) */ \
khint_t size; /* Number of elements */ \
khint_t n_buckets; \
khint_t size; \
uint8_t *ed_flags; \
khkey_t *keys; \
khval_t *vals; \
} kh_##name##_t; \
/* Address calculation functions for optimized memory layout */ \
static inline khkey_t* kh_keys_##name(const kh_##name##_t *h) { \
return (khkey_t*)(h)->data; \
} \
static inline khval_t* kh_vals_##name(const kh_##name##_t *h) { \
return kh_is_map ? \
(khval_t*)((uint8_t*)(h)->data + sizeof(khkey_t) * (h)->n_buckets) : NULL; \
} \
static inline uint8_t* kh_flags_##name(const kh_##name##_t *h) { \
return (uint8_t*)(h)->data + sizeof(khkey_t) * (h)->n_buckets + \
(kh_is_map ? sizeof(khval_t) * (h)->n_buckets : 0); \
} \
void kh_alloc_##name(mrb_state *mrb, kh_##name##_t *h); \
kh_##name##_t *kh_init_##name##_size(mrb_state *mrb, khint_t size); \
kh_##name##_t *kh_init_##name(mrb_state *mrb); \
void kh_destroy_##name(mrb_state *mrb, kh_##name##_t *h); \
@@ -98,10 +74,15 @@ static const uint8_t __m_either[] = {0x03, 0x0c, 0x30, 0xc0};
khint_t kh_put_##name(mrb_state *mrb, kh_##name##_t *h, khkey_t key, int *ret); \
void kh_resize_##name(mrb_state *mrb, kh_##name##_t *h, khint_t new_n_buckets); \
void kh_del_##name(mrb_state *mrb, kh_##name##_t *h, khint_t x); \
kh_##name##_t *kh_copy_##name(mrb_state *mrb, kh_##name##_t *h); \
void kh_init_data_##name(mrb_state *mrb, kh_##name##_t *h, khint_t size); \
void kh_destroy_data_##name(mrb_state *mrb, kh_##name##_t *h); \
void kh_replace_##name(mrb_state *mrb, kh_##name##_t *dst, const kh_##name##_t *src);
kh_##name##_t *kh_copy_##name(mrb_state *mrb, kh_##name##_t *h);
static inline void
kh_fill_flags(uint8_t *p, uint8_t c, size_t len)
{
while (len-- > 0) {
*p++ = c;
}
}
/* define kh_xxx_funcs
@@ -114,136 +95,64 @@ static const uint8_t __m_either[] = {0x03, 0x0c, 0x30, 0xc0};
*/
#define KHASH_DEFINE(name, khkey_t, khval_t, kh_is_map, __hash_func, __hash_equal) \
mrb_noreturn void mrb_raise_nomemory(mrb_state *mrb); \
/* Internal helper functions */ \
static inline size_t kh__kv_size_##name(khint_t count) { \
return sizeof(khkey_t) * count + \
(kh_is_map ? sizeof(khval_t) * count : 0); \
} \
static inline size_t kh__htable_size_##name(khint_t n_buckets) { \
return kh__kv_size_##name(n_buckets) + n_buckets / 4; \
} \
static inline void kh__mark_occupied_##name(kh_##name##_t *h, khint_t i) { \
uint8_t *flags = kh_flags_##name(h); \
flags[i/4] &= ~__m_either[i%4]; /* Clear both empty and deleted bits */ \
} \
static inline void kh__mark_deleted_##name(kh_##name##_t *h, khint_t i) { \
uint8_t *flags = kh_flags_##name(h); \
flags[i/4] |= __m_del[i%4]; /* Set deleted bit */ \
} \
static inline khint_t kh__key_idx_##name(mrb_state *mrb, khkey_t key, kh_##name##_t *h) { \
return __hash_func(mrb, key) & khash_mask(h); \
} \
static inline khint_t kh__next_probe_##name(khint_t k, khint_t *step, kh_##name##_t *h) { \
return (k+(++(*step))) & khash_mask(h); \
} \
static inline khint_t kh__insert_key_##name(kh_##name##_t *h, khint_t index, khkey_t key) { \
khkey_t *keys = kh_keys_##name(h); \
keys[index] = key; \
kh__mark_occupied_##name(h, index); \
h->size++; \
return index; \
} \
static inline void kh__clear_flags_##name(kh_##name##_t *h, khint_t n_buckets) { \
memset(kh_flags_##name(h), 0xaa, n_buckets/4); \
} \
static inline void kh__alloc_##name(mrb_state *mrb, kh_##name##_t *h) { \
int kh_alloc_simple_##name(mrb_state *mrb, kh_##name##_t *h) \
{ \
khint_t sz = h->n_buckets; \
uint8_t *p = (uint8_t*)mrb_malloc(mrb, kh__htable_size_##name(sz)); \
size_t len = sizeof(khkey_t) + (kh_is_map ? sizeof(khval_t) : 0); \
uint8_t *p = (uint8_t*)mrb_malloc_simple(mrb, sizeof(uint8_t)*sz/4+len*sz); \
if (!p) { return 1; } \
h->size = 0; \
h->data = p; /* Single data pointer for optimized layout */ \
kh__clear_flags_##name(h, sz); \
h->keys = (khkey_t*)p; \
h->vals = kh_is_map ? (khval_t*)(p+sizeof(khkey_t)*sz) : NULL; \
h->ed_flags = p+len*sz; \
kh_fill_flags(h->ed_flags, 0xaa, sz/4); \
return 0; \
} \
/* Small table optimization functions */ \
static inline int kh__is_small_##name(const kh_##name##_t *h) { \
return h->n_buckets == 0; /* Small table marker */ \
} \
static inline khint_t kh__get_small_##name(mrb_state *mrb, kh_##name##_t *h, khkey_t key) { \
khkey_t *keys = kh_keys_##name(h); \
for (khint_t i = 0; i < h->size; i++) { \
if (__hash_equal(mrb, keys[i], key)) return i; \
void kh_alloc_##name(mrb_state *mrb, kh_##name##_t *h) \
{ \
if (kh_alloc_simple_##name(mrb, h)) { \
mrb_raise_nomemory(mrb); \
} \
return h->size; /* Not found - return end position */ \
} \
static inline void kh__rebuild_##name(mrb_state *mrb, kh_##name##_t *h, khint_t new_n_buckets) { \
kh_##name##_t hh; \
hh.data = NULL; \
hh.size = 0; \
kh_init_data_##name(mrb, &hh, new_n_buckets); \
/* Rehash from old 'h' to 'hh' */ \
khkey_t *old_keys = kh_keys_##name(h); \
khval_t *old_vals = kh_vals_##name(h); \
uint8_t *old_flags = kh__is_small_##name(h) ? NULL : kh_flags_##name(h); \
khint_t limit = old_flags ? h->n_buckets : h->size; \
for (khint_t i = 0; i < limit; i++) { \
if (old_flags && __ac_iseither(old_flags, i)) continue; \
khint_t k = kh_put_##name(mrb, &hh, old_keys[i], NULL); \
if (kh_is_map) { \
kh_val(name, &hh, k) = old_vals[i]; \
} \
} \
/* Final Swap */ \
mrb_free(mrb, h->data); \
h->data = hh.data; \
h->n_buckets = hh.n_buckets; \
h->size = hh.size; \
} \
static inline khint_t kh__put_small_##name(mrb_state *mrb, kh_##name##_t *h, khkey_t key, int *ret) { \
/* First check if key exists */ \
khint_t pos = kh__get_small_##name(mrb, h, key); \
if (pos < h->size) { \
if (ret) *ret = 0; /* Key exists */ \
return pos; \
} \
/* Check if we need to convert to hash table */ \
if (h->size >= KHASH_SMALL_LIMIT) { \
/* Convert from small table to hash table */ \
kh__rebuild_##name(mrb, h, KHASH_MIN_SIZE); \
/* Now add the new key using regular hash table */ \
return kh_put_##name(mrb, h, key, ret); \
} \
/* Add new element to small table */ \
khkey_t *keys = kh_keys_##name(h); \
keys[h->size] = key; \
h->size++; \
if (ret) *ret = 1; /* New key */ \
return h->size - 1; \
} \
kh_##name##_t *kh_init_##name##_size(mrb_state *mrb, khint_t size) { \
kh_##name##_t *h = (kh_##name##_t*)mrb_calloc(mrb, 1, sizeof(kh_##name##_t)); \
kh_init_data_##name(mrb, h, size); \
if (size < KHASH_MIN_SIZE) \
size = KHASH_MIN_SIZE; \
khash_power2(size); \
h->n_buckets = size; \
if (kh_alloc_simple_##name(mrb, h)) { \
mrb_free(mrb, h); \
mrb_raise_nomemory(mrb); \
} \
return h; \
} \
kh_##name##_t *kh_init_##name(mrb_state *mrb) { \
return kh_init_##name##_size(mrb, KHASH_INITIAL_SIZE); \
return kh_init_##name##_size(mrb, KHASH_DEFAULT_SIZE); \
} \
void kh_destroy_##name(mrb_state *mrb, kh_##name##_t *h) \
{ \
kh_destroy_data_##name(mrb, h); \
mrb_free(mrb, h); \
if (h) { \
mrb_free(mrb, h->keys); \
mrb_free(mrb, h); \
} \
} \
void kh_clear_##name(mrb_state *mrb, kh_##name##_t *h) \
{ \
(void)mrb; \
if (h && h->data) { \
kh__clear_flags_##name(h, h->n_buckets); \
if (h && h->ed_flags) { \
kh_fill_flags(h->ed_flags, 0xaa, h->n_buckets/4); \
h->size = 0; \
} \
} \
khint_t kh_get_##name(mrb_state *mrb, kh_##name##_t *h, khkey_t key) \
{ \
if (kh__is_small_##name(h)) { \
return kh__get_small_##name(mrb, h, key); \
} \
/* Cache calculated pointers for performance */ \
khkey_t *keys = kh_keys_##name(h); \
uint8_t *ed_flags = kh_flags_##name(h); \
khint_t k = kh__key_idx_##name(mrb, key, h), step = 0; \
khint_t k = __hash_func(mrb,key) & khash_mask(h), step = 0; \
(void)mrb; \
while (!__ac_isempty(ed_flags, k)) { \
if (!__ac_isdel(ed_flags, k)) { \
if (__hash_equal(mrb, keys[k], key)) return k; \
while (!__ac_isempty(h->ed_flags, k)) { \
if (!__ac_isdel(h->ed_flags, k)) { \
if (__hash_equal(mrb,h->keys[k], key)) return k; \
} \
k = kh__next_probe_##name(k, &step, h); \
k = (k+(++step)) & khash_mask(h); \
} \
return kh_end(h); \
} \
@@ -252,25 +161,38 @@ static const uint8_t __m_either[] = {0x03, 0x0c, 0x30, 0xc0};
if (new_n_buckets < KHASH_MIN_SIZE) \
new_n_buckets = KHASH_MIN_SIZE; \
khash_power2(new_n_buckets); \
kh__rebuild_##name(mrb, h, new_n_buckets); \
{ \
kh_##name##_t hh; \
uint8_t *old_ed_flags = h->ed_flags; \
khkey_t *old_keys = h->keys; \
khval_t *old_vals = h->vals; \
khint_t old_n_buckets = h->n_buckets; \
khint_t i; \
hh.n_buckets = new_n_buckets; \
kh_alloc_##name(mrb, &hh); \
/* relocate */ \
for (i=0; i<old_n_buckets; i++) { \
if (!__ac_iseither(old_ed_flags, i)) { \
khint_t k = kh_put_##name(mrb, &hh, old_keys[i], NULL); \
if (kh_is_map) kh_value(&hh,k) = old_vals[i]; \
} \
} \
/* copy hh to h */ \
*h = hh; \
mrb_free(mrb, old_keys); \
} \
} \
khint_t kh_put_##name(mrb_state *mrb, kh_##name##_t *h, khkey_t key, int *ret) \
{ \
if (kh__is_small_##name(h)) { \
return kh__put_small_##name(mrb, h, key, ret); \
} \
khint_t k, del_k, step = 0; \
if (h->size >= khash_upper_bound(h)) { \
kh_resize_##name(mrb, h, h->n_buckets*2); \
} \
/* Cache calculated pointers for performance */ \
khkey_t *keys = kh_keys_##name(h); \
uint8_t *ed_flags = kh_flags_##name(h); \
k = kh__key_idx_##name(mrb, key, h); \
k = __hash_func(mrb,key) & khash_mask(h); \
del_k = kh_end(h); \
while (!__ac_isempty(ed_flags, k)) { \
if (!__ac_isdel(ed_flags, k)) { \
if (__hash_equal(mrb, keys[k], key)) { \
while (!__ac_isempty(h->ed_flags, k)) { \
if (!__ac_isdel(h->ed_flags, k)) { \
if (__hash_equal(mrb,h->keys[k], key)) { \
if (ret) *ret = 0; \
return k; \
} \
@@ -278,17 +200,21 @@ static const uint8_t __m_either[] = {0x03, 0x0c, 0x30, 0xc0};
else if (del_k == kh_end(h)) { \
del_k = k; \
} \
k = kh__next_probe_##name(k, &step, h); \
k = (k+(++step)) & khash_mask(h); \
} \
if (del_k != kh_end(h)) { \
/* put at del */ \
kh__insert_key_##name(h, del_k, key); \
h->keys[del_k] = key; \
h->ed_flags[del_k/4] &= ~__m_del[del_k%4]; \
h->size++; \
if (ret) *ret = 2; \
return del_k; \
} \
else { \
/* put at empty */ \
kh__insert_key_##name(h, k, key); \
h->keys[k] = key; \
h->ed_flags[k/4] &= ~__m_empty[k%4]; \
h->size++; \
if (ret) *ret = 1; \
return k; \
} \
@@ -296,81 +222,23 @@ static const uint8_t __m_either[] = {0x03, 0x0c, 0x30, 0xc0};
void kh_del_##name(mrb_state *mrb, kh_##name##_t *h, khint_t x) \
{ \
(void)mrb; \
if (kh__is_small_##name(h)) { \
/* Small table deletion: shift elements down */ \
mrb_assert(x < h->size); \
khkey_t *keys = kh_keys_##name(h); \
khval_t *vals = kh_vals_##name(h); \
for (khint_t i = x; i < h->size - 1; i++) { \
keys[i] = keys[i + 1]; \
if (kh_is_map) vals[i] = vals[i + 1]; \
} \
h->size--; \
} \
else { \
/* Regular hash table deletion */ \
mrb_assert(x != h->n_buckets && !__ac_iseither(kh_flags_##name(h), x)); \
kh__mark_deleted_##name(h, x); \
h->size--; \
} \
mrb_assert(x != h->n_buckets && !__ac_iseither(h->ed_flags, x)); \
h->ed_flags[x/4] |= __m_del[x%4]; \
h->size--; \
} \
kh_##name##_t *kh_copy_##name(mrb_state *mrb, kh_##name##_t *h) \
{ \
kh_##name##_t *h2 = (kh_##name##_t*)mrb_calloc(mrb, 1, sizeof(kh_##name##_t)); \
kh_replace_##name(mrb, h2, h); \
kh_##name##_t *h2; \
khiter_t k, k2; \
\
h2 = kh_init_##name(mrb); \
for (k = kh_begin(h); k != kh_end(h); k++) { \
if (kh_exist(h, k)) { \
k2 = kh_put_##name(mrb, h2, kh_key(h, k), NULL); \
if (kh_is_map) kh_value(h2, k2) = kh_value(h, k); \
} \
} \
return h2; \
} \
void kh_init_data_##name(mrb_state *mrb, kh_##name##_t *h, khint_t size) { \
if (size <= KHASH_SMALL_LIMIT) { \
/* Start as small table */ \
h->n_buckets = 0; /* Small table marker */ \
h->data = mrb_malloc(mrb, kh__kv_size_##name(KHASH_SMALL_LIMIT)); \
h->size = 0; \
} \
else { \
/* Start as regular hash table */ \
if (size < KHASH_MIN_SIZE) \
size = KHASH_MIN_SIZE; \
khash_power2(size); \
h->n_buckets = size; \
kh__alloc_##name(mrb, h); \
} \
} \
void kh_destroy_data_##name(mrb_state *mrb, kh_##name##_t *h) \
{ \
if (h && h->data) { \
mrb_free(mrb, h->data); /* Free only the data allocation */ \
h->data = NULL; \
} \
} \
void kh_replace_##name(mrb_state *mrb, kh_##name##_t *dst, const kh_##name##_t *src) \
{ \
if (!src || (src->n_buckets == 0 && src->size == 0)) { \
/* Empty source */ \
kh_destroy_data_##name(mrb, dst); \
dst->data = NULL; \
dst->n_buckets = 0; \
dst->size = 0; \
} \
else if (src->n_buckets == 0) { \
/* Small table case */ \
size_t data_size = kh__kv_size_##name(KHASH_SMALL_LIMIT); \
dst->data = mrb_realloc(mrb, dst->data, data_size); \
dst->size = src->size; \
dst->n_buckets = 0; \
/* Copy only the used portion of keys and values */ \
size_t copy_size = kh__kv_size_##name(src->size); \
memcpy(dst->data, src->data, copy_size); \
} \
else { \
/* Regular hash table case */ \
size_t data_size = kh__htable_size_##name(src->n_buckets); \
dst->data = mrb_realloc(mrb, dst->data, data_size); \
dst->size = src->size; \
dst->n_buckets = src->n_buckets; \
/* Copy the entire data block: [keys][vals][flags] */ \
memcpy(dst->data, src->data, data_size); \
} \
}
@@ -386,28 +254,13 @@ static const uint8_t __m_either[] = {0x03, 0x0c, 0x30, 0xc0};
#define kh_get(name, mrb, h, k) kh_get_##name(mrb, h, k)
#define kh_del(name, mrb, h, k) kh_del_##name(mrb, h, k)
#define kh_copy(name, mrb, h) kh_copy_##name(mrb, h)
#define kh_init_data(name, mrb, h, size) kh_init_data_##name(mrb, h, size)
#define kh_destroy_data(name, mrb, h) kh_destroy_data_##name(mrb, h)
#define kh_replace(name, mrb, dst, src) kh_replace_##name(mrb, dst, src)
/* BREAKING CHANGE: Field access macros now require type name as first parameter
* The macros keep their familiar names but now need the hash type name.
*
* MIGRATION: Add type name as first parameter:
* kh_key(h, x) -> kh_key(typename, h, x)
* kh_val(h, x) -> kh_val(typename, h, x)
* kh_exist(h, x) -> kh_exist(typename, h, x)
* kh_value(h, x) -> kh_value(typename, h, x)
*/
/* Type-aware access macros - same names, now with type parameter */
#define kh_exist(name, h, x) ((h)->n_buckets == 0 ? ((x) < (h)->size) : (!__ac_iseither(kh_flags_##name(h), (x))))
#define kh_key(name, h, x) (kh_keys_##name(h)[x])
#define kh_val(name, h, x) (kh_vals_##name(h)[x])
#define kh_value(name, h, x) (kh_vals_##name(h)[x])
#define kh_exist(h, x) (!__ac_iseither((h)->ed_flags, (x)))
#define kh_key(h, x) ((h)->keys[x])
#define kh_val(h, x) ((h)->vals[x])
#define kh_value(h, x) ((h)->vals[x])
#define kh_begin(h) (khint_t)(0)
#define kh_end(h) ((h)->n_buckets == 0 ? (h)->size : (h)->n_buckets)
#define kh_is_end(h, i) ((i) >= kh_end(h))
#define kh_end(h) ((h)->n_buckets)
#define kh_size(h) ((h)->size)
#define kh_n_buckets(h) ((h)->n_buckets)
@@ -432,24 +285,20 @@ MRB_END_DECL
* Macro for iterating over all elements in a khash.
*
* Usage:
* KHASH_FOREACH(typename, kh, k) {
* KHASH_FOREACH(mrb, kh, k) {
* // k is the khiter_t iterator
* // Access the key with kh_key(typename, kh, k)
* // Access the value with kh_val(typename, kh, k) if applicable
* // Access the key with kh_key(kh, k)
* // Access the value with kh_val(kh, k) if applicable
* // Your code here
* }
*
* @param name The hash type name
* @param kh The khash to iterate over
* @param k The name to use for the khiter_t iterator variable
* @param mrb The mrb_state
* @param kh The khash to iterate over
* @param k The name to use for the khiter_t iterator variable
*/
/* BREAKING CHANGE: KHASH_FOREACH now requires type name as first parameter
* OLD: KHASH_FOREACH(mrb, kh, k)
* NEW: KHASH_FOREACH(name, kh, k)
*/
#define KHASH_FOREACH(name, kh, k) \
#define KHASH_FOREACH(mrb, kh, k) \
if (kh) \
for (khiter_t k = kh_begin(kh); !kh_is_end(kh, k); k++) \
if (kh_exist(name, kh, k))
for (khiter_t k = kh_begin(kh); k != kh_end(kh); k++) \
if (kh_exist(kh, k))
#endif /* MRUBY_KHASH_H */
+1 -1
View File
@@ -119,12 +119,12 @@ mrb_int_mul_overflow(mrb_int a, mrb_int b, mrb_int *c)
*c = (mrb_int)n;
return n > MRB_INT_MAX || n < MRB_INT_MIN;
#else /* MRB_INT64 */
*c = a * b;
if (a > 0 && b > 0 && a > MRB_INT_MAX / b) return TRUE;
if (a < 0 && b > 0 && a < MRB_INT_MIN / b) return TRUE;
if (a > 0 && b < 0 && b < MRB_INT_MIN / a) return TRUE;
if (a < 0 && b < 0 && (a <= MRB_INT_MIN || b <= MRB_INT_MIN || -a > MRB_INT_MAX / -b))
return TRUE;
*c = a * b;
return FALSE;
#endif
}
+3 -9
View File
@@ -9,6 +9,7 @@
#define MRB_OBJECT_HEADER \
struct RClass *c; \
struct RBasic *gcnext; \
enum mrb_vtype tt:8; \
unsigned int gc_color:3; \
unsigned int frozen:1; \
@@ -24,13 +25,6 @@ struct RBasic {
#define MRB_OBJ_IS_FROZEN 1
#define mrb_frozen_p(o) ((o)->frozen)
/* Object shape flag -- when set, obj->iv is shaped, not iv_tbl* */
/* Bit 5: avoids conflict with MRB_INSTANCE_TT_MASK (bits 0-4);
but conflicts with MRB_HASH_AR_EA_N_USED on 32-bit, so the
predicate must also check tt to avoid false positives */
#define MRB_FL_OBJ_SHAPED (1 << 5)
#define MRB_OBJ_SHAPED_P(o) ((o)->tt == MRB_TT_OBJECT && ((o)->flags & MRB_FL_OBJ_SHAPED))
struct RObject {
MRB_OBJECT_HEADER;
struct iv_tbl *iv;
@@ -45,7 +39,7 @@ struct RFiber {
};
#define mrb_static_assert_object_size(st) \
mrb_static_assert(sizeof(st) <= sizeof(void*) * 5, \
#st " size must be within 5 words")
mrb_static_assert(sizeof(st) <= sizeof(void*) * 6, \
#st " size must be within 6 words")
#endif /* MRUBY_OBJECT_H */
-4
View File
@@ -13,10 +13,6 @@ enum mrb_insn {
#undef OPCODE
};
/* backward compatibility aliases */
#define OP_LOADT OP_LOADTRUE
#define OP_LOADF OP_LOADFALSE
#define OP_L_STRICT 1
#define OP_L_CAPTURE 2
#define OP_L_METHOD OP_L_STRICT
+3 -16
View File
@@ -31,8 +31,8 @@ OPCODE(LOADI32, BSS) /* R[a] = mrb_int((b<<16)+c) */
OPCODE(LOADSYM, BB) /* R[a] = Syms[b] */
OPCODE(LOADNIL, B) /* R[a] = nil */
OPCODE(LOADSELF, B) /* R[a] = self */
OPCODE(LOADTRUE, B) /* R[a] = true */
OPCODE(LOADFALSE, B) /* R[a] = false */
OPCODE(LOADT, B) /* R[a] = true */
OPCODE(LOADF, B) /* R[a] = false */
OPCODE(GETGV, BB) /* R[a] = getglobal(Syms[b]) */
OPCODE(SETGV, BB) /* setglobal(Syms[b], R[a]) */
OPCODE(GETSV, BB) /* R[a] = Special[Syms[b]] */
@@ -48,7 +48,6 @@ OPCODE(SETMCNST, BB) /* R[a+1]::Syms[b] = R[a] */
OPCODE(GETUPVAR, BBB) /* R[a] = uvget(b,c) */
OPCODE(SETUPVAR, BBB) /* uvset(b,c,R[a]) */
OPCODE(GETIDX, B) /* R[a] = R[a][R[a+1]] */
OPCODE(GETIDX0, BB) /* R[a] = R[b][0]; a+1 for method call */
OPCODE(SETIDX, B) /* R[a][R[a+1]] = R[a+2] */
OPCODE(JMP, S) /* pc+=a */
OPCODE(JMPIF, BS) /* if R[a] pc+=b */
@@ -58,35 +57,25 @@ OPCODE(JMPUW, S) /* unwind_and_jump_to(a) */
OPCODE(EXCEPT, B) /* R[a] = exc */
OPCODE(RESCUE, BB) /* R[b] = R[a].isa?(R[b]) */
OPCODE(RAISEIF, B) /* raise(R[a]) if R[a] */
OPCODE(MATCHERR, B) /* raise NoMatchingPatternError unless R[a] */
OPCODE(SSEND, BBB) /* R[a] = self.send(Syms[b],R[a+1]..,R[a+n+1]:R[a+n+2]..) (c=n|k<<4) */
OPCODE(SSEND0, BB) /* R[a] = self.send(Syms[b]) (no args) */
OPCODE(SSENDB, BBB) /* R[a] = self.send(Syms[b],R[a+1]..,R[a+n+1]:R[a+n+2]..,&R[a+n+2k+1]) */
OPCODE(SEND, BBB) /* R[a] = R[a].send(Syms[b],R[a+1]..,R[a+n+1]:R[a+n+2]..) (c=n|k<<4) */
OPCODE(SEND0, BB) /* R[a] = R[a].send(Syms[b]) (no args) */
OPCODE(SENDB, BBB) /* R[a] = R[a].send(Syms[b],R[a+1]..,R[a+n+1]:R[a+n+2]..,&R[a+n+2k+1]) */
OPCODE(CALL, Z) /* self.call(*, **, &) (But overlay the current call frame; tailcall) */
OPCODE(BLKCALL, BB) /* R[a] = R[a].call(R[a+1],... ,R[a+b]); direct block call */
OPCODE(SUPER, BB) /* R[a] = super(R[a+1],... ,R[a+b+1]) */
OPCODE(ARGARY, BS) /* R[a] = argument array (16=m5:r1:m5:d1:lv4) */
OPCODE(ENTER, W) /* arg setup according to flags (24=n1:m5:o5:r1:m5:k5:d1:b1) */
OPCODE(ENTER, W) /* arg setup according to flags (23=m5:o5:r1:m5:k5:d1:b1) */
OPCODE(KEY_P, BB) /* R[a] = kdict.key?(Syms[b]) */
OPCODE(KEYEND, Z) /* raise unless kdict.empty? */
OPCODE(KARG, BB) /* R[a] = kdict[Syms[b]]; kdict.delete(Syms[b]) */
OPCODE(RETURN, B) /* return R[a] (normal) */
OPCODE(RETURN_BLK, B) /* return R[a] (in-block return) */
OPCODE(RETSELF, Z) /* return self */
OPCODE(RETNIL, Z) /* return nil */
OPCODE(RETTRUE, Z) /* return true */
OPCODE(RETFALSE, Z) /* return false */
OPCODE(BREAK, B) /* break R[a] */
OPCODE(BLKPUSH, BS) /* R[a] = block (16=m5:r1:m5:d1:lv4) */
OPCODE(ADD, B) /* R[a] = R[a]+R[a+1] */
OPCODE(ADDI, BB) /* R[a] = R[a]+mrb_int(b) */
OPCODE(SUB, B) /* R[a] = R[a]-R[a+1] */
OPCODE(SUBI, BB) /* R[a] = R[a]-mrb_int(b) */
OPCODE(ADDILV, BBB) /* R[a] = R[a]+mrb_int(c); R[b],R[b+1] for method call */
OPCODE(SUBILV, BBB) /* R[a] = R[a]-mrb_int(c); R[b],R[b+1] for method call */
OPCODE(MUL, B) /* R[a] = R[a]*R[a+1] */
OPCODE(DIV, B) /* R[a] = R[a]/R[a+1] */
OPCODE(EQ, B) /* R[a] = R[a]==R[a+1] */
@@ -119,8 +108,6 @@ OPCODE(CLASS, BB) /* R[a] = newclass(R[a],Syms[b],R[a+1]) */
OPCODE(MODULE, BB) /* R[a] = newmodule(R[a],Syms[b]) */
OPCODE(EXEC, BB) /* R[a] = blockexec(R[a],Irep[b]) */
OPCODE(DEF, BB) /* R[a].newmethod(Syms[b],R[a+1]); R[a] = Syms[b] */
OPCODE(TDEF, BBB) /* target_class.newmethod(Syms[b],Irep[c]); R[a] = Syms[b] */
OPCODE(SDEF, BBB) /* R[a].singleton_class.newmethod(Syms[b],Irep[c]); R[a] = Syms[b] */
OPCODE(ALIAS, BB) /* alias_method(target_class,Syms[a],Syms[b]) */
OPCODE(UNDEF, B) /* undef_method(target_class,Syms[a]) */
OPCODE(SCLASS, B) /* R[a] = R[a].singleton_class */
+11 -35
View File
@@ -7,13 +7,16 @@
#ifndef MRUBY_PRESYM_H
#define MRUBY_PRESYM_H
#if !defined(MRB_PRESYM_SCANNING)
#include <mruby/presym/id.h>
#if defined(MRB_NO_PRESYM)
# include <mruby/presym/disable.h>
#elif !defined(MRB_PRESYM_SCANNING)
# include <mruby/presym/enable.h>
#endif
/*
* Where `mrb_intern_lit` is allowed for symbol interning, it is directly
* replaced by the symbol ID using the following macros.
* replaced by the symbol ID if presym is enabled by using the following
* macros.
*
* MRB_OPSYM(xor) //=> ^ (Operator)
* MRB_GVSYM(xor) //=> $xor (Global Variable)
@@ -29,37 +32,10 @@
* can be specified for it). Other than that, describe only word characters
* excluding leading and ending punctuation.
*
* These macros are expanded to compile-time integer constants.
* These macros are expanded to `mrb_intern_lit` if presym is disabled,
* therefore the mruby state variable is required. The above macros can be
* used when the variable name is `mrb`. If you want to use other variable
* names, you need to use macros with `_2` suffix, such as `MRB_SYM_2`.
*/
#define MRB_OPSYM(name) MRB_OPSYM__##name
#define MRB_GVSYM(name) MRB_GVSYM__##name
#define MRB_CVSYM(name) MRB_CVSYM__##name
#define MRB_IVSYM(name) MRB_IVSYM__##name
#define MRB_SYM_B(name) MRB_SYM_B__##name
#define MRB_SYM_Q(name) MRB_SYM_Q__##name
#define MRB_SYM_E(name) MRB_SYM_E__##name
#define MRB_SYM(name) MRB_SYM__##name
/* backward compatibility: _2 variants accept but ignore mrb_state* */
#define MRB_OPSYM_2(mrb, name) MRB_OPSYM(name)
#define MRB_GVSYM_2(mrb, name) MRB_GVSYM(name)
#define MRB_CVSYM_2(mrb, name) MRB_CVSYM(name)
#define MRB_IVSYM_2(mrb, name) MRB_IVSYM(name)
#define MRB_SYM_B_2(mrb, name) MRB_SYM_B(name)
#define MRB_SYM_Q_2(mrb, name) MRB_SYM_Q(name)
#define MRB_SYM_E_2(mrb, name) MRB_SYM_E(name)
#define MRB_SYM_2(mrb, name) MRB_SYM(name)
#define MRB_PRESYM_DEFINE_VAR_AND_INITER(name, size, ...) \
static const mrb_sym name[] = {__VA_ARGS__};
#define MRB_PRESYM_INIT_SYMBOLS(mrb, name) (void)(mrb)
/* use MRB_SYM() for E_RUNTIME_ERROR etc. */
#undef MRB_ERROR_SYM
#define MRB_ERROR_SYM(sym) MRB_SYM(sym)
#endif /* !MRB_PRESYM_SCANNING */
#endif /* MRUBY_PRESYM_H */
+72
View File
@@ -0,0 +1,72 @@
/**
** @file mruby/presym/disable.h - Disable Preallocated Symbols
**
** See Copyright Notice in mruby.h
*/
#ifndef MRUBY_PRESYM_DISABLE_H
#define MRUBY_PRESYM_DISABLE_H
#include <string.h>
#define MRB_PRESYM_MAX 0
#define MRB_OPSYM(name) MRB_OPSYM__##name(mrb)
#define MRB_GVSYM(name) mrb_intern_lit(mrb, "$" #name)
#define MRB_CVSYM(name) mrb_intern_lit(mrb, "@@" #name)
#define MRB_IVSYM(name) mrb_intern_lit(mrb, "@" #name)
#define MRB_SYM_B(name) mrb_intern_lit(mrb, #name "!")
#define MRB_SYM_Q(name) mrb_intern_lit(mrb, #name "?")
#define MRB_SYM_E(name) mrb_intern_lit(mrb, #name "=")
#define MRB_SYM(name) mrb_intern_lit(mrb, #name)
#define MRB_OPSYM_2(mrb, name) MRB_OPSYM__##name(mrb)
#define MRB_GVSYM_2(mrb, name) mrb_intern_lit(mrb, "$" #name)
#define MRB_CVSYM_2(mrb, name) mrb_intern_lit(mrb, "@@" #name)
#define MRB_IVSYM_2(mrb, name) mrb_intern_lit(mrb, "@" #name)
#define MRB_SYM_B_2(mrb, name) mrb_intern_lit(mrb, #name "!")
#define MRB_SYM_Q_2(mrb, name) mrb_intern_lit(mrb, #name "?")
#define MRB_SYM_E_2(mrb, name) mrb_intern_lit(mrb, #name "=")
#define MRB_SYM_2(mrb, name) mrb_intern_lit(mrb, #name)
#define MRB_OPSYM__not(mrb) mrb_intern_lit(mrb, "!")
#define MRB_OPSYM__mod(mrb) mrb_intern_lit(mrb, "%")
#define MRB_OPSYM__and(mrb) mrb_intern_lit(mrb, "&")
#define MRB_OPSYM__mul(mrb) mrb_intern_lit(mrb, "*")
#define MRB_OPSYM__add(mrb) mrb_intern_lit(mrb, "+")
#define MRB_OPSYM__sub(mrb) mrb_intern_lit(mrb, "-")
#define MRB_OPSYM__div(mrb) mrb_intern_lit(mrb, "/")
#define MRB_OPSYM__lt(mrb) mrb_intern_lit(mrb, "<")
#define MRB_OPSYM__gt(mrb) mrb_intern_lit(mrb, ">")
#define MRB_OPSYM__xor(mrb) mrb_intern_lit(mrb, "^")
#define MRB_OPSYM__tick(mrb) mrb_intern_lit(mrb, "`")
#define MRB_OPSYM__or(mrb) mrb_intern_lit(mrb, "|")
#define MRB_OPSYM__neg(mrb) mrb_intern_lit(mrb, "~")
#define MRB_OPSYM__neq(mrb) mrb_intern_lit(mrb, "!=")
#define MRB_OPSYM__nmatch(mrb) mrb_intern_lit(mrb, "!~")
#define MRB_OPSYM__andand(mrb) mrb_intern_lit(mrb, "&&")
#define MRB_OPSYM__pow(mrb) mrb_intern_lit(mrb, "**")
#define MRB_OPSYM__plus(mrb) mrb_intern_lit(mrb, "+@")
#define MRB_OPSYM__minus(mrb) mrb_intern_lit(mrb, "-@")
#define MRB_OPSYM__lshift(mrb) mrb_intern_lit(mrb, "<<")
#define MRB_OPSYM__le(mrb) mrb_intern_lit(mrb, "<=")
#define MRB_OPSYM__eq(mrb) mrb_intern_lit(mrb, "==")
#define MRB_OPSYM__match(mrb) mrb_intern_lit(mrb, "=~")
#define MRB_OPSYM__ge(mrb) mrb_intern_lit(mrb, ">=")
#define MRB_OPSYM__rshift(mrb) mrb_intern_lit(mrb, ">>")
#define MRB_OPSYM__aref(mrb) mrb_intern_lit(mrb, "[]")
#define MRB_OPSYM__oror(mrb) mrb_intern_lit(mrb, "||")
#define MRB_OPSYM__cmp(mrb) mrb_intern_lit(mrb, "<=>")
#define MRB_OPSYM__eqq(mrb) mrb_intern_lit(mrb, "===")
#define MRB_OPSYM__aset(mrb) mrb_intern_lit(mrb, "[]=")
#define MRB_PRESYM_DEFINE_VAR_AND_INITER(name, size, ...) \
static mrb_sym name[size]; \
static void presym_init_##name(mrb_state *mrb) { \
mrb_sym name__[] = {__VA_ARGS__}; \
memcpy(name, name__, sizeof(name)); \
}
#define MRB_PRESYM_INIT_SYMBOLS(mrb, name) presym_init_##name(mrb)
#endif /* MRUBY_PRESYM_DISABLE_H */
+39
View File
@@ -0,0 +1,39 @@
/**
** @file mruby/presym/enable.h - Enable Preallocated Symbols
**
** See Copyright Notice in mruby.h
*/
#ifndef MRUBY_PRESYM_ENABLE_H
#define MRUBY_PRESYM_ENABLE_H
#include <mruby/presym/id.h>
#define MRB_OPSYM(name) MRB_OPSYM__##name
#define MRB_GVSYM(name) MRB_GVSYM__##name
#define MRB_CVSYM(name) MRB_CVSYM__##name
#define MRB_IVSYM(name) MRB_IVSYM__##name
#define MRB_SYM_B(name) MRB_SYM_B__##name
#define MRB_SYM_Q(name) MRB_SYM_Q__##name
#define MRB_SYM_E(name) MRB_SYM_E__##name
#define MRB_SYM(name) MRB_SYM__##name
#define MRB_OPSYM_2(mrb, name) MRB_OPSYM__##name
#define MRB_GVSYM_2(mrb, name) MRB_GVSYM__##name
#define MRB_CVSYM_2(mrb, name) MRB_CVSYM__##name
#define MRB_IVSYM_2(mrb, name) MRB_IVSYM__##name
#define MRB_SYM_B_2(mrb, name) MRB_SYM_B__##name
#define MRB_SYM_Q_2(mrb, name) MRB_SYM_Q__##name
#define MRB_SYM_E_2(mrb, name) MRB_SYM_E__##name
#define MRB_SYM_2(mrb, name) MRB_SYM__##name
#define MRB_PRESYM_DEFINE_VAR_AND_INITER(name, size, ...) \
static const mrb_sym name[] = {__VA_ARGS__};
#define MRB_PRESYM_INIT_SYMBOLS(mrb, name) (void)(mrb)
/* use MRB_SYM() for E_RUNTIME_ERROR etc. */
#undef MRB_ERROR_SYM
#define MRB_ERROR_SYM(sym) MRB_SYM(sym)
#endif /* MRUBY_PRESYM_ENABLE_H */
+8 -9
View File
@@ -38,15 +38,14 @@
#define MRB_SYM_E(name) MRB_PRESYM_SCANNING_TAGGED(#name "=")
#define MRB_SYM(name) MRB_PRESYM_SCANNING_TAGGED(#name)
/* backward compatibility: _2 variants accept but ignore mrb_state* */
#define MRB_OPSYM_2(mrb, name) MRB_OPSYM(name)
#define MRB_GVSYM_2(mrb, name) MRB_GVSYM(name)
#define MRB_CVSYM_2(mrb, name) MRB_CVSYM(name)
#define MRB_IVSYM_2(mrb, name) MRB_IVSYM(name)
#define MRB_SYM_B_2(mrb, name) MRB_SYM_B(name)
#define MRB_SYM_Q_2(mrb, name) MRB_SYM_Q(name)
#define MRB_SYM_E_2(mrb, name) MRB_SYM_E(name)
#define MRB_SYM_2(mrb, name) MRB_SYM(name)
#define MRB_OPSYM_2(mrb, name) MRB_OPSYM__##name(mrb)
#define MRB_GVSYM_2(mrb, name) MRB_PRESYM_SCANNING_TAGGED("$" #name)
#define MRB_CVSYM_2(mrb, name) MRB_PRESYM_SCANNING_TAGGED("@@" #name)
#define MRB_IVSYM_2(mrb, name) MRB_PRESYM_SCANNING_TAGGED("@" #name)
#define MRB_SYM_B_2(mrb, name) MRB_PRESYM_SCANNING_TAGGED(#name "!")
#define MRB_SYM_Q_2(mrb, name) MRB_PRESYM_SCANNING_TAGGED(#name "?")
#define MRB_SYM_E_2(mrb, name) MRB_PRESYM_SCANNING_TAGGED(#name "=")
#define MRB_SYM_2(mrb, name) MRB_PRESYM_SCANNING_TAGGED(#name)
#define MRB_OPSYM__not(mrb) MRB_PRESYM_SCANNING_TAGGED("!")
#define MRB_OPSYM__mod(mrb) MRB_PRESYM_SCANNING_TAGGED("%")
+10 -8
View File
@@ -72,7 +72,6 @@ struct RProc {
#define MRB_ASPEC_KEY(a) (((a) >> 2) & 0x1f)
#define MRB_ASPEC_KDICT(a) (((a) >> 1) & 0x1)
#define MRB_ASPEC_BLOCK(a) ((a) & 1)
#define MRB_ASPEC_NOBLOCK(a) (((a) >> 23) & 0x1)
#define MRB_PROC_CFUNC_FL 128
#define MRB_PROC_CFUNC_P(p) (((p)->flags & MRB_PROC_CFUNC_FL) != 0)
@@ -97,7 +96,7 @@ struct RProc {
} while (0)
#define MRB_PROC_SCOPE 2048
#define MRB_PROC_SCOPE_P(p) (((p)->flags & MRB_PROC_SCOPE) != 0)
#define MRB_PROC_NOARG 4096 /* for MRB_PROC_CFUNC_FL, aspec == MRB_ARGS_NONE() */
#define MRB_PROC_NOARG 4096 /* for MRB_PROC_CFUNC_FL, it would be something like MRB_ARGS_NONE() or MRB_METHOD_NOARG_FL */
#define MRB_PROC_NOARG_P(p) (((p)->flags & MRB_PROC_NOARG) != 0)
#define MRB_PROC_ALIAS 8192
#define MRB_PROC_ALIAS_P(p) (((p)->flags & MRB_PROC_ALIAS) != 0)
@@ -114,15 +113,18 @@ MRB_API mrb_value mrb_proc_cfunc_env_get(mrb_state *mrb, mrb_int idx);
/* old name */
#define mrb_cfunc_env_get(mrb, idx) mrb_proc_cfunc_env_get(mrb, idx)
#define MRB_METHOD_FUNC_FL (1 << 24)
#define MRB_METHOD_PUBLIC_FL 0
#define MRB_METHOD_PRIVATE_FL (1 << 25)
#define MRB_METHOD_PROTECTED_FL (1 << 26)
#define MRB_METHOD_VDEFAULT_FL ((1 << 25) | (1 << 26))
#define MRB_METHOD_VISIBILITY_MASK ((1 << 25) | (1 << 26))
#define MRB_METHOD_FUNC_FL 8
#define MRB_METHOD_NOARG_FL 4
#define MRB_METHOD_PUBLIC_FL 0
#define MRB_METHOD_PRIVATE_FL 1
#define MRB_METHOD_PROTECTED_FL 2
#define MRB_METHOD_VDEFAULT_FL 3
#define MRB_METHOD_VISIBILITY_MASK 3
#define MRB_METHOD_FUNC_P(m) ((m).flags&MRB_METHOD_FUNC_FL)
#define MRB_METHOD_NOARG_P(m) (((m).flags&MRB_METHOD_NOARG_FL)?1:0)
#define MRB_METHOD_FUNC(m) ((m).as.func)
#define MRB_METHOD_NOARG_SET(m) do{(m).flags|=MRB_METHOD_NOARG_FL;}while(0)
#define MRB_METHOD_FROM_FUNC(m,fn) do{(m).flags=MRB_METHOD_FUNC_FL;(m).as.func=(fn);}while(0)
#define MRB_METHOD_FROM_PROC(m,pr) do{(m).flags=0;(m).as.proc=(pr);}while(0)
#define MRB_METHOD_PROC_P(m) (!MRB_METHOD_FUNC_P(m))
+1 -25
View File
@@ -2,30 +2,6 @@
** @file mruby/throw.h - mruby exception throwing handler
**
** See Copyright Notice in mruby.h
**
** WARNING: This header is for mruby core internal use only.
** Do not include this header in user code or mrbgems.
**
** When MRB_USE_CXX_EXCEPTION is defined, this header requires C++
** compilation. C source files that include this header will fail
** to compile when linked into C++ projects using MRB_USE_CXX_EXCEPTION.
**
** For exception-safe code in mrbgems and user code, use the
** mrb_protect_error() API from <mruby/error.h> instead:
**
** #include <mruby/error.h>
**
** mrb_value my_func_body(mrb_state *mrb, void *data) {
** // code that may raise exceptions
** return result;
** }
**
** void my_func(mrb_state *mrb) {
** mrb_bool error;
** mrb_value result = mrb_protect_error(mrb, my_func_body, data, &error);
** // cleanup code runs here regardless of exception
** if (error) mrb_exc_raise(mrb, result);
** }
*/
#ifndef MRB_THROW_H
@@ -57,7 +33,7 @@ typedef void *mrb_jmpbuf_impl;
#if defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__)
#define MRB_SETJMP _setjmp
#define MRB_LONGJMP _longjmp
#elif defined(__MINGW64__) && !defined(_M_ARM64) && defined(__GNUC__) && __GNUC__ >= 4
#elif defined(__MINGW64__) && defined(__GNUC__) && __GNUC__ >= 4
#define MRB_SETJMP __builtin_setjmp
#define MRB_LONGJMP __builtin_longjmp
#else
+2 -7
View File
@@ -134,7 +134,7 @@ static const unsigned int IEEE754_INFINITY_BITS_SINGLE = 0x7F800000;
#endif
#define MRB_VTYPE_FOREACH(f) \
/* mrb_vtype */ /* C type */ /* Ruby class */ \
/* mrb_vtype */ /* c type */ /* ruby class */ \
f(MRB_TT_FALSE, void, "false") \
f(MRB_TT_TRUE, void, "true") \
f(MRB_TT_SYMBOL, void, "Symbol") \
@@ -163,8 +163,7 @@ static const unsigned int IEEE754_INFINITY_BITS_SINGLE = 0x7F800000;
f(MRB_TT_COMPLEX, struct RComplex, "Complex") \
f(MRB_TT_RATIONAL, struct RRational, "Rational") \
f(MRB_TT_BIGINT, struct RBigint, "Integer") \
f(MRB_TT_BACKTRACE, struct RBacktrace, "backtrace") \
f(MRB_TT_SET, struct RSet, "Set")
f(MRB_TT_BACKTRACE, struct RBacktrace, "backtrace")
enum mrb_vtype {
#define MRB_VTYPE_DEFINE(tt, type, name) tt,
@@ -316,11 +315,7 @@ 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
/**
-6
View File
@@ -7,12 +7,6 @@
#ifndef MRUBY_VARIABLE_H
#define MRUBY_VARIABLE_H
#if defined(__GNUC__) || defined(__clang__)
#define MRB_MEM_PREFETCH(addr) __builtin_prefetch(addr, 0, 1)
#else
#define MRB_MEM_PREFETCH(addr)
#endif
#include "common.h"
/**
+4 -4
View File
@@ -27,7 +27,7 @@ MRB_BEGIN_DECL
/*
* The version of Ruby used by mruby.
*/
#define MRUBY_RUBY_VERSION "4.0"
#define MRUBY_RUBY_VERSION "3.4"
/*
* Ruby engine.
@@ -37,12 +37,12 @@ MRB_BEGIN_DECL
/*
* Major release version number.
*/
#define MRUBY_RELEASE_MAJOR 4
#define MRUBY_RELEASE_MAJOR 3
/*
* Minor release version number.
*/
#define MRUBY_RELEASE_MINOR 0
#define MRUBY_RELEASE_MINOR 4
/*
* Tiny release version number.
@@ -80,7 +80,7 @@ MRB_BEGIN_DECL
/*
* Release year.
*/
#define MRUBY_RELEASE_YEAR 2026
#define MRUBY_RELEASE_YEAR 2025
/*
* Release month.
-568
View File
@@ -1,568 +0,0 @@
module MRuby
class Amalgam
# Top-level headers to include (internal dependencies are inlined recursively)
# mruby.h is the main header - it includes value.h, gc.h, version.h with proper macro order
# Note: internal.h is NOT included here - it goes in mruby.c
HEADER_ORDER = %w[
mruby.h
mruby/array.h
mruby/string.h
mruby/hash.h
mruby/class.h
mruby/proc.h
mruby/range.h
mruby/variable.h
mruby/numeric.h
mruby/error.h
mruby/data.h
mruby/istruct.h
mruby/mempool.h
mruby/debug.h
mruby/dump.h
mruby/irep.h
mruby/opcode.h
mruby/re.h
mruby/throw.h
mruby/khash.h
mruby/endian.h
mruby/presym.h
mruby/compile.h
].freeze
# Boxing headers are conditionally included
BOXING_HEADERS = %w[
mruby/boxing_no.h
mruby/boxing_word.h
mruby/boxing_nan.h
].freeze
# Core sources in recommended order
CORE_SOURCE_ORDER = %w[
allocf.c
readnum.c
readint.c
readfloat.c
state.c
symbol.c
class.c
object.c
gc.c
mempool.c
variable.c
array.c
hash.c
string.c
range.c
numeric.c
numops.c
proc.c
kernel.c
enum.c
error.c
backtrace.c
vm.c
load.c
dump.c
cdump.c
codedump.c
print.c
fmt_fp.c
debug.c
etc.c
version.c
init.c
].freeze
def initialize(build)
@build = build
@processed_guards = {}
@processed_headers = [] # Track header paths for include transformation
# Pre-collect gem header names for source include transformation
@gem_header_names = collect_gem_header_names
end
def collect_gem_header_names
names = []
library_gems.each do |gem|
gem_include = "#{gem.dir}/include"
next unless File.directory?(gem_include)
Dir.glob("#{gem_include}/**/*.h").each do |path|
rel_path = path.sub("#{gem_include}/", "")
names << rel_path
# Also track basename for simple includes like "io_hal.h"
names << File.basename(rel_path)
end
end
names.uniq
end
def generate_header(output_path)
FileUtils.mkdir_p(File.dirname(output_path))
_pp "GEN", output_path.relative_path
File.open(output_path, "w:binary") do |f|
write_header_preamble(f)
write_ordered_headers(f)
# Boxing headers are inlined at their include point in value.h
# Presym headers are inlined via presym.h -> enable.h -> id.h
write_gem_headers(f)
write_header_postamble(f)
end
end
def generate_source(output_path)
FileUtils.mkdir_p(File.dirname(output_path))
_pp "GEN", output_path.relative_path
File.open(output_path, "w:binary") do |f|
write_source_preamble(f)
write_internal_headers(f)
write_core_sources(f)
write_generated_sources(f)
write_gem_sources(f)
end
end
private
def include_dir
"#{MRUBY_ROOT}/include"
end
def build_include_dir
"#{@build.build_dir}/include"
end
def src_dir
"#{MRUBY_ROOT}/src"
end
# Filter out binary gems (they have main() functions)
def library_gems
@build.gems.reject { |gem| gem.name.start_with?("mruby-bin-") }
end
# ========== Header Generation ==========
def write_header_preamble(f)
f.puts <<~PREAMBLE
/*
** mruby amalgamated header
** Generated from mruby source files
**
** This file is auto-generated. Do not edit directly.
*/
#ifndef MRUBY_AMALGAM_H
#define MRUBY_AMALGAM_H
#ifdef __cplusplus
#define __STDC_LIMIT_MACROS
#define __STDC_CONSTANT_MACROS
#define __STDC_FORMAT_MACROS
#endif
#include <stdarg.h>
#include <stdint.h>
#include <stddef.h>
#include <limits.h>
#include <string.h>
PREAMBLE
# Add build-level defines from gems (e.g., MRB_USE_TASK_SCHEDULER)
gem_defines = collect_gem_defines
unless gem_defines.empty?
f.puts "/* Gem-required defines */"
gem_defines.each do |d|
f.puts "#define #{d}"
end
f.puts
end
end
# Collect defines added by gems that affect core headers
def collect_gem_defines
defines = []
@build.defines.each do |d|
# Include defines that affect mrb_state or core functionality
defines << d if d =~ /^MRB_USE_|^MRB_UTF8_|^HAVE_MRUBY_/
end
defines.uniq.sort
end
def write_header_postamble(f)
f.puts <<~POSTAMBLE
#endif /* MRUBY_AMALGAM_H */
POSTAMBLE
end
def write_ordered_headers(f)
# Process top-level headers; internal includes are recursively inlined
HEADER_ORDER.each do |header|
path = "#{include_dir}/#{header}"
next unless File.exist?(path)
write_header_content(f, header, path)
end
end
def write_boxing_headers(f)
f.puts "\n/* Boxing type selection */"
f.puts "#if defined(MRB_NAN_BOXING)"
write_header_content(f, "mruby/boxing_nan.h", "#{include_dir}/mruby/boxing_nan.h")
f.puts "#elif defined(MRB_WORD_BOXING)"
write_header_content(f, "mruby/boxing_word.h", "#{include_dir}/mruby/boxing_word.h")
f.puts "#else"
write_header_content(f, "mruby/boxing_no.h", "#{include_dir}/mruby/boxing_no.h")
f.puts "#endif"
end
def write_presym_headers(f)
presym_dir = "#{@build.build_dir}/include/mruby/presym"
return unless File.directory?(presym_dir)
%w[id.h table.h].each do |header|
path = "#{presym_dir}/#{header}"
next unless File.exist?(path)
write_header_content(f, "mruby/presym/#{header}", path)
end
end
def write_gem_headers(f)
library_gems.each do |gem|
gem_include = "#{gem.dir}/include"
next unless File.directory?(gem_include)
Dir.glob("#{gem_include}/**/*.h").sort.each do |path|
rel_path = path.sub("#{gem_include}/", "")
write_header_content(f, "#{gem.name}: #{rel_path}", path)
# Also track the relative path for source include transformation
# (handles includes like #include "io_hal.h")
@processed_headers << rel_path unless @processed_headers.include?(rel_path)
end
end
end
def write_header_content(f, name, path)
return unless File.exist?(path)
content = File.read(path, mode: "rb")
guard = extract_include_guard(content)
# Skip if already processed
if guard && @processed_guards[guard]
f.puts "/* #{name} - already included */"
return
end
@processed_guards[guard] = true if guard
@processed_headers << name # Track header path for include transformation
f.puts "\n/* ======== #{name} ======== */"
content = strip_include_guard(content, guard) if guard
content = transform_includes(content, inline: true)
f.puts content
end
# ========== Source Generation ==========
def write_source_preamble(f)
f.puts <<~PREAMBLE
/*
** mruby amalgamated source
** Generated from mruby source files
**
** This file is auto-generated. Do not edit directly.
*/
#include "mruby.h"
PREAMBLE
end
def write_internal_headers(f)
f.puts "/* ======== Internal headers ======== */"
# Forward declarations needed for amalgamation
# (functions called before defined due to source file ordering)
# Note: mrb_irep_catch_handler_table is static inline in internal.h, no forward decl needed
f.puts <<~FORWARD
/* Forward declarations for amalgamation */
static void mrb_irep_free(mrb_state *mrb, mrb_irep *irep);
static mrb_value mrb_class_find_path(mrb_state *mrb, struct RClass *c);
static void mrb_method_added(mrb_state *mrb, struct RClass *c, mrb_sym mid);
static void mrb_proc_copy(mrb_state *mrb, struct RProc *a, const struct RProc *b);
static size_t mrb_gc_mark_range(mrb_state *mrb, struct RRange *r);
static void mrb_ary_decref(mrb_state *mrb, mrb_shared_array *shared);
static mrb_int mrb_proc_arity(const struct RProc *p);
FORWARD
# internal.h
internal_path = "#{include_dir}/mruby/internal.h"
if File.exist?(internal_path)
content = File.read(internal_path, mode: "rb")
content = strip_include_guard(content, extract_include_guard(content))
content = transform_source_includes(content)
f.puts "\n/* mruby/internal.h */"
f.puts content
end
# presym/table.h (generated, needed by symbol.c)
table_path = "#{build_include_dir}/mruby/presym/table.h"
if File.exist?(table_path)
content = File.read(table_path, mode: "rb")
content = transform_source_includes(content)
f.puts "\n/* mruby/presym/table.h */"
f.puts content
end
# value_array.h (internal src header)
value_array_path = "#{src_dir}/value_array.h"
if File.exist?(value_array_path)
content = File.read(value_array_path, mode: "rb")
content = strip_include_guard(content, extract_include_guard(content))
content = transform_source_includes(content)
f.puts "\n/* src/value_array.h */"
f.puts content
end
end
def write_core_sources(f)
f.puts "\n/* ======== Core sources ======== */"
CORE_SOURCE_ORDER.each do |source|
path = "#{src_dir}/#{source}"
next unless File.exist?(path)
write_source_content(f, "src/#{source}", path)
end
# Clear potentially conflicting macros from core sources
write_macro_cleanup(f, "core")
end
def write_generated_sources(f)
# mrblib.c - compiled Ruby stdlib
mrblib_path = "#{@build.build_dir}/mrblib/mrblib.c"
if File.exist?(mrblib_path)
write_source_content(f, "mrblib.c", mrblib_path)
end
end
# Macros that may conflict between source files in amalgamation
# These are #undef-ed after each source file within a gem
CONFLICTING_MACROS = %w[
mrb_stat
mrb_lstat
mrb_fstat
lesser
greater
CASE
NEXT
JUMP
CALL
node_type
push
pop
peek
].freeze
def write_gem_sources(f)
f.puts "\n/* ======== Gem sources ======== */"
library_gems.each do |gem|
# Some gems use 'core/' instead of 'src/' (mruby-compiler, mruby-bigint)
source_dirs = ["#{gem.dir}/src", "#{gem.dir}/core"].select { |d| File.directory?(d) }
# Include C sources if the gem has any
unless source_dirs.empty?
sources = source_dirs.flat_map { |d| Dir.glob("#{d}/**/*.c") }.sort
sources.each_with_index do |path, idx|
rel_path = path.sub("#{gem.dir}/", "")
write_source_content(f, "#{gem.name}: #{rel_path}", path)
# Clear macros between source files to avoid conflicts
# (e.g., mrb_stat macro in file.c vs function in file_test.c)
write_macro_cleanup(f, "#{gem.name}/#{File.basename(path)}") if idx < sources.size - 1
end
end
# Gem's compiled mrblib (Ruby-only gems like mruby-enum-ext have this)
gem_mrblib = "#{gem.build_dir}/gem_mrblib.c"
if File.exist?(gem_mrblib)
write_source_content(f, "#{gem.name}: gem_mrblib.c", gem_mrblib)
end
# Gem's init functions (GENERATED_TMP_mrb_*_gem_init/final)
# Required for both C and Ruby-only gems
gem_init = "#{gem.build_dir}/gem_init.c"
if File.exist?(gem_init)
write_source_content(f, "#{gem.name}: gem_init.c", gem_init)
end
# Clear potentially conflicting macros after each gem
write_macro_cleanup(f, gem.name)
end
# gem_init.c - gem registration
gem_init_path = "#{@build.build_dir}/mrbgems/gem_init.c"
if File.exist?(gem_init_path)
write_source_content(f, "gem_init.c", gem_init_path)
end
end
def write_source_content(f, name, path)
return unless File.exist?(path)
content = File.read(path, mode: "rb")
# For source files, comment out all mruby includes (they're in the header)
# and inline local includes (like .cstub files)
source_dir = File.dirname(path)
content = transform_source_includes(content, source_dir)
f.puts "\n/* ======== #{name} ======== */"
f.puts content
end
def write_macro_cleanup(f, gem_name)
f.puts "\n/* Cleanup macros from #{gem_name} to avoid conflicts */"
CONFLICTING_MACROS.each do |macro|
f.puts "#ifdef #{macro}"
f.puts "#undef #{macro}"
f.puts "#endif"
end
end
# X-macro pattern headers that must be inlined every time they're included
# (not commented out) because they expand differently based on macro definitions
XMACRO_HEADERS = %w[
mruby/ops.h
].freeze
def transform_source_includes(content, source_dir = nil)
content.gsub(/^(\s*)(#\s*include\s+([<"])([^>"]+)[>"])/m) do |match|
prefix = $1
include_stmt = $2
quote_type = $3 # < or "
header = $4
# X-macro headers must be inlined every time (not commented out)
# because they expand differently based on surrounding macro definitions
if XMACRO_HEADERS.include?(header)
xmacro_path = "#{include_dir}/#{header}"
if File.exist?(xmacro_path)
xmacro_content = File.read(xmacro_path, mode: "rb")
"#{prefix}/* Inlined X-macro: #{header} */\n#{xmacro_content}"
else
match
end
# Comment out all mruby-related includes and any header already in amalgam
elsif mruby_header?(header) || header == "mruby.h" ||
@processed_headers.include?(header) || @gem_header_names.include?(header)
"#{prefix}// #{include_stmt} - in amalgam header"
elsif source_dir && quote_type == '"' && !header.include?("/")
# Check for local includes like "known_errors_def.cstub"
local_path = "#{source_dir}/#{header}"
if File.exist?(local_path)
local_content = File.read(local_path, mode: "rb")
"#{prefix}/* Inlined: #{header} */\n#{local_content}"
else
match
end
else
match
end
end
end
# ========== Content Transformation ==========
def extract_include_guard(content)
# Match #ifndef GUARD_NAME at start of file (after comments)
if content =~ /\A(?:\/\*.*?\*\/\s*|\/\/[^\n]*\n)*\s*#ifndef\s+(\w+)\s*\n\s*#define\s+\1/m
$1
end
end
def strip_include_guard(content, guard)
return content unless guard
# Remove opening #ifndef GUARD but KEEP #define GUARD (needed for #ifdef checks)
content = content.sub(/\A((?:\/\*.*?\*\/\s*|\/\/[^\n]*\n)*\s*)#ifndef\s+#{guard}\s*\n/m, '\1')
# Remove closing #endif (any comment is ok, guard name may not match exactly)
content = content.sub(/\n#endif\s*(?:\/\*[^*]*\*\/|\/\/[^\n]*)?\s*\z/m, "\n")
content
end
def transform_includes(content, inline: false)
# Match both "#include" and "# include" (preprocessor allows spaces)
# Only match includes at the start of a line (real preprocessor directives)
content.gsub(/^(\s*)(#\s*include\s+[<"]([^>"]+)[>"])/m) do |match|
prefix = $1
include_stmt = $2
header = $3
if already_included?(header)
# Keep original whitespace, comment out the include
"#{prefix}// #{include_stmt} - in amalgam"
elsif inline && mruby_header?(header)
# Recursively inline this header
"#{prefix}#{inline_header(header)}"
else
match
end
end
end
def already_included?(header)
# Check if header was already processed
# Only use end_with? matching for mruby headers to avoid matching
# system headers like <time.h> against mruby/time.h
if mruby_header?(header)
@processed_headers.any? { |h| h == header || h.end_with?("/#{header}") }
else
@processed_headers.include?(header)
end
end
def mruby_header?(header)
# Check if this is an mruby header that should be inlined/transformed
header == "mruby.h" || header.start_with?("mruby/") ||
%w[mrbconf.h boxing_nan.h boxing_word.h boxing_no.h common.h object.h value_array.h].include?(header)
end
def inline_header(header)
# Find the full path for this header
if header == "mruby.h" || header == "mrbconf.h" || header.start_with?("mruby/")
full_header = header
# Check both source include dir and build include dir (for generated headers)
path = "#{include_dir}/#{header}"
path = "#{build_include_dir}/#{header}" unless File.exist?(path)
else
# Relative includes like "boxing_word.h" -> "mruby/boxing_word.h"
full_header = "mruby/#{header}"
path = "#{include_dir}/#{full_header}"
path = "#{build_include_dir}/#{full_header}" unless File.exist?(path)
end
return "/* #{header} - not found */" unless File.exist?(path)
# Mark as processed to avoid infinite recursion
@processed_headers << header
@processed_headers << full_header
content = File.read(path, mode: "rb")
guard = extract_include_guard(content)
if guard && @processed_guards[guard]
return "/* #{header} - already included */"
end
@processed_guards[guard] = true if guard
content = strip_include_guard(content, guard) if guard
content = transform_includes(content, inline: true)
"\n/* ======== #{full_header} (inlined) ======== */\n#{content}"
end
end
end
+26 -10
View File
@@ -60,11 +60,7 @@ module MRuby
def mruby_config_path
path = ENV['MRUBY_CONFIG'] || ENV['CONFIG']
if path.nil? || path.empty?
path = if Dir.pwd != MRUBY_ROOT && File.file?("./build_config.rb")
"./build_config.rb"
else
"#{MRUBY_ROOT}/build_config/default.rb"
end
path = "#{MRUBY_ROOT}/build_config/default.rb"
elsif !File.file?(path) && !Pathname.new(path).absolute?
f = "#{MRUBY_ROOT}/build_config/#{path}.rb"
path = File.exist?(f) ? f : File.extname(path).empty? ? f : path
@@ -134,6 +130,7 @@ module MRuby
@enable_bintest = false
@enable_test = false
@enable_lock = true
@enable_presym = true
@enable_benchmark = true
@mrbcfile_external = false
@internal = internal
@@ -155,9 +152,13 @@ module MRuby
current.instance_eval(&block)
ensure
if current.libmruby_enabled? && !current.mrbcfile_external?
current.create_mrbc_build if current.host? || current.gems["mruby-bin-mrbc"]
if current.presym_enabled?
current.create_mrbc_build if current.host? || current.gems["mruby-bin-mrbc"]
elsif current.host?
current.build_mrbc_exec
end
end
current.presym = Presym.new(current)
current.presym = Presym.new(current) if current.presym_enabled?
end
end
@@ -183,6 +184,17 @@ module MRuby
@enable_debug = true
end
def presym_enabled?
@enable_presym
end
def disable_presym
if @enable_presym
@enable_presym = false
compilers.each{|c| c.defines << "MRB_NO_PRESYM"}
end
end
def disable_lock
@enable_lock = false
end
@@ -253,7 +265,7 @@ module MRuby
if cxx_src
obj ||= cxx_src + @exts.object
dsts = [obj]
dsts << (cxx_src + @exts.presym_preprocessed)
dsts << (cxx_src + @exts.presym_preprocessed) if presym_enabled?
defines = []
include_paths = ["#{MRUBY_ROOT}/src", *includes]
dsts.each do |dst|
@@ -367,7 +379,7 @@ EOS
end
[@cc, *(@cxx if cxx_exception_enabled?)].each do |compiler|
compiler.define_rules(@build_dir, MRUBY_ROOT, @exts.object)
compiler.define_rules(@build_dir, MRUBY_ROOT, @exts.presym_preprocessed)
compiler.define_rules(@build_dir, MRUBY_ROOT, @exts.presym_preprocessed) if presym_enabled?
end
end
@@ -553,7 +565,7 @@ EOS
end
build.build_mrbc_exec
build.disable_libmruby
build.presym = Presym.new(build)
build.disable_presym
@mrbc_build = build
self.mrbcfile = build.mrbcfile
build
@@ -576,6 +588,7 @@ EOS
conf.toolchain
conf.build_mrbc_exec
conf.disable_libmruby
conf.disable_presym
end
end
end
@@ -600,6 +613,9 @@ EOS
targets = @gems.select { |v| File.directory? "#{v.dir}/bintest" }.map { |v| filename v.dir }
mrbc = @gems["mruby-bin-mrbc"] ? exefile("#{@build_dir}/bin/mrbc") : mrbcfile
emulator = @test_runner.command
emulator = @test_runner.shellquote(emulator) if emulator
env = {
"BUILD_DIR" => @build_dir,
"MRBCFILE" => mrbc,
+2 -2
View File
@@ -144,13 +144,13 @@ module MRuby
#
# === Example of +.d+ file
#
# ==== Without `-MP` compiler flag
# ==== Without <tt>-MP</tt> compiler flag
#
# /build/host/src/array.o: /src/array.c \
# /include/mruby/common.h /include/mruby/value.h \
# /src/value_array.h
#
# ==== With `-MP` compiler flag
# ==== With <tt>-MP</tt> compiler flag
#
# /build/host/src/array.o: /src/array.c \
# /include/mruby/common.h /include/mruby/value.h \
+2 -9
View File
@@ -32,21 +32,14 @@ module MRuby
gemrake = File.join(checkout.full_gemdir, "mrbgem.rake")
fail "Can't find #{gemrake}" unless File.exist?(gemrake)
current_build = MRuby::Build.current
build = self.is_a?(MRuby::Build) ? self : MRuby::Build.current
MRuby::Build.current = build
Gem.current = nil
begin
load gemrake
ensure
MRuby::Build.current = current_build
end
load gemrake
return nil unless Gem.current
current = Gem.current
# Add it to gems
current.dir = checkout.full_gemdir
current.build = build
current.build = self.is_a?(MRuby::Build) ? self : MRuby::Build.current
current.build_config_initializer = block
gems << current
+9 -61
View File
@@ -53,8 +53,10 @@ module MRuby
return if defined?(@bins) # return if already set up
MRuby::Gem.current = self
reset_commands # for backward compatibility, reset the commands from the beginning.
@build_settings = nil
MRuby::Build::COMMANDS.each do |command|
instance_variable_set("@#{command}", @build.send(command).clone)
end
@linker.run_attrs.each(&:clear)
@rbfiles = Dir.glob("#{@dir}/mrblib/**/*.rb").sort
@objs = srcs_to_objs("src")
@@ -68,10 +70,7 @@ module MRuby
@requirements = []
@export_include_paths = []
# Headers in include/ are for inter-gem use only
# Headers in include/export/ are exported to external users via mruby-config
export_dir = "#{dir}/include/export"
@export_include_paths << export_dir if File.directory?(export_dir)
@export_include_paths << "#{dir}/include" if File.directory? "#{dir}/include"
instance_eval(&@initializer)
@@ -96,7 +95,7 @@ module MRuby
def setup_compilers
(core? ? [@cc, *(@cxx if build.cxx_exception_enabled?)] : compilers).each do |compiler|
compiler.define_rules build_dir, @dir, @build.exts.presym_preprocessed
compiler.define_rules build_dir, @dir, @build.exts.presym_preprocessed if build.presym_enabled?
compiler.define_rules build_dir, @dir, @build.exts.object
compiler.defines << %Q[MRBGEM_#{funcname.upcase}_VERSION=#{version}]
compiler.include_paths << "#{@dir}/include" if File.directory? "#{@dir}/include"
@@ -109,8 +108,7 @@ 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}:") } ||
('a'..'z').to_a.any? { |vol| Dir.exist?("/#{vol}/") }
return ('A'..'Z').to_a.any? { |vol| Dir.exist?("#{vol}:") }
end
return false
end
@@ -120,7 +118,7 @@ module MRuby
end
def cdump?
@cdump
build.presym_enabled? && @cdump
end
def core?
@@ -194,19 +192,6 @@ module MRuby
end
end
def build_settings(&blk)
@build_settings = blk
end
def setup_build
if @build_settings
# by this point, build.cc or other commands may have been modified.
# therefore, reset the commands again before calling build_settings.
reset_commands
@build_settings.call(self)
end
end
def define_gem_init_builder
file "#{build_dir}/gem_init.c" => [build.mrbcfile, __FILE__] + [rbfiles].flatten do |t|
mkdir_p build_dir
@@ -318,13 +303,6 @@ module MRuby
self
end
private def reset_commands
MRuby::Build::COMMANDS.each do |command|
instance_variable_set("@#{command}", @build.send(command).clone)
end
@linker.run_attrs.each(&:clear)
end
end # Specification
class Version
@@ -423,21 +401,7 @@ module MRuby
end
end
def setup(build)
gemset = nil
begin
gemset_prev = gemset
self.each(&:setup)
gemset = self.setup_dependencies(build).keys.sort
end until gemset == gemset_prev
end
def setup_build
each(&:setup_build)
self
end
def setup_dependencies(build)
def generate_gem_table build
gem_table = each_with_object({}) { |spec, h| h[spec.name] = spec }
default_gems = {}
@@ -460,12 +424,6 @@ module MRuby
end
end
gem_table
end
def generate_gem_table(build)
gem_table = setup_dependencies(build)
each do |g|
g.dependencies.each do |dep|
name = dep[:gem]
@@ -546,16 +504,6 @@ module MRuby
# as circular dependency has already detected in the caller.
import_include_paths(dep_g)
# Add dependency's include/ to compiler paths (for inter-gem use)
dep_include = "#{dep_g.dir}/include"
if File.directory?(dep_include)
g.compilers.each do |compiler|
compiler.include_paths << dep_include
compiler.include_paths.uniq!
end
end
# Propagate any explicitly set export_include_paths
dep_g.export_include_paths.uniq!
g.compilers.each do |compiler|
compiler.include_paths += dep_g.export_include_paths
+1 -1
View File
@@ -1,6 +1,6 @@
MRuby::GemBox.new do |conf|
Dir.glob("#{root}/mrbgems/mruby-*/mrbgem.rake") do |x|
g = File.basename(File.dirname(x))
conf.gem :core => g unless g =~ /^mruby-(?:bin-debugger|test|sleep)$/
conf.gem :core => g unless g =~ /^mruby-(?:bin-debugger|test)$/
end
end
-7
View File
@@ -1,7 +0,0 @@
MRuby::Gem::Specification.new('hal-posix-dir') do |spec|
spec.license = 'MIT'
spec.authors = 'mruby developers'
spec.summary = 'POSIX HAL for mruby-dir (Linux, macOS, BSD, Unix)'
spec.add_dependency 'mruby-dir', core: 'mruby-dir'
end
-193
View File
@@ -1,193 +0,0 @@
/*
** dir_hal.c - POSIX HAL implementation for mruby-dir
**
** See Copyright Notice in mruby.h
**
** POSIX implementation for directory operations using standard POSIX APIs.
** Supported platforms: Linux, macOS, BSD, Unix
*/
#include <mruby.h>
#include "dir_hal.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <unistd.h>
#include <errno.h>
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif
/* On POSIX, mrb_dir_handle wraps DIR */
struct mrb_dir_handle {
DIR *dir;
};
/*
* Directory Operations
*/
mrb_dir_handle*
mrb_hal_dir_open(mrb_state *mrb, const char *path)
{
DIR *dir = opendir(path);
if (dir == NULL) {
return NULL;
}
mrb_dir_handle *handle = (mrb_dir_handle*)mrb_malloc(mrb, sizeof(mrb_dir_handle));
handle->dir = dir;
return handle;
}
int
mrb_hal_dir_close(mrb_state *mrb, mrb_dir_handle *handle)
{
int result = closedir(handle->dir);
mrb_free(mrb, handle);
return result;
}
const char*
mrb_hal_dir_read(mrb_state *mrb, mrb_dir_handle *handle)
{
(void)mrb;
struct dirent *dp = readdir(handle->dir);
return dp ? dp->d_name : NULL;
}
void
mrb_hal_dir_rewind(mrb_state *mrb, mrb_dir_handle *handle)
{
(void)mrb;
rewinddir(handle->dir);
}
/*
* Optional Operations
*/
int
mrb_hal_dir_seek(mrb_state *mrb, mrb_dir_handle *handle, long pos)
{
#if defined(__ANDROID__)
/* Android doesn't have reliable seekdir */
(void)mrb; (void)handle; (void)pos;
errno = ENOSYS;
return -1;
#else
(void)mrb;
seekdir(handle->dir, pos);
return 0;
#endif
}
long
mrb_hal_dir_tell(mrb_state *mrb, mrb_dir_handle *handle)
{
#if defined(__ANDROID__)
/* Android doesn't have reliable telldir */
(void)mrb; (void)handle;
errno = ENOSYS;
return -1;
#else
(void)mrb;
return telldir(handle->dir);
#endif
}
/*
* Filesystem Operations
*/
int
mrb_hal_dir_mkdir(mrb_state *mrb, const char *path, int mode)
{
(void)mrb;
return mkdir(path, (mode_t)mode);
}
int
mrb_hal_dir_rmdir(mrb_state *mrb, const char *path)
{
(void)mrb;
return rmdir(path);
}
int
mrb_hal_dir_chdir(mrb_state *mrb, const char *path)
{
(void)mrb;
return chdir(path);
}
int
mrb_hal_dir_getcwd(mrb_state *mrb, char *buf, size_t size)
{
(void)mrb;
return getcwd(buf, size) ? 0 : -1;
}
int
mrb_hal_dir_chroot(mrb_state *mrb, const char *path)
{
#if defined(__ANDROID__) || defined(__MSDOS__)
/* Not available on these platforms */
(void)mrb; (void)path;
errno = ENOSYS;
return -1;
#else
(void)mrb;
return chroot(path);
#endif
}
int
mrb_hal_dir_is_directory(mrb_state *mrb, const char *path)
{
struct stat sb;
(void)mrb;
if (stat(path, &sb) == 0 && S_ISDIR(sb.st_mode)) {
return 1;
}
return 0;
}
/*
* HAL Initialization/Finalization
*/
void
mrb_hal_dir_init(mrb_state *mrb)
{
(void)mrb;
/* No initialization needed for POSIX */
}
void
mrb_hal_dir_final(mrb_state *mrb)
{
(void)mrb;
/* No cleanup needed for POSIX */
}
/*
* Gem initialization
*/
void
mrb_hal_posix_dir_gem_init(mrb_state *mrb)
{
(void)mrb;
/* HAL interface functions are called by mruby-dir gem */
}
void
mrb_hal_posix_dir_gem_final(mrb_state *mrb)
{
(void)mrb;
/* Cleanup handled by mrb_hal_dir_final called from mruby-dir */
}
-8
View File
@@ -1,8 +0,0 @@
MRuby::Gem::Specification.new('hal-posix-io') do |spec|
spec.license = 'MIT'
spec.author = 'mruby developers'
spec.summary = 'POSIX HAL for mruby-io (Linux, macOS, BSD, Unix)'
# HAL gem depends on feature gem - brings in mruby-io automatically
spec.add_dependency 'mruby-io', core: 'mruby-io'
end
-602
View File
@@ -1,602 +0,0 @@
/*
** io_hal.c - POSIX HAL implementation for mruby-io
**
** See Copyright Notice in mruby.h
**
** POSIX implementation for I/O operations using standard POSIX APIs.
** Supported platforms: Linux, macOS, BSD, Unix
*/
#include <mruby.h>
#include "io_hal.h"
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/file.h>
#include <sys/param.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <pwd.h>
#ifndef __DJGPP__
#include <libgen.h>
#endif
/* Maximum path length */
#ifndef PATH_MAX
#define PATH_MAX 4096
#endif
/*
* Helper Functions
*/
/* Convert POSIX struct stat to mrb_io_stat */
static void
convert_stat(const struct stat *src, mrb_io_stat *dst)
{
/* Extract time values FIRST while macros are still defined.
* On POSIX systems, st_atime may be a macro for st_atim.tv_sec */
time_t atime_val, mtime_val, ctime_val;
#if defined(st_atime)
/* st_atime is a macro - use it to extract from src */
atime_val = src->st_atime;
mtime_val = src->st_mtime;
ctime_val = src->st_ctime;
#elif defined(__APPLE__) || defined(__FreeBSD__) || \
defined(__OpenBSD__) || defined(__NetBSD__) || defined(__DragonFly__)
/* BSD/macOS: st_atime is typically a direct member */
atime_val = src->st_atime;
mtime_val = src->st_mtime;
ctime_val = src->st_ctime;
#else
/* POSIX.1-2008: use st_atim.tv_sec directly */
atime_val = src->st_atim.tv_sec;
mtime_val = src->st_mtim.tv_sec;
ctime_val = src->st_ctim.tv_sec;
#endif
/* Undefine macros to avoid interference with mrb_io_stat fields */
#undef st_atime
#undef st_mtime
#undef st_ctime
dst->st_dev = (uint64_t)src->st_dev;
dst->st_ino = (uint64_t)src->st_ino;
dst->st_mode = (uint32_t)src->st_mode;
dst->st_nlink = (uint32_t)src->st_nlink;
dst->st_uid = (uint32_t)src->st_uid;
dst->st_gid = (uint32_t)src->st_gid;
dst->st_rdev = (uint64_t)src->st_rdev;
dst->st_size = (int64_t)src->st_size;
dst->st_atime = (int64_t)atime_val;
dst->st_mtime = (int64_t)mtime_val;
dst->st_ctime = (int64_t)ctime_val;
#ifdef HAVE_STRUCT_STAT_ST_BLKSIZE
dst->st_blksize = (int64_t)src->st_blksize;
#else
dst->st_blksize = 512;
#endif
#ifdef HAVE_STRUCT_STAT_ST_BLOCKS
dst->st_blocks = (int64_t)src->st_blocks;
#else
dst->st_blocks = (dst->st_size + 511) / 512;
#endif
}
/*
* File Operations
*/
int
mrb_hal_io_stat(mrb_state *mrb, const char *path, mrb_io_stat *st)
{
struct stat s;
(void)mrb;
if (stat(path, &s) == -1) {
return -1;
}
convert_stat(&s, st);
return 0;
}
int
mrb_hal_io_fstat(mrb_state *mrb, int fd, mrb_io_stat *st)
{
struct stat s;
(void)mrb;
if (fstat(fd, &s) == -1) {
return -1;
}
convert_stat(&s, st);
return 0;
}
int
mrb_hal_io_lstat(mrb_state *mrb, const char *path, mrb_io_stat *st)
{
struct stat s;
(void)mrb;
if (lstat(path, &s) == -1) {
return -1;
}
convert_stat(&s, st);
return 0;
}
int
mrb_hal_io_chmod(mrb_state *mrb, const char *path, uint32_t mode)
{
(void)mrb;
return chmod(path, (mode_t)mode);
}
uint32_t
mrb_hal_io_umask(mrb_state *mrb, int32_t mask)
{
mode_t old;
(void)mrb;
if (mask < 0) {
/* Just query current value */
old = umask(0);
umask(old);
}
else {
old = umask((mode_t)mask);
}
return (uint32_t)old;
}
int
mrb_hal_io_ftruncate(mrb_state *mrb, int fd, int64_t length)
{
(void)mrb;
return ftruncate(fd, (off_t)length);
}
int
mrb_hal_io_flock(mrb_state *mrb, int fd, int operation)
{
(void)mrb;
while (flock(fd, operation) == -1) {
if (errno == EINTR) {
continue; /* Retry on interrupt */
}
return -1;
}
return 0;
}
int
mrb_hal_io_unlink(mrb_state *mrb, const char *path)
{
(void)mrb;
return unlink(path);
}
int
mrb_hal_io_rename(mrb_state *mrb, const char *oldpath, const char *newpath)
{
(void)mrb;
return rename(oldpath, newpath);
}
int
mrb_hal_io_symlink(mrb_state *mrb, const char *target, const char *linkpath)
{
(void)mrb;
return symlink(target, linkpath);
}
int64_t
mrb_hal_io_readlink(mrb_state *mrb, const char *path, char *buf, size_t bufsize)
{
ssize_t rc;
(void)mrb;
rc = readlink(path, buf, bufsize);
return (int64_t)rc;
}
char*
mrb_hal_io_realpath(mrb_state *mrb, const char *path, char *resolved)
{
(void)mrb;
return realpath(path, resolved);
}
char*
mrb_hal_io_getcwd(mrb_state *mrb, char *buf, size_t size)
{
(void)mrb;
return getcwd(buf, size);
}
const char*
mrb_hal_io_getenv(mrb_state *mrb, const char *name)
{
(void)mrb;
return getenv(name);
}
const char*
mrb_hal_io_gethome(mrb_state *mrb, const char *username)
{
const char *home;
if (username == NULL || *username == '\0') {
/* Get current user's home */
home = getenv("HOME");
if (home == NULL) {
errno = ENOENT;
return NULL;
}
}
else {
/* Get specified user's home */
struct passwd *pwd = getpwnam(username);
if (pwd == NULL) {
errno = ENOENT;
return NULL;
}
home = pwd->pw_dir;
}
return home;
}
/*
* Core I/O Operations
*/
int
mrb_hal_io_open(mrb_state *mrb, const char *path, int flags, uint32_t mode)
{
int fd;
(void)mrb;
fd = open(path, flags, (mode_t)mode);
if (fd == -1) {
return -1;
}
/* Set close-on-exec for non-standard descriptors */
#if defined(F_GETFD) && defined(F_SETFD) && defined(FD_CLOEXEC)
if (fd > 2) {
int fd_flags = fcntl(fd, F_GETFD);
if (fd_flags != -1) {
fcntl(fd, F_SETFD, fd_flags | FD_CLOEXEC);
}
}
#endif
return fd;
}
int
mrb_hal_io_close(mrb_state *mrb, int fd)
{
(void)mrb;
return close(fd);
}
int64_t
mrb_hal_io_read(mrb_state *mrb, int fd, void *buf, size_t count)
{
ssize_t n;
(void)mrb;
n = read(fd, buf, count);
return (int64_t)n;
}
int64_t
mrb_hal_io_write(mrb_state *mrb, int fd, const void *buf, size_t count)
{
ssize_t n;
(void)mrb;
n = write(fd, buf, count);
return (int64_t)n;
}
int64_t
mrb_hal_io_lseek(mrb_state *mrb, int fd, int64_t offset, int whence)
{
off_t pos;
int posix_whence;
(void)mrb;
/* Convert MRB_IO_SEEK_* to POSIX SEEK_* */
switch (whence) {
case MRB_IO_SEEK_SET: posix_whence = SEEK_SET; break;
case MRB_IO_SEEK_CUR: posix_whence = SEEK_CUR; break;
case MRB_IO_SEEK_END: posix_whence = SEEK_END; break;
default:
errno = EINVAL;
return -1;
}
pos = lseek(fd, (off_t)offset, posix_whence);
return (int64_t)pos;
}
int
mrb_hal_io_dup(mrb_state *mrb, int fd)
{
int new_fd;
(void)mrb;
new_fd = dup(fd);
if (new_fd == -1) {
return -1;
}
/* Set close-on-exec */
#if defined(F_GETFD) && defined(F_SETFD) && defined(FD_CLOEXEC)
if (new_fd > 2) {
int fd_flags = fcntl(new_fd, F_GETFD);
if (fd_flags != -1) {
fcntl(new_fd, F_SETFD, fd_flags | FD_CLOEXEC);
}
}
#endif
return new_fd;
}
int
mrb_hal_io_fcntl(mrb_state *mrb, int fd, int cmd, int arg)
{
(void)mrb;
return fcntl(fd, cmd, arg);
}
int
mrb_hal_io_isatty(mrb_state *mrb, int fd)
{
(void)mrb;
return isatty(fd) ? 1 : 0;
}
int
mrb_hal_io_pipe(mrb_state *mrb, int fds[2])
{
int ret;
(void)mrb;
ret = pipe(fds);
if (ret == -1) {
return -1;
}
/* Set close-on-exec on both ends */
#if defined(F_GETFD) && defined(F_SETFD) && defined(FD_CLOEXEC)
for (int i = 0; i < 2; i++) {
int fd_flags = fcntl(fds[i], F_GETFD);
if (fd_flags != -1) {
fcntl(fds[i], F_SETFD, fd_flags | FD_CLOEXEC);
}
}
#endif
return 0;
}
/*
* Process Operations
*/
int
mrb_hal_io_spawn_process(mrb_state *mrb, const char *cmd,
int stdin_fd, int stdout_fd, int stderr_fd,
int *pid)
{
pid_t child_pid;
(void)mrb;
/* Skip leading whitespace */
while (*cmd == ' ' || *cmd == '\t' || *cmd == '\n') {
cmd++;
}
if (!*cmd) {
errno = ENOENT;
return -1;
}
child_pid = fork();
if (child_pid == -1) {
/* Fork failed */
return -1;
}
if (child_pid == 0) {
/* Child process */
/* Redirect stdin */
if (stdin_fd != -1) {
dup2(stdin_fd, STDIN_FILENO);
if (stdin_fd > 2) close(stdin_fd);
}
/* Redirect stdout */
if (stdout_fd != -1) {
dup2(stdout_fd, STDOUT_FILENO);
if (stdout_fd > 2) close(stdout_fd);
}
/* Redirect stderr */
if (stderr_fd != -1) {
dup2(stderr_fd, STDERR_FILENO);
if (stderr_fd > 2) close(stderr_fd);
}
/* Close all other file descriptors */
int max_fd = sysconf(_SC_OPEN_MAX);
if (max_fd == -1) max_fd = 1024;
for (int i = 3; i < max_fd; i++) {
close(i);
}
/* Execute command via shell */
execl("/bin/sh", "sh", "-c", cmd, (char*)NULL);
/* If execl returns, it failed */
_exit(127);
}
/* Parent process */
*pid = (int)child_pid;
return 0;
}
int
mrb_hal_io_waitpid(mrb_state *mrb, int pid, int *status, int options)
{
pid_t result;
int stat;
(void)mrb;
result = waitpid((pid_t)pid, &stat, options);
if (result == -1) {
return -1;
}
if (status != NULL) {
*status = stat;
}
return (int)result;
}
/*
* I/O Multiplexing
*/
struct mrb_io_fdset {
fd_set fds;
};
mrb_io_fdset*
mrb_hal_io_fdset_alloc(mrb_state *mrb)
{
mrb_io_fdset *fdset = (mrb_io_fdset*)mrb_malloc(mrb, sizeof(mrb_io_fdset));
FD_ZERO(&fdset->fds);
return fdset;
}
void
mrb_hal_io_fdset_free(mrb_state *mrb, mrb_io_fdset *fdset)
{
if (fdset) {
mrb_free(mrb, fdset);
}
}
void
mrb_hal_io_fdset_zero(mrb_state *mrb, mrb_io_fdset *fdset)
{
(void)mrb;
if (fdset) {
FD_ZERO(&fdset->fds);
}
}
void
mrb_hal_io_fdset_set(mrb_state *mrb, int fd, mrb_io_fdset *fdset)
{
(void)mrb;
if (fd < 0 || fd >= FD_SETSIZE) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "fd is out of range");
return;
}
if (fdset) {
FD_SET(fd, &fdset->fds);
}
}
int
mrb_hal_io_fdset_isset(mrb_state *mrb, int fd, mrb_io_fdset *fdset)
{
(void)mrb;
if (fd < 0 || fd >= FD_SETSIZE) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "fd is out of range");
return 0;
}
if (fdset) {
return FD_ISSET(fd, &fdset->fds);
}
return 0;
}
int
mrb_hal_io_select(mrb_state *mrb, int nfds,
mrb_io_fdset *readfds,
mrb_io_fdset *writefds,
mrb_io_fdset *errorfds,
mrb_io_timeval *timeout)
{
fd_set *r = readfds ? &readfds->fds : NULL;
fd_set *w = writefds ? &writefds->fds : NULL;
fd_set *e = errorfds ? &errorfds->fds : NULL;
struct timeval *tv = NULL;
struct timeval tv_storage;
(void)mrb;
if (timeout) {
tv_storage.tv_sec = (time_t)timeout->tv_sec;
tv_storage.tv_usec = (suseconds_t)timeout->tv_usec;
tv = &tv_storage;
}
return select(nfds, r, w, e, tv);
}
/*
* HAL Initialization/Finalization
*/
void
mrb_hal_io_init(mrb_state *mrb)
{
(void)mrb;
/* No special initialization needed for POSIX */
}
void
mrb_hal_io_final(mrb_state *mrb)
{
(void)mrb;
/* No special cleanup needed for POSIX */
}
/*
* Gem initialization
*/
void
mrb_hal_posix_io_gem_init(mrb_state *mrb)
{
(void)mrb;
/* HAL interface functions are called by mruby-io gem */
}
void
mrb_hal_posix_io_gem_final(mrb_state *mrb)
{
(void)mrb;
/* Cleanup handled by mrb_hal_io_final called from mruby-io */
}

Some files were not shown because too many files have changed in this diff Show More