Compare commits

..

1 Commits

Author SHA1 Message Date
Yukihiro "Matz" Matsumoto 7ac96912e9 pre-commit: fix prettier formatting in doc/guides/capi.md
Co-authored-by: Claude <noreply@anthropic.com>
2026-03-12 13:09:44 +09:00
250 changed files with 6250 additions and 20117 deletions
-18
View File
@@ -5,29 +5,11 @@ 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
+4 -10
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@v6
- name: Ruby version
run: ruby -v
- name: Compiler version
@@ -46,13 +44,11 @@ jobs:
timeout-minutes: 15
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
uses: actions/checkout@v6
- name: Ruby version
run: ruby -v
- name: Cache cosmocc
uses: actions/cache@27d5ce7f107fe9357f9df03efb73ab90386fccae # v5.0.5
uses: actions/cache@v5
id: cache-cosmocc
with:
path: ~/cosmo
@@ -74,9 +70,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@v6
- name: Ruby version
run: ruby -v
- name: Build and test
+4 -6
View File
@@ -18,16 +18,14 @@ jobs:
language: ["actions"]
steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
uses: actions/checkout@v6
- name: Initialize CodeQL
uses: github/codeql-action/init@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0
uses: github/codeql-action/init@v4
with:
languages: ${{ matrix.language }}
- name: Autobuild
uses: github/codeql-action/autobuild@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0
uses: github/codeql-action/autobuild@v4
- name: Perform CodeQL Analysis
uses: github/codeql-action/analyze@7211b7c8077ea37d8641b6271f6a365a22a5fbfa # v4.36.0
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@v6
- 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@v7
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@f27b608878404679385c85cfa523b85ccb86e213 # v6.1.0
- uses: actions/labeler@v6
with:
repo-token: "${{ secrets.GITHUB_TOKEN }}"
sync-labels: true
+2 -4
View File
@@ -11,9 +11,7 @@ jobs:
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
- uses: actions/checkout@v6
- uses: ls-lint/action@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@v7
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@bdca6f102f98e2b4c7029491a53dfd366469e33d # v2.0.4
with:
install-only: true
- name: Run manual pre-commit hooks
run: prek run --color=always --all-files --hook-stage manual
+4 -4
View File
@@ -12,9 +12,9 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: "Checkout ${{ github.ref }} ( ${{ github.sha }} )"
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
persist-credentials: false
- uses: j178/prek-action@bdca6f102f98e2b4c7029491a53dfd366469e33d # v2.0.4
uses: actions/checkout@v6
- uses: j178/prek-action@v1
with:
extra-args: --all-files
- name: Run manual pre-commit hooks
run: prek run --color=always --all-files --hook-stage manual
+2 -4
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@v6
- name: Builds
id: builds
run: |
@@ -40,7 +38,7 @@ jobs:
mv .sha256 "$packagename.sha256"
)
- name: Release
uses: softprops/action-gh-release@b4309332981a82ec1c5618f44dd2e27cc8bfbfda # v3.0.0
uses: softprops/action-gh-release@v2
with:
draft: true
prerelease: ${{ contains(github.ref_name, '-rc') }}
+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@v6
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@v8.5.0
env:
# VALIDATE_BASH_EXEC: true
VALIDATE_DOCKERFILE_HADOLINT: true
+9 -22
View File
@@ -3,7 +3,7 @@
default_stages: [pre-commit, pre-push]
default_language_version:
python: python3
node: 24.14.0
node: 24.11.1
minimum_pre_commit_version: "3.2.0"
exclude: "^tools/lrama/"
repos:
@@ -15,9 +15,6 @@ repos:
- id: check-hooks-apply
name: run check-hooks-apply
description: check hooks apply to the repository
- id: check-useless-excludes
name: run check-useless-excludes
description: clean up unnecessary exclusion patterns
- repo: local
hooks:
- id: prettier
@@ -27,16 +24,6 @@ repos:
files: \.(md|ya?ml)$
language: node
additional_dependencies: ["prettier@3.7.4"]
pass_filenames: false
stages: [manual]
- id: check-makefile-indentation
name: check Makefiles are indented with tabs
description: ensures that Makefiles are indented with tabs
entry: ./scripts/check_makefiles_for_tabs.sh
language: system
files: "(?i)^makefile$"
pass_filenames: true # <-- Crucial change: pass filenames to the script
types: [file] # Ensure only regular files are passed, not directories
stages: [manual]
- id: check-zip-file-is-not-committed
name: disallow zip files
@@ -47,13 +34,13 @@ repos:
track and have security implications. Please remove the zip file from the repository.
files: \.zip$
- repo: https://github.com/gitleaks/gitleaks
rev: v8.30.1
rev: v8.30.0
hooks:
- id: gitleaks
name: run gitleaks
description: detect hardcoded secrets with gitleaks
- repo: https://github.com/oxipng/oxipng
rev: v10.1.1
rev: v10.0.0
hooks:
- id: oxipng
name: run oxipng
@@ -85,7 +72,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
@@ -97,19 +84,19 @@ repos:
args: [--whitespaces-count, "2"]
exclude: Makefile$
- repo: https://github.com/rhysd/actionlint
rev: v1.7.12
rev: v1.7.9
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/igorshubovych/markdownlint-cli
rev: v0.48.0
rev: v0.46.0
hooks:
- id: markdownlint
name: run markdownlint
@@ -118,7 +105,7 @@ repos:
types: [markdown]
files: \.md$
- repo: https://github.com/rubocop/rubocop
rev: v1.87.0
rev: v1.81.7
hooks:
- id: rubocop
name: run rubocop
@@ -132,7 +119,7 @@ repos:
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.1
hooks:
- id: yamllint
name: run yamllint
-1
View File
@@ -3,4 +3,3 @@ build
coverage
doc/internal/opcode.md
tools/lrama
.venv
+12 -16
View File
@@ -1,25 +1,25 @@
# Authors of mruby (mruby developers)
## The List of Contributors sorted by number of commits (as of 2026-05-31 9d084b0)
## The List of Contributors sorted by number of commits (as of 2026-03-02 02877f0)
7747 Yukihiro "Matz" Matsumoto (@matz)*
749 dearblue (@dearblue)*
7532 Yukihiro "Matz" Matsumoto (@matz)*
712 dearblue (@dearblue)*
587 KOBAYASHI Shuji (@shuujii)
353 Daniel Bovensiepen (@bovi)*
345 Takeshi Watanabe (@take-cheeze)*
333 Masaki Muranaka (@monaka)
266 John Bampton (@jbampton)
255 John Bampton (@jbampton)
234 Jun Hiroe (@suzukaze)
228 Tomoyuki Sahara (@tsahara)*
220 Cremno (@cremno)*
209 Yuki Kurihara (@ksss)+
146 Yasuhiro Matsumoto (@mattn)*
144 Yasuhiro Matsumoto (@mattn)*
113 Carson McDonald (@carsonmcdonald)
104 Tomasz Pędraszewski (@dabroz)*
83 Akira Yumiyama (@akiray03)*
83 skandhas (@skandhas)
80 Masamitsu MURASE (@masamitsu-murase)
79 Hiroshi Mimaki (@mimaki)*
73 Hiroshi Mimaki (@mimaki)*
71 Tatsuhiko Kubo (@cubicdaiya)*
71 Yuichiro MASUI (@masuidrive)
62 Yuichiro Kaneko (@yui-knk)+
@@ -36,10 +36,8 @@
32 Masayoshi Takahashi (@takahashim)+
31 MATSUMOTO Ryosuke (@matsumotory)*
30 Nobuyoshi Nakada (@nobu)
29 HASUMI Hitoshi (@hasumikin)
26 Hoshiumi Arata (@hoshiumiarata)*
25 Julian Aron Prenner (@furunkel)*
23 leviongit (@leviongit)
22 Clayton Smith (@clayton-shopify)
22 Uchio Kondo (@udzura)*
22 Zachary Scott (@zzak)*
@@ -52,9 +50,10 @@
18 Corey Powell (@IceDragon200)
18 Hidetaka Takano (@TJ-Hidetaka-Takano)
18 Jon Maken (@jonforums)+
18 leviongit (@leviongit)
18 mirichi (@mirichi)
17 Mitchell Blank Jr (@mitchblank)*
16 Hendrik (@Asmod4n)
16 HASUMI Hitoshi (@hasumikin)
16 bggd (@bggd)
16 kano4 (@kano4)
15 Felix Jones (@felixjones)*
@@ -77,7 +76,7 @@
11 RIZAL Reckordp (@Reckordp)+
11 Seeker (@SeekingMeaning)
11 takkaw (@takkaw)
10 Chris Hasiński (@khasinski)
10 Hendrik (@Asmod4n)
10 Miura Hideki (@miura1729)
10 Narihiro Nakamura (@authorNari)
10 YAMAMOTO Masaya (pandax381)
@@ -89,7 +88,6 @@
8 Wataru Ashihara (@wataash)*
7 Bhargava Shastry (@bshastry)*
7 Kouichi Nakanishi (@keizo042)
7 Paweł Świątkowski (@katafrakt)
7 Rubyist (@expeditiousRubyist)
7 Simon Génier (@simon-shopify)
7 Terence Lee (@hone)
@@ -103,6 +101,7 @@
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)
@@ -121,6 +120,7 @@
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)
@@ -140,7 +140,6 @@
4 Yuji Yamano (@yyamano)
4 kurodash (@kurodash)*
4 wanabe (@wanabe)*
2 0x1eef (@0x1eef)
3 Anton Davydov (@davydovanton)
3 Aurora Nockert (@auroranockert)
3 Carlo Prelz (@asfluido)*
@@ -193,7 +192,6 @@
2 Masahiro Wakame (@vvkame)+
2 Minao Yamamoto (@tarosay)+
2 Nihad Abbasov (@NARKOZ)
2 Pete Kinnecom (@petekinnecom)
2 Robert Mosolgo (@rmosolgo)
2 Russel Hunter Yukawa (@rhykw)+
2 Ryunosuke SATO (@tricknotes)
@@ -222,7 +220,6 @@
1 Colin MacKenzie IV (@sinisterchipmunk)
1 Daehyub Kim (@lateau)
1 Daniel Varga (@vargad)
1 David Korczynski (@DavidKorczynski)
1 Diamond Rivero (@diamant3)
1 Edgar Boda-Majer (@eboda)
1 Fangrui Song (@MaskRay)
@@ -277,6 +274,7 @@
1 Patrick Ellis (@pje)
1 Patrick Pokatilo (@SHyx0rmZ)
1 Pavel Evstigneev (@Paxa)+
1 Pete Kinnecom (@petekinnecom)
1 Piotr Usewicz (@pusewicz)
1 Prayag Verma (@pra85)
1 Ranmocy (@ranmocy)
@@ -284,7 +282,6 @@
1 Ryan Scott Lewis (@RyanScottLewis)
1 Ryo Okubo (@syucream)
1 SAkira a.k.a. Akira Suzuki (@sakisakira)
1 SaekiMototsune (@saeki-mototsune)
1 Santiago Rodriguez (@sanrodari)
1 Satoh, Hiroh (@cho45)+
1 Satoru Naba (@snaba)+
@@ -328,7 +325,6 @@
1 sbsoftware (@sbsoftware)
1 ssmallkirby (@smallkirby)
1 taku toyama (@tsuichu)
1 vobloeb (@vobloeb)
`*` - Entries unified according to names and addresses
`+` - Entries with names different from commits
+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.4.2)
yard (0.9.44)
rake (13.3.1)
yard (0.9.38)
yard-coderay (0.1.0)
coderay
yard
-44
View File
@@ -23,8 +23,6 @@ mruby now supports pattern matching (case/in) syntax:
- Trailing comma in method definition parameters: `def foo(a, b,)` ([f78334b](https://github.com/mruby/mruby/commit/f78334b))
- Array/Hash/String subclasses can now override `[]` and `[]=` methods ([#6675](https://github.com/mruby/mruby/pull/6675))
- `OP_SETIDX` optimization for Array and Hash ([ddd8fe1](https://github.com/mruby/mruby/commit/ddd8fe1))
- `case`/`in` without `else` now raises `NoMatchingPatternError` ([d8de35b](https://github.com/mruby/mruby/commit/d8de35b))
- Allow compound statement in parenthesized argument context ([919cbd8](https://github.com/mruby/mruby/commit/919cbd8))
# Changes in C API
@@ -39,8 +37,6 @@ mruby now supports pattern matching (case/in) syntax:
- `mrb_open()` returns mrb_state with exc set on init failure ([05ffe0c](https://github.com/mruby/mruby/commit/05ffe0c))
- `mrb_utf8_to_buf()` for UTF-8 encoding consolidation ([7e28e68](https://github.com/mruby/mruby/commit/7e28e68))
- `kh_is_end()` macro for safe khash iteration ([893cc75](https://github.com/mruby/mruby/commit/893cc75))
- `mrb_bigint_p()` always defined regardless of bigint gem presence ([6c4a8c0](https://github.com/mruby/mruby/commit/6c4a8c0))
- `RInteger` and `RFloat` added to `RVALUE` union ([13dbca0](https://github.com/mruby/mruby/commit/13dbca0))
# ROM Method Tables
@@ -80,7 +76,6 @@ mruby-symbol-ext, mruby-range-ext, mruby-object-ext.
- Emscripten: use native WASM exception handling ([ca364e3](https://github.com/mruby/mruby/commit/ca364e3))
- HAL (Hardware Abstraction Layer) for platform abstraction in mruby-io, mruby-socket, mruby-dir, mruby-task ([74ca22f](https://github.com/mruby/mruby/commit/74ca22f))
- `MRUBY_MIRB_READLINE` environment variable to control readline library selection ([0aafb83](https://github.com/mruby/mruby/commit/0aafb83))
- MSYS2 drive letter support in build script ([77f6ffe](https://github.com/mruby/mruby/commit/77f6ffe))
- Inter-gem headers separated from external API headers ([#6671](https://github.com/mruby/mruby/pull/6671))
# Changes in mrbgems
@@ -115,7 +110,6 @@ mruby-symbol-ext, mruby-range-ext, mruby-object-ext.
## Other Gem Changes
- **_NOTE_**: `Hash#deconstruct_keys` removed for CRuby compatibility ([34b9412](https://github.com/mruby/mruby/commit/34b9412))
- **mruby-enum-lazy**: Fix `Lazy#flat_map` to handle non-enumerable block return values ([#6765](https://github.com/mruby/mruby/pull/6765))
- **mruby-array-ext**: Add `Array#find` and `Array#rfind` methods
- **mruby-io**: Add `IO#putc` and `Kernel#putc` ([baff6e6](https://github.com/mruby/mruby/commit/baff6e6))
- **mruby-random**: Replace xoshiro with PCG for better memory efficiency ([f1bab01](https://github.com/mruby/mruby/commit/f1bab01))
@@ -131,8 +125,6 @@ mruby-symbol-ext, mruby-range-ext, mruby-object-ext.
- Optimized masgn to generate literals directly into target registers ([fb5d966](https://github.com/mruby/mruby/commit/fb5d966))
- Optimized splat of literal arrays in args/literals ([1cb8d73](https://github.com/mruby/mruby/commit/1cb8d73))
- Early termination after too many parse errors ([510ebd7](https://github.com/mruby/mruby/commit/510ebd7))
- Chunk array literals at 64 elements to reduce register pressure ([f98d641](https://github.com/mruby/mruby/commit/f98d641))
- Chunk `%w()` and `%i()` literals to reduce register pressure ([62cf0dc](https://github.com/mruby/mruby/commit/62cf0dc))
# VM Optimizations
@@ -193,10 +185,6 @@ Other optimizations:
- [#6705](https://github.com/mruby/mruby/issues/6705) Can't get outer class of an object in C
- [#6713](https://github.com/mruby/mruby/issues/6713) mruby-polarssl not work
- [#6720](https://github.com/mruby/mruby/issues/6720) Random float range: different behavior from CRuby
- [#6722](https://github.com/mruby/mruby/issues/6722) RBreak size overflow on 32-bit platforms with MRB_NO_BOXING
- [#6740](https://github.com/mruby/mruby/issues/6740) `%w()`/`%i()` register pressure with large literals
- [#6741](https://github.com/mruby/mruby/issues/6741) `case`/`in` without `else` should raise `NoMatchingPatternError`
- [#6760](https://github.com/mruby/mruby/issues/6760) `mrb_gc_unregister()` not removing all matching entries
# Merged Pull Requests
@@ -272,7 +260,6 @@ Other optimizations:
- [#6589](https://github.com/mruby/mruby/pull/6589) Add pre-commit hook `check-zip-file-is-not-committed`
- [#6591](https://github.com/mruby/mruby/pull/6591) mruby-eval fix license link in README
- [#6593](https://github.com/mruby/mruby/pull/6593) README: Add Contributors Avatars, Star History, Table of Contents
- [#6598](https://github.com/mruby/mruby/pull/6598) Fix heap buffer overflow in `#method_missing`
- [#6599](https://github.com/mruby/mruby/pull/6599) pre-commit: run `markdown-link-check`, `oxipng`, `prettier` manually
- [#6600](https://github.com/mruby/mruby/pull/6600) `dreamcast_shelf build config`: update to use KallistiOS wrappers
- [#6601](https://github.com/mruby/mruby/pull/6601) fix: skip local build_config.rb when working in MRUBY_ROOT
@@ -316,33 +303,6 @@ Other optimizations:
- [#6716](https://github.com/mruby/mruby/pull/6716) Fixes identity for proc object
- [#6717](https://github.com/mruby/mruby/pull/6717) Fix mruby-task: wrapping by critical section and setting initial task receiver
- [#6718](https://github.com/mruby/mruby/pull/6718) Add installation instructions for conda and Homebrew
- [#6723](https://github.com/mruby/mruby/pull/6723) Add `RInteger` and `RFloat` to `RVALUE`
- [#6727](https://github.com/mruby/mruby/pull/6727) Language documentation: update wording of "overloading" section
- [#6729](https://github.com/mruby/mruby/pull/6729) Simplifying dependency addition for gensym task
- [#6730](https://github.com/mruby/mruby/pull/6730) Simplifying presym file generation actions
- [#6733](https://github.com/mruby/mruby/pull/6733) Include `mruby/presym.h` for all source files
- [#6734](https://github.com/mruby/mruby/pull/6734) Chunk array literals at 64 elements to reduce register pressure
- [#6735](https://github.com/mruby/mruby/pull/6735) Prevent full recompilation without changes to presym file
- [#6739](https://github.com/mruby/mruby/pull/6739) Fix MSYS2 build error with drive letters
- [#6743](https://github.com/mruby/mruby/pull/6743) Chunk `%w()` and `%i()` literals to reduce register pressure
- [#6744](https://github.com/mruby/mruby/pull/6744) Raise `NoMatchingPatternError` in `case`/`in` without `else`
- [#6747](https://github.com/mruby/mruby/pull/6747) Correctly handle empty hash as default named argument
- [#6749](https://github.com/mruby/mruby/pull/6749) Fix microcontroller profile
- [#6750](https://github.com/mruby/mruby/pull/6750) Fix out-of-bounds read and divide-by-zero in `Array#product`
- [#6752](https://github.com/mruby/mruby/pull/6752) Fix `attr_reader`-generated methods accepting extra arguments
- [#6753](https://github.com/mruby/mruby/pull/6753) Further optimize `Array#product`
- [#6754](https://github.com/mruby/mruby/pull/6754) Mark `attr_reader` procs as noarg
- [#6755](https://github.com/mruby/mruby/pull/6755) Reload `ci` after `mrb_hash_delete_key()` in keyword argument handling
- [#6756](https://github.com/mruby/mruby/pull/6756) Avoid impact of object modifications caused by `mrb_vm_exec()` calls
- [#6758](https://github.com/mruby/mruby/pull/6758) Don't assign result of `mrb_funcall()` directly to `regs`
- [#6759](https://github.com/mruby/mruby/pull/6759) Define `mrb_bigint_p()` always
- [#6761](https://github.com/mruby/mruby/pull/6761) Fix `mrb_gc_unregister()` to remove all matching entries
- [#6762](https://github.com/mruby/mruby/pull/6762) Write generated test C files atomically to avoid build race condition
- [#6765](https://github.com/mruby/mruby/pull/6765) Fix `Lazy#flat_map` to handle non-enumerable block return values
- [#6767](https://github.com/mruby/mruby/pull/6767) Allow compound statement in parenthesized argument context
- [#6780](https://github.com/mruby/mruby/pull/6780) Fix `String#prepend` with self-referencing arguments
- [#6781](https://github.com/mruby/mruby/pull/6781) Protect `sprintf` format string from mutation during callbacks
- [#6783](https://github.com/mruby/mruby/pull/6783) Pin GitHub Actions workflows to commit hashes
# Security Fixes
@@ -360,8 +320,4 @@ Other optimizations:
- Heap-use-after-free in insertion_sort ([099d2c47](https://github.com/mruby/mruby/commit/099d2c47))
- Integer overflow in str_check_length ([6afff1c3](https://github.com/mruby/mruby/commit/6afff1c3))
- Integer overflow in Integer#lcm ([070bef24](https://github.com/mruby/mruby/commit/070bef24))
- Heap buffer overflow in `#method_missing` ([550d10a](https://github.com/mruby/mruby/commit/550d10a))
- Out-of-bounds read and divide-by-zero in `Array#product` ([8441eaf](https://github.com/mruby/mruby/commit/8441eaf))
- Heap buffer overflow in `String#prepend` with self-referencing arguments ([18ba026](https://github.com/mruby/mruby/commit/18ba026))
- Use-after-free in `sprintf` via `to_s` callback mutating format string ([48fc422](https://github.com/mruby/mruby/commit/48fc422))
- Multiple memory leak fixes in bigint, Set, Array, and Task gems
-2
View File
@@ -51,8 +51,6 @@ To get mruby, you can download the stable version 4.0.0 from the official mruby
GitHub repository or clone the trunk of the mruby source tree with the "git
clone" command. You can also install and compile mruby using [ruby-install](https://github.com/postmodern/ruby-install), [ruby-build](https://github.com/rbenv/ruby-build), [rvm](https://github.com/rvm/rvm), [conda](https://anaconda.org/channels/conda-forge/packages/mruby/overview) or [Homebrew](https://formulae.brew.sh/formula/mruby).
The release candidate version 4.0.0 of mruby can be downloaded via the following URL: [https://github.com/mruby/mruby/archive/4.0.0-rc3.zip](https://github.com/mruby/mruby/archive/4.0.0-rc3.zip)
The latest development version of mruby can be downloaded via the following URL: [https://github.com/mruby/mruby/zipball/master](https://github.com/mruby/mruby/zipball/master)
The trunk of the mruby source tree can be checked out with the
+4 -2
View File
@@ -292,7 +292,9 @@ class Scene
r = rad.x / nsfs
g = rad.y / nsfs
b = rad.z / nsfs
print([clamp(r), clamp(g), clamp(b)].pack("CCC"))
printf("%c", clamp(r))
printf("%c", clamp(g))
printf("%c", clamp(b))
end
end
end
@@ -301,7 +303,7 @@ end
# File.open("ao.ppm", "w") do |fp|
printf("P6\n")
printf("%d %d\n", IMAGE_WIDTH, IMAGE_HEIGHT)
printf("255\n")
printf("255\n", IMAGE_WIDTH, IMAGE_HEIGHT)
Scene.new.render(IMAGE_WIDTH, IMAGE_HEIGHT, NSUBSAMPLES)
# Scene.new.render(256, 256, 2)
# end
+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
+4 -2
View File
@@ -64,8 +64,10 @@ MRuby::Build.new do |conf|
# APE binaries use .com extension
conf.exts.executable = '.com'
# Cosmopolitan provides POSIX compatibility
conf.ports :posix
# 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'
-88
View File
@@ -1,88 +0,0 @@
MRuby::Build.new do |conf|
# load specific toolchain settings
conf.toolchain
# Use mrbgems
# conf.gem 'examples/mrbgems/ruby_extension_example'
# conf.gem 'examples/mrbgems/c_extension_example' do |g|
# g.cc.flags << '-g' # append cflags in this gem
# end
# conf.gem 'examples/mrbgems/c_and_ruby_extension_example'
# conf.gem :core => 'mruby-eval'
# conf.gem :mgem => 'mruby-onig-regexp'
# conf.gem :github => 'mattn/mruby-onig-regexp'
# conf.gem :git => 'git@github.com:mattn/mruby-onig-regexp.git', :branch => 'master', :options => '-v'
# include the GEM box
#conf.gembox 'default'
# C compiler settings
# conf.cc do |cc|
# cc.command = ENV['CC'] || 'gcc'
# cc.flags = [ENV['CFLAGS'] || %w()]
# cc.include_paths = ["#{root}/include"]
# cc.defines = %w()
# cc.option_include_path = %q[-I"%s"]
# cc.option_define = '-D%s'
# cc.compile_options = %Q[%{flags} -MMD -o "%{outfile}" -c "%{infile}"]
# end
# mrbc settings
# conf.mrbc do |mrbc|
# mrbc.compile_options = "-g -B%{funcname} -o-" # The -g option is required for line numbers
# end
# Linker settings
# conf.linker do |linker|
# linker.command = ENV['LD'] || 'gcc'
# linker.flags = [ENV['LDFLAGS'] || []]
# linker.flags_before_libraries = []
# linker.libraries = %w()
# linker.flags_after_libraries = []
# linker.library_paths = []
# linker.option_library = '-l%s'
# linker.option_library_path = '-L%s'
# linker.link_options = %Q[%{flags} -o "%{outfile}" %{objs} %{libs}]
# end
# Archiver settings
# conf.archiver do |archiver|
# archiver.command = ENV['AR'] || 'ar'
# archiver.archive_options = 'rs "%{outfile}" %{objs}'
# end
# Parser generator settings
# conf.yacc do |yacc|
# yacc.command = ENV['YACC'] || 'bison'
# yacc.compile_options = %q[-o "%{outfile}" "%{infile}"]
# end
# gperf settings
# conf.gperf do |gperf|
# gperf.command = 'gperf'
# gperf.compile_options = %q[-L ANSI-C -C -j1 -i 1 -o -t -N mrb_reserved_word -k"1,3,$" "%{infile}" > "%{outfile}"]
# end
# file extensions
# conf.exts do |exts|
# exts.object = '.o'
# exts.executable = '' # '.exe' if Windows
# exts.library = '.a'
# end
# file separator
# conf.file_separator = '/'
# change library directory name from the default "lib" if necessary
# conf.libdir_name = 'lib64'
# Turn on `enable_debug` for better debugging
conf.enable_sanitizer 'address,undefined'
conf.enable_debug
conf.enable_bintest
conf.enable_test
conf.ports :glib
conf.cc.defines << 'MRB_TASK_BUILD_DEMO'
conf.gem core: 'mruby-task'
conf.gem core: 'mruby-compiler'
end
+1 -1
View File
@@ -1,4 +1,4 @@
MRuby::Build.new('host-cxx') do |conf|
MRuby::Build.new do |conf|
conf.toolchain
# include the default GEMs
-2
View File
@@ -13,8 +13,6 @@ MRuby::Build.new('host') do |conf|
# Generate mruby debugger command (require mruby-eval)
conf.gem :core => "mruby-bin-debugger"
# Regexp is included via stdlib.gembox
# test
conf.enable_test
# bintest
+20 -78
View File
@@ -1,94 +1,36 @@
# Build mruby with a shared libmruby.so (in addition to the usual
# libmruby.a / executables).
#
# Produces (in build/host/lib/):
# libmruby.a the static archive (as in the default build)
# libmruby.so the shared library, with SONAME=libmruby.so.<MAJOR>.<MINOR>
# libmruby.so.<MAJOR>.<MINOR> symlink to libmruby.so (matches SONAME)
# libmruby.map linker version script (MRUBY_<RELEASE_NO>)
#
# Also produces the matching libmruby_core.so + symlink for completeness.
#
# Symbol versioning ties to MRUBY_RELEASE_NO (e.g. MRUBY_40000 for 4.0.0).
# mruby has historically had ABI breaks between TEENY versions, so the
# version tag uses the full release number rather than just MAJOR.MINOR.
#
# The shared library is built FROM the static archive via
# `-Wl,--whole-archive`, so the existing static-build pipeline (including
# the test infrastructure) is unaffected. Executables in build/host/bin
# remain statically linked; distros that want dynamically-linked
# executables can rebuild them against the .so.
#
# NOTE: gcc/clang only — VisualC++ support requires a separate config.
require "mruby/source"
# NOTE: Currently, this configuration file does not support VisualC++!
# Your help is needed!
MRuby::Build.new do |conf|
# load specific toolchain settings
conf.toolchain
# include the GEM box
conf.gembox 'default'
# -fPIC so the static archive's contents can be linked into the .so.
# C compiler settings
conf.compilers.each do |cc|
cc.flags << '-fPIC'
end
conf.archiver do |archiver|
archiver.command = cc.command
archiver.archive_options = '-shared -o %{outfile} %{objs}'
end
# file extensions
conf.exts do |exts|
exts.library = '.so'
end
# file separator
# conf.file_separator = '/'
# enable this if better compatibility with C++ is desired
#conf.enable_cxx_exception
# Turn on `enable_debug` for better debugging
conf.enable_debug
conf.enable_bintest
conf.enable_test
end
# Add the shared-library targets as a post-build pass, so the default
# static-build pipeline remains untouched.
MRuby.each_target do
next unless name == "host"
libdir = File.join(build_dir, libdir_name)
vermap = File.join(build_dir, "libmruby.map")
vertag = "MRUBY_#{MRuby::Source::MRUBY_RELEASE_NO}"
# Generate the version script eagerly — it has no .o dependencies and is
# tiny enough that lazy generation isn't worth the rake plumbing.
mkdir_p File.dirname(vermap)
File.write(vermap, <<~MAP)
#{vertag} {
global: *;
local: *;
};
MAP
major = MRuby::Source::MRUBY_RELEASE_MAJOR
minor = MRuby::Source::MRUBY_RELEASE_MINOR
[
[libmruby_static, "libmruby"],
[libmruby_core_static, "libmruby_core"],
].each do |archive, basename|
so = File.join(libdir, "#{basename}.so")
symlink = "#{so}.#{major}.#{minor}"
soname = "#{basename}.so.#{major}.#{minor}"
# Build .so from the static archive via --whole-archive.
file so => [archive, vermap] do |t|
_pp "LD", so.relative_path
sh "#{cc.command} -shared -fPIC -o #{so}" \
" -Wl,-soname,#{soname}" \
" -Wl,--version-script=#{vermap}" \
" -Wl,--whole-archive #{archive} -Wl,--no-whole-archive" \
" -lm"
end
products << so
# SONAME-matching symlink: needed so executables linked with -lmruby
# (which embeds the SONAME as DT_NEEDED) can find the library at
# runtime via standard search paths.
file symlink => so do |t|
_pp "LN", "#{symlink.relative_path} -> #{File.basename(so)}"
rm_f symlink
File.symlink(File.basename(so), symlink)
end
products << symlink
end
end
+3 -5
View File
@@ -72,9 +72,7 @@ MRuby::CrossBuild.new("playstationportable") do |conf|
conf.gem :core => "mruby-os-memsize"
conf.gem :core => "mruby-proc-binding"
conf.gem :core => "mruby-sleep"
# Disabled until PSP-specific HALs are available; the POSIX HALs depend on
# APIs that the PSP SDK does not fully provide.
# conf.gem :core => "mruby-io"
# conf.gem :core => "mruby-dir"
# conf.gem :core => "mruby-socket"
conf.gem :core => "mruby-io"
conf.gem :core => "mruby-dir"
#conf.gem :core => "mruby-socket" unsupported
end
+30 -30
View File
@@ -4,44 +4,44 @@
New to mruby? Start here:
| Document | Description |
| -------------------------------------------- | -------------------------------------- |
| Document | Description |
| -------- | ----------- |
| [Getting Started](guides/getting-started.md) | Build mruby and run your first program |
| [Language Features](guides/language.md) | Ruby subset supported by mruby |
| [Limitations](limitations.md) | Behavioral differences from CRuby |
| [Language Features](guides/language.md) | Ruby subset supported by mruby |
| [Limitations](limitations.md) | Behavioral differences from CRuby |
## Guides (for embedders and gem authors)
### Embedding mruby in C
| Document | Description |
| --------------------------------------- | ------------------------------------------------ |
| [C API Reference](guides/capi.md) | Values, classes, methods, error handling, fibers |
| [GC Arena](guides/gc-arena-howto.md) | Managing temporary objects in C extensions |
| [Linking](guides/link.md) | Linking with `libmruby` |
| [Amalgamation](guides/amalgamation.md) | Single-file build for easy integration |
| [Precompiled Symbols](guides/symbol.md) | Compile-time symbol allocation |
| Document | Description |
| -------- | ----------- |
| [C API Reference](guides/capi.md) | Values, classes, methods, error handling, fibers |
| [GC Arena](guides/gc-arena-howto.md) | Managing temporary objects in C extensions |
| [Linking](guides/link.md) | Linking with `libmruby` |
| [Amalgamation](guides/amalgamation.md) | Single-file build for easy integration |
| [Precompiled Symbols](guides/symbol.md) | Compile-time symbol allocation |
### Building and Configuring
| Document | Description |
| ---------------------------------------- | ------------------------------------------- |
| [Compilation](guides/compile.md) | Build system, cross-compilation, toolchains |
| [Build Configuration](guides/mrbconf.md) | Compile-time macros (`MRB_*` flags) |
| [mrbgems](guides/mrbgems.md) | Creating and managing gems |
| [Memory](guides/memory.md) | Allocator customization and heap regions |
| Document | Description |
| -------- | ----------- |
| [Compilation](guides/compile.md) | Build system, cross-compilation, toolchains |
| [Build Configuration](guides/mrbconf.md) | Compile-time macros (`MRB_*` flags) |
| [mrbgems](guides/mrbgems.md) | Creating and managing gems |
| [Memory](guides/memory.md) | Allocator customization and heap regions |
### Tools
| Document | Description |
| ----------------------------------------------- | ----------------------------------------------- |
| [Debugger](guides/debugger.md) | Using `mrdb` for debugging |
| Document | Description |
| -------- | ----------- |
| [Debugger](guides/debugger.md) | Using `mrdb` for debugging |
| [ROM Method Tables](guides/rom-method-table.md) | Read-only method tables for constrained devices |
### Reference
| Document | Description |
| ------------------------------------- | ------------------ |
| Document | Description |
| -------- | ----------- |
| [Directory Structure](guides/hier.md) | Source tree layout |
## Internals (for mruby contributors)
@@ -49,14 +49,14 @@ New to mruby? Start here:
Start with [Architecture](internal/architecture.md) for an overview,
then dive into the subsystem you need:
| Document | Description |
| ----------------------------------------- | -------------------------------------------------- |
| [Architecture](internal/architecture.md) | Overview of object model, VM, GC, compiler |
| [Virtual Machine](internal/vm.md) | Dispatch loop, call frames, method lookup, fibers |
| [Garbage Collector](internal/gc.md) | Tri-color marking, write barriers, generational GC |
| [Compiler Pipeline](internal/compiler.md) | Parser, code generator, IRep, binary format |
| [Opcodes](internal/opcode.md) | VM instruction set reference |
| [Value Boxing](internal/boxing.md) | How `mrb_value` encodes types |
| Document | Description |
| -------- | ----------- |
| [Architecture](internal/architecture.md) | Overview of object model, VM, GC, compiler |
| [Virtual Machine](internal/vm.md) | Dispatch loop, call frames, method lookup, fibers |
| [Garbage Collector](internal/gc.md) | Tri-color marking, write barriers, generational GC |
| [Compiler Pipeline](internal/compiler.md) | Parser, code generator, IRep, binary format |
| [Opcodes](internal/opcode.md) | VM instruction set reference |
| [Value Boxing](internal/boxing.md) | How `mrb_value` encodes types |
## Release Notes
+3 -3
View File
@@ -73,14 +73,14 @@ The following gems work with amalgamation:
- `mruby-enum-ext`, `mruby-compar-ext`
- `mruby-error`, `mruby-math`, `mruby-struct`
- `mruby-bigint`, `mruby-rational`, `mruby-complex`
- `mruby-io` (with the active `ports/<name>/` HAL)
- `mruby-task` (with the active `ports/<name>/` HAL)
- `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 its POSIX port on Linux, the generated `mruby.c`
`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
-7
View File
@@ -381,13 +381,6 @@ mrb_define_method(mrb, point, "initialize", point_init, MRB_ARGS_REQ(2));
mrb_define_method(mrb, point, "x", point_x, MRB_ARGS_NONE());
```
**Do not call into mruby from a `dfree` handler.** The handler runs
from inside GC sweep; allocating Ruby objects, calling
`mrb_funcall`, `mrb_yield`, raising exceptions, or otherwise
re-entering the VM can trigger a recursive GC that revisits the
same object and causes double-free. Keep `dfree` to `mrb_free` /
plain C cleanup of the wrapped data only.
## Exception Handling
### Raising Exceptions
+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
+87 -87
View File
@@ -205,11 +205,11 @@ mruby's numeric type sizes depend on the boxing mode and platform.
### Integer
| Configuration | Range |
| -------------------------------------- | ---------------- |
| Configuration | Range |
| ------------- | ----- |
| 64-bit word boxing (default on 64-bit) | roughly +/- 2^62 |
| 32-bit word boxing (default on 32-bit) | roughly +/- 2^30 |
| NaN boxing (64-bit only) | -2^31 to 2^31-1 |
| NaN boxing (64-bit only) | -2^31 to 2^31-1 |
Integer overflow raises a `RangeError` unless the `mruby-bigint` gem
is included, in which case integers automatically promote to
@@ -235,32 +235,32 @@ With word boxing on 64-bit, many float values are stored inline
These classes are always available in mruby (no gem required):
| Class | Notes |
| ------------- | -------------------------------------- |
| Object | Base class for all objects |
| Module | Module definition and mixin |
| Class | Class definition and instantiation |
| NilClass | Singleton `nil` |
| TrueClass | Singleton `true` |
| FalseClass | Singleton `false` |
| Integer | Fixed-precision integer |
| Float | Floating-point (unless `MRB_NO_FLOAT`) |
| Symbol | Interned identifier |
| String | Mutable byte string |
| Array | Ordered collection |
| Hash | Key-value mapping |
| Range | Interval representation |
| Proc | Closure / callable object |
| Exception | Exception hierarchy root |
| StandardError | Common error base |
| Class | Notes |
| ----- | ----- |
| Object | Base class for all objects |
| Module | Module definition and mixin |
| Class | Class definition and instantiation |
| NilClass | Singleton `nil` |
| TrueClass | Singleton `true` |
| FalseClass | Singleton `false` |
| Integer | Fixed-precision integer |
| Float | Floating-point (unless `MRB_NO_FLOAT`) |
| Symbol | Interned identifier |
| String | Mutable byte string |
| Array | Ordered collection |
| Hash | Key-value mapping |
| Range | Interval representation |
| Proc | Closure / callable object |
| Exception | Exception hierarchy root |
| StandardError | Common error base |
### Core Modules
| Module | Notes |
| ---------- | ----------------------------------------- |
| Kernel | Core methods (`puts`, `p`, `raise`, etc.) |
| Comparable | Comparison operators via `<=>` |
| Enumerable | Collection iteration methods |
| Module | Notes |
| ------ | ----- |
| Kernel | Core methods (`puts`, `p`, `raise`, etc.) |
| Comparable | Comparison operators via `<=>` |
| Enumerable | Collection iteration methods |
## Standard Library (via gemboxes)
@@ -270,70 +270,70 @@ gembox provides the class or feature you need:
### Classes and Modules
| Class/Module | Gembox | Gem |
| --------------------- | ---------- | ----------------- |
| Fiber | stdlib | mruby-fiber |
| Enumerator | stdlib | mruby-enumerator |
| Enumerator::Lazy | stdlib | mruby-enum-lazy |
| Set | stdlib | mruby-set |
| ObjectSpace | stdlib | mruby-objectspace |
| Time | stdlib-ext | mruby-time |
| Struct | stdlib-ext | mruby-struct |
| Data | stdlib-ext | mruby-data |
| Random | stdlib-ext | mruby-random |
| IO, File | stdlib-io | mruby-io |
| Socket | stdlib-io | mruby-socket |
| Dir | stdlib-io | mruby-dir |
| Errno | stdlib-io | mruby-errno |
| Math | math | mruby-math |
| Rational | math | mruby-rational |
| Complex | math | mruby-complex |
| Bigint | math | mruby-bigint |
| Method, UnboundMethod | metaprog | mruby-method |
| Class/Module | Gembox | Gem |
| ------------ | ------ | --- |
| Fiber | stdlib | mruby-fiber |
| Enumerator | stdlib | mruby-enumerator |
| Enumerator::Lazy | stdlib | mruby-enum-lazy |
| Set | stdlib | mruby-set |
| ObjectSpace | stdlib | mruby-objectspace |
| Time | stdlib-ext | mruby-time |
| Struct | stdlib-ext | mruby-struct |
| Data | stdlib-ext | mruby-data |
| Random | stdlib-ext | mruby-random |
| IO, File | stdlib-io | mruby-io |
| Socket | stdlib-io | mruby-socket |
| Dir | stdlib-io | mruby-dir |
| Errno | stdlib-io | mruby-errno |
| Math | math | mruby-math |
| Rational | math | mruby-rational |
| Complex | math | mruby-complex |
| Bigint | math | mruby-bigint |
| Method, UnboundMethod | metaprog | mruby-method |
### Methods and Features
| Feature | Gembox | Gem |
| ----------------------------- | ---------- | ------------------ |
| `catch`/`throw` | stdlib | mruby-catch |
| `Kernel#sprintf`, `String#%` | stdlib-ext | mruby-sprintf |
| `Array#pack`, `String#unpack` | stdlib-ext | mruby-pack |
| `Kernel#rand` | stdlib-ext | mruby-random |
| `Kernel#eval` | metaprog | mruby-eval |
| `Kernel#binding` | metaprog | mruby-binding |
| `Proc#binding` | metaprog | mruby-proc-binding |
| Runtime compiler | metaprog | mruby-compiler |
| Feature | Gembox | Gem |
| ------- | ------ | --- |
| `catch`/`throw` | stdlib | mruby-catch |
| `Kernel#sprintf`, `String#%` | stdlib-ext | mruby-sprintf |
| `Array#pack`, `String#unpack` | stdlib-ext | mruby-pack |
| `Kernel#rand` | stdlib-ext | mruby-random |
| `Kernel#eval` | metaprog | mruby-eval |
| `Kernel#binding` | metaprog | mruby-binding |
| `Proc#binding` | metaprog | mruby-proc-binding |
| Runtime compiler | metaprog | mruby-compiler |
### Core Class Extensions
The `stdlib` gembox also extends built-in classes with additional
methods. These are included by default:
| Extension | Examples |
| ----------------------- | ---------------------------------------------- |
| Array extensions | `#dig`, `#union`, `#difference` |
| Hash extensions | `#dig`, `#transform_keys`, `#transform_values` |
| String extensions | `#encode`, `#bytes`, `#chars` |
| Numeric extensions | `Integer#digits`, `Integer#pow` |
| Comparable extensions | `#clamp` |
| Enumerable extensions | `#sort_by`, `#min_by`, `#max_by`, `#tally` |
| Range extensions | `#size`, `#cover?` |
| Proc extensions | `#<<`, `#>>` (composition) |
| Symbol extensions | `#to_proc` |
| Object extensions | `#then`, `#yield_self` |
| Kernel extensions | `#__method__` |
| Class/Module extensions | `Module#name` |
| Extension | Examples |
| --------- | -------- |
| Array extensions | `#dig`, `#union`, `#difference` |
| Hash extensions | `#dig`, `#transform_keys`, `#transform_values` |
| String extensions | `#encode`, `#bytes`, `#chars` |
| Numeric extensions | `Integer#digits`, `Integer#pow` |
| Comparable extensions | `#clamp` |
| Enumerable extensions | `#sort_by`, `#min_by`, `#max_by`, `#tally` |
| Range extensions | `#size`, `#cover?` |
| Proc extensions | `#<<`, `#>>` (composition) |
| Symbol extensions | `#to_proc` |
| Object extensions | `#then`, `#yield_self` |
| Kernel extensions | `#__method__` |
| Class/Module extensions | `Module#name` |
### Gembox Summary
| Gembox | Contents | Notes |
| ------------ | --------------------------------------------- | -------------------------------------------- |
| `stdlib` | Core class extensions, Fiber, Enumerator, Set | Works with `MRB_NO_STDIO` and `MRB_NO_FLOAT` |
| `stdlib-ext` | Time, Struct, Data, Random, sprintf, pack | Works with `MRB_NO_STDIO` and `MRB_NO_FLOAT` |
| `stdlib-io` | IO, File, Dir, Socket, Errno | Requires stdio |
| `math` | Math, Rational, Complex, Bigint | Works with `MRB_NO_STDIO` |
| `metaprog` | eval, binding, Method, compiler | Works with `MRB_NO_STDIO` and `MRB_NO_FLOAT` |
| `default` | All of the above + CLI tools | Full installation |
| Gembox | Contents | Notes |
| ------ | -------- | ----- |
| `stdlib` | Core class extensions, Fiber, Enumerator, Set | Works with `MRB_NO_STDIO` and `MRB_NO_FLOAT` |
| `stdlib-ext` | Time, Struct, Data, Random, sprintf, pack | Works with `MRB_NO_STDIO` and `MRB_NO_FLOAT` |
| `stdlib-io` | IO, File, Dir, Socket, Errno | Requires stdio |
| `math` | Math, Rational, Complex, Bigint | Works with `MRB_NO_STDIO` |
| `metaprog` | eval, binding, Method, compiler | Works with `MRB_NO_STDIO` and `MRB_NO_FLOAT` |
| `default` | All of the above + CLI tools | Full installation |
## Key Differences from CRuby
@@ -416,15 +416,15 @@ differently on 32-bit or NaN boxing configurations.
Key compile-time macros that affect language behavior:
| Macro | Effect |
| -------------------- | ---------------------------------- |
| `MRB_NO_FLOAT` | Remove all float support |
| `MRB_USE_FLOAT32` | Use 32-bit float instead of double |
| `MRB_UTF8_STRING` | Enable UTF-8 string handling |
| `MRB_INT32` | Force 32-bit integer |
| `MRB_INT64` | Force 64-bit integer |
| `MRB_STR_LENGTH_MAX` | Max string length (default 1MB) |
| `MRB_ARY_LENGTH_MAX` | Max array length (default 2^17) |
| Macro | Effect |
| ----- | ------ |
| `MRB_NO_FLOAT` | Remove all float support |
| `MRB_USE_FLOAT32` | Use 32-bit float instead of double |
| `MRB_UTF8_STRING` | Enable UTF-8 string handling |
| `MRB_INT32` | Force 32-bit integer |
| `MRB_INT64` | Force 64-bit integer |
| `MRB_STR_LENGTH_MAX` | Max string length (default 1MB) |
| `MRB_ARY_LENGTH_MAX` | Max array length (default 2^17) |
See [mrbconf.md](mrbconf.md) for the complete list of configuration
macros.
+4 -4
View File
@@ -202,10 +202,10 @@ any other heap page. The only differences are:
The page size is controlled by `MRB_HEAP_PAGE_SIZE` (default: 1024 slots).
Each page occupies:
| Platform | Slot size | Page size (approx) |
| -------- | --------- | ------------------ |
| 64-bit | 40 bytes | ~41 KB |
| 32-bit | 24 bytes | ~25 KB |
| Platform | Slot size | Page size (approx) |
|----------|-----------|---------------------|
| 64-bit | 40 bytes | ~41 KB |
| 32-bit | 24 bytes | ~25 KB |
To estimate pages for a given buffer: `pages = buffer_size / sizeof(mrb_heap_page)`.
Each page provides `MRB_HEAP_PAGE_SIZE` object slots.
+3 -3
View File
@@ -233,10 +233,10 @@ end
- Specifies 4th argument(`argc`) max value of `mrb_funcall`.
- Raises `ArgumentError` when the `argc` argument is bigger then this value `mrb_funcall`.
`KHASH_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`
@@ -272,7 +272,7 @@ deployment targets. Define one of the following:
`MRB_CONSTRAINED_BASELINE_PROFILE`
- For micro controllers.
- Enables `MRB_NO_METHOD_CACHE`, reduces `KHASH_INITIAL_SIZE` to `16`,
- Enables `MRB_NO_METHOD_CACHE`, reduces `KHASH_DEFAULT_SIZE` to `16`,
and `MRB_HEAP_PAGE_SIZE` to `256`.
`MRB_BASELINE_PROFILE`
-62
View File
@@ -172,8 +172,6 @@ The maximal GEM structure looks like this:
|
+- src/ <- Source for C extension
|
+- ports/<name>/ <- Platform-specific C sources (see Platform Ports)
|
+- tools/ <- Source for Executable (in C)
|
+- test/ <- Test code (Ruby)
@@ -184,8 +182,6 @@ contains C/C++ files to extend mruby. The `include` directory contains C/C++ hea
files. The `test` directory contains C/C++ and pure Ruby files for testing purposes
which will be used by `mrbtest`. `mrbgem.rake` contains the specification
to compile C and Ruby files. `README.md` is a short description of your GEM.
The optional `ports/<name>/` directories hold platform-specific C sources
selected at build time; see [Platform Ports](#platform-ports-ports) below.
## Build process
@@ -336,64 +332,6 @@ 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.
## Platform Ports (ports/)
A gem may ship platform-specific C sources under `ports/<name>/`
subdirectories. The build configuration selects which port name(s)
are active via `conf.ports`, and each gem compiles the sources of
the first matching `ports/<name>/` it ships:
```ruby
MRuby::Build.new do |conf|
conf.toolchain
conf.ports :posix # selects ports/posix/ across all gems
end
```
`conf.ports` accepts multiple names as a fallback chain. Each gem
picks the first directory in the list that exists on its side:
```ruby
conf.ports :rp2040, :posix # try rp2040 per-gem, else posix
```
Host builds auto-detect `:posix` or `:win` when `conf.ports` is
not set. Sources outside `ports/` (i.e. `src/`) are always
compiled regardless of the port selection.
### External HAL Provider Gems
A third-party gem may replace another gem's bundled port at build
time. A gem whose name matches `hal-<short>-<conf>` is recognized
as the external HAL provider for the target gem whose name's last
`-`-separated segment is `<short>`. For example, `hal-task-glib`
overrides the HAL of `mruby-task`; `hal-io-uring` would override
`mruby-io`. The HAL provider must depend on its target so it can
`#include` the target's HAL header:
```ruby
MRuby::Gem::Specification.new('hal-task-glib') do |spec|
spec.license = 'MIT'
spec.author = 'Your Name'
spec.summary = 'GLib HAL for mruby-task'
spec.add_dependency 'mruby-task', core: 'mruby-task'
# src/ contains the HAL implementation
end
```
When a matching HAL provider gem is present in the build, the
target gem's `ports/<conf.ports>/` sources are dropped from the
build automatically. The HAL provider's own sources supply the
implementation instead, avoiding duplicate symbol errors at link
time. Loading two gems that match the same `hal-<short>-*`
prefix is a build error.
The naming convention is the only signal -- no spec attribute,
no `add_dependency` flag is required. A gem author who wants to
contribute an additional bundled port upstream sends a PR adding
`<target-gem>/ports/<name>/`; a gem author who prefers to ship
out of tree publishes a `hal-<short>-<conf>` gem instead.
## C Extension
mruby can be extended with C. This is possible by using the C API to
+3 -6
View File
@@ -117,8 +117,8 @@ Defined in `include/mruby/class.h`:
```c
union mrb_mt_ptr {
mrb_func_t func; /* first member: see MRB_MT_ENTRY note */
const struct RProc *proc;
mrb_func_t func;
};
typedef struct mrb_mt_entry {
@@ -139,12 +139,9 @@ typedef struct mrb_mt_tbl {
```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.
`func` must be the first member of `union mrb_mt_ptr` so that
positional initialization works on legacy C++ compilers that do
not accept C99 designated initializers. */
MRB_MT_PRIVATE. The macro OR's in MRB_MT_FUNC automatically. */
#define MRB_MT_ENTRY(fn, sym, flags) \
{ { (fn) }, (sym), (flags) | MRB_MT_FUNC }
{ { .func = (fn) }, (sym), (flags) | MRB_MT_FUNC }
/* Extract aspec from combined flags */
#define MRB_MT_ASPEC(flags) ((mrb_aspec)((flags) & 0xffffff))
+51 -51
View File
@@ -31,17 +31,17 @@ struct RBasic (8 bytes on 64-bit)
All object structs embed this header via `MRB_OBJECT_HEADER`:
| Struct | Ruby Type | Extra Fields |
| ------------ | ---------------- | ---------------------------------- |
| `RObject` | Object instances | `iv` (instance variables) |
| `RClass` | Class/Module | `iv`, `mt` (method table), `super` |
| `RString` | String | embedded or heap buffer, length |
| `RArray` | Array | embedded or heap buffer, length |
| `RHash` | Hash | hash table or k-v array |
| `RProc` | Proc/Lambda | `irep` or C function, environment |
| `RData` | C data wrapper | `void *data`, `mrb_data_type` |
| `RFiber` | Fiber | `mrb_context` |
| `RException` | Exception | `iv` |
| Struct | Ruby Type | Extra Fields |
| ------ | --------- | ------------ |
| `RObject` | Object instances | `iv` (instance variables) |
| `RClass` | Class/Module | `iv`, `mt` (method table), `super` |
| `RString` | String | embedded or heap buffer, length |
| `RArray` | Array | embedded or heap buffer, length |
| `RHash` | Hash | hash table or k-v array |
| `RProc` | Proc/Lambda | `irep` or C function, environment |
| `RData` | C data wrapper | `void *data`, `mrb_data_type` |
| `RFiber` | Fiber | `mrb_context` |
| `RException` | Exception | `iv` |
Immediate values (Integer, Symbol, `true`, `false`, `nil`) are encoded
directly in `mrb_value` without heap allocation. The encoding depends on
@@ -111,52 +111,52 @@ See [compiler.md](compiler.md) for detailed compiler internals,
### Core (`src/`)
| File | Responsibility |
| ------------- | ---------------------------------------------- |
| `vm.c` | Bytecode dispatch loop, method invocation |
| `state.c` | `mrb_state` init/close, irep management |
| `gc.c` | Garbage collector (mark-sweep, incremental) |
| `class.c` | Class/module definition, method tables |
| `object.c` | Core object operations |
| `variable.c` | Instance/class/global variables, object shapes |
| `proc.c` | Proc/Lambda/closure handling |
| `array.c` | Array implementation |
| `string.c` | String implementation (embedded, shared, heap) |
| `hash.c` | Hash implementation (open addressing) |
| `numeric.c` | Integer/Float arithmetic |
| `symbol.c` | Symbol table and interning |
| `range.c` | Range implementation |
| `error.c` | Exception creation, raise, backtrace |
| `kernel.c` | Kernel module methods |
| `load.c` | `.mrb` bytecode loading |
| `dump.c` | Bytecode serialization (write `.mrb`) |
| `print.c` | Print/puts/p output |
| `backtrace.c` | Stack trace generation |
| File | Responsibility |
| ---- | -------------- |
| `vm.c` | Bytecode dispatch loop, method invocation |
| `state.c` | `mrb_state` init/close, irep management |
| `gc.c` | Garbage collector (mark-sweep, incremental) |
| `class.c` | Class/module definition, method tables |
| `object.c` | Core object operations |
| `variable.c` | Instance/class/global variables, object shapes |
| `proc.c` | Proc/Lambda/closure handling |
| `array.c` | Array implementation |
| `string.c` | String implementation (embedded, shared, heap) |
| `hash.c` | Hash implementation (open addressing) |
| `numeric.c` | Integer/Float arithmetic |
| `symbol.c` | Symbol table and interning |
| `range.c` | Range implementation |
| `error.c` | Exception creation, raise, backtrace |
| `kernel.c` | Kernel module methods |
| `load.c` | `.mrb` bytecode loading |
| `dump.c` | Bytecode serialization (write `.mrb`) |
| `print.c` | Print/puts/p output |
| `backtrace.c` | Stack trace generation |
### Compiler (`mrbgems/mruby-compiler/core/`)
| File | Responsibility |
| ----------- | ------------------------------- |
| `parse.y` | Yacc grammar → AST |
| `y.tab.c` | Generated parser (from parse.y) |
| `codegen.c` | AST → bytecode (irep) |
| `node.h` | AST node type definitions |
| File | Responsibility |
| ---- | -------------- |
| `parse.y` | Yacc grammar → AST |
| `y.tab.c` | Generated parser (from parse.y) |
| `codegen.c` | AST → bytecode (irep) |
| `node.h` | AST node type definitions |
### Key Headers (`include/mruby/`)
| Header | Contents |
| ------------ | ------------------------------------- |
| `mruby.h` | `mrb_state`, core API declarations |
| `value.h` | `mrb_value`, type enums, value macros |
| `object.h` | `RBasic`, `RObject`, object header |
| `class.h` | `RClass`, method table types |
| `string.h` | `RString`, string macros |
| `array.h` | `RArray`, array macros |
| `hash.h` | `RHash`, hash API |
| `data.h` | `RData`, C data wrapping |
| `irep.h` | `mrb_irep`, bytecode structures |
| `compile.h` | Compiler context, `mrb_load_string` |
| `boxing_*.h` | Value boxing implementations |
| Header | Contents |
| ------ | -------- |
| `mruby.h` | `mrb_state`, core API declarations |
| `value.h` | `mrb_value`, type enums, value macros |
| `object.h` | `RBasic`, `RObject`, object header |
| `class.h` | `RClass`, method table types |
| `string.h` | `RString`, string macros |
| `array.h` | `RArray`, array macros |
| `hash.h` | `RHash`, hash API |
| `data.h` | `RData`, C data wrapping |
| `irep.h` | `mrb_irep`, bytecode structures |
| `compile.h` | Compiler context, `mrb_load_string` |
| `boxing_*.h` | Value boxing implementations |
## mrbgems System
+10 -10
View File
@@ -77,16 +77,16 @@ No boxing represents `mrb_value` by the C struct with `type` and the value union
## Comparison
| Property | Word Boxing | NaN Boxing | No Boxing |
| ---------------------- | ----------------- | ---------------- | -------------------- |
| `mrb_value` size | 1 word (4/8 byte) | 8 bytes | 2 words (8/16 bytes) |
| Default on | most platforms | (manual opt-in) | `host-debug` |
| Macro | `MRB_WORD_BOXING` | `MRB_NAN_BOXING` | `MRB_NO_BOXING` |
| Inline integers | yes (31/63 bit) | yes (32 bit) | yes (full width) |
| Inline floats (64-bit) | yes (rotation) | yes (native) | yes (struct field) |
| Inline floats (32-bit) | no (heap RFloat) | yes (native) | yes (struct field) |
| Pointer size limit | none | 48 bits | none |
| Debugger friendly | no | no | yes |
| Property | Word Boxing | NaN Boxing | No Boxing |
| ---------------------- | ------------------ | ------------------ | -------------------- |
| `mrb_value` size | 1 word (4/8 byte) | 8 bytes | 2 words (8/16 bytes) |
| Default on | most platforms | (manual opt-in) | `host-debug` |
| Macro | `MRB_WORD_BOXING` | `MRB_NAN_BOXING` | `MRB_NO_BOXING` |
| Inline integers | yes (31/63 bit) | yes (32 bit) | yes (full width) |
| Inline floats (64-bit) | yes (rotation) | yes (native) | yes (struct field) |
| Inline floats (32-bit) | no (heap RFloat) | yes (native) | yes (struct field) |
| Pointer size limit | none | 48 bits | none |
| Debugger friendly | no | no | yes |
## ABI Compatibility
+59 -59
View File
@@ -40,18 +40,18 @@ file: `mrbgems/mruby-compiler/core/parse.y`.
The parser maintains extensive state in `mrb_parser_state`:
- **lstate**: current lexer state (EXPR_BEG, EXPR_END, EXPR_ARG,
EXPR_DOT, EXPR_FNAME, etc.). Controls how tokens like `+`/`-`
- **lstate**: current lexer state (EXPR\_BEG, EXPR\_END, EXPR\_ARG,
EXPR\_DOT, EXPR\_FNAME, etc.). Controls how tokens like `+`/`-`
are interpreted (sign vs operator) and whether newlines are
significant.
- **locals**: stack of local variable lists (one per scope), stored
as cons-lists of symbols.
- **lex_strterm**: string/heredoc parsing state for handling nested
- **lex\_strterm**: string/heredoc parsing state for handling nested
interpolation.
- **cond_stack**, **cmdarg_stack**: bit stacks tracking
- **cond\_stack**, **cmdarg\_stack**: bit stacks tracking
conditional and command argument contexts.
- **tree**: root AST node after successful parse.
- **error_buffer**: accumulated parse errors.
- **error\_buffer**: accumulated parse errors.
### AST Nodes
@@ -123,10 +123,10 @@ reuse temporaries within an expression.
Instructions are emitted via helper functions:
- `genop_0(opcode)`: no operands
- `genop_1(opcode, a)`: one operand (auto-extends with OP_EXT1
- `genop_1(opcode, a)`: one operand (auto-extends with OP\_EXT1
if a > 255)
- `genop_2(opcode, a, b)`: two operands (auto-extends with
OP_EXT1/2/3 as needed)
OP\_EXT1/2/3 as needed)
- `genop_3(opcode, a, b, c)`: three operands
- `genop_W(opcode, a)`: 24-bit operand
- `genop_2S(opcode, a, b)`: one 8-bit + one 16-bit operand
@@ -176,14 +176,14 @@ mrb_irep
Pool entries store constants referenced by instructions:
| Type | Tag | Description |
| ---------------- | --- | ------------------------------- |
| `IREP_TT_STR` | 0 | Dynamic string (heap allocated) |
| `IREP_TT_SSTR` | 2 | Static string (read-only) |
| `IREP_TT_INT32` | 1 | 32-bit integer |
| `IREP_TT_INT64` | 3 | 64-bit integer |
| `IREP_TT_FLOAT` | 5 | Floating-point number |
| `IREP_TT_BIGINT` | 7 | Arbitrary-precision integer |
| Type | Tag | Description |
| ---- | --- | ----------- |
| `IREP_TT_STR` | 0 | Dynamic string (heap allocated) |
| `IREP_TT_SSTR` | 2 | Static string (read-only) |
| `IREP_TT_INT32` | 1 | 32-bit integer |
| `IREP_TT_INT64` | 3 | 64-bit integer |
| `IREP_TT_FLOAT` | 5 | Floating-point number |
| `IREP_TT_BIGINT` | 7 | Arbitrary-precision integer |
The code generator deduplicates pool entries: identical strings
and equal numeric values share the same pool index.
@@ -209,28 +209,28 @@ During exception unwinding, handlers are searched in reverse order
Standard instructions use 8-bit operands. When a value exceeds
255, extension prefixes widen operands to 16 bits:
| Prefix | Effect |
| --------- | --------------------------------- |
| `OP_EXT1` | First operand (a) becomes 16-bit |
| Prefix | Effect |
| ------ | ------ |
| `OP_EXT1` | First operand (a) becomes 16-bit |
| `OP_EXT2` | Second operand (b) becomes 16-bit |
| `OP_EXT3` | Both a and b become 16-bit |
| `OP_EXT3` | Both a and b become 16-bit |
Instruction formats:
| Format | Layout | Size |
| ------ | ----------------------------- | ------- |
| Z | opcode only | 1 byte |
| B | opcode + a(8) | 2 bytes |
| BB | opcode + a(8) + b(8) | 3 bytes |
| BBB | opcode + a(8) + b(8) + c(8) | 4 bytes |
| BS | opcode + a(8) + b(16) | 4 bytes |
| BSS | opcode + a(8) + b(16) + c(16) | 6 bytes |
| S | opcode + a(16) | 3 bytes |
| W | opcode + a(24) | 4 bytes |
| Format | Layout | Size |
| ------ | ------ | ---- |
| Z | opcode only | 1 byte |
| B | opcode + a(8) | 2 bytes |
| BB | opcode + a(8) + b(8) | 3 bytes |
| BBB | opcode + a(8) + b(8) + c(8) | 4 bytes |
| BS | opcode + a(8) + b(16) | 4 bytes |
| BSS | opcode + a(8) + b(16) + c(16) | 6 bytes |
| S | opcode + a(16) | 3 bytes |
| W | opcode + a(24) | 4 bytes |
See [opcode.md](opcode.md) for the full instruction table.
## OP_ENTER: Argument Specification
## OP\_ENTER: Argument Specification
`OP_ENTER` encodes a method's argument layout in a 24-bit value
(W format). The bit fields are defined by the `MRB_ARGS_*` macros:
@@ -258,16 +258,16 @@ string interning for common symbols.
Generated by `lib/mruby/presym.rb`, the presym table maps symbol
names to compile-time constants:
| Macro | Example | Symbol |
| ----------------- | --------------------- | ------------- |
| `MRB_SYM(name)` | `MRB_SYM(initialize)` | `:initialize` |
| `MRB_SYM_B(name)` | `MRB_SYM_B(map)` | `:map!` |
| `MRB_SYM_Q(name)` | `MRB_SYM_Q(nil)` | `:nil?` |
| `MRB_SYM_E(name)` | `MRB_SYM_E(name)` | `:name=` |
| `MRB_OPSYM(op)` | `MRB_OPSYM(add)` | `:+` |
| `MRB_IVSYM(name)` | `MRB_IVSYM(name)` | `:@name` |
| `MRB_CVSYM(name)` | `MRB_CVSYM(count)` | `:@@count` |
| `MRB_GVSYM(name)` | `MRB_GVSYM(stdout)` | `:$stdout` |
| Macro | Example | Symbol |
| ----- | ------- | ------ |
| `MRB_SYM(name)` | `MRB_SYM(initialize)` | `:initialize` |
| `MRB_SYM_B(name)` | `MRB_SYM_B(map)` | `:map!` |
| `MRB_SYM_Q(name)` | `MRB_SYM_Q(nil)` | `:nil?` |
| `MRB_SYM_E(name)` | `MRB_SYM_E(name)` | `:name=` |
| `MRB_OPSYM(op)` | `MRB_OPSYM(add)` | `:+` |
| `MRB_IVSYM(name)` | `MRB_IVSYM(name)` | `:@name` |
| `MRB_CVSYM(name)` | `MRB_CVSYM(count)` | `:@@count` |
| `MRB_GVSYM(name)` | `MRB_GVSYM(stdout)` | `:$stdout` |
## Binary Format (.mrb)
@@ -299,25 +299,25 @@ mrbc -Boutput source.rb # C array format
## Compilation Limits
| Limit | Value |
| ---------------------- | ----------------------------- |
| Max nesting depth | 256 (`MRB_CODEGEN_LEVEL_MAX`) |
| Max local variables | 255 (uint16 `nlocals`) |
| Max symbols per irep | 65535 |
| Max operand (standard) | 255 (8-bit) |
| Max operand (extended) | 65535 (16-bit) |
| Limit | Value |
| ----- | ----- |
| Max nesting depth | 256 (`MRB_CODEGEN_LEVEL_MAX`) |
| Max local variables | 255 (uint16 `nlocals`) |
| Max symbols per irep | 65535 |
| Max operand (standard) | 255 (8-bit) |
| Max operand (extended) | 65535 (16-bit) |
## Source Files
| File | Contents |
| --------------------------------------- | ------------------------- |
| `mrbgems/mruby-compiler/core/parse.y` | Lrama/Bison grammar |
| `mrbgems/mruby-compiler/core/y.tab.c` | Generated parser |
| `mrbgems/mruby-compiler/core/codegen.c` | Code generator |
| `mrbgems/mruby-compiler/core/node.h` | AST node types |
| `include/mruby/irep.h` | IRep structure definition |
| `include/mruby/compile.h` | Compiler context API |
| `include/mruby/ops.h` | Opcode definitions |
| `src/load.c` | Binary format loader |
| `src/dump.c` | Binary format writer |
| `lib/mruby/presym.rb` | Presym table generator |
| File | Contents |
| ---- | -------- |
| `mrbgems/mruby-compiler/core/parse.y` | Lrama/Bison grammar |
| `mrbgems/mruby-compiler/core/y.tab.c` | Generated parser |
| `mrbgems/mruby-compiler/core/codegen.c` | Code generator |
| `mrbgems/mruby-compiler/core/node.h` | AST node types |
| `include/mruby/irep.h` | IRep structure definition |
| `include/mruby/compile.h` | Compiler context API |
| `include/mruby/ops.h` | Opcode definitions |
| `src/load.c` | Binary format loader |
| `src/dump.c` | Binary format writer |
| `lib/mruby/presym.rb` | Presym table generator |
+35 -165
View File
@@ -26,12 +26,12 @@ pauses.
Every heap-allocated object has a color stored in
`RBasic::gc_color` (3 bits):
| Color | Value | Meaning |
| -------------- | ------ | ------------------------------------ |
| White (A or B) | 1 or 2 | Unmarked, candidate for collection |
| Gray | 0 | Marked, but children not yet scanned |
| Black | 4 | Fully marked and scanned |
| Red | 7 | Static/ROM object, never collected |
| Color | Value | Meaning |
| ----- | ----- | ------- |
| White (A or B) | 1 or 2 | Unmarked, candidate for collection |
| Gray | 0 | Marked, but children not yet scanned |
| Black | 4 | Fully marked and scanned |
| Red | 7 | Static/ROM object, never collected |
The GC uses two white types (A and B) in a flip-flop scheme. At the
start of each GC cycle, the meaning of "current white" is flipped by
@@ -107,7 +107,7 @@ The GC operates as a three-state machine:
GC_STATE_ROOT --> GC_STATE_MARK --> GC_STATE_SWEEP --> GC_STATE_ROOT
```
### Root Scan (GC_STATE_ROOT)
### Root Scan (GC\_STATE\_ROOT)
Marks objects directly reachable from the VM:
@@ -121,7 +121,7 @@ Marks objects directly reachable from the VM:
After root scanning, the white color is flipped.
### Incremental Marking (GC_STATE_MARK)
### Incremental Marking (GC\_STATE\_MARK)
Gray objects are popped from the gray stack and their children
marked. Each step processes a limited number of objects:
@@ -131,15 +131,13 @@ limit = (GC_STEP_SIZE / 100) * step_ratio
```
With default `step_ratio = 200` and `GC_STEP_SIZE = 1024`, the
limit is 2048 objects per step. After each step, `gc_debt` is
decremented by the actual number of objects processed, so larger
steps repay more debt.
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)
### Sweep (GC\_STATE\_SWEEP)
Iterates through heap pages. For each object:
@@ -275,7 +273,7 @@ From Ruby: `GC.generational_mode = true/false`.
`mrb_obj_alloc()` is the core allocation function:
1. If `MRB_GC_STRESS` is defined, run a full GC
2. Increment `gc->gc_debt`; if positive, run `mrb_incremental_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`)
@@ -301,43 +299,18 @@ The object's type is set to `MRB_TT_FREE` after freeing.
## Triggering GC
### Debt Model
### Automatic
GC uses a **debt-based feedback model** to balance allocation
rate against collection work. The key field is `gc->gc_debt`
(signed integer):
- **Negative** = credit (GC is ahead, no collection needed)
- **Zero** = balanced
- **Positive** = debt (allocation outpacing collection, GC runs)
Each object allocation increments `gc_debt` by 1. When debt
goes positive, `mrb_incremental_gc()` runs. Each incremental
step decrements debt by `GC_STEP_SIZE` (1024), giving credit
for many future allocations.
When a GC cycle completes, credit is calculated from
`interval_ratio`:
GC runs automatically when `gc->live >= gc->threshold` during
object allocation. After each cycle:
```text
credit = (live_after_mark / 100) * interval_ratio - live_after_mark
threshold = (live_after_mark / 100) * interval_ratio
minimum: GC_STEP_SIZE (1024)
gc_debt = -credit
```
With default `interval_ratio = 200` and 1000 live objects:
`credit = (1000/100)*200 - 1000 = 1000`, so approximately 1000
allocations can occur before the next GC cycle begins.
### Malloc Pressure
When `gc->malloc_threshold` is set (non-zero), the GC also
tracks bytes allocated through `mrb_realloc_simple()` in
`gc->malloc_increase`. When `malloc_increase` exceeds
`malloc_threshold`, the counter resets and an incremental GC
step runs. This captures memory pressure from large buffers
(e.g., long strings) that would otherwise be invisible to the
object-count-based debt model.
With default `interval_ratio = 200`, GC triggers when live objects
roughly double.
### Manual
@@ -352,136 +325,33 @@ From Ruby: `GC.start`.
### Compile-Time
| Macro | Default | Description |
| ------------------------------ | ------- | --------------------------------------- |
| `MRB_HEAP_PAGE_SIZE` | 1024 | Objects per heap page |
| `MRB_GRAY_STACK_SIZE` | 1024 | Gray stack capacity |
| `MRB_GC_ARENA_SIZE` | 100 | Arena size (fixed mode) or initial size |
| `MRB_GC_FIXED_ARENA` | off | Use fixed-size arena |
| `MRB_GC_TURN_OFF_GENERATIONAL` | off | Disable generational mode |
| `MRB_GC_STRESS` | off | Full GC on every allocation (debug) |
| `MRB_GC_STATS` | off | Enable GC statistics counters |
| `MRB_USE_MALLOC_TRIM` | off | Call `malloc_trim()` after full GC |
| Macro | Default | Description |
| ----- | ------- | ----------- |
| `MRB_HEAP_PAGE_SIZE` | 1024 | Objects per heap page |
| `MRB_GRAY_STACK_SIZE` | 1024 | Gray stack capacity |
| `MRB_GC_ARENA_SIZE` | 100 | Arena size (fixed mode) or initial size |
| `MRB_GC_FIXED_ARENA` | off | Use fixed-size arena |
| `MRB_GC_TURN_OFF_GENERATIONAL` | off | Disable generational mode |
| `MRB_GC_STRESS` | off | Full GC on every allocation (debug) |
| `MRB_USE_MALLOC_TRIM` | off | Call `malloc_trim()` after full GC |
### Runtime
From Ruby code:
```ruby
GC.interval_ratio = 200 # controls debt credit after GC cycle
GC.step_ratio = 200 # objects per incremental step
GC.step_limit = 0 # 0=unlimited, >0=absolute step cap
GC.malloc_threshold = 0 # 0=disabled, >0=bytes to trigger GC
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
GC.start # force full GC
GC.enable # re-enable GC
GC.disable # disable GC
```
### GC Statistics
`GC.stat` returns a Hash with GC state and statistics:
```ruby
GC.stat
# => {
# :live => 5432, # live object count
# :debt => -1024, # GC debt (negative=credit, positive=behind)
# :state => 0, # 0=root, 1=marking, 2=sweeping
# :generational => true, # generational mode enabled
# :full => false, # major GC in progress
# :step_limit => 0, # current step limit setting
# :malloc_increase => 8192, # malloc bytes since last cycle
# :malloc_threshold => 0, # current malloc threshold setting
# }
```
With `MRB_GC_STATS` enabled, additional keys are available:
```ruby
# :total => 15, # total GC invocations
# :minor => 12, # minor GC count
# :major => 3, # major GC count
```
### Tuning Guide
**`interval_ratio`** (default 200): Controls how many allocations
occur between GC cycles. Higher values reduce GC frequency but
increase peak memory. The debt credit after each cycle is
`(live_after_mark / 100) * interval_ratio - live_after_mark`.
**`step_ratio`** (default 200): Controls how much work each
incremental step performs. Higher values make each step larger,
reducing total GC overhead but increasing individual pause times.
**`step_limit`** (default 0, unlimited): Caps the maximum work
per incremental step regardless of `step_ratio`. Useful for
real-time applications that need bounded pause times. The
effective step size is `min(step_ratio calculation, step_limit)`.
**`malloc_threshold`** (default 0, disabled): Triggers GC when
cumulative `malloc`/`realloc` bytes exceed this threshold. Useful
when applications allocate large buffers (strings, data objects)
that create memory pressure without proportional object count
increase.
### Practical Tuning Examples
**Allocation-heavy workloads** (many short-lived Procs, closures,
blocks): GC sweep dominates because of high object churn. Increase
`interval_ratio` to reduce GC frequency:
```ruby
GC.interval_ratio = 400 # ~12% faster than default (200)
```
Higher values (400-600) reduce sweep overhead at the cost of more
dead objects accumulating before collection. Values above 600 show
diminishing returns. Peak memory usage increases temporarily, but
live object count after GC remains the same.
**CPU-intensive workloads** (numeric computation, recursive methods
with no object allocation): GC parameters have negligible impact
because GC rarely runs. No tuning needed.
**Real-time or latency-sensitive** applications: Use `step_limit`
to bound pause times:
```ruby
GC.step_limit = 256 # cap incremental step to 256 objects
```
This makes GC pauses more predictable but increases total GC
overhead (more steps needed per cycle).
**Large buffer workloads** (reading files, building long strings):
Set `malloc_threshold` to trigger GC when buffer allocations
accumulate, even if object count is low:
```ruby
GC.malloc_threshold = 1024 * 1024 # trigger GC per ~1MB allocated
```
### Diagnosing GC Overhead
Use `GC.stat` to monitor GC behavior at runtime:
```ruby
s = GC.stat
puts "live objects: #{s[:live]}"
puts "GC debt: #{s[:debt]}" # positive = GC is behind
puts "GC state: #{s[:state]}" # 0=idle, 1=marking, 2=sweeping
```
If `debt` is frequently positive during performance-critical
sections, increase `interval_ratio`. If memory usage is too high,
decrease it.
## Source Files
| File | Contents |
| -------------------- | --------------------------------- |
| `src/gc.c` | GC implementation |
| File | Contents |
| ---- | -------- |
| `src/gc.c` | GC implementation (~1400 lines) |
| `include/mruby/gc.h` | `mrb_gc` structure, public GC API |
| `include/mruby.h` | Arena save/restore macros |
| `include/mruby.h` | Arena save/restore macros |
+19 -19
View File
@@ -97,12 +97,12 @@ return n + 1 (skip self)
### Call Context Info (cci)
| Value | Name | Meaning |
| ----- | --------------- | ------------------------------------- |
| 0 | `CINFO_NONE` | Normal VM-to-VM call |
| 1 | `CINFO_DIRECT` | Explicit VM call (block, lambda.call) |
| 2 | `CINFO_SKIP` | Skip frame in stack traces |
| 3 | `CINFO_RESUMED` | Fiber resumed (stop execution) |
| Value | Name | Meaning |
| ----- | ---- | ------- |
| 0 | `CINFO_NONE` | Normal VM-to-VM call |
| 1 | `CINFO_DIRECT` | Explicit VM call (block, lambda.call) |
| 2 | `CINFO_SKIP` | Skip frame in stack traces |
| 3 | `CINFO_RESUMED` | Fiber resumed (stop execution) |
## Dispatch Loop
@@ -241,13 +241,13 @@ correctness.
### Proc Types
| Flag | Meaning |
| ------------------- | ------------------------------ |
| `MRB_PROC_CFUNC_FL` | C function (not irep-based) |
| `MRB_PROC_STRICT` | Lambda (strict argument check) |
| `MRB_PROC_ORPHAN` | No environment attachment |
| `MRB_PROC_ENVSET` | Has captured environment |
| `MRB_PROC_SCOPE` | Defines a new variable scope |
| Flag | Meaning |
| ---- | ------- |
| `MRB_PROC_CFUNC_FL` | C function (not irep-based) |
| `MRB_PROC_STRICT` | Lambda (strict argument check) |
| `MRB_PROC_ORPHAN` | No environment attachment |
| `MRB_PROC_ENVSET` | Has captured environment |
| `MRB_PROC_SCOPE` | Defines a new variable scope |
## Fiber Switching
@@ -317,9 +317,9 @@ ensuring the incremental GC correctly tracks live references.
## Source Files
| File | Contents |
| ----------------------- | ---------------------------------------------- |
| `src/vm.c` | Dispatch loop, method invocation (~1900 lines) |
| `include/mruby.h` | `mrb_state`, `mrb_callinfo`, `mrb_context` |
| `include/mruby/proc.h` | `RProc`, `REnv` structures |
| `include/mruby/throw.h` | `MRB_TRY`/`MRB_CATCH` macros |
| File | Contents |
| ---- | -------- |
| `src/vm.c` | Dispatch loop, method invocation (~1900 lines) |
| `include/mruby.h` | `mrb_state`, `mrb_callinfo`, `mrb_context` |
| `include/mruby/proc.h` | `RProc`, `REnv` structures |
| `include/mruby/throw.h` | `MRB_TRY`/`MRB_CATCH` macros |
+9 -168
View File
@@ -15,23 +15,6 @@ This document is collecting these limitations.
This document does not contain a complete list of limitations.
Please help to improve it by submitting your findings.
## Features provided by mrbgems
Many Ruby features that CRuby builds into its core are provided by
mrbgems in mruby. Which features are actually available depends on
which mrbgems are linked into the build. The `default.gembox` and
`stdlib.gembox` cover the common cases, but a minimal build can omit
familiar features such as `Kernel#binding` (provided by
`mruby-binding`), `Kernel#catch`/`throw` (by `mruby-catch`),
`Enumerable` extensions, `Comparable`, IO, regular expressions, and
many more.
This is by design rather than a limitation per se. When porting Ruby
code to mruby, a `NoMethodError` or `NameError` often means "the gem
providing this feature is not linked in" rather than "mruby does not
support it." Adding the relevant gem to the build configuration is
usually enough.
## `Kernel.raise` in rescue clause
`Kernel.raise` without arguments does not raise the current exception within
@@ -150,6 +133,12 @@ The re-defined `+` operator does not accept any arguments.
`'ab'`
Behavior of the operator wasn't changed.
## `Kernel#binding` is not supported without mruby-binding gem
`Kernel#binding` method requires the `mruby-binding` gem (included
in the `metaprog` gembox). Without this gem, `binding` is not
available.
## `nil?` redefinition in conditional expressions
Redefinition of `nil?` is ignored in conditional expressions.
@@ -285,11 +274,11 @@ enabled with the `MRB_UTF8_STRING` compile flag.
Integer size depends on the value boxing configuration:
| Configuration | Integer range |
| ----------------------------- | ---------------- |
| Configuration | Integer range |
| ------------- | ------------- |
| Word boxing, 64-bit (default) | roughly +/- 2^62 |
| Word boxing, 32-bit (default) | roughly +/- 2^30 |
| NaN boxing (64-bit only) | -2^31 to 2^31-1 |
| NaN boxing (64-bit only) | -2^31 to 2^31-1 |
Code relying on 64-bit integer precision may behave differently
across configurations. The `mruby-bigint` gem provides
@@ -301,151 +290,3 @@ arbitrary-precision integers when included.
(included in the `stdlib` gembox). Even with the gem,
`ObjectSpace.each_object` has limited functionality compared
to CRuby.
## No Implicit Type Conversion (`to_int`, `to_str`, `to_ary`, ...)
mruby does not perform implicit type conversion through methods
like `to_int`, `to_str`, `to_ary`, or `to_hash`. CRuby uses these
to let user-defined classes duck-type as built-in types — for
example `Array#[]` calls `to_int` on its argument, `String#+` calls
`to_str`, and multiple assignment calls `to_ary` on its right-hand
side. mruby's built-in operations require the actual built-in type
and do not consult these conversion methods.
```ruby
class MyInt; def to_int; 42; end; end
class MyStr; def to_str; "x"; end; end
class MyAry; def to_ary; [1,2,3]; end; end
```
#### CRuby
```
[1,2,3][MyInt.new] # => nil (to_int called -> ary[42])
"a" + MyStr.new # => "ax" (to_str called)
a, b, c = MyAry.new # => a=1, b=2, c=3 (to_ary called)
```
#### mruby
```
[1,2,3][MyInt.new] # TypeError
"a" + MyStr.new # TypeError
a, b, c = MyAry.new # a=<MyAry obj>, b=nil, c=nil (treated as single value)
```
Identity versions of `to_int`, `to_str`, `to_sym`, and `to_hash`
remain defined on the corresponding built-in types so that
`respond_to?(:to_str)`-style checks work for built-in instances.
`Float#to_int` and `Array#to_ary` are intentionally not defined.
Explicit conversion methods (`to_i`, `to_s`, `to_a`) work as in
CRuby and are called by features such as string interpolation and
the splat operator (`*obj`).
This is a deliberate trade-off: implicit conversion forces every
coercion site to go through method dispatch and can silently mask
type-mismatch bugs.
## Nested `def` in Singleton-Method Context
`def` written inside a singleton method (`def self.foo`) is placed
on a different class in mruby than in CRuby. CRuby registers the
inner method as an instance method of the lexical enclosing class.
mruby registers it as a method of the enclosing receiver's
singleton class, which makes it visible as a class method of the
enclosing class.
```ruby
class SomeClass
def self.class_method
def nested; 'nested!'; end
end
end
SomeClass.class_method
```
#### CRuby
```
SomeClass.nested # NoMethodError
SomeClass.new.nested # => "nested!" (instance method)
```
#### mruby
```
SomeClass.nested # => "nested!" (class method)
SomeClass.new.nested # NoMethodError
```
Writing nested `def` like this is unusual; this difference rarely
surfaces in practical code.
## `Proc#dup` / `Proc#clone` is Always Orphan
A `dup` or `clone` of a block given to a method is always treated as
an orphan block in mruby — calling it raises `LocalJumpError` if the
block contains `break` or `return`. CRuby is finer-grained: the copy
inherits the orphan status of its original, so the copy only becomes
orphan once the original yielding method returns.
```ruby
def m(&b)
b.dup
end
x = m { break 1 }
x.call
```
#### CRuby
```
LocalJumpError # raised only after m returns; if called inside m,
# the dup is still a live block
```
#### mruby
```
LocalJumpError # always raised — the dup is orphan from the moment
# it is created
```
mruby's stricter rule keeps `RProc` from needing a back-pointer to
the original block (which would also enlarge the GC mark set).
## `Class#initialize` Can Be Re-Invoked
CRuby raises `TypeError: already initialized class` when `initialize`
is invoked on a class that has already been set up. mruby's
`Class#initialize` has no such guard — invoking it on an existing
class through `__send__`, `send`, or `UnboundMethod#bind_call`
silently succeeds. The superclass argument is ignored in this case,
so the call cannot rewrite the class hierarchy; only the block (if
any) is evaluated with the class as receiver.
```ruby
Klass = Class.new
Klass.__send__(:initialize) {}
```
#### CRuby
```
TypeError: already initialized class
```
#### mruby
```
The block is evaluated in the context of Klass; no error is raised.
The superclass is not changed even when one is passed as an argument.
```
`Module#initialize` is re-callable in both implementations, so this
divergence is `Class`-specific. Adding the CRuby check would require
an additional flag bit on every `RClass`; mruby leaves the bit
unspent because no destructive side effects are possible through
this path.
-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
+4 -11
View File
@@ -138,8 +138,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
@@ -172,13 +172,6 @@
#define MRB_SYMBOL_LINEAR_THRESHOLD 256
#endif
/* Maximum number of dynamic symbols (created at runtime via to_sym etc.)
Presyms, inline symbols, and mrb_intern_static symbols are excluded.
Set to 0 to disable the limit. */
#ifndef MRB_SYMBOL_MAX
#define MRB_SYMBOL_MAX 4096
#endif
/* obsolete configurations */
#if defined(DISABLE_STDIO) || defined(MRB_DISABLE_STDIO)
# define MRB_NO_STDIO
@@ -215,8 +208,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
+9 -54
View File
@@ -113,8 +113,6 @@
#include "mrbconf.h"
typedef struct mrb_state mrb_state;
#include <mruby/common.h>
#include <mruby/value.h>
#include <mruby/gc.h>
@@ -158,6 +156,8 @@ typedef uint32_t mrb_aspec;
typedef struct mrb_irep mrb_irep;
struct mrb_state;
#ifndef MRB_FIXED_STATE_ATEXIT_STACK_SIZE
#define MRB_FIXED_STATE_ATEXIT_STACK_SIZE 5
#endif
@@ -224,7 +224,7 @@ mrb_static_assert_powerof2(MRB_METHOD_CACHE_SIZE);
* @param self The self object
* @return [mrb_value] The function's return value
*/
typedef mrb_value (*mrb_func_t)(mrb_state *mrb, mrb_value self);
typedef mrb_value (*mrb_func_t)(struct mrb_state *mrb, mrb_value self);
typedef struct {
uint32_t flags; /* method flags (no symbol packed) */
@@ -243,26 +243,9 @@ struct mrb_cache_entry {
};
#endif
#ifdef MRB_CONST_CACHE_SIZE
# undef MRB_NO_CONST_CACHE
mrb_static_assert_powerof2(MRB_CONST_CACHE_SIZE);
#else
/* default constant cache size: 64 */
/* cache size needs to be power of 2 */
# define MRB_CONST_CACHE_SIZE (1<<6)
#endif
#ifndef MRB_NO_CONST_CACHE
struct mrb_const_cache_entry {
const struct mrb_irep *irep;
mrb_sym sym;
mrb_value value;
};
#endif
struct mrb_jmpbuf;
typedef void (*mrb_atexit_func)(mrb_state*);
typedef void (*mrb_atexit_func)(struct mrb_state*);
#ifdef MRB_USE_TASK_SCHEDULER
struct mrb_task;
@@ -274,12 +257,10 @@ typedef struct mrb_task_state {
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_bool loop_running; /* Active mrb_task_run loop flag */
mrb_bool exception_as_result; /* Return unhandled task exceptions as values */
} mrb_task_state;
#endif
struct mrb_state {
typedef struct mrb_state {
struct mrb_jmpbuf *jmp;
struct mrb_context *c;
@@ -316,28 +297,22 @@ struct mrb_state {
struct mrb_cache_entry cache[MRB_METHOD_CACHE_SIZE];
#endif
#ifndef MRB_NO_CONST_CACHE
struct mrb_const_cache_entry const_cache[MRB_CONST_CACHE_SIZE];
#endif
mrb_sym symidx;
const char **symtbl;
uint8_t *sym_flags; /* per-symbol flags (SYM_FL_*) */
size_t symcapa;
struct mrb_sym_hash_table *symhash;
void *sym_pool;
mrb_sym dynamic_sym_count; /* count of dynamic (GC-candidate) symbols */
#ifndef MRB_USE_ALL_SYMBOLS
char symbuf[8]; /* buffer for small symbol names */
#endif
#ifdef MRB_USE_DEBUG_HOOK
void (*code_fetch_hook)(mrb_state* mrb, const struct mrb_irep *irep, const mrb_code *pc, mrb_value *regs);
void (*debug_op_hook)(mrb_state* mrb, const struct mrb_irep *irep, const mrb_code *pc, mrb_value *regs);
void (*code_fetch_hook)(struct mrb_state* mrb, const struct mrb_irep *irep, const mrb_code *pc, mrb_value *regs);
void (*debug_op_hook)(struct mrb_state* mrb, const struct mrb_irep *irep, const mrb_code *pc, mrb_value *regs);
#endif
#ifdef MRB_BYTECODE_DECODE_OPTION
mrb_code (*bytecode_decoder)(mrb_state* mrb, mrb_code code);
mrb_code (*bytecode_decoder)(struct mrb_state* mrb, mrb_code code);
#endif
struct RClass *eException_class;
@@ -364,7 +339,7 @@ struct mrb_state {
#ifdef MRB_USE_TASK_SCHEDULER
mrb_task_state task; /* Task scheduler state */
#endif
};
} mrb_state;
/**
* Defines a new class.
@@ -1184,21 +1159,6 @@ MRB_API mrb_value mrb_funcall_id(mrb_state *mrb, mrb_value val, mrb_sym mid, mrb
* @see mrb_funcall
*/
MRB_API mrb_value mrb_funcall_argv(mrb_state *mrb, mrb_value val, mrb_sym name, mrb_int argc, const mrb_value *argv);
/*
* Convenience wrappers for `mrb_funcall_argv` with a fixed argument count.
* Avoids the 16-slot fixed argv buffer used by the variadic `mrb_funcall_id`.
*/
MRB_INLINE mrb_value
mrb_funcall_argv1(mrb_state *mrb, mrb_value val, mrb_sym name, mrb_value a1)
{
return mrb_funcall_argv(mrb, val, name, 1, &a1);
}
MRB_INLINE mrb_value
mrb_funcall_argv2(mrb_state *mrb, mrb_value val, mrb_sym name, mrb_value a1, mrb_value a2)
{
const mrb_value argv[] = { a1, a2 };
return mrb_funcall_argv(mrb, val, name, 2, argv);
}
/**
* Call existing Ruby functions with a block.
*/
@@ -1324,11 +1284,6 @@ MRB_API void mrb_method_cache_clear(mrb_state *mrb);
#else
#define mrb_method_cache_clear(mrb) ((void)0)
#endif
#ifndef MRB_NO_CONST_CACHE
MRB_API void mrb_const_cache_clear(mrb_state *mrb);
#else
#define mrb_const_cache_clear(mrb) ((void)0)
#endif
/**
* Check if mrb_open() failed
+2 -24
View File
@@ -20,17 +20,7 @@ typedef struct mrb_shared_array {
mrb_value *ptr;
} mrb_shared_array;
/* On 32-bit platforms whose ABI gives 8-byte members 8-byte alignment
(ARM, MIPS, xtensa, ...), an embedded mrb_value array forces 8-byte
alignment of the inner union, padding the heap-form layout and
inflating struct size past the 5-word RVALUE limit. Disable embedding
whenever mrb_value contains an 8-byte aligned member: nan-boxing
(uint64_t), or no-boxing with int64_t/double inside the union. */
#if defined(MRB_32BIT) && \
(defined(MRB_NAN_BOXING) || \
(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_INT64)) && !defined(MRB_ARY_NO_EMBED)
# define MRB_ARY_NO_EMBED
#endif
@@ -67,7 +57,7 @@ struct RArray {
#define ARY_UNSET_EMBED_FLAG(a) (void)0
#define ARY_EMBED_LEN(a) 0
#define ARY_SET_EMBED_LEN(a,len) (void)0
#define ARY_EMBED_PTR(a) ((mrb_value*)NULL)
#define ARY_EMBED_PTR(a) 0
#else
#define MRB_ARY_EMBED_MASK 7
#define ARY_EMBED_P(a) ((a)->flags & MRB_ARY_EMBED_MASK)
@@ -81,7 +71,6 @@ struct RArray {
#define ARY_PTR(a) (ARY_EMBED_P(a)?ARY_EMBED_PTR(a):(a)->as.heap.ptr)
#define RARRAY_LEN(a) ARY_LEN(RARRAY(a))
#define RARRAY_PTR(a) ARY_PTR(RARRAY(a))
#define RARRAY_GETMEM(a, ptr, len) ARY_GETMEM(RARRAY(a), ptr, len)
#define ARY_SET_LEN(a,n) do {\
if (ARY_EMBED_P(a)) {\
mrb_assert((n) <= MRB_ARY_EMBED_LEN_MAX); \
@@ -95,17 +84,6 @@ struct RArray {
#define ARY_SHARED_P(a) ((a)->flags & MRB_ARY_SHARED)
#define ARY_SET_SHARED_FLAG(a) ((a)->flags |= MRB_ARY_SHARED)
#define ARY_UNSET_SHARED_FLAG(a) ((a)->flags &= ~MRB_ARY_SHARED)
#define ARY_GETMEM(a, ptr, len) do { \
struct RArray *MRB_UNIQNAME(_a_) = (a); \
if (ARY_EMBED_P(MRB_UNIQNAME(_a_))) { \
(len) = ARY_EMBED_LEN(MRB_UNIQNAME(_a_)); \
(ptr) = ARY_EMBED_PTR(MRB_UNIQNAME(_a_)); \
} \
else { \
(len) = MRB_UNIQNAME(_a_)->as.heap.len; \
(ptr) = MRB_UNIQNAME(_a_)->as.heap.ptr; \
} \
} while (0)
MRB_API void mrb_ary_modify(mrb_state*, struct RArray*);
MRB_API mrb_value mrb_ary_dup(mrb_state*, mrb_value ary);
+2 -3
View File
@@ -103,8 +103,7 @@ mrb_unboxed_type(mrb_value o)
{
if (!mrb_float_p(o) && mrb_nb_tt(o) == MRB_NANBOX_TT_OBJECT && o.u != 0) {
return ((struct RBasic*)(uintptr_t)o.u)->tt;
}
else {
} else {
return MRB_TT_FALSE;
}
}
@@ -151,7 +150,7 @@ mrb_nan_boxing_value_int(mrb_value v)
#define SET_TRUE_VALUE(r) NANBOX_SET_MISC_VALUE(r, MRB_TT_TRUE, 1)
#define SET_BOOL_VALUE(r,b) NANBOX_SET_MISC_VALUE(r, (b) ? MRB_TT_TRUE : MRB_TT_FALSE, 1)
#ifdef MRB_INT64
MRB_API mrb_value mrb_boxing_int_value(mrb_state*, mrb_int);
MRB_API mrb_value mrb_boxing_int_value(struct mrb_state*, mrb_int);
#define SET_INT_VALUE(mrb, r, n) ((r) = mrb_boxing_int_value(mrb, n))
#else
#define SET_INT_VALUE(mrb, r, n) SET_FIXNUM_VALUE(r, n)
+5 -7
View File
@@ -165,11 +165,11 @@ mrb_val_union(mrb_value v)
return x;
}
MRB_API mrb_value mrb_word_boxing_cptr_value(mrb_state*, void*);
MRB_API mrb_value mrb_word_boxing_cptr_value(struct mrb_state*, void*);
#ifndef MRB_NO_FLOAT
MRB_API mrb_value mrb_word_boxing_float_value(mrb_state*, mrb_float);
MRB_API mrb_value mrb_word_boxing_float_value(struct mrb_state*, mrb_float);
#endif
MRB_API mrb_value mrb_boxing_int_value(mrb_state*, mrb_int);
MRB_API mrb_value mrb_boxing_int_value(struct mrb_state*, mrb_int);
#if WORDBOX_IMMEDIATE_MASK == 0x3
#define mrb_immediate_p(o) ((o).w & WORDBOX_IMMEDIATE_MASK || (o).w <= MRB_Qundef)
@@ -266,11 +266,9 @@ mrb_unboxed_type(mrb_value o)
{
if (mrb_nil_p(o)) {
return MRB_TT_FALSE;
}
else if ((o.w & WORDBOX_IMMEDIATE_MASK) == 0) {
} else if ((o.w & WORDBOX_IMMEDIATE_MASK) == 0) {
return mrb_val_union(o).bp->tt;
}
else {
} else {
return MRB_TT_FALSE;
}
}
+3 -6
View File
@@ -100,13 +100,10 @@ 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.
* NOTE: `func` is kept as the first union member so that positional
* aggregate initialization in MRB_MT_ENTRY works without C99
* designated initializers (required for legacy C++ compilers). */
/* ROM method table types for static method registration */
union mrb_mt_ptr {
mrb_func_t func;
const struct RProc *proc;
mrb_func_t func;
};
/* entry combining function pointer, symbol key, and flags */
@@ -131,7 +128,7 @@ typedef struct mrb_mt_tbl {
/* ROM table entry: 3rd param is MRB_ARGS_*() optionally OR'd with MRB_MT_PRIVATE. */
#define MRB_MT_ENTRY(fn, sym, flags) \
{ { (fn) }, (sym), (flags) | MRB_MT_FUNC }
{ { .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.
-1
View File
@@ -134,7 +134,6 @@ struct mrb_parser_state {
mrb_sym* filename_table;
uint16_t filename_table_length;
uint16_t current_filename_index;
uint16_t prev_file_lineno; /* saved lineno before partial_hook file switch */
/* Variable-sized node management */
mrb_ast_node *nvars;
+6 -27
View File
@@ -37,14 +37,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, ...);
/* On 32-bit platforms whose ABI gives uint64_t/double 8-byte alignment
(ARM, MIPS, PowerPC, xtensa, ...), embedding mrb_value directly in
RBreak forces 8-byte alignment that pushes the struct past the 5-word
RVALUE budget via padding. Store the value bits as a uint32_t array
(4-byte aligned) to dodge that padding. Word-boxing's mrb_value is
just a uintptr_t with no over-alignment, and 64-bit platforms have no
alignment gap to begin with, so neither needs the workaround. */
#if defined(MRB_64BIT) || defined(MRB_WORD_BOXING)
#if defined(MRB_NAN_BOXING) || defined(MRB_WORD_BOXING) || defined(MRB_64BIT)
#undef MRB_USE_RBREAK_VALUE_UNION
#else
#define MRB_USE_RBREAK_VALUE_UNION 1
@@ -52,8 +45,7 @@ MRB_API mrb_noreturn void mrb_no_method_error(mrb_state *mrb, mrb_sym id, mrb_va
/*
* flags:
* 0..7: enum mrb_vtype (only when MRB_USE_RBREAK_VALUE_UNION and
* !MRB_NAN_BOXING; nan-boxing encodes the type in the bits)
* 0..7: enum mrb_vtype (only when defined MRB_USE_RBREAK_VALUE_UNION)
* 8..10: RBREAK_TAGs in src/vm.c (otherwise, set to 0)
*/
struct RBreak {
@@ -61,11 +53,11 @@ struct RBreak {
uintptr_t ci_break_index; // The top-level ci index to break. One before the return destination.
#ifndef MRB_USE_RBREAK_VALUE_UNION
mrb_value val;
#elif defined(MRB_NAN_BOXING)
/* nan-boxing: mrb_value is a single 64-bit word (type encoded in NaN bits) */
uint32_t value[sizeof(mrb_value) / sizeof(uint32_t)];
#else
/* no-boxing: store only the union bits; tt goes in flags */
/* 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)];
#endif
};
@@ -73,19 +65,6 @@ struct RBreak {
#ifndef MRB_USE_RBREAK_VALUE_UNION
#define mrb_break_value_get(brk) ((brk)->val)
#define mrb_break_value_set(brk, v) ((brk)->val = v)
#elif defined(MRB_NAN_BOXING)
static inline mrb_value
mrb_break_value_get(struct RBreak *brk)
{
mrb_value val;
memcpy(&val, brk->value, sizeof(val));
return val;
}
static inline void
mrb_break_value_set(struct RBreak *brk, mrb_value val)
{
memcpy(brk->value, &val, sizeof(val));
}
#else
#define RBREAK_VALUE_TT_MASK ((1 << 8) - 1)
static inline mrb_value
+9 -15
View File
@@ -14,12 +14,15 @@
*/
MRB_BEGIN_DECL
struct mrb_state;
#define MRB_EACH_OBJ_OK 0
#define MRB_EACH_OBJ_BREAK 1
typedef int (mrb_each_object_callback)(mrb_state *mrb, struct RBasic *obj, void *data);
void mrb_objspace_each_objects(mrb_state *mrb, mrb_each_object_callback *callback, void *data);
typedef int (mrb_each_object_callback)(struct mrb_state *mrb, struct RBasic *obj, void *data);
void mrb_objspace_each_objects(struct mrb_state *mrb, mrb_each_object_callback *callback, void *data);
size_t mrb_objspace_page_slot_size(void);
MRB_API void mrb_free_context(mrb_state *mrb, struct mrb_context *c);
MRB_API void mrb_free_context(struct mrb_state *mrb, struct mrb_context *c);
#ifndef MRB_GC_ARENA_SIZE
#define MRB_GC_ARENA_SIZE 100
@@ -45,7 +48,7 @@ typedef struct mrb_gc {
mrb_bool gray_overflow:1; /* gray stack overflowed; needs heap rescan */
size_t live; /* count of live objects */
size_t live_after_mark; /* old generation objects */
mrb_int gc_debt; /* <0:credit, >0:needs GC */
size_t threshold; /* threshold to start GC */
size_t oldgen_threshold; /* threshold to kick major GC */
mrb_gc_state state; /* current state of gc */
int interval_ratio;
@@ -56,9 +59,6 @@ typedef struct mrb_gc {
mrb_bool generational :1; /* generational GC mode */
mrb_bool full :1; /* major GC mode */
mrb_bool out_of_memory :1; /* out-of-memory error occurred */
size_t step_limit; /* 0=unlimited, >0=absolute step cap */
size_t malloc_increase; /* malloc bytes since last GC cycle */
size_t malloc_threshold; /* 0=disabled, >0=bytes to trigger GC */
#ifdef MRB_GC_FIXED_ARENA
struct RBasic *arena[MRB_GC_ARENA_SIZE]; /* GC protection array */
@@ -67,16 +67,10 @@ typedef struct mrb_gc {
int arena_capa; /* size of protection array */
#endif
int arena_idx;
#ifdef MRB_GC_STATS
uint32_t gc_total_count; /* total GC invocations */
uint32_t minor_gc_count; /* minor GC count */
uint32_t major_gc_count; /* major GC count */
#endif
} mrb_gc;
MRB_API mrb_bool mrb_object_dead_p(mrb_state *mrb, struct RBasic *object);
MRB_API int mrb_gc_add_region(mrb_state *mrb, void *start, size_t size);
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
-3
View File
@@ -279,7 +279,4 @@ mrb_value mrb_bint_abs(mrb_state *mrb, mrb_value x);
void mrb_task_mark_all(mrb_state *mrb);
#endif
/* Internal object allocation without type validation (gc.c) */
struct RBasic* mrb_obj_alloc_core(mrb_state*, enum mrb_vtype, struct RClass*);
#endif /* MRUBY_INTERNAL_H */
+1 -1
View File
@@ -20,7 +20,7 @@ enum irep_pool_type {
IREP_TT_SSTR = 2, /* string (static) */
IREP_TT_INT32 = 1, /* 32-bit integer */
IREP_TT_INT64 = 3, /* 64-bit integer */
IREP_TT_BIGINT = 7, /* big integer */
IREP_TT_BIGINT = 7, /* big integer (not yet supported) */
IREP_TT_FLOAT = 5, /* float (double/float) */
};
+1 -1
View File
@@ -21,7 +21,7 @@ typedef uint32_t khint_t;
typedef khint_t khiter_t;
#ifndef KHASH_INITIAL_SIZE
# define KHASH_INITIAL_SIZE 32
# define KHASH_INITIAL_SIZE 8
#endif
#define KHASH_MIN_SIZE 8
#define KHASH_SMALL_LIMIT 4
-54
View File
@@ -102,60 +102,6 @@ struct RProc {
#define MRB_PROC_ALIAS 8192
#define MRB_PROC_ALIAS_P(p) (((p)->flags & MRB_PROC_ALIAS) != 0)
/* Compressed aspec for cfunc procs (13 bits in RProc.flags).
* Uses free bits 0-6 and 14-19 to store a compressed argument spec.
* Layout: block(0) kdict(1) key(2-3) post(4-5) rest(6) opt(14-16) req(17-19)
* Field widths are smaller than the full 24-bit aspec: req/opt max 7, post/key max 3.
* Values exceeding the compressed range are clamped and rest is forced to 1. */
#define MRB_PROC_CASPEC_MASK 0xfc07fu /* bits 0-6 and 14-19 */
static inline uint32_t
mrb_proc_compress_aspec(mrb_aspec aspec)
{
uint32_t req = MRB_ASPEC_REQ(aspec);
uint32_t opt = MRB_ASPEC_OPT(aspec);
uint32_t rest = MRB_ASPEC_REST(aspec);
uint32_t post = MRB_ASPEC_POST(aspec);
uint32_t key = MRB_ASPEC_KEY(aspec);
uint32_t kdict = MRB_ASPEC_KDICT(aspec);
uint32_t block = MRB_ASPEC_BLOCK(aspec);
if (req > 7 || opt > 7 || post > 3 || key > 3) {
if (req > 7) req = 7;
if (opt > 7) opt = 7;
if (post > 3) post = 3;
if (key > 3) key = 3;
rest = 1;
}
return block | (kdict << 1) | (key << 2) | (post << 4) | (rest << 6)
| (opt << 14) | (req << 17);
}
static inline mrb_aspec
mrb_proc_decompress_caspec(uint32_t flags)
{
return (((flags >> 17) & 0x7) << 18) /* req */
| (((flags >> 14) & 0x7) << 13) /* opt */
| (((flags >> 6) & 0x1) << 12) /* rest */
| (((flags >> 4) & 0x3) << 7) /* post */
| (((flags >> 2) & 0x3) << 2) /* key */
| (((flags >> 1) & 0x1) << 1) /* kdict */
| (flags & 0x1); /* block */
}
static inline void
mrb_proc_set_cfunc_aspec(struct RProc *p, mrb_aspec aspec)
{
p->flags &= ~(MRB_PROC_NOARG | MRB_PROC_CASPEC_MASK);
if (aspec == 0) {
p->flags |= MRB_PROC_NOARG;
}
else {
p->flags |= mrb_proc_compress_aspec(aspec);
}
}
#define mrb_proc_ptr(v) ((struct RProc*)(mrb_ptr(v)))
struct RProc *mrb_proc_new(mrb_state*, const mrb_irep*);
-10
View File
@@ -342,16 +342,6 @@ MRB_API const char *mrb_string_value_cstr(mrb_state *mrb, mrb_value *str);
*/
MRB_API mrb_value mrb_str_dup(mrb_state *mrb, mrb_value str);
/**
* Returns a frozen string object.
* The string will be duplicated and frozen if it is not already frozen.
*
* @param mrb The current mruby state.
* @param str An original Ruby string.
* @return [mrb_value] Ruby frozen string.
*/
MRB_API mrb_value mrb_str_dup_frozen(mrb_state *mrb, mrb_value str);
/**
* Returns a symbol from a passed in Ruby string.
*
+5 -7
View File
@@ -55,6 +55,8 @@ typedef uint8_t mrb_bool;
# endif
#endif
struct mrb_state;
#if defined _MSC_VER && _MSC_VER < 1800
# define PRIo64 "llo"
# define PRId64 "lld"
@@ -314,11 +316,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
/**
@@ -328,7 +326,7 @@ struct RCptr {
*/
#ifndef MRB_NO_FLOAT
MRB_INLINE mrb_value
mrb_float_value(mrb_state *mrb, mrb_float f)
mrb_float_value(struct mrb_state *mrb, mrb_float f)
{
mrb_value v;
(void) mrb;
@@ -338,7 +336,7 @@ mrb_float_value(mrb_state *mrb, mrb_float f)
#endif
MRB_INLINE mrb_value
mrb_cptr_value(mrb_state *mrb, void *p)
mrb_cptr_value(struct mrb_state *mrb, void *p)
{
mrb_value v;
(void) mrb;
@@ -350,7 +348,7 @@ mrb_cptr_value(mrb_state *mrb, void *p)
* Returns an integer in Ruby.
*/
MRB_INLINE mrb_value
mrb_int_value(mrb_state *mrb, mrb_int i)
mrb_int_value(struct mrb_state *mrb, mrb_int i)
{
mrb_value v;
SET_INT_VALUE(mrb, v, i);
+1 -1
View File
@@ -80,7 +80,7 @@ MRB_BEGIN_DECL
/*
* Release year.
*/
#define MRUBY_RELEASE_YEAR 2026
#define MRUBY_RELEASE_YEAR 2025
/*
* Release month.
+2 -1
View File
@@ -41,7 +41,7 @@ module MRuby
allocf.c
readnum.c
readint.c
fp_uscale.c
readfloat.c
state.c
symbol.c
class.c
@@ -66,6 +66,7 @@ module MRuby
cdump.c
codedump.c
print.c
fmt_fp.c
debug.c
etc.c
version.c
+1 -26
View File
@@ -81,7 +81,7 @@ module MRuby
include LoadGems
attr_accessor :name, :bins, :exts, :file_separator, :build_dir, :gem_clone_dir, :defines, :libdir_name
attr_reader :products, :libmruby_core_objs, :libmruby_objs, :gems, :toolchains, :presym, :mrbc_build, :gem_dir_to_repo_url
attr_reader :install_excludes, :port_names
attr_reader :install_excludes
alias libmruby libmruby_objs
@@ -138,7 +138,6 @@ module MRuby
@mrbcfile_external = false
@internal = internal
@toolchains = []
@port_names = nil
@gem_dir_to_repo_url = {}
# Add lambda instead of string because libdir_name or lib may be changed by user configuration
@@ -184,30 +183,6 @@ module MRuby
@enable_debug = true
end
# Set target port names for this build.
# Each gem compiles the first matching ports/<name>/ directory;
# later names in the list act as fallbacks for gems that don't
# ship a port for the earlier names.
# conf.ports :esp32
# conf.ports :rp2040, :posix # use rp2040 if available, else posix
def ports(*names)
@port_names = names.map { |n| n.to_s }
end
# Returns the effective port names for this build.
# If not explicitly set, auto-detects :posix or :win for host builds.
def effective_ports
return @port_names if @port_names
if kind_of?(MRuby::CrossBuild)
[]
elsif ENV['OS'] == 'Windows_NT' ||
('A'..'Z').any? { |v| Dir.exist?("#{v}:") }
['win']
else
['posix']
end
end
def disable_lock
@enable_lock = false
end
+5 -10
View File
@@ -343,21 +343,16 @@ module MRuby
opt = @compile_options % {funcname: funcname}
opt << " -S" if cdump
opt << " -s" if static
# Have mrbc write to a private tempfile (-o) instead of stdout (-o-)
# to avoid pipe-inheritance races with parallel rake on Windows MinGW,
# where unrelated _pp build-progress lines from sibling workers can
# leak into the captured stdout and corrupt the generated C file.
tmpout = "#{out.path}.#{funcname}.mrbcout"
opt = opt.sub(/\s-o-(?=\s|\z)/, %Q[ -o "#{filename tmpout}"])
cmd = %["#{filename @command}" #{opt} #{filename(infiles).map{|f| %["#{f}"]}.join(' ')}]
puts cmd if Rake.verbose
unless system(cmd)
rm_f tmpout
IO.popen(cmd, 'r') do |io|
out.puts io.read
end
# if mrbc execution fail, drop the file
unless $?.success?
rm_f out.path
fail "Command failed with status (#{$?.exitstatus}): [#{cmd[0,42]}...]"
end
out.write File.binread(tmpout)
rm_f tmpout
end
end
+1 -56
View File
@@ -25,7 +25,6 @@ module MRuby
alias :author= :authors=
attr_accessor :rbfiles, :objs
attr_reader :port_objs
attr_writer :test_objs, :test_rbfiles
attr_accessor :test_args, :test_preload
@@ -44,7 +43,6 @@ module MRuby
def initialize(name, &block)
@name = name
@initializer = block
@post_user_config = nil
@version = "0.0.0"
@dependencies = []
@conflicts = []
@@ -61,22 +59,6 @@ module MRuby
@rbfiles = Dir.glob("#{@dir}/mrblib/**/*.rb").sort
@objs = srcs_to_objs("src")
# Add platform-specific sources from the first matching
# ports/<name>/ directory. effective_ports is a fallback
# chain: later names act as defaults for gems that don't ship
# a port for the earlier names. These objs are tracked
# separately so List#resolve_external_hal! can drop them when
# an external HAL provider (gem named hal-<short>-*) is loaded.
@port_objs = []
build.effective_ports.each do |port|
port_dir = "#{@dir}/ports/#{port}"
if File.directory?(port_dir)
@port_objs = srcs_to_objs("ports/#{port}")
@objs += @port_objs
break
end
end
@test_preload = nil # 'test/assert.rb'
@test_args = {}
@skip_test = false
@@ -103,7 +85,6 @@ module MRuby
build.libmruby_objs << @objs
instance_eval(&@build_config_initializer) if @build_config_initializer
instance_eval(&@post_user_config) if @post_user_config
repo_url = build.gem_dir_to_repo_url[dir]
build.locks[repo_url]['version'] = version if repo_url
@@ -128,8 +109,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
@@ -213,19 +193,6 @@ module MRuby
end
end
# Register a block that runs after the user's `build.gem` block has
# been processed. Intended for gem authors to fill in defaults that
# depend on user-supplied configuration (e.g. auto-detect a library
# only if the user didn't specify which to use).
#
# Initialization order:
# 1. block in `MRuby::Gem::Specification.new` (gem author)
# 2. block in `build.gem` (user's build_config)
# 3. block in `post_user_config` (gem author, this hook)
def post_user_config(&block)
@post_user_config = block
end
def build_settings(&blk)
@build_settings = blk
end
@@ -462,28 +429,6 @@ module MRuby
self.each(&:setup)
gemset = self.setup_dependencies(build).keys.sort
end until gemset == gemset_prev
resolve_external_hal!
end
# A gem named `hal-<short>-<conf>` is treated as the external
# HAL provider for the gem whose name's last `-`-separated
# segment is <short> (e.g., hal-task-glib provides the HAL for
# mruby-task). The target gem's ports/* sources are dropped
# from its object list -- the matching gem supplies the
# implementation. Two or more matches is a build error.
def resolve_external_hal!
each do |target|
next if target.port_objs.nil? || target.port_objs.empty?
short = target.name.split('-').last
pattern = /\Ahal-#{Regexp.escape(short)}-.+\z/
overriders = select { |g| g != target && g.name =~ pattern }
next if overriders.empty?
if overriders.size > 1
fail "Multiple HAL providers for '#{target.name}': " +
overriders.map(&:name).join(", ")
end
target.objs.reject! { |o| target.port_objs.include?(o) }
end
end
def setup_build
+7
View File
@@ -0,0 +1,7 @@
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
@@ -173,3 +173,21 @@ 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
@@ -0,0 +1,8 @@
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
@@ -67,25 +67,25 @@ convert_stat(const struct stat *src, mrb_io_stat *dst)
#undef st_mtime
#undef st_ctime
dst->st_dev = (mrb_int)src->st_dev;
dst->st_ino = (mrb_int)src->st_ino;
dst->st_mode = (mrb_int)src->st_mode;
dst->st_nlink = (mrb_int)src->st_nlink;
dst->st_uid = (mrb_int)src->st_uid;
dst->st_gid = (mrb_int)src->st_gid;
dst->st_rdev = (mrb_int)src->st_rdev;
dst->st_size = (mrb_int)src->st_size;
dst->st_atime = (mrb_int)atime_val;
dst->st_mtime = (mrb_int)mtime_val;
dst->st_ctime = (mrb_int)ctime_val;
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 = (mrb_int)src->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 = (mrb_int)src->st_blocks;
dst->st_blocks = (int64_t)src->st_blocks;
#else
dst->st_blocks = (dst->st_size + 511) / 512;
#endif
@@ -135,14 +135,14 @@ mrb_hal_io_lstat(mrb_state *mrb, const char *path, mrb_io_stat *st)
}
int
mrb_hal_io_chmod(mrb_state *mrb, const char *path, mrb_int mode)
mrb_hal_io_chmod(mrb_state *mrb, const char *path, uint32_t mode)
{
(void)mrb;
return chmod(path, (mode_t)mode);
}
mrb_int
mrb_hal_io_umask(mrb_state *mrb, mrb_int mask)
uint32_t
mrb_hal_io_umask(mrb_state *mrb, int32_t mask)
{
mode_t old;
(void)mrb;
@@ -155,11 +155,11 @@ mrb_hal_io_umask(mrb_state *mrb, mrb_int mask)
else {
old = umask((mode_t)mask);
}
return (mrb_int)old;
return (uint32_t)old;
}
int
mrb_hal_io_ftruncate(mrb_state *mrb, int fd, mrb_int length)
mrb_hal_io_ftruncate(mrb_state *mrb, int fd, int64_t length)
{
(void)mrb;
return ftruncate(fd, (off_t)length);
@@ -200,11 +200,14 @@ mrb_hal_io_symlink(mrb_state *mrb, const char *target, const char *linkpath)
return symlink(target, linkpath);
}
mrb_int
int64_t
mrb_hal_io_readlink(mrb_state *mrb, const char *path, char *buf, size_t bufsize)
{
ssize_t rc;
(void)mrb;
return (mrb_int)readlink(path, buf, bufsize);
rc = readlink(path, buf, bufsize);
return (int64_t)rc;
}
char*
@@ -259,7 +262,7 @@ mrb_hal_io_gethome(mrb_state *mrb, const char *username)
*/
int
mrb_hal_io_open(mrb_state *mrb, const char *path, int flags, mrb_int mode)
mrb_hal_io_open(mrb_state *mrb, const char *path, int flags, uint32_t mode)
{
int fd;
(void)mrb;
@@ -289,23 +292,30 @@ mrb_hal_io_close(mrb_state *mrb, int fd)
return close(fd);
}
mrb_int
int64_t
mrb_hal_io_read(mrb_state *mrb, int fd, void *buf, size_t count)
{
ssize_t n;
(void)mrb;
return (mrb_int)read(fd, buf, count);
n = read(fd, buf, count);
return (int64_t)n;
}
mrb_int
int64_t
mrb_hal_io_write(mrb_state *mrb, int fd, const void *buf, size_t count)
{
ssize_t n;
(void)mrb;
return (mrb_int)write(fd, buf, count);
n = write(fd, buf, count);
return (int64_t)n;
}
mrb_int
mrb_hal_io_lseek(mrb_state *mrb, int fd, mrb_int offset, int whence)
int64_t
mrb_hal_io_lseek(mrb_state *mrb, int fd, int64_t offset, int whence)
{
off_t pos;
int posix_whence;
(void)mrb;
@@ -319,7 +329,8 @@ mrb_hal_io_lseek(mrb_state *mrb, int fd, mrb_int offset, int whence)
return -1;
}
return (mrb_int)lseek(fd, (off_t)offset, posix_whence);
pos = lseek(fd, (off_t)offset, posix_whence);
return (int64_t)pos;
}
int
@@ -571,3 +582,21 @@ 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 */
}
+8
View File
@@ -0,0 +1,8 @@
MRuby::Gem::Specification.new('hal-posix-socket') do |spec|
spec.license = 'MIT'
spec.author = 'mruby developers'
spec.summary = 'POSIX HAL for mruby-socket (Linux, macOS, BSD, Unix)'
# HAL gem depends on feature gem - brings in mruby-socket automatically
spec.add_dependency 'mruby-socket', core: 'mruby-socket'
end
@@ -8,7 +8,6 @@
*/
#include <mruby.h>
#include <mruby/array.h>
#include <mruby/string.h>
#include <mruby/class.h>
#include <mruby/error.h>
@@ -21,7 +20,6 @@
#include <arpa/inet.h>
#include <fcntl.h>
#include <unistd.h>
#include <ifaddrs.h>
#include <string.h>
#include <errno.h>
@@ -43,16 +41,6 @@ mrb_hal_socket_final(mrb_state *mrb)
/* No cleanup needed for POSIX sockets */
}
/*
* Error Handling
*/
void
mrb_hal_socket_set_errno_from_last_error(void)
{
/* POSIX socket calls already set errno on failure; nothing to do. */
}
/*
* Socket Control Operations
*/
@@ -150,26 +138,20 @@ mrb_hal_socket_unix_path(mrb_state *mrb, const char *sockaddr, size_t socklen)
return mrb_str_new_cstr(mrb, ((const struct sockaddr_un*)sockaddr)->sun_path);
}
mrb_value
mrb_hal_socket_ip_address_list(mrb_state *mrb)
/*
* Gem initialization
*/
void
mrb_hal_posix_socket_gem_init(mrb_state *mrb)
{
struct ifaddrs *ifap = NULL;
if (getifaddrs(&ifap) != 0) {
mrb_sys_fail(mrb, "getifaddrs");
}
mrb_value ary = mrb_ary_new(mrb);
int arena_idx = mrb_gc_arena_save(mrb);
for (struct ifaddrs *ifa = ifap; ifa != NULL; ifa = ifa->ifa_next) {
if (ifa->ifa_addr == NULL) continue;
socklen_t salen;
switch (ifa->ifa_addr->sa_family) {
case AF_INET: salen = sizeof(struct sockaddr_in); break;
case AF_INET6: salen = sizeof(struct sockaddr_in6); break;
default: continue;
}
mrb_ary_push(mrb, ary, mrb_str_new(mrb, (const char*)ifa->ifa_addr, salen));
mrb_gc_arena_restore(mrb, arena_idx);
}
freeifaddrs(ifap);
return ary;
(void)mrb;
/* HAL interface functions are called by mruby-socket gem */
}
void
mrb_hal_posix_socket_gem_final(mrb_state *mrb)
{
(void)mrb;
/* Cleanup handled by mrb_hal_socket_final called from mruby-socket */
}
+102
View File
@@ -0,0 +1,102 @@
# hal-posix-task
POSIX Hardware Abstraction Layer (HAL) implementation for mruby-task.
## Description
Provides timer and interrupt support for the mruby-task cooperative scheduler on POSIX-compliant platforms. Uses `SIGALRM` and `setitimer()` for periodic timer ticks, and `sigprocmask()` for interrupt protection.
## Supported Platforms
- Linux
- macOS
- BSD (FreeBSD, OpenBSD, NetBSD)
- Other POSIX-compliant Unix systems
## Requirements
- POSIX-compliant operating system
- Signal support (`SIGALRM`, `sigaction`, `sigprocmask`)
- Timer support (`setitimer`, `ITIMER_REAL`)
## Usage
### Explicit HAL Selection (Recommended)
```ruby
MRuby::Build.new do |conf|
# ... other configuration ...
# Specify POSIX HAL - automatically brings in mruby-task
conf.gem core: 'hal-posix-task'
end
```
### Auto-detection (Development)
```ruby
MRuby::Build.new do |conf|
# ... other configuration ...
# Auto-detects and selects hal-posix-task on POSIX platforms
conf.gem core: 'mruby-task'
end
```
## Implementation Details
### Timer Mechanism
- Uses `setitimer(ITIMER_REAL, ...)` to generate periodic `SIGALRM` signals
- Timer interval configured by `MRB_TICK_UNIT` (default: 4ms)
- Signal handler calls `mrb_tick()` for all registered VM instances
### Interrupt Protection
- Critical sections protected using `sigprocmask()` to block `SIGALRM`
- Prevents race conditions during task queue modifications
- Supports nested critical sections through signal masking
### Multi-VM Support
- Supports up to `MRB_TASK_MAX_VMS` concurrent mruby VM instances (default: 8)
- Single shared timer ticks all registered VMs
- Per-VM task counters optimize timer usage (timer disabled when idle)
### Timer Optimization
The implementation dynamically enables/disables the timer based on task state:
- **Timer enabled** when: Multiple ready tasks OR any waiting tasks exist
- **Timer disabled** when: Single task or all tasks dormant/suspended
- Reduces CPU usage and power consumption when scheduler is idle
## Configuration
Override these macros in your build config if needed:
```ruby
conf.gem core: 'hal-posix-task' do |spec|
# Custom tick interval (10ms instead of default 4ms)
spec.build.defines << 'MRB_TICK_UNIT=10'
# Custom timeslice (5 ticks instead of default 3)
spec.build.defines << 'MRB_TIMESLICE_TICK_COUNT=5'
# More concurrent VMs (16 instead of default 8)
spec.build.defines << 'MRB_TASK_MAX_VMS=16'
end
```
## Known Limitations
- `SIGALRM` conflicts with other code using the same signal
- Timer resolution limited by platform (typically 1-10ms)
- Signal delivery may be delayed under heavy system load
- Not suitable for hard real-time requirements
## See Also
- `mruby-task` - Core task scheduler
- `hal-win-task` - Windows HAL implementation
- Task scheduler documentation: `mrbgems/mruby-task/README.md`
+8
View File
@@ -0,0 +1,8 @@
MRuby::Gem::Specification.new('hal-posix-task') do |spec|
spec.license = 'MIT'
spec.authors = 'mruby developers'
spec.summary = 'POSIX HAL for mruby-task (Linux, macOS, BSD, Unix)'
# HAL gem depends on feature gem - brings in mruby-task automatically
spec.add_dependency 'mruby-task', core: 'mruby-task'
end
@@ -232,3 +232,21 @@ mrb_hal_task_final(mrb_state *mrb)
(void)mrb;
#endif
}
/*
* Gem initialization (empty - HAL functions called by mruby-task)
*/
void
mrb_hal_posix_task_gem_init(mrb_state *mrb)
{
(void)mrb;
/* HAL interface functions are called by mruby-task gem */
}
void
mrb_hal_posix_task_gem_final(mrb_state *mrb)
{
(void)mrb;
/* Cleanup handled by mrb_hal_task_final called from mruby-task */
}
+7
View File
@@ -0,0 +1,7 @@
MRuby::Gem::Specification.new('hal-win-dir') do |spec|
spec.license = 'MIT and MIT-like license'
spec.authors = ['mruby developers', 'Kevlin Henney']
spec.summary = 'Windows HAL for mruby-dir'
spec.add_dependency 'mruby-dir', core: 'mruby-dir'
end
@@ -214,3 +214,36 @@ mrb_hal_dir_final(mrb_state *mrb)
(void)mrb;
/* No cleanup needed for Windows */
}
/*
* Gem initialization
*/
void
mrb_hal_win_dir_gem_init(mrb_state *mrb)
{
(void)mrb;
/* HAL interface functions are called by mruby-dir gem */
}
void
mrb_hal_win_dir_gem_final(mrb_state *mrb)
{
(void)mrb;
/* Cleanup handled by mrb_hal_dir_final called from mruby-dir */
}
/*
** Portions derived from dirent.c by Kevlin Henney:
**
** Copyright Kevlin Henney, 1997, 2003, 2012. All rights reserved.
**
** Permission to use, copy, modify, and distribute this software and its
** documentation for any purpose is hereby granted without fee, provided
** that this copyright and permissions notice appear in all copies and
** derivatives.
**
** This software is supplied "as is" without express or implied warranty.
**
** But that said, if there are any problems please get in touch.
*/
+11
View File
@@ -0,0 +1,11 @@
MRuby::Gem::Specification.new('hal-win-io') do |spec|
spec.license = 'MIT'
spec.author = 'mruby developers'
spec.summary = 'Windows HAL for mruby-io (Windows, MinGW)'
# HAL gem depends on feature gem - brings in mruby-io automatically
spec.add_dependency 'mruby-io', core: 'mruby-io'
# Link Windows socket library
spec.linker.libraries << "ws2_32"
end
@@ -34,17 +34,17 @@
static void
convert_stat(const struct _stat64 *src, mrb_io_stat *dst)
{
dst->st_dev = (mrb_int)src->st_dev;
dst->st_ino = (mrb_int)src->st_ino;
dst->st_mode = (mrb_int)src->st_mode;
dst->st_nlink = (mrb_int)src->st_nlink;
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 = 0; /* Windows doesn't have Unix-style UIDs */
dst->st_gid = 0; /* Windows doesn't have Unix-style GIDs */
dst->st_rdev = (mrb_int)src->st_rdev;
dst->st_size = (mrb_int)src->st_size;
dst->st_atime = (mrb_int)src->st_atime;
dst->st_mtime = (mrb_int)src->st_mtime;
dst->st_ctime = (mrb_int)src->st_ctime;
dst->st_rdev = (uint64_t)src->st_rdev;
dst->st_size = (int64_t)src->st_size;
dst->st_atime = (int64_t)src->st_atime;
dst->st_mtime = (int64_t)src->st_mtime;
dst->st_ctime = (int64_t)src->st_ctime;
dst->st_blksize = 512;
dst->st_blocks = (dst->st_size + 511) / 512;
}
@@ -114,14 +114,14 @@ mrb_hal_io_lstat(mrb_state *mrb, const char *path, mrb_io_stat *st)
}
int
mrb_hal_io_chmod(mrb_state *mrb, const char *path, mrb_int mode)
mrb_hal_io_chmod(mrb_state *mrb, const char *path, uint32_t mode)
{
(void)mrb;
return _chmod(path, (int)mode);
}
mrb_int
mrb_hal_io_umask(mrb_state *mrb, mrb_int mask)
uint32_t
mrb_hal_io_umask(mrb_state *mrb, int32_t mask)
{
int old;
(void)mrb;
@@ -134,14 +134,14 @@ mrb_hal_io_umask(mrb_state *mrb, mrb_int mask)
else {
old = _umask((int)mask);
}
return (mrb_int)old;
return (uint32_t)old;
}
int
mrb_hal_io_ftruncate(mrb_state *mrb, int fd, mrb_int length)
mrb_hal_io_ftruncate(mrb_state *mrb, int fd, int64_t length)
{
(void)mrb;
return _chsize_s(fd, (__int64)length);
return _chsize_s(fd, length);
}
int
@@ -207,7 +207,7 @@ mrb_hal_io_symlink(mrb_state *mrb, const char *target, const char *linkpath)
return -1; /* not reached */
}
mrb_int
int64_t
mrb_hal_io_readlink(mrb_state *mrb, const char *path, char *buf, size_t bufsize)
{
(void)path;
@@ -280,7 +280,7 @@ mrb_hal_io_gethome(mrb_state *mrb, const char *username)
*/
int
mrb_hal_io_open(mrb_state *mrb, const char *path, int flags, mrb_int mode)
mrb_hal_io_open(mrb_state *mrb, const char *path, int flags, uint32_t mode)
{
int fd;
(void)mrb;
@@ -306,23 +306,30 @@ mrb_hal_io_close(mrb_state *mrb, int fd)
return _close(fd);
}
mrb_int
int64_t
mrb_hal_io_read(mrb_state *mrb, int fd, void *buf, size_t count)
{
int n;
(void)mrb;
return (mrb_int)_read(fd, buf, (unsigned int)count);
n = _read(fd, buf, (unsigned int)count);
return (int64_t)n;
}
mrb_int
int64_t
mrb_hal_io_write(mrb_state *mrb, int fd, const void *buf, size_t count)
{
int n;
(void)mrb;
return (mrb_int)_write(fd, buf, (unsigned int)count);
n = _write(fd, buf, (unsigned int)count);
return (int64_t)n;
}
mrb_int
mrb_hal_io_lseek(mrb_state *mrb, int fd, mrb_int offset, int whence)
int64_t
mrb_hal_io_lseek(mrb_state *mrb, int fd, int64_t offset, int whence)
{
__int64 pos;
int win_whence;
(void)mrb;
@@ -336,7 +343,8 @@ mrb_hal_io_lseek(mrb_state *mrb, int fd, mrb_int offset, int whence)
return -1;
}
return (mrb_int)_lseeki64(fd, (__int64)offset, win_whence);
pos = _lseeki64(fd, (__int64)offset, win_whence);
return (int64_t)pos;
}
int
@@ -618,3 +626,21 @@ mrb_hal_io_final(mrb_state *mrb)
/* Cleanup Winsock */
WSACleanup();
}
/*
* Gem initialization
*/
void
mrb_hal_win_io_gem_init(mrb_state *mrb)
{
(void)mrb;
/* HAL interface functions are called by mruby-io gem */
}
void
mrb_hal_win_io_gem_final(mrb_state *mrb)
{
(void)mrb;
/* Cleanup handled by mrb_hal_io_final called from mruby-io */
}
+12
View File
@@ -0,0 +1,12 @@
MRuby::Gem::Specification.new('hal-win-socket') do |spec|
spec.license = 'MIT'
spec.author = 'mruby developers'
spec.summary = 'Windows HAL for mruby-socket (Windows, MinGW)'
# HAL gem depends on feature gem - brings in mruby-socket automatically
spec.add_dependency 'mruby-socket', core: 'mruby-socket'
# Link Windows socket libraries
spec.linker.libraries << "wsock32"
spec.linker.libraries << "ws2_32"
end
+176
View File
@@ -0,0 +1,176 @@
/*
** socket_hal.c - Windows HAL implementation for mruby-socket
**
** See Copyright Notice in mruby.h
**
** Windows implementation for socket operations using Winsock APIs.
** Supported platforms: Windows, MinGW
*/
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0501 // need Windows XP or later
#endif
#include <mruby.h>
#include <mruby/string.h>
#include <mruby/class.h>
#include <mruby/error.h>
#include "socket_hal.h"
#include <winsock2.h>
#include <ws2tcpip.h>
#include <windows.h>
#include <errno.h>
#include <string.h>
/*
* Socket HAL Initialization/Finalization
*/
void
mrb_hal_socket_init(mrb_state *mrb)
{
WSADATA wsaData;
int result = WSAStartup(MAKEWORD(2, 2), &wsaData);
if (result != NO_ERROR) {
mrb_raise(mrb, mrb_class_get_id(mrb, MRB_SYM(RuntimeError)), "WSAStartup failed");
}
}
void
mrb_hal_socket_final(mrb_state *mrb)
{
(void)mrb;
WSACleanup();
}
/*
* Socket Control Operations
*/
int
mrb_hal_socket_set_nonblock(mrb_state *mrb, int fd, int nonblock)
{
(void)mrb;
u_long mode = nonblock ? 1 : 0;
int result = ioctlsocket(fd, FIONBIO, &mode);
if (result != NO_ERROR) {
return -1;
}
return 0;
}
/*
* Address Conversion Functions
*/
const char*
mrb_hal_socket_inet_ntop(int af, const void *src, char *dst, size_t size)
{
if (af == AF_INET) {
struct sockaddr_in in = {0};
in.sin_family = AF_INET;
memcpy(&in.sin_addr, src, sizeof(struct in_addr));
if (getnameinfo((struct sockaddr*)&in, sizeof(struct sockaddr_in),
dst, (DWORD)size, NULL, 0, NI_NUMERICHOST) == 0) {
return dst;
}
return NULL;
}
else if (af == AF_INET6) {
struct sockaddr_in6 in = {0};
in.sin6_family = AF_INET6;
memcpy(&in.sin6_addr, src, sizeof(struct in6_addr));
if (getnameinfo((struct sockaddr*)&in, sizeof(struct sockaddr_in6),
dst, (DWORD)size, NULL, 0, NI_NUMERICHOST) == 0) {
return dst;
}
return NULL;
}
return NULL;
}
int
mrb_hal_socket_inet_pton(int af, const char *src, void *dst)
{
struct addrinfo hints = {0};
hints.ai_family = af;
hints.ai_flags = AI_NUMERICHOST;
struct addrinfo *res;
if (getaddrinfo(src, NULL, &hints, &res) != 0) {
return 0; /* Invalid address */
}
if (res == NULL) {
return 0;
}
if (af == AF_INET && res->ai_family == AF_INET) {
memcpy(dst, &((struct sockaddr_in*)res->ai_addr)->sin_addr, sizeof(struct in_addr));
freeaddrinfo(res);
return 1;
}
else if (af == AF_INET6 && res->ai_family == AF_INET6) {
memcpy(dst, &((struct sockaddr_in6*)res->ai_addr)->sin6_addr, sizeof(struct in6_addr));
freeaddrinfo(res);
return 1;
}
freeaddrinfo(res);
return 0;
}
/*
* Platform-Specific Socket Features
*/
mrb_value
mrb_hal_socket_sockaddr_un(mrb_state *mrb, const char *path, size_t pathlen)
{
(void)path;
(void)pathlen;
mrb_raise(mrb, mrb_class_get_id(mrb, MRB_SYM(NotImplementedError)),
"sockaddr_un unsupported on Windows");
return mrb_nil_value();
}
int
mrb_hal_socket_socketpair(mrb_state *mrb, int domain, int type, int protocol, int sv[2])
{
(void)mrb;
(void)domain;
(void)type;
(void)protocol;
(void)sv;
/* socketpair is not supported on Windows */
errno = ENOSYS;
return -1;
}
mrb_value
mrb_hal_socket_unix_path(mrb_state *mrb, const char *sockaddr, size_t socklen)
{
(void)sockaddr;
(void)socklen;
mrb_raise(mrb, mrb_class_get_id(mrb, MRB_SYM(NotImplementedError)),
"unix_path unsupported on Windows");
return mrb_nil_value();
}
/*
* Gem initialization
*/
void
mrb_hal_win_socket_gem_init(mrb_state *mrb)
{
(void)mrb;
/* HAL interface functions are called by mruby-socket gem */
}
void
mrb_hal_win_socket_gem_final(mrb_state *mrb)
{
(void)mrb;
/* Cleanup handled by mrb_hal_socket_final called from mruby-socket */
}
+109
View File
@@ -0,0 +1,109 @@
# hal-win-task
Windows Hardware Abstraction Layer (HAL) implementation for mruby-task.
## Description
Provides timer and interrupt support for the mruby-task cooperative scheduler on Windows platforms. Uses multimedia timer (`timeSetEvent`/`timeKillEvent`) for periodic timer ticks, and `CRITICAL_SECTION` for interrupt protection.
## Supported Platforms
- Windows 7 and later
- Windows Server 2008 R2 and later
- All versions with multimedia timer support
## Requirements
- Windows operating system
- Multimedia timer API (`winmm.lib`)
- Visual C++, MinGW, or compatible compiler
## Usage
### Explicit HAL Selection (Recommended)
```ruby
MRuby::Build.new do |conf|
# ... other configuration ...
# Specify Windows HAL - automatically brings in mruby-task
conf.gem core: 'hal-win-task'
end
```
### Auto-detection (Development)
```ruby
MRuby::Build.new do |conf|
# ... other configuration ...
# Auto-detects and selects hal-win-task on Windows platforms
conf.gem core: 'mruby-task'
end
```
## Implementation Details
### Timer Mechanism
- Uses Windows multimedia timer (`timeSetEvent`) for periodic callbacks
- Timer interval configured by `MRB_TICK_UNIT` (default: 4ms)
- `TIME_KILL_SYNCHRONOUS` flag ensures clean timer shutdown
- Requests 1ms timer resolution via `timeBeginPeriod(1)`
### Interrupt Protection
- Critical sections protected using `CRITICAL_SECTION` objects
- Prevents race conditions during task queue modifications
- `EnterCriticalSection`/`LeaveCriticalSection` used for mutual exclusion
- Supports nested critical sections (automatic lock counting)
### Multi-VM Support
- Supports up to `MRB_TASK_MAX_VMS` concurrent mruby VM instances (default: 8)
- Single shared timer ticks all registered VMs
- Per-VM task counters optimize timer usage (timer disabled when idle)
- Interlocked operations (`InterlockedIncrement`/`InterlockedDecrement`) for thread safety
### Timer Optimization
The implementation dynamically enables/disables the timer based on task state:
- **Timer enabled** when: Multiple ready tasks OR any waiting tasks exist
- **Timer disabled** when: Single task or all tasks dormant/suspended
- Reduces CPU usage and power consumption when scheduler is idle
## Configuration
Override these macros in your build config if needed:
```ruby
conf.gem core: 'hal-win-task' do |spec|
# Custom tick interval (10ms instead of default 4ms)
spec.build.defines << 'MRB_TICK_UNIT=10'
# Custom timeslice (5 ticks instead of default 3)
spec.build.defines << 'MRB_TIMESLICE_TICK_COUNT=5'
# More concurrent VMs (16 instead of default 8)
spec.build.defines << 'MRB_TASK_MAX_VMS=16'
end
```
## Known Limitations
- Timer resolution typically limited to 1-2ms even with `timeBeginPeriod(1)`
- Multimedia timers consume system resources (kernel timer objects)
- Timer callbacks execute in separate thread context (handled internally)
- Not suitable for hard real-time requirements
- May interfere with other multimedia applications requesting different timer resolutions
## See Also
- `mruby-task` - Core task scheduler
- `hal-posix-task` - POSIX/Unix HAL implementation
- Task scheduler documentation: `mrbgems/mruby-task/README.md`
## Build Notes
The `winmm` library is automatically linked by the gem specification. No additional linker configuration is needed.
+11
View File
@@ -0,0 +1,11 @@
MRuby::Gem::Specification.new('hal-win-task') do |spec|
spec.license = 'MIT'
spec.authors = 'mruby developers'
spec.summary = 'Windows HAL for mruby-task'
# HAL gem depends on feature gem - brings in mruby-task automatically
spec.add_dependency 'mruby-task', core: 'mruby-task'
# Windows multimedia timer library
spec.linker.libraries << 'winmm'
end
@@ -167,3 +167,21 @@ mrb_hal_task_final(mrb_state *mrb)
LeaveCriticalSection(&irq_lock);
}
}
/*
* Gem initialization (empty - HAL functions called by mruby-task)
*/
void
mrb_hal_win_task_gem_init(mrb_state *mrb)
{
(void)mrb;
/* HAL interface functions are called by mruby-task gem */
}
void
mrb_hal_win_task_gem_final(mrb_state *mrb)
{
(void)mrb;
/* Cleanup handled by mrb_hal_task_final called from mruby-task */
}
-74
View File
@@ -1,74 +0,0 @@
# hw-adc - ADC peripheral interface for mruby
This gem provides the `ADC` class for reading analog input from
mruby. It is designed for embedded platforms such as ESP32 and
RP2040.
## Architecture
Platform-specific HAL implementations are in `ports/` directories:
- `ports/esp32/` - ESP32 using ESP-IDF ADC oneshot driver
- `ports/rp2040/` - RP2040 using Pico SDK ADC hardware
## Build Configuration
```ruby
MRuby::CrossBuild.new('esp32') do |conf|
conf.ports :esp32
conf.gem core: 'hw-adc'
end
```
## Ruby API
### ADC.new
```ruby
adc = ADC.new(pin)
```
- `pin` - ADC-capable GPIO pin number (Integer)
- Raises `ArgumentError` if the pin is not valid for ADC
### ADC#read / ADC#read_voltage
Read the analog value as voltage (Float).
```ruby
voltage = adc.read # => 1.65
voltage = adc.read_voltage # same
```
### ADC#read_raw
Read the raw ADC value (Integer, typically 0-4095 for 12-bit).
```ruby
raw = adc.read_raw # => 2048
```
### ADC#input
Returns the ADC input channel number assigned during initialization.
## HAL Interface
To add support for a new platform, create a `ports/<name>/`
directory and implement the following C functions declared in
`<mruby/adc.h>`:
```c
int mrb_adc_init(uint8_t pin);
uint32_t mrb_adc_read_raw(uint8_t input);
float mrb_adc_read_voltage(uint8_t input);
```
- `mrb_adc_init` returns the input channel number (>= 0) on
success, negative on error
- `input` parameter for read functions is the value returned by
`mrb_adc_init`
## License
MIT
-19
View File
@@ -1,19 +0,0 @@
#ifndef MRUBY_ADC_H
#define MRUBY_ADC_H
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/* HAL functions - implemented in ports/<platform>/adc.c */
int mrb_adc_init(uint8_t pin);
uint32_t mrb_adc_read_raw(uint8_t input);
float mrb_adc_read_voltage(uint8_t input);
#ifdef __cplusplus
}
#endif
#endif /* MRUBY_ADC_H */
-5
View File
@@ -1,5 +0,0 @@
MRuby::Gem::Specification.new('hw-adc') do |spec|
spec.license = 'MIT'
spec.authors = ['HASUMI Hitoshi', 'mruby developers']
spec.summary = 'ADC peripheral interface'
end
-7
View File
@@ -1,7 +0,0 @@
class ADC
def initialize(pin)
@input = __init(pin)
end
attr_reader :input
end
-147
View File
@@ -1,147 +0,0 @@
#include <stdbool.h>
#include "esp_adc/adc_oneshot.h"
#include <mruby/adc.h>
#define VOLTAGE_MAX 3.3f
#define RESOLUTION 4095
#define UNIT_NUM 2
static adc_oneshot_unit_handle_t adc_handles[UNIT_NUM];
static bool adc_initialized;
static adc_unit_t
pin_to_unit(uint8_t pin)
{
switch (pin) {
#if CONFIG_IDF_TARGET_ESP32C3
case 0: case 1: case 2: case 3: case 4:
return ADC_UNIT_1;
case 5:
return ADC_UNIT_2;
#elif CONFIG_IDF_TARGET_ESP32
case 32: case 33: case 34: case 35: case 36: case 39:
return ADC_UNIT_1;
case 0: case 2: case 4: case 12: case 13: case 14: case 15:
case 25: case 26: case 27:
return ADC_UNIT_2;
#elif (CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32S2)
case 1: case 2: case 3: case 4: case 5:
case 6: case 7: case 8: case 9: case 10:
return ADC_UNIT_1;
case 11: case 12: case 13: case 14: case 15:
case 16: case 17: case 18: case 19: case 20:
return ADC_UNIT_2;
#endif
}
return -1;
}
static adc_channel_t
pin_to_channel(uint8_t pin)
{
switch (pin) {
#if CONFIG_IDF_TARGET_ESP32C3
case 0: return ADC_CHANNEL_0;
case 1: return ADC_CHANNEL_1;
case 2: return ADC_CHANNEL_2;
case 3: return ADC_CHANNEL_3;
case 4: return ADC_CHANNEL_4;
case 5: return ADC_CHANNEL_0;
#elif CONFIG_IDF_TARGET_ESP32
case 36: return ADC_CHANNEL_0;
case 39: return ADC_CHANNEL_3;
case 32: return ADC_CHANNEL_4;
case 33: return ADC_CHANNEL_5;
case 34: return ADC_CHANNEL_6;
case 35: return ADC_CHANNEL_7;
case 4: return ADC_CHANNEL_0;
case 0: return ADC_CHANNEL_1;
case 2: return ADC_CHANNEL_2;
case 15: return ADC_CHANNEL_3;
case 13: return ADC_CHANNEL_4;
case 12: return ADC_CHANNEL_5;
case 14: return ADC_CHANNEL_6;
case 27: return ADC_CHANNEL_7;
case 25: return ADC_CHANNEL_8;
case 26: return ADC_CHANNEL_9;
#elif (CONFIG_IDF_TARGET_ESP32S3 || CONFIG_IDF_TARGET_ESP32S2)
case 1: return ADC_CHANNEL_0;
case 2: return ADC_CHANNEL_1;
case 3: return ADC_CHANNEL_2;
case 4: return ADC_CHANNEL_3;
case 5: return ADC_CHANNEL_4;
case 6: return ADC_CHANNEL_5;
case 7: return ADC_CHANNEL_6;
case 8: return ADC_CHANNEL_7;
case 9: return ADC_CHANNEL_8;
case 10: return ADC_CHANNEL_9;
case 11: return ADC_CHANNEL_0;
case 12: return ADC_CHANNEL_1;
case 13: return ADC_CHANNEL_2;
case 14: return ADC_CHANNEL_3;
case 15: return ADC_CHANNEL_4;
case 16: return ADC_CHANNEL_5;
case 17: return ADC_CHANNEL_6;
case 18: return ADC_CHANNEL_7;
case 19: return ADC_CHANNEL_8;
case 20: return ADC_CHANNEL_9;
#endif
}
return -1;
}
static int
init_units(void)
{
adc_unit_t units[] = { ADC_UNIT_1, ADC_UNIT_2 };
for (int i = 0; i < UNIT_NUM; i++) {
adc_oneshot_unit_init_cfg_t cfg = {
.unit_id = units[i],
.ulp_mode = ADC_ULP_MODE_DISABLE,
};
if (adc_oneshot_new_unit(&cfg, &adc_handles[i]) != ESP_OK)
return -1;
}
return 0;
}
int
mrb_adc_init(uint8_t pin)
{
if (!adc_initialized) {
if (init_units() != 0) return -1;
adc_initialized = true;
}
int ch = pin_to_channel(pin);
if (ch < 0) return -1;
adc_unit_t unit = pin_to_unit(pin);
if (unit < 0 || unit >= UNIT_NUM) return -1;
adc_oneshot_chan_cfg_t cfg = {
.atten = ADC_ATTEN_DB_12,
.bitwidth = ADC_BITWIDTH_DEFAULT,
};
if (adc_oneshot_config_channel(adc_handles[unit], ch, &cfg) != ESP_OK)
return -1;
return (int)pin;
}
uint32_t
mrb_adc_read_raw(uint8_t input)
{
adc_unit_t unit = pin_to_unit(input);
int ch = pin_to_channel(input);
int raw = 0;
if (unit >= 0 && unit < UNIT_NUM)
adc_oneshot_read(adc_handles[unit], ch, &raw);
return (uint32_t)raw;
}
float
mrb_adc_read_voltage(uint8_t input)
{
return (float)mrb_adc_read_raw(input) * VOLTAGE_MAX / RESOLUTION;
}
-43
View File
@@ -1,43 +0,0 @@
#include <stdbool.h>
#include "hardware/adc.h"
#include <mruby/adc.h>
#define VOLTAGE_MAX 3.3f
#define RESOLUTION 4095
#define TEMP_INPUT 4
static bool adc_initialized;
int
mrb_adc_init(uint8_t pin)
{
if (!adc_initialized) {
adc_init();
adc_initialized = true;
}
uint input;
switch (pin) {
case 26: input = 0; break;
case 27: input = 1; break;
case 28: input = 2; break;
case 29: input = 3; break;
default: return -1;
}
adc_gpio_init(pin);
return (int)input;
}
uint32_t
mrb_adc_read_raw(uint8_t input)
{
adc_select_input(input);
return (uint32_t)adc_read();
}
float
mrb_adc_read_voltage(uint8_t input)
{
adc_select_input(input);
return (float)adc_read() * VOLTAGE_MAX / RESOLUTION;
}
-48
View File
@@ -1,48 +0,0 @@
#include <mruby.h>
#include <mruby/presym.h>
#include <mruby/variable.h>
#include <mruby/adc.h>
/* ADC#__init(pin) */
static mrb_value
mrb_adc_m_init(mrb_state *mrb, mrb_value self)
{
mrb_int pin;
mrb_get_args(mrb, "i", &pin);
int input = mrb_adc_init((uint8_t)pin);
if (input < 0) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "invalid ADC pin");
}
return mrb_fixnum_value(input);
}
/* ADC#read_raw */
static mrb_value
mrb_adc_m_read_raw(mrb_state *mrb, mrb_value self)
{
mrb_int input = mrb_integer(mrb_iv_get(mrb, self, MRB_IVSYM(input)));
return mrb_fixnum_value(mrb_adc_read_raw((uint8_t)input));
}
/* ADC#read_voltage (also aliased as read) */
static mrb_value
mrb_adc_m_read_voltage(mrb_state *mrb, mrb_value self)
{
mrb_int input = mrb_integer(mrb_iv_get(mrb, self, MRB_IVSYM(input)));
return mrb_float_value(mrb, (mrb_float)mrb_adc_read_voltage((uint8_t)input));
}
void
mrb_hw_adc_gem_init(mrb_state *mrb)
{
struct RClass *cls = mrb_define_class_id(mrb, MRB_SYM(ADC), mrb->object_class);
mrb_define_method_id(mrb, cls, MRB_SYM(__init), mrb_adc_m_init, MRB_ARGS_REQ(1));
mrb_define_method_id(mrb, cls, MRB_SYM(read_raw), mrb_adc_m_read_raw, MRB_ARGS_NONE());
mrb_define_method_id(mrb, cls, MRB_SYM(read_voltage), mrb_adc_m_read_voltage, MRB_ARGS_NONE());
mrb_define_method_id(mrb, cls, MRB_SYM(read), mrb_adc_m_read_voltage, MRB_ARGS_NONE());
}
void
mrb_hw_adc_gem_final(mrb_state *mrb)
{
}
-144
View File
@@ -1,144 +0,0 @@
# hw-gpio - GPIO peripheral interface for mruby
This gem provides the `GPIO` class for controlling general-purpose
input/output pins from mruby.
It is designed for embedded platforms such as ESP32 and RP2040.
## Architecture
Platform-specific HAL implementations are in `ports/` directories:
- `ports/esp32/` - ESP32 using ESP-IDF GPIO driver
- `ports/rp2040/` - RP2040 using Pico SDK
The build system automatically compiles matching port sources based
on `conf.ports` setting.
## Build Configuration
```ruby
# For ESP32
MRuby::CrossBuild.new('esp32') do |conf|
conf.ports :esp32
conf.gem core: 'hw-gpio'
end
# For RP2040
MRuby::CrossBuild.new('rp2040') do |conf|
conf.ports :rp2040
conf.gem core: 'hw-gpio'
end
```
## Ruby API
### Constants (direction/mode flags)
These flags can be combined with bitwise OR.
| Flag | Value | Description |
| ------------------ | ------ | -------------------------- |
| `GPIO::IN` | `0x01` | Input mode |
| `GPIO::OUT` | `0x02` | Output mode |
| `GPIO::HIGH_Z` | `0x04` | High-impedance (tri-state) |
| `GPIO::PULL_UP` | `0x08` | Enable pull-up resistor |
| `GPIO::PULL_DOWN` | `0x10` | Enable pull-down resistor |
| `GPIO::OPEN_DRAIN` | `0x20` | Enable open-drain output |
### GPIO.new
```ruby
gpio = GPIO.new(pin, flags)
```
- `pin` - GPIO pin number (Integer)
- `flags` - direction and mode flags (combined with `|`)
- Exactly one of `IN`, `OUT`, or `HIGH_Z` must be specified
- `PULL_UP` and `PULL_DOWN` are mutually exclusive
- Raises `ArgumentError` on invalid flag combinations
```ruby
# Input with pull-up
button = GPIO.new(2, GPIO::IN | GPIO::PULL_UP)
# Output
led = GPIO.new(25, GPIO::OUT)
```
### Instance Methods
#### GPIO#read
Read the current pin value.
```ruby
val = gpio.read # => 0 or 1
```
#### GPIO#write
Set the pin output value.
```ruby
gpio.write(1) # set high
gpio.write(0) # set low
```
- Raises `ArgumentError` if value is not 0 or 1
#### GPIO#high? / GPIO#low?
```ruby
gpio.high? # => true if read != 0
gpio.low? # => true if read == 0
```
#### GPIO#pin
```ruby
gpio.pin # => pin number passed to new
```
#### GPIO#setmode
Reconfigure pin direction and mode flags after initialization.
```ruby
gpio.setmode(GPIO::OUT)
```
### Class Methods
These operate directly on pin numbers without creating an instance.
```ruby
GPIO.read_at(pin) # => 0 or 1
GPIO.write_at(pin, val) # set pin output (0 or 1)
GPIO.set_dir_at(pin, flags) # set direction (IN/OUT/HIGH_Z)
GPIO.pull_up_at(pin) # enable pull-up
GPIO.pull_down_at(pin) # enable pull-down
GPIO.open_drain_at(pin) # enable open-drain
```
## HAL Interface
To add support for a new platform, create a `ports/<name>/`
directory and implement the following C functions declared in
`<mruby/gpio.h>`:
```c
void mrb_gpio_init(uint8_t pin);
void mrb_gpio_set_dir(uint8_t pin, uint8_t flags);
void mrb_gpio_pull_up(uint8_t pin);
void mrb_gpio_pull_down(uint8_t pin);
void mrb_gpio_open_drain(uint8_t pin);
int mrb_gpio_read(uint8_t pin);
void mrb_gpio_write(uint8_t pin, uint8_t val);
```
The port sources are compiled automatically when the build
configuration includes a matching `conf.ports` tag.
## License
MIT
-31
View File
@@ -1,31 +0,0 @@
#ifndef MRUBY_GPIO_H
#define MRUBY_GPIO_H
#include <stdint.h>
#ifdef __cplusplus
extern "C" {
#endif
/* direction/mode flags (bitmask) */
#define MRB_GPIO_IN 0x01
#define MRB_GPIO_OUT 0x02
#define MRB_GPIO_HIGH_Z 0x04
#define MRB_GPIO_PULL_UP 0x08
#define MRB_GPIO_PULL_DOWN 0x10
#define MRB_GPIO_OPEN_DRAIN 0x20
/* HAL functions - implemented by hw-<platform>-gpio gems */
void mrb_gpio_init(uint8_t pin);
void mrb_gpio_set_dir(uint8_t pin, uint8_t flags);
void mrb_gpio_pull_up(uint8_t pin);
void mrb_gpio_pull_down(uint8_t pin);
void mrb_gpio_open_drain(uint8_t pin);
int mrb_gpio_read(uint8_t pin);
void mrb_gpio_write(uint8_t pin, uint8_t val);
#ifdef __cplusplus
}
#endif
#endif /* MRUBY_GPIO_H */
-5
View File
@@ -1,5 +0,0 @@
MRuby::Gem::Specification.new('hw-gpio') do |spec|
spec.license = 'MIT'
spec.authors = ['HASUMI Hitoshi', 'mruby developers']
spec.summary = 'GPIO peripheral interface'
end
-44
View File
@@ -1,44 +0,0 @@
class GPIO
IN = 0x01
OUT = 0x02
HIGH_Z = 0x04
PULL_UP = 0x08
PULL_DOWN = 0x10
OPEN_DRAIN = 0x20
attr_reader :pin
def initialize(pin, flags)
@pin = pin
__init(pin)
setmode(flags)
end
def setmode(flags)
dir = flags & (IN | OUT | HIGH_Z)
n = (flags & IN != 0 ? 1 : 0) + (flags & OUT != 0 ? 1 : 0) + (flags & HIGH_Z != 0 ? 1 : 0)
if n == 0
raise ArgumentError, "specify one of IN, OUT, or HIGH_Z"
elsif n > 1
raise ArgumentError, "IN, OUT, and HIGH_Z are exclusive"
end
GPIO.set_dir_at(@pin, dir)
pull = flags & (PULL_UP | PULL_DOWN)
if pull == (PULL_UP | PULL_DOWN)
raise ArgumentError, "PULL_UP and PULL_DOWN are exclusive"
end
GPIO.pull_up_at(@pin) if pull == PULL_UP
GPIO.pull_down_at(@pin) if pull == PULL_DOWN
GPIO.open_drain_at(@pin) if flags & OPEN_DRAIN != 0
nil
end
def high?
read != 0
end
def low?
read == 0
end
end
-50
View File
@@ -1,50 +0,0 @@
#include "driver/gpio.h"
#include <mruby/gpio.h>
void
mrb_gpio_init(uint8_t pin)
{
gpio_reset_pin(pin);
}
void
mrb_gpio_set_dir(uint8_t pin, uint8_t flags)
{
if (flags & MRB_GPIO_IN) {
gpio_set_direction(pin, GPIO_MODE_INPUT);
}
else if (flags & MRB_GPIO_OUT) {
gpio_set_direction(pin, GPIO_MODE_OUTPUT);
}
/* HIGH_Z: not yet implemented */
}
void
mrb_gpio_pull_up(uint8_t pin)
{
gpio_pullup_en(pin);
}
void
mrb_gpio_pull_down(uint8_t pin)
{
gpio_pulldown_en(pin);
}
void
mrb_gpio_open_drain(uint8_t pin)
{
/* not yet implemented */
}
int
mrb_gpio_read(uint8_t pin)
{
return gpio_get_level(pin);
}
void
mrb_gpio_write(uint8_t pin, uint8_t val)
{
gpio_set_level(pin, val);
}
-51
View File
@@ -1,51 +0,0 @@
#include <stdbool.h>
#include "hardware/gpio.h"
#include <mruby/gpio.h>
void
mrb_gpio_init(uint8_t pin)
{
gpio_init(pin);
}
void
mrb_gpio_set_dir(uint8_t pin, uint8_t flags)
{
if (flags & MRB_GPIO_IN) {
gpio_set_dir(pin, false);
}
else if (flags & MRB_GPIO_OUT) {
gpio_set_dir(pin, true);
}
/* HIGH_Z: not yet implemented */
}
void
mrb_gpio_pull_up(uint8_t pin)
{
gpio_pull_up(pin);
}
void
mrb_gpio_pull_down(uint8_t pin)
{
gpio_pull_down(pin);
}
void
mrb_gpio_open_drain(uint8_t pin)
{
/* not yet implemented */
}
int
mrb_gpio_read(uint8_t pin)
{
return gpio_get(pin);
}
void
mrb_gpio_write(uint8_t pin, uint8_t val)
{
gpio_put(pin, val == 1);
}
-119
View File
@@ -1,119 +0,0 @@
#include <mruby.h>
#include <mruby/presym.h>
#include <mruby/variable.h>
#include <mruby/gpio.h>
static mrb_value
mrb_gpio_m_init(mrb_state *mrb, mrb_value self)
{
mrb_int pin;
mrb_get_args(mrb, "i", &pin);
mrb_gpio_init((uint8_t)pin);
return mrb_nil_value();
}
/* GPIO.set_dir_at(pin, flags) */
static mrb_value
mrb_gpio_s_set_dir_at(mrb_state *mrb, mrb_value klass)
{
mrb_int pin, flags;
mrb_get_args(mrb, "ii", &pin, &flags);
mrb_gpio_set_dir((uint8_t)pin, (uint8_t)flags);
return mrb_nil_value();
}
/* GPIO.pull_up_at(pin) */
static mrb_value
mrb_gpio_s_pull_up_at(mrb_state *mrb, mrb_value klass)
{
mrb_int pin;
mrb_get_args(mrb, "i", &pin);
mrb_gpio_pull_up((uint8_t)pin);
return mrb_nil_value();
}
/* GPIO.pull_down_at(pin) */
static mrb_value
mrb_gpio_s_pull_down_at(mrb_state *mrb, mrb_value klass)
{
mrb_int pin;
mrb_get_args(mrb, "i", &pin);
mrb_gpio_pull_down((uint8_t)pin);
return mrb_nil_value();
}
/* GPIO.open_drain_at(pin) */
static mrb_value
mrb_gpio_s_open_drain_at(mrb_state *mrb, mrb_value klass)
{
mrb_int pin;
mrb_get_args(mrb, "i", &pin);
mrb_gpio_open_drain((uint8_t)pin);
return mrb_nil_value();
}
/* GPIO.read_at(pin) */
static mrb_value
mrb_gpio_s_read_at(mrb_state *mrb, mrb_value klass)
{
mrb_int pin;
mrb_get_args(mrb, "i", &pin);
return mrb_fixnum_value(mrb_gpio_read((uint8_t)pin));
}
/* GPIO.write_at(pin, val) */
static mrb_value
mrb_gpio_s_write_at(mrb_state *mrb, mrb_value klass)
{
mrb_int pin, val;
mrb_get_args(mrb, "ii", &pin, &val);
if (val != 0 && val != 1) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "value must be 0 or 1");
}
mrb_gpio_write((uint8_t)pin, (uint8_t)val);
return mrb_nil_value();
}
/* GPIO#read */
static mrb_value
mrb_gpio_m_read(mrb_state *mrb, mrb_value self)
{
mrb_int pin = mrb_integer(mrb_iv_get(mrb, self, MRB_IVSYM(pin)));
return mrb_fixnum_value(mrb_gpio_read((uint8_t)pin));
}
/* GPIO#write(val) */
static mrb_value
mrb_gpio_m_write(mrb_state *mrb, mrb_value self)
{
mrb_int val;
mrb_get_args(mrb, "i", &val);
if (val != 0 && val != 1) {
mrb_raise(mrb, E_ARGUMENT_ERROR, "value must be 0 or 1");
}
mrb_int pin = mrb_integer(mrb_iv_get(mrb, self, MRB_IVSYM(pin)));
mrb_gpio_write((uint8_t)pin, (uint8_t)val);
return mrb_nil_value();
}
void
mrb_hw_gpio_gem_init(mrb_state *mrb)
{
struct RClass *cls = mrb_define_class_id(mrb, MRB_SYM(GPIO), mrb->object_class);
mrb_define_method_id(mrb, cls, MRB_SYM(__init), mrb_gpio_m_init, MRB_ARGS_REQ(1));
mrb_define_method_id(mrb, cls, MRB_SYM(read), mrb_gpio_m_read, MRB_ARGS_NONE());
mrb_define_method_id(mrb, cls, MRB_SYM(write), mrb_gpio_m_write, MRB_ARGS_REQ(1));
mrb_define_class_method_id(mrb, cls, MRB_SYM(set_dir_at), mrb_gpio_s_set_dir_at, MRB_ARGS_REQ(2));
mrb_define_class_method_id(mrb, cls, MRB_SYM(pull_up_at), mrb_gpio_s_pull_up_at, MRB_ARGS_REQ(1));
mrb_define_class_method_id(mrb, cls, MRB_SYM(pull_down_at), mrb_gpio_s_pull_down_at, MRB_ARGS_REQ(1));
mrb_define_class_method_id(mrb, cls, MRB_SYM(open_drain_at), mrb_gpio_s_open_drain_at, MRB_ARGS_REQ(1));
mrb_define_class_method_id(mrb, cls, MRB_SYM(read_at), mrb_gpio_s_read_at, MRB_ARGS_REQ(1));
mrb_define_class_method_id(mrb, cls, MRB_SYM(write_at), mrb_gpio_s_write_at, MRB_ARGS_REQ(2));
}
void
mrb_hw_gpio_gem_final(mrb_state *mrb)
{
}
-162
View File
@@ -1,162 +0,0 @@
# hw-i2c - I2C peripheral interface for mruby
This gem provides the `I2C` class for communicating with I2C devices from mruby. It is designed for embedded platforms such as ESP32 and RP2040.
## Architecture
Platform-specific HAL implementations are in `ports/` directories:
- `ports/esp32/` - ESP32 using ESP-IDF I2C master driver
- `ports/rp2040/` - RP2040 using Pico SDK
The build system automatically compiles matching port sources based
on `conf.ports` setting.
## Build Configuration
```ruby
# For ESP32
MRuby::CrossBuild.new('esp32') do |conf|
conf.ports :esp32
conf.gem core: 'hw-i2c'
end
# For RP2040
MRuby::CrossBuild.new('rp2040') do |conf|
conf.ports :rp2040
conf.gem core: 'hw-i2c'
end
```
## Ruby API
### I2C.new
```ruby
i2c = I2C.new(
unit: :ESP32_I2C0, # I2C unit name (platform-specific, required)
frequency: 100_000, # bus frequency in Hz (default: 100kHz)
sda_pin: 21, # SDA GPIO pin number (default: -1 for platform default)
scl_pin: 22, # SCL GPIO pin number (default: -1 for platform default)
timeout: 500 # default timeout in ms (default: 500)
)
```
#### Unit Names
| Platform | Available Units |
| -------- | ------------------------------ |
| ESP32 | `:ESP32_I2C0`, `:ESP32_I2C1` |
| RP2040 | `:RP2040_I2C0`, `:RP2040_I2C1` |
On RP2040, if `sda_pin` or `scl_pin` is -1, the Pico SDK default pins are used.
### I2C#write
Write data to an I2C device.
```ruby
i2c.write(addr, *data, timeout: 500)
```
- `addr` - 7-bit I2C device address (Integer)
- `data` - one or more data arguments, each can be:
- **Integer** - a single byte (0-255)
- **Array of Integer** - multiple bytes
- **String** - raw bytes
- `timeout:` - optional timeout in ms (overrides instance default)
- Returns the number of bytes written (Integer)
- Raises `IOError` on failure
```ruby
# Write a single byte
i2c.write(0x3C, 0x00)
# Write multiple bytes
i2c.write(0x3C, 0x00, [0xAE, 0xD5, 0x80])
# Write a string
i2c.write(0x3C, "hello")
# Mix data types
i2c.write(0x3C, 0x40, [0x01, 0x02], "data")
```
### I2C#read
Read data from an I2C device. Optionally write data before reading (repeated START).
```ruby
i2c.read(addr, length, *write_data, timeout: 500)
```
- `addr` - 7-bit I2C device address (Integer)
- `length` - number of bytes to read (Integer, must be positive)
- `write_data` - optional data to write before reading (same format as `write`). When provided, the gem performs a write-then-read transaction using I2C repeated START condition. This is the standard way to read from a specific register.
- `timeout:` - optional timeout in ms (overrides instance default)
- Returns the data read (String)
- Raises `IOError` on failure, `ArgumentError` if length <= 0
```ruby
# Simple read (2 bytes from device)
data = i2c.read(0x50, 2)
# Register read: write register address 0x00, then read 2 bytes
data = i2c.read(0x50, 2, 0x00)
# Multi-byte register address
data = i2c.read(0x50, 4, [0x00, 0x10])
```
### I2C#scan
Scan the I2C bus for responsive devices.
```ruby
i2c.scan(timeout: 500)
```
- `timeout:` - optional timeout per probe in ms
- Returns an Array of 7-bit addresses (Integer) that responded
```ruby
found = i2c.scan
# => [0x3C, 0x50, 0x68]
```
## HAL Interface
To add support for a new platform, create a `ports/<name>/`
directory and implement the following C functions declared in
`<mruby/i2c.h>`:
```c
int mrb_i2c_unit_name_to_num(const char *name);
mrb_i2c_status mrb_i2c_init(int unit, uint32_t freq, int8_t sda, int8_t scl);
int mrb_i2c_read(int unit, uint8_t addr, uint8_t *dst, size_t len,
uint32_t timeout_us);
int mrb_i2c_write(int unit, uint8_t addr, const uint8_t *src, size_t len,
uint32_t timeout_us);
int mrb_i2c_write_read(int unit, uint8_t addr,
const uint8_t *src, size_t wlen,
uint8_t *dst, size_t rlen,
uint32_t timeout_us);
```
The port sources are compiled automatically when the build
configuration includes a matching `conf.ports` tag.
### Error Codes
```c
typedef enum {
MRB_I2C_OK = 0,
MRB_I2C_ERROR_UNIT = -1, /* invalid or uninitialized unit */
MRB_I2C_ERROR_TIMEOUT = -2, /* communication timeout */
MRB_I2C_ERROR_NACK = -3, /* device did not acknowledge */
} mrb_i2c_status;
```
## License
MIT
-31
View File
@@ -1,31 +0,0 @@
#ifndef MRUBY_I2C_H
#define MRUBY_I2C_H
#include <stdint.h>
#include <stddef.h>
#include <stdbool.h>
#ifdef __cplusplus
extern "C" {
#endif
typedef enum {
MRB_I2C_OK = 0,
MRB_I2C_ERROR_UNIT = -1,
MRB_I2C_ERROR_TIMEOUT = -2,
MRB_I2C_ERROR_NACK = -3,
} mrb_i2c_status;
/* HAL functions - implemented by hw-<platform>-i2c gems */
mrb_i2c_status mrb_i2c_init(int unit, uint32_t freq, int8_t sda, int8_t scl);
int mrb_i2c_read(int unit, uint8_t addr, uint8_t *dst, size_t len, uint32_t timeout_us);
int mrb_i2c_write(int unit, uint8_t addr, const uint8_t *src, size_t len, uint32_t timeout_us);
int mrb_i2c_write_read(int unit, uint8_t addr, const uint8_t *src, size_t wlen,
uint8_t *dst, size_t rlen, uint32_t timeout_us);
int mrb_i2c_unit_name_to_num(const char *name);
#ifdef __cplusplus
}
#endif
#endif /* MRUBY_I2C_H */
-5
View File
@@ -1,5 +0,0 @@
MRuby::Gem::Specification.new('hw-i2c') do |spec|
spec.license = 'MIT'
spec.authors = ['HASUMI Hitoshi', 'mruby developers']
spec.summary = 'I2C peripheral interface'
end
-21
View File
@@ -1,21 +0,0 @@
class I2C
DEFAULT_FREQUENCY = 100_000 # Hz
DEFAULT_TIMEOUT = 500 # ms
def initialize(unit:, frequency: DEFAULT_FREQUENCY, sda_pin: -1, scl_pin: -1, timeout: DEFAULT_TIMEOUT)
@timeout = timeout
@unit_num = __init(unit.to_s, frequency, sda_pin, scl_pin)
end
def scan(timeout: @timeout)
found = []
(0x08..0x77).each do |addr|
begin
read(addr, 1, timeout: timeout)
found << addr
rescue IOError
end
end
found
end
end

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