doc: add getting-started guide, C API reference, and architecture overview

Three new documents:
- doc/guides/getting-started.md: building, running, and embedding mruby
- doc/guides/capi.md: C API reference for values, classes, methods, etc.
- doc/internal/architecture.md: internal architecture for developers

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Yukihiro "Matz" Matsumoto
2026-02-27 09:18:17 +09:00
parent b76d92db2d
commit 4ed338bdb9
4 changed files with 1108 additions and 1 deletions
+5 -1
View File
@@ -128,15 +128,19 @@ extensions in C and/or Ruby. For a guide on how to use mrbgems, consult the
- [About the Limitations of mruby](doc/limitations.md)
- [About Amalgamation (Single-File Build)](doc/guides/amalgamation.md)
- [C API Reference](doc/guides/capi.md)
- [About the Compile](doc/guides/compile.md)
- [About the Debugger with the `mrdb` Command](doc/guides/debugger.md)
- [About GC Arena](doc/guides/gc-arena-howto.md)
- [Getting Started with mruby](doc/guides/getting-started.md)
- [About the mruby directory structure](doc/guides/hier.md)
- [About Linking with `libmruby`](doc/guides/link.md)
- [About Memory Allocator Customization](doc/guides/memory.md)
- [About Memory Allocator Customization and Heap Regions](doc/guides/memory.md)
- [About Build-time Configurations](doc/guides/mrbconf.md)
- [About the Build-time Library Manager](doc/guides/mrbgems.md)
- [ROM Method Tables for Memory-Efficient Method Registration](doc/guides/rom-method-table.md)
- [About the Symbols](doc/guides/symbol.md)
- [Internal Implementation / About mruby Architecture](doc/internal/architecture.md)
- [Internal Implementation / About Value Boxing](doc/internal/boxing.md)
- [Internal Implementation / About mruby Virtual Machine Instructions](doc/internal/opcode.md)
+529
View File
@@ -0,0 +1,529 @@
<!-- summary: C API Reference -->
# C API Reference
This document covers the mruby C API for embedding and extending mruby.
## Headers
```c
#include <mruby.h> /* core types, state, class/method definition */
#include <mruby/compile.h> /* mrb_load_string, mrb_load_file */
#include <mruby/string.h> /* string operations */
#include <mruby/array.h> /* array operations */
#include <mruby/hash.h> /* hash operations */
#include <mruby/data.h> /* wrapping C structs */
#include <mruby/class.h> /* class inspection */
#include <mruby/value.h> /* value type macros */
#include <mruby/irep.h> /* loading precompiled bytecode */
#include <mruby/error.h> /* error handling (mrb_protect etc.) */
#include <mruby/variable.h> /* instance/class/global variables */
```
## State Management
```c
mrb_state *mrb = mrb_open(); /* create state with all gems */
mrb_state *mrb = mrb_open_core(); /* create state without gems */
mrb_close(mrb); /* close and free state */
```
`mrb_open()` returns `NULL` on allocation failure. Always check the
return value.
## Values
All Ruby values are represented as `mrb_value` in C.
### Creating Values
```c
mrb_nil_value() /* nil */
mrb_true_value() /* true */
mrb_false_value() /* false */
mrb_bool_value(mrb_bool b) /* true or false */
mrb_fixnum_value(mrb_int i) /* Integer */
mrb_float_value(mrb_state *mrb, mrb_float f) /* Float */
mrb_symbol_value(mrb_sym sym) /* Symbol */
mrb_obj_value(void *p) /* object pointer to value */
mrb_cptr_value(mrb_state *mrb, void *p) /* C pointer */
```
### Type Checking
```c
mrb_type(v) /* returns enum mrb_vtype */
mrb_nil_p(v) /* true if nil */
mrb_integer_p(v) /* true if Integer */
mrb_float_p(v) /* true if Float */
mrb_symbol_p(v) /* true if Symbol */
mrb_string_p(v) /* true if String */
mrb_array_p(v) /* true if Array */
mrb_hash_p(v) /* true if Hash */
mrb_true_p(v) /* true if true */
mrb_false_p(v) /* true if false */
mrb_undef_p(v) /* true if undefined */
mrb_immediate_p(v) /* true if not a heap object */
```
### Extracting C Values
```c
mrb_integer(v) /* mrb_int from Integer value */
mrb_float(v) /* mrb_float from Float value */
mrb_symbol(v) /* mrb_sym from Symbol value */
mrb_ptr(v) /* void* from object value */
mrb_str_to_cstr(mrb, v) /* const char* from String value */
```
### Value Types
| `mrb_vtype` | Ruby Class | Notes |
| ----------- | ---------- | ----- |
| `MRB_TT_FALSE` | FalseClass/NilClass | `nil` has `MRB_TT_FALSE` |
| `MRB_TT_TRUE` | TrueClass | |
| `MRB_TT_INTEGER` | Integer | Immediate value |
| `MRB_TT_FLOAT` | Float | May be immediate |
| `MRB_TT_SYMBOL` | Symbol | Immediate value |
| `MRB_TT_STRING` | String | Heap object |
| `MRB_TT_ARRAY` | Array | Heap object |
| `MRB_TT_HASH` | Hash | Heap object |
| `MRB_TT_OBJECT` | Object | User-defined classes |
| `MRB_TT_CLASS` | Class | |
| `MRB_TT_MODULE` | Module | |
| `MRB_TT_PROC` | Proc | |
| `MRB_TT_CDATA` | (C data) | Wrapped C structs |
| `MRB_TT_EXCEPTION` | Exception | |
| `MRB_TT_FIBER` | Fiber | |
## Defining Classes and Modules
```c
/* Define a class under Object */
struct RClass *my_class = mrb_define_class(mrb, "MyClass", mrb->object_class);
/* Define a class under another class/module */
struct RClass *inner = mrb_define_class_under(mrb, outer, "Inner", mrb->object_class);
/* Define a module */
struct RClass *my_mod = mrb_define_module(mrb, "MyModule");
struct RClass *inner_mod = mrb_define_module_under(mrb, outer, "InnerMod");
/* Include/prepend a module */
mrb_include_module(mrb, my_class, my_mod);
mrb_prepend_module(mrb, my_class, my_mod);
/* Look up existing class/module */
struct RClass *c = mrb_class_get(mrb, "String");
struct RClass *m = mrb_module_get(mrb, "Kernel");
/* Define a constant */
mrb_define_const(mrb, my_class, "VERSION", mrb_str_new_lit(mrb, "1.0"));
```
## Defining Methods
All C methods have the same signature:
```c
static mrb_value
my_method(mrb_state *mrb, mrb_value self)
{
/* self is the receiver */
return mrb_nil_value();
}
```
Register with:
```c
mrb_define_method(mrb, klass, "name", my_method, MRB_ARGS_NONE());
mrb_define_class_method(mrb, klass, "name", my_method, MRB_ARGS_REQ(1));
mrb_define_module_function(mrb, mod, "name", my_method, MRB_ARGS_ANY());
```
### Argument Specifiers
| Macro | Meaning |
| ----- | ------- |
| `MRB_ARGS_NONE()` | No arguments |
| `MRB_ARGS_REQ(n)` | `n` required arguments |
| `MRB_ARGS_OPT(n)` | `n` optional arguments |
| `MRB_ARGS_ARG(r,o)` | `r` required + `o` optional |
| `MRB_ARGS_REST()` | Splat (`*args`) |
| `MRB_ARGS_BLOCK()` | Block (`&block`) |
| `MRB_ARGS_ANY()` | Any number (same as REST) |
| `MRB_ARGS_KEY(n,rest)` | `n` keyword args, `rest`=1 for `**kw` |
These can be combined with `|`:
```c
MRB_ARGS_REQ(1) | MRB_ARGS_OPT(2) | MRB_ARGS_BLOCK()
```
## Parsing Arguments
`mrb_get_args()` extracts arguments from the Ruby call stack:
```c
mrb_int mrb_get_args(mrb_state *mrb, const char *format, ...);
```
### Format Specifiers
| Spec | Ruby Type | C Type(s) | Notes |
| ---- | --------- | --------- | ----- |
| `o` | any | `mrb_value` | No type check |
| `i` | Numeric | `mrb_int` | Coerces to integer |
| `f` | Numeric | `mrb_float` | Coerces to float |
| `b` | any | `mrb_bool` | Truthiness |
| `n` | String/Symbol | `mrb_sym` | Converts to symbol |
| `s` | String | `const char*, mrb_int` | Pointer + length |
| `z` | String | `const char*` | Null-terminated |
| `S` | String | `mrb_value` | String value |
| `A` | Array | `mrb_value` | Array value |
| `H` | Hash | `mrb_value` | Hash value |
| `C` | Class | `mrb_value` | Class/Module value |
| `c` | Class | `struct RClass*` | Class pointer |
| `a` | Array | `const mrb_value*, mrb_int` | Array pointer + length |
| `d` | C Data | `void*` | Requires `mrb_data_type*` |
| `&` | Block | `mrb_value` | Block argument |
| `*` | rest | `const mrb_value*, mrb_int` | Rest arguments |
| `\|` | — | — | Following args are optional |
| `?` | — | `mrb_bool` | Was previous optional arg given? |
| `:` | keywords | `mrb_kwargs` | Keyword arguments |
Adding `!` to `S`, `A`, `H`, `C`, `c`, `s`, `z`, `a`, `d` allows `nil`
(returns NULL/zero for nil).
### Examples
```c
/* def method(name, count) */
const char *name; mrb_int len, count;
mrb_get_args(mrb, "si", &name, &len, &count);
/* def method(required, optional=nil) */
mrb_value req, opt = mrb_nil_value();
mrb_get_args(mrb, "o|o", &req, &opt);
/* def method(*args) */
const mrb_value *args; mrb_int argc;
mrb_get_args(mrb, "*", &args, &argc);
/* def method(&block) */
mrb_value block;
mrb_get_args(mrb, "&", &block);
/* def method(name:, age: 0) */
mrb_sym kw_names[] = { mrb_intern_lit(mrb, "name"), mrb_intern_lit(mrb, "age") };
mrb_value kw_values[2];
mrb_kwargs kw = { 2, 1, kw_names, kw_values, NULL };
mrb_get_args(mrb, ":", &kw);
/* kw_values[0] = name (required), kw_values[1] = age (optional, undef if not given) */
```
## Calling Ruby Methods from C
```c
/* Call obj.method(arg1, arg2) */
mrb_funcall(mrb, obj, "method", 2, arg1, arg2);
/* Call with symbol (faster, no string lookup) */
mrb_funcall_id(mrb, obj, mrb_intern_lit(mrb, "method"), 2, arg1, arg2);
/* Call with argv array */
mrb_value argv[] = { arg1, arg2 };
mrb_funcall_argv(mrb, obj, mrb_intern_lit(mrb, "method"), 2, argv);
/* Call with block */
mrb_funcall_with_block(mrb, obj, mid, argc, argv, block);
/* Yield to block */
mrb_yield(mrb, block, arg);
mrb_yield_argv(mrb, block, argc, argv);
```
## String Operations
```c
/* Creation */
mrb_str_new_lit(mrb, "hello") /* from string literal */
mrb_str_new(mrb, ptr, len) /* from pointer + length */
mrb_str_new_cstr(mrb, cstr) /* from null-terminated C string */
mrb_str_new_static(mrb, ptr, len) /* from static data (no copy) */
/* Access */
RSTRING_PTR(str) /* char* pointer */
RSTRING_LEN(str) /* length */
mrb_str_to_cstr(mrb, str) /* null-terminated (may copy) */
/* Modification */
mrb_str_cat(mrb, str, ptr, len) /* append bytes */
mrb_str_cat_cstr(mrb, str, cstr) /* append C string */
mrb_str_cat_str(mrb, str, str2) /* append String */
/* Comparison */
mrb_str_equal(mrb, str1, str2) /* equality */
mrb_str_cmp(mrb, str1, str2) /* comparison (-1, 0, 1) */
```
## Array Operations
```c
/* Creation */
mrb_ary_new(mrb) /* empty array */
mrb_ary_new_capa(mrb, capa) /* preallocated */
mrb_ary_new_from_values(mrb, n, vals) /* from C array */
/* Access */
RARRAY_PTR(ary) /* mrb_value* pointer */
RARRAY_LEN(ary) /* length */
mrb_ary_entry(ary, idx) /* get element (no mrb needed) */
/* Modification */
mrb_ary_push(mrb, ary, val) /* append */
mrb_ary_pop(mrb, ary) /* remove last */
mrb_ary_shift(mrb, ary) /* remove first */
mrb_ary_unshift(mrb, ary, val) /* prepend */
mrb_ary_set(mrb, ary, idx, val) /* set element */
mrb_ary_splice(mrb, ary, pos, len, rpl) /* splice */
mrb_ary_concat(mrb, ary, other) /* extend */
```
## Hash Operations
```c
/* Creation */
mrb_hash_new(mrb) /* empty hash */
/* Access */
mrb_hash_get(mrb, hash, key) /* get value */
mrb_hash_fetch(mrb, hash, key, def) /* get with default */
mrb_hash_key_p(mrb, hash, key) /* key exists? */
mrb_hash_empty_p(mrb, hash) /* empty? */
mrb_hash_size(mrb, hash) /* number of entries */
/* Modification */
mrb_hash_set(mrb, hash, key, val) /* set key-value */
mrb_hash_delete_key(mrb, hash, key) /* delete key */
mrb_hash_merge(mrb, hash1, hash2) /* merge hash2 into hash1 */
/* Iteration */
mrb_hash_keys(mrb, hash) /* Array of keys */
mrb_hash_values(mrb, hash) /* Array of values */
```
## Wrapping C Structures
To expose a C struct to Ruby, use `mrb_data_type` and `Data_Wrap_Struct`:
```c
/* 1. Define the data type with a name and destructor */
static void point_free(mrb_state *mrb, void *p) {
mrb_free(mrb, p);
}
static const mrb_data_type point_type = {
"Point", point_free
};
/* 2. Allocate and initialize */
static mrb_value
point_init(mrb_state *mrb, mrb_value self)
{
mrb_float x, y;
mrb_get_args(mrb, "ff", &x, &y);
double *data = (double*)mrb_malloc(mrb, sizeof(double) * 2);
data[0] = x;
data[1] = y;
DATA_PTR(self) = data;
DATA_TYPE(self) = &point_type;
return self;
}
/* 3. Access the wrapped data */
static mrb_value
point_x(mrb_state *mrb, mrb_value self)
{
double *data = (double*)mrb_data_get_ptr(mrb, self, &point_type);
return mrb_float_value(mrb, data[0]);
}
/* 4. Register the class */
struct RClass *point = mrb_define_class(mrb, "Point", mrb->object_class);
MRB_SET_INSTANCE_TT(point, MRB_TT_CDATA);
mrb_define_method(mrb, point, "initialize", point_init, MRB_ARGS_REQ(2));
mrb_define_method(mrb, point, "x", point_x, MRB_ARGS_NONE());
```
## Exception Handling
### Raising Exceptions
```c
mrb_raise(mrb, E_RUNTIME_ERROR, "something went wrong");
mrb_raisef(mrb, E_ARGUMENT_ERROR, "expected %d, got %d", expected, actual);
mrb_raise(mrb, E_TYPE_ERROR, "wrong type");
```
Common exception classes: `E_RUNTIME_ERROR`, `E_TYPE_ERROR`,
`E_ARGUMENT_ERROR`, `E_RANGE_ERROR`, `E_NAME_ERROR`,
`E_NOMETHOD_ERROR`, `E_NOTIMP_ERROR`, `E_KEY_ERROR`.
### Catching Exceptions
```c
/* Check after mrb_load_string or mrb_funcall */
mrb_value result = mrb_load_string(mrb, code);
if (mrb->exc) {
mrb_print_error(mrb);
mrb->exc = NULL; /* clear exception */
}
/* Protected call */
mrb_bool error;
mrb_value result = mrb_protect(mrb, my_func, data, &error);
if (error) {
/* result contains the exception */
}
```
## Symbols
```c
/* Create symbol from string */
mrb_sym sym = mrb_intern_lit(mrb, "name"); /* from literal */
mrb_sym sym = mrb_intern_cstr(mrb, cstr); /* from C string */
mrb_sym sym = mrb_intern(mrb, ptr, len); /* from pointer + length */
/* Symbol to string */
const char *name = mrb_sym_name(mrb, sym);
mrb_int len;
const char *name = mrb_sym_name_len(mrb, sym, &len);
```
## Instance Variables
```c
/* Get/set instance variables on an object */
mrb_iv_get(mrb, obj, mrb_intern_lit(mrb, "@x"));
mrb_iv_set(mrb, obj, mrb_intern_lit(mrb, "@x"), val);
mrb_iv_defined(mrb, obj, mrb_intern_lit(mrb, "@x"));
mrb_iv_remove(mrb, obj, mrb_intern_lit(mrb, "@x"));
```
## Global Variables
```c
mrb_gv_get(mrb, mrb_intern_lit(mrb, "$verbose"));
mrb_gv_set(mrb, mrb_intern_lit(mrb, "$verbose"), mrb_true_value());
```
## Class Variables
```c
mrb_cv_get(mrb, klass, mrb_intern_lit(mrb, "@@count"));
mrb_cv_set(mrb, klass, mrb_intern_lit(mrb, "@@count"), mrb_fixnum_value(0));
```
## Loading and Executing Code
```c
/* Load and execute a string (requires mruby-compiler gem) */
mrb_value result = mrb_load_string(mrb, "1 + 2");
/* Load and execute a file */
FILE *f = fopen("script.rb", "r");
mrb_value result = mrb_load_file(mrb, f);
fclose(f);
/* Load precompiled bytecode (no compiler needed) */
mrb_value result = mrb_load_irep(mrb, bytecode_array);
```
## GC Arena
When creating many temporary Ruby objects in C, use the GC arena to
prevent them from being collected prematurely:
```c
int ai = mrb_gc_arena_save(mrb);
/* create temporary objects here */
mrb_gc_arena_restore(mrb, ai);
```
See [gc-arena-howto.md](gc-arena-howto.md) for details.
## Memory Allocation
```c
void *p = mrb_malloc(mrb, size); /* raises on failure */
void *p = mrb_calloc(mrb, nmemb, size); /* zero-initialized */
void *p = mrb_realloc(mrb, ptr, size); /* resize */
mrb_free(mrb, p); /* free */
/* NULL-returning variants (for custom error handling) */
void *p = mrb_malloc_simple(mrb, size);
void *p = mrb_realloc_simple(mrb, ptr, size);
```
## Type Conversion
```c
mrb_obj_as_string(mrb, val) /* to_s */
mrb_inspect(mrb, val) /* inspect */
mrb_any_to_s(mrb, val) /* default to_s */
mrb_str_to_integer(mrb, str, base, badcheck) /* String to Integer */
mrb_str_to_dbl(mrb, str, badcheck) /* String to Float */
mrb_ensure_float_type(mrb, val) /* ensure Float */
```
## Object Comparison
```c
mrb_equal(mrb, a, b) /* Ruby == */
mrb_eql(mrb, a, b) /* Ruby eql? */
mrb_obj_eq(mrb, a, b) /* Ruby equal? (identity) */
mrb_cmp(mrb, a, b) /* Ruby <=> (returns mrb_int) */
```
## Object Inspection
```c
mrb_obj_classname(mrb, obj) /* class name as C string */
mrb_obj_class(mrb, obj) /* class as RClass* */
mrb_obj_is_kind_of(mrb, obj, klass) /* is_a? / kind_of? */
mrb_obj_respond_to(mrb, klass, mid) /* respond_to? */
mrb_obj_id(obj) /* object_id */
mrb_obj_freeze(mrb, obj) /* freeze */
mrb_obj_dup(mrb, obj) /* dup */
```
## Compile-Time Flags
When compiling C code that uses mruby, you must use the same flags as
the library was built with. Use `mruby-config` to get them:
```console
$ build/host/bin/mruby-config --cflags # compiler flags
$ build/host/bin/mruby-config --ldflags # linker flags
$ build/host/bin/mruby-config --libs # libraries
```
Key macros that affect ABI:
| Macro | Effect |
| ----- | ------ |
| `MRB_NO_BOXING` | Struct-based values (larger, debuggable) |
| `MRB_WORD_BOXING` | Single-word values (fast, 32-bit safe) |
| `MRB_NAN_BOXING` | NaN-tagged values (default on 32-bit) |
| `MRB_NO_FLOAT` | Disable Float support |
| `MRB_INT64` | 64-bit integers |
| `MRB_USE_FLOAT32` | 32-bit floats |
Mismatching these between library and application causes silent
data corruption.
+281
View File
@@ -0,0 +1,281 @@
<!-- summary: Getting Started with mruby -->
# Getting Started with mruby
This guide walks you through building mruby, running your first Ruby program,
and embedding mruby in a C application.
## Prerequisites
You need:
- C compiler (`gcc` or `clang`)
- Ruby 2.5 or later (for the build system)
- `rake` (bundled with Ruby)
- `git` (optional, for cloning the source)
## Building mruby
Clone the repository and build:
```console
$ git clone https://github.com/mruby/mruby.git
$ cd mruby
$ rake
```
This compiles the default configuration and produces:
- `bin/mruby` — Ruby script interpreter
- `bin/mirb` — interactive Ruby shell
- `bin/mrbc` — bytecode compiler
- `build/host/lib/libmruby.a` — library for embedding
## Running Ruby Code
### Interactive shell
```console
$ bin/mirb
mirb - Pair interactive mruby
> puts "Hello, mruby!"
Hello, mruby!
=> nil
> 1 + 2
=> 3
```
### Running a script file
Create `hello.rb`:
```ruby
puts "Hello from mruby!"
```
Run it:
```console
$ bin/mruby hello.rb
Hello from mruby!
```
### One-liner
```console
$ bin/mruby -e 'puts "Hello!"'
Hello!
```
## Compiling to Bytecode
mruby can compile Ruby scripts to bytecode (`.mrb` files) for faster
loading and deployment without source code:
```console
$ bin/mrbc hello.rb # produces hello.mrb
$ bin/mruby -b hello.mrb # run bytecode
Hello from mruby!
```
You can also generate C source from Ruby scripts:
```console
$ bin/mrbc -Bhello_code hello.rb # produces hello.c with byte array
```
This generates a C file with a `const uint8_t hello_code[]` array that
can be loaded with `mrb_load_irep()` in your C application.
## Embedding mruby in C
The primary use case of mruby is embedding in C/C++ applications.
### Minimal example
Create `embed.c`:
```c
#include <mruby.h>
#include <mruby/compile.h>
int main(void)
{
mrb_state *mrb = mrb_open();
if (!mrb) return 1;
mrb_load_string(mrb, "puts 'Hello from embedded mruby!'");
if (mrb->exc) {
mrb_print_error(mrb);
}
mrb_close(mrb);
return 0;
}
```
### Compile and link
Use `mruby-config` to get the correct compiler and linker flags:
```console
$ gcc -I include `build/host/bin/mruby-config --cflags` embed.c \
`build/host/bin/mruby-config --ldflags --libs` -o embed
$ ./embed
Hello from embedded mruby!
```
**Important**: Always use `mruby-config --cflags` when compiling code
that uses mruby. The build configuration may define macros (such as
`MRB_NO_BOXING` or `MRB_USE_BIGINT`) that change the internal data
layout. Compiling without these flags causes silent data corruption.
### Calling Ruby from C
```c
#include <stdio.h>
#include <mruby.h>
#include <mruby/compile.h>
#include <mruby/string.h>
int main(void)
{
mrb_state *mrb = mrb_open();
/* Define a Ruby method */
mrb_load_string(mrb, "def greet(name) \"Hello, #{name}!\" end");
/* Call it from C */
mrb_value result = mrb_funcall(mrb, mrb_top_self(mrb),
"greet", 1, mrb_str_new_lit(mrb, "World"));
printf("%s\n", mrb_str_to_cstr(mrb, result));
mrb_close(mrb);
return 0;
}
```
### Defining C functions callable from Ruby
```c
#include <mruby.h>
#include <mruby/compile.h>
static mrb_value
my_add(mrb_state *mrb, mrb_value self)
{
mrb_int a, b;
mrb_get_args(mrb, "ii", &a, &b);
return mrb_fixnum_value(a + b);
}
int main(void)
{
mrb_state *mrb = mrb_open();
/* Define method on Kernel (available everywhere) */
mrb_define_method(mrb, mrb->kernel_module, "my_add",
my_add, MRB_ARGS_REQ(2));
mrb_load_string(mrb, "puts my_add(3, 4)"); /* prints 7 */
mrb_close(mrb);
return 0;
}
```
## Loading Precompiled Bytecode
For deployment without the compiler gem, precompile your Ruby code:
```console
$ bin/mrbc -Bruby_code app.rb
```
Then load in C:
```c
#include <mruby.h>
#include <mruby/irep.h>
#include "app.c" /* contains ruby_code[] */
int main(void)
{
mrb_state *mrb = mrb_open();
mrb_load_irep(mrb, ruby_code);
if (mrb->exc) {
mrb_print_error(mrb);
}
mrb_close(mrb);
return 0;
}
```
This approach does not require the `mruby-compiler` gem, resulting in
a smaller binary.
## Customizing the Build
mruby's functionality is controlled by the build configuration file.
The default is `build_config/default.rb`.
### Using a custom configuration
```console
$ MRUBY_CONFIG=build_config/minimal.rb rake
```
### Selecting gems
Gems add features to mruby. A minimal configuration:
```ruby
MRuby::Build.new do |conf|
conf.toolchain :gcc
# Core language extensions
conf.gem core: 'mruby-array-ext'
conf.gem core: 'mruby-string-ext'
conf.gem core: 'mruby-hash-ext'
# Tools
conf.gem core: 'mruby-bin-mruby' # mruby command
conf.gem core: 'mruby-bin-mirb' # interactive shell
conf.gem core: 'mruby-bin-mrbc' # bytecode compiler
# Compiler (needed for mrb_load_string)
conf.gem core: 'mruby-compiler'
end
```
### Using a gembox
Gemboxes are predefined collections of gems:
```ruby
MRuby::Build.new do |conf|
conf.toolchain :gcc
conf.gembox 'default' # standard set of gems
end
```
## Amalgamation (Single-File Build)
For the simplest integration, use amalgamation to combine all mruby
source into a single `mruby.c` and `mruby.h`:
```console
$ rake amalgam
$ gcc -I build/host/amalgam your_app.c build/host/amalgam/mruby.c -o your_app -lm
```
See [amalgamation.md](amalgamation.md) for details.
## What's Next
- [Compile](compile.md) — full build system reference
- [mrbgems](mrbgems.md) — creating and using gems
- [Linking](link.md) — linking `libmruby` to applications
- [Build-time Configurations](mrbconf.md) — compile-time options
- [GC Arena](gc-arena-howto.md) — managing GC arena in C extensions
- [Limitations](../limitations.md) — differences from CRuby
+293
View File
@@ -0,0 +1,293 @@
<!-- summary: About mruby Architecture -->
# mruby Architecture
This document provides a map of mruby's internals for developers who
want to understand, debug, or contribute to the codebase.
## Overview
mruby's execution pipeline:
```text
Ruby source → Parser → AST → Code Generator → Bytecode (irep)
VM → Result
```
The design priority is **memory > performance > readability**.
## Object Model
All heap-allocated Ruby objects share a common header (`MRB_OBJECT_HEADER`):
```text
struct RBasic (8 bytes on 64-bit)
┌──────────────┬─────┬──────────┬────────┬───────┐
│ RClass *c │ tt │ gc_color │ frozen │ flags │
│ (class ptr) │ 8b │ 3b │ 1b │ 20b │
└──────────────┴─────┴──────────┴────────┴───────┘
```
All object structs embed this header via `MRB_OBJECT_HEADER`:
| Struct | Ruby Type | Extra Fields |
| ------ | --------- | ------------ |
| `RObject` | Object instances | `iv` (instance variables) |
| `RClass` | Class/Module | `iv`, `mt` (method table), `super` |
| `RString` | String | embedded or heap buffer, length |
| `RArray` | Array | embedded or heap buffer, length |
| `RHash` | Hash | hash table or k-v array |
| `RProc` | Proc/Lambda | `irep` or C function, environment |
| `RData` | C data wrapper | `void *data`, `mrb_data_type` |
| `RFiber` | Fiber | `mrb_context` |
| `RException` | Exception | `iv` |
Immediate values (Integer, Symbol, `true`, `false`, `nil`) are encoded
directly in `mrb_value` without heap allocation. The encoding depends on
the boxing mode (see [boxing.md](boxing.md)).
Objects must fit within 5 words (`mrb_static_assert_object_size`).
## Virtual Machine
### Execution Context
The VM uses two stacks stored in `mrb_context`:
```text
mrb_context
├── stbase..stend value stack (mrb_value[])
├── cibase..ciend call info stack (mrb_callinfo[])
├── ci current call frame (→ cibase[n])
└── status fiber state
```
Each method call pushes a `mrb_callinfo` frame:
```text
mrb_callinfo
├── mid method symbol
├── proc current RProc
├── stack pointer into value stack
├── pc program counter (bytecode position)
├── n, nk argument counts
├── cci nonzero if called from C
└── u.env / u.target_class
```
The value stack is register-based: local variables and temporaries
occupy fixed register slots (determined at compile time by `nregs`).
### Dispatch Loop
The main loop in `mrb_vm_run()` (`src/vm.c`) decodes and dispatches
opcodes. Each opcode operates on registers:
```text
OP_MOVE R(a) = R(b)
OP_LOADI R(a) = integer
OP_ADD R(a) = R(a) + R(a+1)
OP_SEND R(a) = call R(a).method(R(a+1)..R(a+n))
OP_RETURN return R(a)
```
See [opcode.md](opcode.md) for the full instruction set.
### Method Dispatch
When `OP_SEND` executes:
1. Look up method in receiver's class method table (`mt`)
2. Walk superclass chain if not found
3. If method is a C function (`MRB_METHOD_CFUNC_P`), call directly
4. If method is Ruby (irep-based), push new `mrb_callinfo` and jump
Method tables use a hash map (`mrb_mt_tbl`). A per-state method cache
speeds up repeated lookups.
### Exception Handling
mruby uses `setjmp`/`longjmp` for exception unwinding (or C++ exceptions
if `enable_cxx_exception` is configured). The `rescue`/`ensure` entries
are tracked in `mrb_callinfo` entries and unwound when an exception
propagates.
## Garbage Collector
### Tri-Color Mark-and-Sweep
The GC uses tri-color marking with incremental execution:
| Color | Meaning |
| ----- | ------- |
| White | Unmarked — candidate for collection |
| Gray | Marked but children not yet scanned |
| Black | Fully marked (reachable) |
| Red | Static/ROM — never collected |
### GC Phases
```text
GC_STATE_ROOT → GC_STATE_MARK → GC_STATE_SWEEP → GC_STATE_ROOT
```
1. **Root marking**: marks objects directly reachable from the VM
(stack, globals, arena)
2. **Incremental marking**: processes gray objects from `gray_stack[]`,
marking their children. Runs in small steps between VM instructions.
3. **Sweep**: iterates heap pages, freeing white objects and flipping
the white bit for the next cycle
### Write Barriers
When a black object stores a reference to a white object, a write
barrier is required to prevent premature collection:
```c
mrb_field_write_barrier(mrb, parent, child); /* specific field */
mrb_write_barrier(mrb, obj); /* general */
```
The barrier paints the parent gray, adding it back to the scan queue.
### GC Arena
The arena (`gc.arena[]`) protects newly created objects from collection
before they are stored in a reachable location. C extensions must save
and restore the arena index when creating many temporary objects:
```c
int ai = mrb_gc_arena_save(mrb);
/* ... create temporary objects ... */
mrb_gc_arena_restore(mrb, ai);
```
See [../guides/gc-arena-howto.md](../guides/gc-arena-howto.md) for details.
### Heap Structure
Objects are allocated from fixed-size heap pages (`HEAP_PAGE_SIZE`
objects per page). Each page maintains a freelist of available slots.
Dead objects are returned to their page's freelist during sweep.
The `mrb_gc_add_region()` API allows pre-allocating contiguous heap
regions for reduced fragmentation and faster allocation.
### Generational Mode
Optional generational GC (`mrb_gc_generational_mode_set`) treats
objects surviving a full GC as "old generation" and performs minor
collections that only scan young objects.
## Compiler Pipeline
### Stage 1: Parser (`mrbgems/mruby-compiler/core/parse.y`)
The yacc/bison grammar (~16K lines) produces an AST of `mrb_ast_node`
linked structures. The parser tracks lexer state (expression context,
heredocs, string interpolation) and local variable scopes.
### Stage 2: Code Generator (`mrbgems/mruby-compiler/core/codegen.c`)
Walks the AST and emits bytecode into `mrb_irep` structures:
```text
mrb_irep
├── iseq[] instruction stream (bytecode)
├── pool[] constant pool (strings, numbers)
├── syms[] symbol table (method names, variable names)
├── reps[] child ireps (nested methods, blocks)
├── nlocals local variable count
└── nregs register count (locals + temporaries)
```
The code generator:
- Assigns register slots for local variables and temporaries
- Emits instructions for each AST node type
- Builds jump tables for control flow (if/unless/while/for)
- Encodes exception handler ranges for rescue/ensure
### Stage 3: Execution
The irep is wrapped in an `RProc` and executed by the VM. Alternative
loading paths:
- `mrb_load_string()` — compile from source and execute
- `mrb_load_irep()` — load precompiled `.mrb` bytecode
- `mrbc` tool — ahead-of-time compilation to `.mrb` or C array
## Source File Map
### Core (`src/`)
| File | Responsibility |
| ---- | -------------- |
| `vm.c` | Bytecode dispatch loop, method invocation |
| `state.c` | `mrb_state` init/close, irep management |
| `gc.c` | Garbage collector (mark-sweep, incremental) |
| `class.c` | Class/module definition, method tables |
| `object.c` | Core object operations |
| `variable.c` | Instance/class/global variables, object shapes |
| `proc.c` | Proc/Lambda/closure handling |
| `array.c` | Array implementation |
| `string.c` | String implementation (embedded, shared, heap) |
| `hash.c` | Hash implementation (open addressing) |
| `numeric.c` | Integer/Float arithmetic |
| `symbol.c` | Symbol table and interning |
| `range.c` | Range implementation |
| `error.c` | Exception creation, raise, backtrace |
| `kernel.c` | Kernel module methods |
| `load.c` | `.mrb` bytecode loading |
| `dump.c` | Bytecode serialization (write `.mrb`) |
| `print.c` | Print/puts/p output |
| `backtrace.c` | Stack trace generation |
### Compiler (`mrbgems/mruby-compiler/core/`)
| File | Responsibility |
| ---- | -------------- |
| `parse.y` | Yacc grammar → AST |
| `y.tab.c` | Generated parser (from parse.y) |
| `codegen.c` | AST → bytecode (irep) |
| `node.h` | AST node type definitions |
### Key Headers (`include/mruby/`)
| Header | Contents |
| ------ | -------- |
| `mruby.h` | `mrb_state`, core API declarations |
| `value.h` | `mrb_value`, type enums, value macros |
| `object.h` | `RBasic`, `RObject`, object header |
| `class.h` | `RClass`, method table types |
| `string.h` | `RString`, string macros |
| `array.h` | `RArray`, array macros |
| `hash.h` | `RHash`, hash API |
| `data.h` | `RData`, C data wrapping |
| `irep.h` | `mrb_irep`, bytecode structures |
| `compile.h` | Compiler context, `mrb_load_string` |
| `boxing_*.h` | Value boxing implementations |
## mrbgems System
Gems are the module system for mruby. Each gem lives in
`mrbgems/mruby-*/` and contains:
```text
mruby-example/
├── mrbgem.rake gem specification (name, deps, bins)
├── src/ C source files
├── mrblib/ Ruby source files (compiled to bytecode)
├── include/ C headers
├── test/ mrbtest test files
└── bintest/ binary test files (CRuby)
```
At build time, gem Ruby files are compiled with `mrbc` and linked into
`libmruby.a`. Gem initialization runs in dependency order via
`gem_init.c` (auto-generated).
GemBoxes (`mrbgems/*.gembox`) define named collections of gems
(e.g., `default.gembox` includes `stdlib`, `stdlib-ext`, `stdlib-io`,
`math`, `metaprog`, and binary tools).