When there is a corresponding tag, the `RBreak` object is used to make a global jump.
Like CRuby, it can't be caught by `rescue`.
It is also the same as CRuby that it can be canceled in the middle by `ensure`.
### How to find the corresponding tag with `throw`
The called `catch` method remains in the call stack, and the tag also remains in the stack at that time.
So it is possible to find the called location by searching the two.
Note that no method can be given to the `proc` object specified in `RBreak`.
Therefore, inside the `catch` method, the argument block is called in a seemingly meaningless closure.
Also, as a countermeasure against `alias` etc., the `proc` object, which is the body of the `catch` method, is saved when mrbgem is initialized.
Because if the configuration file didn't contain any `conf.enable_test`, `rake test` would report an exception and exit.
```console
% cat my_config.rb
MRuby::Build.new { toolchain }
% rake MRUBY_CONFIG=my_config.rb test
...SNIP...
rake aborted!
NoMethodError: undefined method `invoke' for nil:NilClass
/var/tmp/mruby/tasks/test.rake:24:in `block (3 levels) in <top (required)>'
Tasks: TOP => test => test:build => test:build:lib
(See full trace by running task with --trace)
```
If the current directory is different from `MRUBY_ROOT` and it has` conf.build_dir` and `conf.enable_cxx_exception` set, it was generating a pathname outside of` build_dir`.
As a result, in some cases files unrelated to mruby could be linked.
```console
% pwd
/tmp/mruby/1/2/3/4/5/6
% mruby_dir=/tmp/mruby/a/b/c/d/mruby
% cat my_config.rb
MRuby::Build.new("host", "build/to/custom/directory") do
toolchain
enable_cxx_exception
end
% rake MRUBY_CONFIG=my_config.rb -f $mruby_dir/Rakefile > logs
% grep CXX logs
CXX a/b/c/d/mruby/src/error-cxx.cxx -> a/b/c/d/mruby/src/error-cxx.o
CXX a/b/c/d/mruby/src/gc-cxx.cxx -> a/b/c/d/mruby/src/gc-cxx.o
CXX a/b/c/d/mruby/src/vm-cxx.cxx -> a/b/c/d/mruby/src/vm-cxx.o
CXX a/b/c/d/mruby/mrbgems/mruby-compiler/core/codegen-cxx.cxx -> a/b/c/d/mruby/mrbgems/mruby-compiler/core/codegen-cxx.o
CXX a/b/c/d/mruby/mrbgems/mruby-compiler/core/y.tab-cxx.cxx -> a/b/c/d/mruby/mrbgems/mruby-compiler/core/y.tab-cxx.o
CXX ../a/b/c/d/mruby/src/error-cxx.cxx -> ../a/b/c/d/mruby/src/error-cxx.o
CXX ../a/b/c/d/mruby/src/gc-cxx.cxx -> ../a/b/c/d/mruby/src/gc-cxx.o
CXX ../a/b/c/d/mruby/src/vm-cxx.cxx -> ../a/b/c/d/mruby/src/vm-cxx.o
```
Prevents the auto-generated mrbc target source code from being compiled under host conditions.
This is because a build error occurred when `conf.enable_cxx_exception` was set.
* Use `_Complex` instead of `complex` (MSYS2 do not support `complex`)
* Use `_Dcomplex` instead of `_Complex` on MSCV
* Avoid operator division and multiplication of complex
### Example
```ruby
begin
throw 1
rescue Exception => e
puts e.message
end
```
#### Before this patch:
```console
$ bin/mruby example.rb
uncaught throw :1
```
#### After this patch (same as Ruby):
```console
$ bin/mruby example.rb
uncaught throw 1
```
This gem uses C99 `_Complex` features. You need a C compiler that
supports `_Complex` to enable this gem. All `gcc`, `clang`, `VC` support
`_Complex` so there should not be a big problem.
`.pi` files are created for `.o` files that `build.products` depends on, but
an error will occur if the build rule is unknown, so add a check.
I don't think this situation would normally arise. However, in
`mattn/mruby-onig-regexp`, when using bundled onigmo, onigmo's `.o` files
are added to dependency of `libmruby.a` in the second and subsequent builds,
and mruby does not know the build rule, so the following error had occured.
```console
rake aborted!
Don't know how to build task '/mruby/build/host/mrbgems/mruby-onig-regexp/onigmo-6.2.0/libonig_objs/ascii.pi' (See the list of available tasks with `rake --tasks`)
```
## Implementation Summary
* Only keys and only values of hash table are contiguous to eliminate
structure padding.
* Change upper limit of `iv_tbl` size to `UINT16_MAX` (it seems to be
acceptable in mruby because the total number of classes/modules
immediately after starting Redmine is 20,000 or less).
* `iv_tbl*` point hash buckets directly.
## Benchmark Summary
Only the results of typical situations on 64-bit Word-boxing are present
here. For more detailed information, including consideration, see below
report (although most of the body is written in Japanese).
* https://shuujii.github.io/mruby-iv-benchmark
### Memory Usage
Lower value is better.
| iv_tbl Size | Baseline | New | Factor |
|------------:|---------------:|---------------:|-----------:|
| 4 | 88B | 52B | 0.59091x |
| 30 | 536B | 388B | 0.72388x |
| 100 | 2072B | 1540B | 0.74324x |
| 200 | 4120B | 3076B | 0.74660x |
Although not mentioned in the above report, the memory usage of `mrbtest`
(full-core gembox) is as follows in the result by Valgrind.
* Baseline: 108,086 allocs, 16,313,122 bytes allocated
* New: 94,273 allocs, 15,875,214 bytes allocated
### Performance
Higher value is better.
#### `mrb_obj_iv_set`
| iv_tbl Size | Baseline | New | Factor |
|------------:|---------------:|---------------:|-----------:|
| 4 | 88.63003M i/s | 92.60611M i/s | 1.04486x |
| 30 | 32.97066M i/s | 25.25095M i/s | 0.76586x |
| 100 | 16.33224M i/s | 22.74998M i/s | 1.39295x |
| 200 | 5.64484M i/s | 6.79949M i/s | 1.20455x |
#### `mrb_obj_iv_get`
| iv_tbl Size | Baseline | New | Factor |
|------------:|---------------:|---------------:|-----------:|
| 4 | 217.58391M i/s | 237.59912M i/s | 1.09199x |
| 30 | 139.56195M i/s | 160.49470M i/s | 1.14999x |
| 100 | 143.09716M i/s | 190.95047M i/s | 1.33441x |
| 200 | 89.75291M i/s | 134.78717M i/s | 1.50176x |
### Binary Size
Lower value is better.
| File | Baseline | New | Factor |
|:------------|---------------:|---------------:|-----------:|
| mruby | 697,520B | 697,520B | 1.00000x |
| libmruby.a | 1,046,570B | 1,046,682B | 0.99989x |
## Note
The address in `struct RObject::iv` may change after initialization because
`iv_tbl*` points directly to hash buckets. Therefore, the address cannot be
copied and shared when include/prepend. So, when sharing `iv_tbl`, refer to
it via the sharing source class. As a result, the following bug have also
been fixed.
* [An `iv_tbl` is not shared when a class includes or prepends an empty module](https://gist.github.com/shuujii/0ac23fa24b0c55b2c602b534d81e4a95)
We have introduced following new instructions.
* `OP_LAMBDA16`
* `OP_BLOCK16`
* `OP_METHOD16`
* `OP_EXEC16`
Each instruction uses 16 bits operand for `reps` index. Since new
instructions are added, `mruby/c` VM should be updated.
Due to new instructions, dump format compatibility is lost, we have
increased `RITE_BINARY_MAJOR_VER`.
In addition, we have decreased the size of `refcnt` in `mrb_irep` from
`uint32_t` to `uint16_t`, which is reasonably big enough.
The `init_SYMBOLS()` function implicitly defined in `MRB_PRESYM_DEFINE_VAR_AND_INITER()` requires some familiarity when trying to find it from the caller.
By introducing `MRB_PRESYM_INIT_SYMBOLS()`, it is possible to find directly from the identifier.