2006-12-31 18:02:22 +03:00
|
|
|
/**********************************************************************
|
|
|
|
|
|
|
|
iseq.c -
|
|
|
|
|
|
|
|
$Author$
|
|
|
|
created at: 2006-07-11(Tue) 09:00:03 +0900
|
|
|
|
|
|
|
|
Copyright (C) 2006 Koichi Sasada
|
|
|
|
|
|
|
|
**********************************************************************/
|
|
|
|
|
2019-12-04 11:16:30 +03:00
|
|
|
#define RUBY_VM_INSNS_INFO 1
|
|
|
|
/* #define RUBY_MARK_FREE_DEBUG 1 */
|
|
|
|
|
2020-05-08 12:31:09 +03:00
|
|
|
#include "ruby/internal/config.h"
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2014-12-16 04:14:27 +03:00
|
|
|
#ifdef HAVE_DLADDR
|
|
|
|
# include <dlfcn.h>
|
|
|
|
#endif
|
|
|
|
|
2019-12-04 11:16:30 +03:00
|
|
|
#include "eval_intern.h"
|
2016-05-14 21:43:11 +03:00
|
|
|
#include "id_table.h"
|
2019-12-04 11:16:30 +03:00
|
|
|
#include "internal.h"
|
|
|
|
#include "internal/bits.h"
|
2020-10-16 09:20:40 +03:00
|
|
|
#include "internal/class.h"
|
2019-12-04 11:16:30 +03:00
|
|
|
#include "internal/compile.h"
|
|
|
|
#include "internal/error.h"
|
|
|
|
#include "internal/file.h"
|
2023-02-08 14:56:53 +03:00
|
|
|
#include "internal/gc.h"
|
2019-12-04 11:16:30 +03:00
|
|
|
#include "internal/hash.h"
|
2024-01-11 21:51:32 +03:00
|
|
|
#include "internal/io.h"
|
2023-05-28 14:00:20 +03:00
|
|
|
#include "internal/ruby_parser.h"
|
2019-12-04 11:16:30 +03:00
|
|
|
#include "internal/sanitizers.h"
|
|
|
|
#include "internal/symbol.h"
|
|
|
|
#include "internal/thread.h"
|
|
|
|
#include "internal/variable.h"
|
|
|
|
#include "iseq.h"
|
2023-03-07 10:17:25 +03:00
|
|
|
#include "rjit.h"
|
2019-12-04 11:16:30 +03:00
|
|
|
#include "ruby/util.h"
|
|
|
|
#include "vm_core.h"
|
VALUE size packed callinfo (ci).
Now, rb_call_info contains how to call the method with tuple of
(mid, orig_argc, flags, kwarg). Most of cases, kwarg == NULL and
mid+argc+flags only requires 64bits. So this patch packed
rb_call_info to VALUE (1 word) on such cases. If we can not
represent it in VALUE, then use imemo_callinfo which contains
conventional callinfo (rb_callinfo, renamed from rb_call_info).
iseq->body->ci_kw_size is removed because all of callinfo is VALUE
size (packed ci or a pointer to imemo_callinfo).
To access ci information, we need to use these functions:
vm_ci_mid(ci), _flag(ci), _argc(ci), _kwarg(ci).
struct rb_call_info_kw_arg is renamed to rb_callinfo_kwarg.
rb_funcallv_with_cc() and rb_method_basic_definition_p_with_cc()
is temporary removed because cd->ci should be marked.
2020-01-08 02:20:36 +03:00
|
|
|
#include "vm_callinfo.h"
|
2021-03-07 02:46:56 +03:00
|
|
|
#include "yjit.h"
|
2021-09-30 11:30:04 +03:00
|
|
|
#include "ruby/ractor.h"
|
2019-12-04 11:16:30 +03:00
|
|
|
#include "builtin.h"
|
2006-12-31 18:02:22 +03:00
|
|
|
#include "insns.inc"
|
|
|
|
#include "insns_info.inc"
|
2023-08-28 23:55:58 +03:00
|
|
|
|
2007-01-17 11:48:52 +03:00
|
|
|
VALUE rb_cISeq;
|
2015-12-07 20:23:18 +03:00
|
|
|
static VALUE iseqw_new(const rb_iseq_t *iseq);
|
|
|
|
static const rb_iseq_t *iseqw_check(VALUE iseqw);
|
2007-01-17 11:48:52 +03:00
|
|
|
|
2018-01-09 17:05:23 +03:00
|
|
|
#if VM_INSN_INFO_TABLE_IMPL == 2
|
|
|
|
static struct succ_index_table *succ_index_table_create(int max_pos, int *data, int size);
|
|
|
|
static unsigned int *succ_index_table_invert(int max_pos, struct succ_index_table *sd, int size);
|
|
|
|
static int succ_index_lookup(const struct succ_index_table *sd, int x);
|
|
|
|
#endif
|
|
|
|
|
2009-02-12 13:42:36 +03:00
|
|
|
#define hidden_obj_p(obj) (!SPECIAL_CONST_P(obj) && !RBASIC(obj)->klass)
|
|
|
|
|
2009-02-18 08:33:36 +03:00
|
|
|
static inline VALUE
|
|
|
|
obj_resurrect(VALUE obj)
|
|
|
|
{
|
|
|
|
if (hidden_obj_p(obj)) {
|
|
|
|
switch (BUILTIN_TYPE(obj)) {
|
|
|
|
case T_STRING:
|
|
|
|
obj = rb_str_resurrect(obj);
|
|
|
|
break;
|
|
|
|
case T_ARRAY:
|
|
|
|
obj = rb_ary_resurrect(obj);
|
|
|
|
break;
|
2018-12-21 02:21:50 +03:00
|
|
|
case T_HASH:
|
|
|
|
obj = rb_hash_resurrect(obj);
|
|
|
|
break;
|
2020-04-08 09:13:37 +03:00
|
|
|
default:
|
|
|
|
break;
|
2009-02-18 08:33:36 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
2019-09-13 01:15:43 +03:00
|
|
|
static void
|
|
|
|
free_arena(struct iseq_compile_data_storage *cur)
|
|
|
|
{
|
|
|
|
struct iseq_compile_data_storage *next;
|
|
|
|
|
|
|
|
while (cur) {
|
|
|
|
next = cur->next;
|
|
|
|
ruby_xfree(cur);
|
|
|
|
cur = next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
static void
|
|
|
|
compile_data_free(struct iseq_compile_data *compile_data)
|
|
|
|
{
|
|
|
|
if (compile_data) {
|
2019-09-13 01:21:18 +03:00
|
|
|
free_arena(compile_data->node.storage_head);
|
|
|
|
free_arena(compile_data->insn.storage_head);
|
2016-05-14 21:43:11 +03:00
|
|
|
if (compile_data->ivar_cache_table) {
|
|
|
|
rb_id_table_free(compile_data->ivar_cache_table);
|
|
|
|
}
|
2006-12-31 18:02:22 +03:00
|
|
|
ruby_xfree(compile_data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
New constant caching insn: opt_getconstant_path
Previously YARV bytecode implemented constant caching by having a pair
of instructions, opt_getinlinecache and opt_setinlinecache, wrapping a
series of getconstant calls (with putobject providing supporting
arguments).
This commit replaces that pattern with a new instruction,
opt_getconstant_path, handling both getting/setting the inline cache and
fetching the constant on a cache miss.
This is implemented by storing the full constant path as a
null-terminated array of IDs inside of the IC structure. idNULL is used
to signal an absolute constant reference.
$ ./miniruby --dump=insns -e '::Foo::Bar::Baz'
== disasm: #<ISeq:<main>@-e:1 (1,0)-(1,13)> (catch: FALSE)
0000 opt_getconstant_path <ic:0 ::Foo::Bar::Baz> ( 1)[Li]
0002 leave
The motivation for this is that we had increasingly found the need to
disassemble the instructions between the opt_getinlinecache and
opt_setinlinecache in order to determine the constant we are fetching,
or otherwise store metadata.
This disassembly was done:
* In opt_setinlinecache, to register the IC against the constant names
it is using for granular invalidation.
* In rb_iseq_free, to unregister the IC from the invalidation table.
* In YJIT to find the position of a opt_getinlinecache instruction to
invalidate it when the cache is populated
* In YJIT to register the constant names being used for invalidation.
With this change we no longe need disassemly for these (in fact
rb_iseq_each is now unused), as the list of constant names being
referenced is held in the IC. This should also make it possible to make
more optimizations in the future.
This may also reduce the size of iseqs, as previously each segment
required 32 bytes (on 64-bit platforms) for each constant segment. This
implementation only stores one ID per-segment.
There should be no significant performance change between this and the
previous implementation. Previously opt_getinlinecache was a "leaf"
instruction, but it included a jump (almost always to a separate cache
line). Now opt_getconstant_path is a non-leaf (it may
raise/autoload/call const_missing) but it does not jump. These seem to
even out.
2022-08-10 20:35:48 +03:00
|
|
|
static void
|
2022-12-02 17:43:53 +03:00
|
|
|
remove_from_constant_cache(ID id, IC ic)
|
|
|
|
{
|
New constant caching insn: opt_getconstant_path
Previously YARV bytecode implemented constant caching by having a pair
of instructions, opt_getinlinecache and opt_setinlinecache, wrapping a
series of getconstant calls (with putobject providing supporting
arguments).
This commit replaces that pattern with a new instruction,
opt_getconstant_path, handling both getting/setting the inline cache and
fetching the constant on a cache miss.
This is implemented by storing the full constant path as a
null-terminated array of IDs inside of the IC structure. idNULL is used
to signal an absolute constant reference.
$ ./miniruby --dump=insns -e '::Foo::Bar::Baz'
== disasm: #<ISeq:<main>@-e:1 (1,0)-(1,13)> (catch: FALSE)
0000 opt_getconstant_path <ic:0 ::Foo::Bar::Baz> ( 1)[Li]
0002 leave
The motivation for this is that we had increasingly found the need to
disassemble the instructions between the opt_getinlinecache and
opt_setinlinecache in order to determine the constant we are fetching,
or otherwise store metadata.
This disassembly was done:
* In opt_setinlinecache, to register the IC against the constant names
it is using for granular invalidation.
* In rb_iseq_free, to unregister the IC from the invalidation table.
* In YJIT to find the position of a opt_getinlinecache instruction to
invalidate it when the cache is populated
* In YJIT to register the constant names being used for invalidation.
With this change we no longe need disassemly for these (in fact
rb_iseq_each is now unused), as the list of constant names being
referenced is held in the IC. This should also make it possible to make
more optimizations in the future.
This may also reduce the size of iseqs, as previously each segment
required 32 bytes (on 64-bit platforms) for each constant segment. This
implementation only stores one ID per-segment.
There should be no significant performance change between this and the
previous implementation. Previously opt_getinlinecache was a "leaf"
instruction, but it included a jump (almost always to a separate cache
line). Now opt_getconstant_path is a non-leaf (it may
raise/autoload/call const_missing) but it does not jump. These seem to
even out.
2022-08-10 20:35:48 +03:00
|
|
|
rb_vm_t *vm = GET_VM();
|
|
|
|
VALUE lookup_result;
|
|
|
|
st_data_t ic_data = (st_data_t)ic;
|
|
|
|
|
|
|
|
if (rb_id_table_lookup(vm->constant_cache, id, &lookup_result)) {
|
|
|
|
st_table *ics = (st_table *)lookup_result;
|
|
|
|
st_delete(ics, &ic_data, NULL);
|
|
|
|
|
|
|
|
if (ics->num_entries == 0) {
|
|
|
|
rb_id_table_delete(vm->constant_cache, id);
|
|
|
|
st_free_table(ics);
|
2022-03-31 18:04:25 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// When an ISEQ is being freed, all of its associated ICs are going to go away
|
New constant caching insn: opt_getconstant_path
Previously YARV bytecode implemented constant caching by having a pair
of instructions, opt_getinlinecache and opt_setinlinecache, wrapping a
series of getconstant calls (with putobject providing supporting
arguments).
This commit replaces that pattern with a new instruction,
opt_getconstant_path, handling both getting/setting the inline cache and
fetching the constant on a cache miss.
This is implemented by storing the full constant path as a
null-terminated array of IDs inside of the IC structure. idNULL is used
to signal an absolute constant reference.
$ ./miniruby --dump=insns -e '::Foo::Bar::Baz'
== disasm: #<ISeq:<main>@-e:1 (1,0)-(1,13)> (catch: FALSE)
0000 opt_getconstant_path <ic:0 ::Foo::Bar::Baz> ( 1)[Li]
0002 leave
The motivation for this is that we had increasingly found the need to
disassemble the instructions between the opt_getinlinecache and
opt_setinlinecache in order to determine the constant we are fetching,
or otherwise store metadata.
This disassembly was done:
* In opt_setinlinecache, to register the IC against the constant names
it is using for granular invalidation.
* In rb_iseq_free, to unregister the IC from the invalidation table.
* In YJIT to find the position of a opt_getinlinecache instruction to
invalidate it when the cache is populated
* In YJIT to register the constant names being used for invalidation.
With this change we no longe need disassemly for these (in fact
rb_iseq_each is now unused), as the list of constant names being
referenced is held in the IC. This should also make it possible to make
more optimizations in the future.
This may also reduce the size of iseqs, as previously each segment
required 32 bytes (on 64-bit platforms) for each constant segment. This
implementation only stores one ID per-segment.
There should be no significant performance change between this and the
previous implementation. Previously opt_getinlinecache was a "leaf"
instruction, but it included a jump (almost always to a separate cache
line). Now opt_getconstant_path is a non-leaf (it may
raise/autoload/call const_missing) but it does not jump. These seem to
even out.
2022-08-10 20:35:48 +03:00
|
|
|
// as well. Because of this, we need to iterate over the ICs, and clear them
|
|
|
|
// from the VM's constant cache.
|
2022-03-31 18:04:25 +03:00
|
|
|
static void
|
|
|
|
iseq_clear_ic_references(const rb_iseq_t *iseq)
|
|
|
|
{
|
2022-12-02 18:44:10 +03:00
|
|
|
// In some cases (when there is a compilation error), we end up with
|
|
|
|
// ic_size greater than 0, but no allocated is_entries buffer.
|
|
|
|
// If there's no is_entries buffer to loop through, return early.
|
|
|
|
// [Bug #19173]
|
|
|
|
if (!ISEQ_BODY(iseq)->is_entries) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
New constant caching insn: opt_getconstant_path
Previously YARV bytecode implemented constant caching by having a pair
of instructions, opt_getinlinecache and opt_setinlinecache, wrapping a
series of getconstant calls (with putobject providing supporting
arguments).
This commit replaces that pattern with a new instruction,
opt_getconstant_path, handling both getting/setting the inline cache and
fetching the constant on a cache miss.
This is implemented by storing the full constant path as a
null-terminated array of IDs inside of the IC structure. idNULL is used
to signal an absolute constant reference.
$ ./miniruby --dump=insns -e '::Foo::Bar::Baz'
== disasm: #<ISeq:<main>@-e:1 (1,0)-(1,13)> (catch: FALSE)
0000 opt_getconstant_path <ic:0 ::Foo::Bar::Baz> ( 1)[Li]
0002 leave
The motivation for this is that we had increasingly found the need to
disassemble the instructions between the opt_getinlinecache and
opt_setinlinecache in order to determine the constant we are fetching,
or otherwise store metadata.
This disassembly was done:
* In opt_setinlinecache, to register the IC against the constant names
it is using for granular invalidation.
* In rb_iseq_free, to unregister the IC from the invalidation table.
* In YJIT to find the position of a opt_getinlinecache instruction to
invalidate it when the cache is populated
* In YJIT to register the constant names being used for invalidation.
With this change we no longe need disassemly for these (in fact
rb_iseq_each is now unused), as the list of constant names being
referenced is held in the IC. This should also make it possible to make
more optimizations in the future.
This may also reduce the size of iseqs, as previously each segment
required 32 bytes (on 64-bit platforms) for each constant segment. This
implementation only stores one ID per-segment.
There should be no significant performance change between this and the
previous implementation. Previously opt_getinlinecache was a "leaf"
instruction, but it included a jump (almost always to a separate cache
line). Now opt_getconstant_path is a non-leaf (it may
raise/autoload/call const_missing) but it does not jump. These seem to
even out.
2022-08-10 20:35:48 +03:00
|
|
|
for (unsigned int ic_idx = 0; ic_idx < ISEQ_BODY(iseq)->ic_size; ic_idx++) {
|
|
|
|
IC ic = &ISEQ_IS_IC_ENTRY(ISEQ_BODY(iseq), ic_idx);
|
|
|
|
|
|
|
|
// Iterate over the IC's constant path's segments and clean any references to
|
|
|
|
// the ICs out of the VM's constant cache table.
|
|
|
|
const ID *segments = ic->segments;
|
|
|
|
|
|
|
|
// It's possible that segments is NULL if we overallocated an IC but
|
|
|
|
// optimizations removed the instruction using it
|
|
|
|
if (segments == NULL)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
for (int i = 0; segments[i]; i++) {
|
|
|
|
ID id = segments[i];
|
|
|
|
if (id == idNULL) continue;
|
|
|
|
remove_from_constant_cache(id, ic);
|
|
|
|
}
|
|
|
|
|
|
|
|
ruby_xfree((void *)segments);
|
|
|
|
}
|
2022-03-31 18:04:25 +03:00
|
|
|
}
|
|
|
|
|
2015-07-22 01:52:59 +03:00
|
|
|
void
|
|
|
|
rb_iseq_free(const rb_iseq_t *iseq)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2007-06-25 06:44:20 +04:00
|
|
|
RUBY_FREE_ENTER("iseq");
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2022-03-23 22:19:48 +03:00
|
|
|
if (iseq && ISEQ_BODY(iseq)) {
|
2022-03-31 18:04:25 +03:00
|
|
|
iseq_clear_ic_references(iseq);
|
2022-03-23 22:19:48 +03:00
|
|
|
struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
|
2023-03-08 10:44:26 +03:00
|
|
|
rb_rjit_free_iseq(iseq); /* Notify RJIT */
|
2022-08-15 20:05:12 +03:00
|
|
|
#if USE_YJIT
|
2024-04-25 17:04:53 +03:00
|
|
|
rb_yjit_iseq_free(iseq);
|
2023-12-22 03:01:16 +03:00
|
|
|
if (FL_TEST_RAW((VALUE)iseq, ISEQ_TRANSLATED)) {
|
|
|
|
RUBY_ASSERT(rb_yjit_live_iseq_count > 0);
|
|
|
|
rb_yjit_live_iseq_count--;
|
|
|
|
}
|
Rust YJIT
In December 2021, we opened an [issue] to solicit feedback regarding the
porting of the YJIT codebase from C99 to Rust. There were some
reservations, but this project was given the go ahead by Ruby core
developers and Matz. Since then, we have successfully completed the port
of YJIT to Rust.
The new Rust version of YJIT has reached parity with the C version, in
that it passes all the CRuby tests, is able to run all of the YJIT
benchmarks, and performs similarly to the C version (because it works
the same way and largely generates the same machine code). We've even
incorporated some design improvements, such as a more fine-grained
constant invalidation mechanism which we expect will make a big
difference in Ruby on Rails applications.
Because we want to be careful, YJIT is guarded behind a configure
option:
```shell
./configure --enable-yjit # Build YJIT in release mode
./configure --enable-yjit=dev # Build YJIT in dev/debug mode
```
By default, YJIT does not get compiled and cargo/rustc is not required.
If YJIT is built in dev mode, then `cargo` is used to fetch development
dependencies, but when building in release, `cargo` is not required,
only `rustc`. At the moment YJIT requires Rust 1.60.0 or newer.
The YJIT command-line options remain mostly unchanged, and more details
about the build process are documented in `doc/yjit/yjit.md`.
The CI tests have been updated and do not take any more resources than
before.
The development history of the Rust port is available at the following
commit for interested parties:
https://github.com/Shopify/ruby/commit/1fd9573d8b4b65219f1c2407f30a0a60e537f8be
Our hope is that Rust YJIT will be compiled and included as a part of
system packages and compiled binaries of the Ruby 3.2 release. We do not
anticipate any major problems as Rust is well supported on every
platform which YJIT supports, but to make sure that this process works
smoothly, we would like to reach out to those who take care of building
systems packages before the 3.2 release is shipped and resolve any
issues that may come up.
[issue]: https://bugs.ruby-lang.org/issues/18481
Co-authored-by: Maxime Chevalier-Boisvert <maximechevalierb@gmail.com>
Co-authored-by: Noah Gibbs <the.codefolio.guy@gmail.com>
Co-authored-by: Kevin Newton <kddnewton@gmail.com>
2022-04-19 21:40:21 +03:00
|
|
|
#endif
|
2018-05-12 04:24:18 +03:00
|
|
|
ruby_xfree((void *)body->iseq_encoded);
|
|
|
|
ruby_xfree((void *)body->insns_info.body);
|
2023-06-29 23:31:35 +03:00
|
|
|
ruby_xfree((void *)body->insns_info.positions);
|
2018-01-09 17:05:23 +03:00
|
|
|
#if VM_INSN_INFO_TABLE_IMPL == 2
|
2023-06-29 23:31:35 +03:00
|
|
|
ruby_xfree(body->insns_info.succ_index_table);
|
2018-01-09 17:05:23 +03:00
|
|
|
#endif
|
2019-04-11 13:36:36 +03:00
|
|
|
if (LIKELY(body->local_table != rb_iseq_shared_exc_local_tbl))
|
|
|
|
ruby_xfree((void *)body->local_table);
|
2018-05-12 04:24:18 +03:00
|
|
|
ruby_xfree((void *)body->is_entries);
|
2024-01-17 23:55:08 +03:00
|
|
|
ruby_xfree(body->call_data);
|
2018-05-12 04:24:18 +03:00
|
|
|
ruby_xfree((void *)body->catch_table);
|
|
|
|
ruby_xfree((void *)body->param.opt_table);
|
2022-06-24 02:02:42 +03:00
|
|
|
if (ISEQ_MBITS_BUFLEN(body->iseq_size) > 1 && body->mark_bits.list) {
|
2022-06-24 01:46:53 +03:00
|
|
|
ruby_xfree((void *)body->mark_bits.list);
|
|
|
|
}
|
2015-07-23 12:34:31 +03:00
|
|
|
|
2023-09-26 02:03:04 +03:00
|
|
|
ruby_xfree(body->variable.original_iseq);
|
|
|
|
|
2018-05-12 04:24:18 +03:00
|
|
|
if (body->param.keyword != NULL) {
|
2023-09-26 02:03:04 +03:00
|
|
|
if (body->param.keyword->table != &body->local_table[body->param.keyword->bits_start - body->param.keyword->num])
|
|
|
|
ruby_xfree((void *)body->param.keyword->table);
|
2024-08-11 13:58:57 +03:00
|
|
|
if (body->param.keyword->default_values) {
|
|
|
|
ruby_xfree((void *)body->param.keyword->default_values);
|
|
|
|
}
|
2018-05-12 04:24:18 +03:00
|
|
|
ruby_xfree((void *)body->param.keyword);
|
2015-07-07 05:41:52 +03:00
|
|
|
}
|
2018-05-12 04:24:16 +03:00
|
|
|
compile_data_free(ISEQ_COMPILE_DATA(iseq));
|
2020-10-23 07:27:21 +03:00
|
|
|
if (body->outer_variables) rb_id_table_free(body->outer_variables);
|
2018-05-12 04:24:18 +03:00
|
|
|
ruby_xfree(body);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
2018-11-26 21:16:39 +03:00
|
|
|
|
2018-12-23 03:45:11 +03:00
|
|
|
if (iseq && ISEQ_EXECUTABLE_P(iseq) && iseq->aux.exec.local_hooks) {
|
2018-12-06 13:52:27 +03:00
|
|
|
rb_hook_list_free(iseq->aux.exec.local_hooks);
|
2018-11-26 21:16:39 +03:00
|
|
|
}
|
|
|
|
|
2007-06-25 06:44:20 +04:00
|
|
|
RUBY_FREE_LEAVE("iseq");
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
2019-04-20 04:19:47 +03:00
|
|
|
typedef VALUE iseq_value_itr_t(void *ctx, VALUE obj);
|
2018-03-19 21:21:54 +03:00
|
|
|
|
2022-06-24 01:46:53 +03:00
|
|
|
static inline void
|
2023-01-17 19:21:21 +03:00
|
|
|
iseq_scan_bits(unsigned int page, iseq_bits_t bits, VALUE *code, VALUE *original_iseq)
|
2022-06-24 01:46:53 +03:00
|
|
|
{
|
2022-06-24 12:30:59 +03:00
|
|
|
unsigned int offset;
|
2022-06-25 02:37:53 +03:00
|
|
|
unsigned int page_offset = (page * ISEQ_MBITS_BITLENGTH);
|
|
|
|
|
2022-06-24 12:30:59 +03:00
|
|
|
while (bits) {
|
|
|
|
offset = ntz_intptr(bits);
|
2022-06-25 02:37:53 +03:00
|
|
|
VALUE op = code[page_offset + offset];
|
2023-01-17 19:21:21 +03:00
|
|
|
rb_gc_mark_and_move(&code[page_offset + offset]);
|
|
|
|
VALUE newop = code[page_offset + offset];
|
|
|
|
if (original_iseq && newop != op) {
|
|
|
|
original_iseq[page_offset + offset] = newop;
|
2022-06-24 01:46:53 +03:00
|
|
|
}
|
2022-07-08 09:59:25 +03:00
|
|
|
bits &= bits - 1; // Reset Lowest Set Bit (BLSR)
|
2022-06-24 01:46:53 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-19 21:21:54 +03:00
|
|
|
static void
|
2023-01-17 19:21:21 +03:00
|
|
|
rb_iseq_mark_and_move_each_value(const rb_iseq_t *iseq, VALUE *original_iseq)
|
2018-03-19 21:21:54 +03:00
|
|
|
{
|
|
|
|
unsigned int size;
|
2019-04-20 04:19:47 +03:00
|
|
|
VALUE *code;
|
2022-03-23 22:19:48 +03:00
|
|
|
const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
|
2018-03-19 21:21:54 +03:00
|
|
|
|
2018-05-12 04:24:18 +03:00
|
|
|
size = body->iseq_size;
|
|
|
|
code = body->iseq_encoded;
|
2018-03-19 21:21:54 +03:00
|
|
|
|
2022-06-18 01:28:14 +03:00
|
|
|
union iseq_inline_storage_entry *is_entries = body->is_entries;
|
|
|
|
|
2022-06-30 20:38:48 +03:00
|
|
|
if (body->is_entries) {
|
2022-10-03 18:14:32 +03:00
|
|
|
// Skip iterating over ivc caches
|
|
|
|
is_entries += body->ivc_size;
|
2022-06-18 01:28:14 +03:00
|
|
|
|
2022-07-18 22:38:12 +03:00
|
|
|
// ICVARC entries
|
|
|
|
for (unsigned int i = 0; i < body->icvarc_size; i++, is_entries++) {
|
|
|
|
ICVARC icvarc = (ICVARC)is_entries;
|
|
|
|
if (icvarc->entry) {
|
|
|
|
RUBY_ASSERT(!RB_TYPE_P(icvarc->entry->class_value, T_NONE));
|
|
|
|
|
2023-01-17 19:21:21 +03:00
|
|
|
rb_gc_mark_and_move(&icvarc->entry->class_value);
|
2022-07-18 22:38:12 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-30 20:38:48 +03:00
|
|
|
// ISE entries
|
|
|
|
for (unsigned int i = 0; i < body->ise_size; i++, is_entries++) {
|
|
|
|
union iseq_inline_storage_entry *const is = (union iseq_inline_storage_entry *)is_entries;
|
|
|
|
if (is->once.value) {
|
2023-01-17 19:21:21 +03:00
|
|
|
rb_gc_mark_and_move(&is->once.value);
|
2022-06-18 01:28:14 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-06-30 20:38:48 +03:00
|
|
|
// IC Entries
|
|
|
|
for (unsigned int i = 0; i < body->ic_size; i++, is_entries++) {
|
|
|
|
IC ic = (IC)is_entries;
|
|
|
|
if (ic->entry) {
|
2023-01-17 19:21:21 +03:00
|
|
|
rb_gc_mark_and_move_ptr(&ic->entry);
|
2022-06-18 01:28:14 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Embedded VALUEs
|
2022-06-30 20:38:48 +03:00
|
|
|
if (body->mark_bits.list) {
|
|
|
|
if (ISEQ_MBITS_BUFLEN(size) == 1) {
|
2023-01-17 19:21:21 +03:00
|
|
|
iseq_scan_bits(0, body->mark_bits.single, code, original_iseq);
|
2022-06-30 20:38:48 +03:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (body->mark_bits.list) {
|
|
|
|
for (unsigned int i = 0; i < ISEQ_MBITS_BUFLEN(size); i++) {
|
|
|
|
iseq_bits_t bits = body->mark_bits.list[i];
|
2023-01-17 19:21:21 +03:00
|
|
|
iseq_scan_bits(i, bits, code, original_iseq);
|
2022-06-30 20:38:48 +03:00
|
|
|
}
|
2022-06-24 02:02:42 +03:00
|
|
|
}
|
2022-06-18 01:28:14 +03:00
|
|
|
}
|
2018-03-19 21:21:54 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-26 07:39:31 +03:00
|
|
|
static bool
|
|
|
|
cc_is_active(const struct rb_callcache *cc, bool reference_updating)
|
|
|
|
{
|
|
|
|
if (cc) {
|
2024-04-12 16:18:47 +03:00
|
|
|
if (cc == rb_vm_empty_cc() || rb_vm_empty_cc_for_super()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2023-07-26 07:39:31 +03:00
|
|
|
if (reference_updating) {
|
|
|
|
cc = (const struct rb_callcache *)rb_gc_location((VALUE)cc);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (vm_cc_markable(cc)) {
|
|
|
|
if (cc->klass) { // cc is not invalidated
|
|
|
|
const struct rb_callable_method_entry_struct *cme = vm_cc_cme(cc);
|
|
|
|
if (reference_updating) {
|
|
|
|
cme = (const struct rb_callable_method_entry_struct *)rb_gc_location((VALUE)cme);
|
|
|
|
}
|
|
|
|
if (!METHOD_ENTRY_INVALIDATED(cme)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2019-04-20 04:19:47 +03:00
|
|
|
void
|
2023-02-08 20:43:25 +03:00
|
|
|
rb_iseq_mark_and_move(rb_iseq_t *iseq, bool reference_updating)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2015-07-15 11:29:22 +03:00
|
|
|
RUBY_MARK_ENTER("iseq");
|
2009-07-13 13:30:23 +04:00
|
|
|
|
2023-01-17 19:21:21 +03:00
|
|
|
rb_gc_mark_and_move(&iseq->wrapper);
|
2018-12-06 13:52:27 +03:00
|
|
|
|
2022-03-23 22:19:48 +03:00
|
|
|
if (ISEQ_BODY(iseq)) {
|
2023-01-17 19:21:21 +03:00
|
|
|
struct rb_iseq_constant_body *body = ISEQ_BODY(iseq);
|
2012-05-22 12:31:38 +04:00
|
|
|
|
2023-01-17 19:21:21 +03:00
|
|
|
rb_iseq_mark_and_move_each_value(iseq, reference_updating ? ISEQ_ORIGINAL_ISEQ(iseq) : NULL);
|
2018-03-19 21:21:54 +03:00
|
|
|
|
2023-01-17 19:21:21 +03:00
|
|
|
rb_gc_mark_and_move(&body->variable.coverage);
|
|
|
|
rb_gc_mark_and_move(&body->variable.pc2branchindex);
|
|
|
|
rb_gc_mark_and_move(&body->variable.script_lines);
|
|
|
|
rb_gc_mark_and_move(&body->location.label);
|
|
|
|
rb_gc_mark_and_move(&body->location.base_label);
|
|
|
|
rb_gc_mark_and_move(&body->location.pathobj);
|
|
|
|
if (body->local_iseq) rb_gc_mark_and_move_ptr(&body->local_iseq);
|
|
|
|
if (body->parent_iseq) rb_gc_mark_and_move_ptr(&body->parent_iseq);
|
|
|
|
if (body->mandatory_only_iseq) rb_gc_mark_and_move_ptr(&body->mandatory_only_iseq);
|
2018-03-19 21:21:54 +03:00
|
|
|
|
VALUE size packed callinfo (ci).
Now, rb_call_info contains how to call the method with tuple of
(mid, orig_argc, flags, kwarg). Most of cases, kwarg == NULL and
mid+argc+flags only requires 64bits. So this patch packed
rb_call_info to VALUE (1 word) on such cases. If we can not
represent it in VALUE, then use imemo_callinfo which contains
conventional callinfo (rb_callinfo, renamed from rb_call_info).
iseq->body->ci_kw_size is removed because all of callinfo is VALUE
size (packed ci or a pointer to imemo_callinfo).
To access ci information, we need to use these functions:
vm_ci_mid(ci), _flag(ci), _argc(ci), _kwarg(ci).
struct rb_call_info_kw_arg is renamed to rb_callinfo_kwarg.
rb_funcallv_with_cc() and rb_method_basic_definition_p_with_cc()
is temporary removed because cd->ci should be marked.
2020-01-08 02:20:36 +03:00
|
|
|
if (body->call_data) {
|
2023-01-17 19:21:21 +03:00
|
|
|
for (unsigned int i = 0; i < body->ci_size; i++) {
|
|
|
|
struct rb_call_data *cds = body->call_data;
|
2020-05-29 09:20:57 +03:00
|
|
|
|
2023-01-17 19:21:21 +03:00
|
|
|
if (cds[i].ci) rb_gc_mark_and_move_ptr(&cds[i].ci);
|
2021-11-16 12:14:50 +03:00
|
|
|
|
2023-07-26 07:39:31 +03:00
|
|
|
if (cc_is_active(cds[i].cc, reference_updating)) {
|
|
|
|
rb_gc_mark_and_move_ptr(&cds[i].cc);
|
|
|
|
}
|
2024-06-03 09:39:36 +03:00
|
|
|
else if (cds[i].cc != rb_vm_empty_cc()) {
|
2023-07-26 07:39:31 +03:00
|
|
|
cds[i].cc = rb_vm_empty_cc();
|
2020-01-08 10:14:01 +03:00
|
|
|
}
|
VALUE size packed callinfo (ci).
Now, rb_call_info contains how to call the method with tuple of
(mid, orig_argc, flags, kwarg). Most of cases, kwarg == NULL and
mid+argc+flags only requires 64bits. So this patch packed
rb_call_info to VALUE (1 word) on such cases. If we can not
represent it in VALUE, then use imemo_callinfo which contains
conventional callinfo (rb_callinfo, renamed from rb_call_info).
iseq->body->ci_kw_size is removed because all of callinfo is VALUE
size (packed ci or a pointer to imemo_callinfo).
To access ci information, we need to use these functions:
vm_ci_mid(ci), _flag(ci), _argc(ci), _kwarg(ci).
struct rb_call_info_kw_arg is renamed to rb_callinfo_kwarg.
rb_funcallv_with_cc() and rb_method_basic_definition_p_with_cc()
is temporary removed because cd->ci should be marked.
2020-01-08 02:20:36 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-19 21:21:54 +03:00
|
|
|
if (body->param.flags.has_kw && ISEQ_COMPILE_DATA(iseq) == NULL) {
|
2018-05-16 04:40:44 +03:00
|
|
|
const struct rb_iseq_param_keyword *const keyword = body->param.keyword;
|
2018-03-19 21:21:54 +03:00
|
|
|
|
2023-01-17 19:21:21 +03:00
|
|
|
for (int j = 0, i = keyword->required_num; i < keyword->num; i++, j++) {
|
|
|
|
rb_gc_mark_and_move(&keyword->default_values[j]);
|
2018-03-19 21:21:54 +03:00
|
|
|
}
|
|
|
|
}
|
2022-07-21 19:23:58 +03:00
|
|
|
|
2018-03-19 21:21:54 +03:00
|
|
|
if (body->catch_table) {
|
2023-01-17 19:21:21 +03:00
|
|
|
struct iseq_catch_table *table = body->catch_table;
|
|
|
|
|
|
|
|
for (unsigned int i = 0; i < table->size; i++) {
|
|
|
|
struct iseq_catch_table_entry *entry;
|
2019-05-31 09:58:50 +03:00
|
|
|
entry = UNALIGNED_MEMBER_PTR(table, entries[i]);
|
2018-03-19 21:21:54 +03:00
|
|
|
if (entry->iseq) {
|
2023-01-17 19:21:21 +03:00
|
|
|
rb_gc_mark_and_move_ptr(&entry->iseq);
|
2018-03-19 21:21:54 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2020-01-08 10:14:01 +03:00
|
|
|
|
2023-01-17 19:21:21 +03:00
|
|
|
if (reference_updating) {
|
2023-03-07 10:15:30 +03:00
|
|
|
#if USE_RJIT
|
2023-03-07 10:17:25 +03:00
|
|
|
rb_rjit_iseq_update_references(body);
|
2020-02-22 05:23:30 +03:00
|
|
|
#endif
|
2022-08-15 20:05:12 +03:00
|
|
|
#if USE_YJIT
|
2024-04-25 17:04:53 +03:00
|
|
|
rb_yjit_iseq_update_references(iseq);
|
Rust YJIT
In December 2021, we opened an [issue] to solicit feedback regarding the
porting of the YJIT codebase from C99 to Rust. There were some
reservations, but this project was given the go ahead by Ruby core
developers and Matz. Since then, we have successfully completed the port
of YJIT to Rust.
The new Rust version of YJIT has reached parity with the C version, in
that it passes all the CRuby tests, is able to run all of the YJIT
benchmarks, and performs similarly to the C version (because it works
the same way and largely generates the same machine code). We've even
incorporated some design improvements, such as a more fine-grained
constant invalidation mechanism which we expect will make a big
difference in Ruby on Rails applications.
Because we want to be careful, YJIT is guarded behind a configure
option:
```shell
./configure --enable-yjit # Build YJIT in release mode
./configure --enable-yjit=dev # Build YJIT in dev/debug mode
```
By default, YJIT does not get compiled and cargo/rustc is not required.
If YJIT is built in dev mode, then `cargo` is used to fetch development
dependencies, but when building in release, `cargo` is not required,
only `rustc`. At the moment YJIT requires Rust 1.60.0 or newer.
The YJIT command-line options remain mostly unchanged, and more details
about the build process are documented in `doc/yjit/yjit.md`.
The CI tests have been updated and do not take any more resources than
before.
The development history of the Rust port is available at the following
commit for interested parties:
https://github.com/Shopify/ruby/commit/1fd9573d8b4b65219f1c2407f30a0a60e537f8be
Our hope is that Rust YJIT will be compiled and included as a part of
system packages and compiled binaries of the Ruby 3.2 release. We do not
anticipate any major problems as Rust is well supported on every
platform which YJIT supports, but to make sure that this process works
smoothly, we would like to reach out to those who take care of building
systems packages before the 3.2 release is shipped and resolve any
issues that may come up.
[issue]: https://bugs.ruby-lang.org/issues/18481
Co-authored-by: Maxime Chevalier-Boisvert <maximechevalierb@gmail.com>
Co-authored-by: Noah Gibbs <the.codefolio.guy@gmail.com>
Co-authored-by: Kevin Newton <kddnewton@gmail.com>
2022-04-19 21:40:21 +03:00
|
|
|
#endif
|
2023-01-17 19:21:21 +03:00
|
|
|
}
|
|
|
|
else {
|
2023-03-07 10:15:30 +03:00
|
|
|
#if USE_RJIT
|
2023-03-07 10:17:25 +03:00
|
|
|
rb_rjit_iseq_mark(body->rjit_blocks);
|
2023-01-17 19:21:21 +03:00
|
|
|
#endif
|
|
|
|
#if USE_YJIT
|
|
|
|
rb_yjit_iseq_mark(body->yjit_payload);
|
|
|
|
#endif
|
|
|
|
}
|
2015-07-22 13:55:02 +03:00
|
|
|
}
|
|
|
|
|
2023-01-19 22:47:17 +03:00
|
|
|
if (FL_TEST_RAW((VALUE)iseq, ISEQ_NOT_LOADED_YET)) {
|
|
|
|
rb_gc_mark_and_move(&iseq->aux.loader.obj);
|
|
|
|
}
|
|
|
|
else if (FL_TEST_RAW((VALUE)iseq, ISEQ_USE_COMPILE_DATA)) {
|
|
|
|
const struct iseq_compile_data *const compile_data = ISEQ_COMPILE_DATA(iseq);
|
2019-09-17 03:19:44 +03:00
|
|
|
|
2023-12-02 00:10:11 +03:00
|
|
|
if (!reference_updating) {
|
|
|
|
/* The operands in each instruction needs to be pinned because
|
|
|
|
* if auto-compaction runs in iseq_set_sequence, then the objects
|
|
|
|
* could exist on the generated_iseq buffer, which would not be
|
|
|
|
* reference updated which can lead to T_MOVED (and subsequently
|
|
|
|
* T_NONE) objects on the iseq. */
|
|
|
|
rb_iseq_mark_and_pin_insn_storage(compile_data->insn.storage_head);
|
|
|
|
}
|
2019-09-17 03:19:44 +03:00
|
|
|
|
2023-01-19 22:47:17 +03:00
|
|
|
rb_gc_mark_and_move((VALUE *)&compile_data->err_info);
|
|
|
|
rb_gc_mark_and_move((VALUE *)&compile_data->catch_table_ary);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
/* executable */
|
|
|
|
VM_ASSERT(ISEQ_EXECUTABLE_P(iseq));
|
|
|
|
|
|
|
|
if (iseq->aux.exec.local_hooks) {
|
|
|
|
rb_hook_list_mark_and_update(iseq->aux.exec.local_hooks);
|
2018-12-06 13:52:27 +03:00
|
|
|
}
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
2015-07-15 11:29:22 +03:00
|
|
|
|
2007-06-25 06:44:20 +04:00
|
|
|
RUBY_MARK_LEAVE("iseq");
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
2015-08-12 15:18:51 +03:00
|
|
|
static size_t
|
|
|
|
param_keyword_size(const struct rb_iseq_param_keyword *pkw)
|
|
|
|
{
|
|
|
|
size_t size = 0;
|
|
|
|
|
|
|
|
if (!pkw) return size;
|
|
|
|
|
|
|
|
size += sizeof(struct rb_iseq_param_keyword);
|
|
|
|
size += sizeof(VALUE) * (pkw->num - pkw->required_num);
|
|
|
|
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
2019-04-23 01:24:52 +03:00
|
|
|
size_t
|
|
|
|
rb_iseq_memsize(const rb_iseq_t *iseq)
|
2009-06-17 02:23:53 +04:00
|
|
|
{
|
2015-08-12 15:18:51 +03:00
|
|
|
size_t size = 0; /* struct already counted as RVALUE size */
|
2022-03-23 22:19:48 +03:00
|
|
|
const struct rb_iseq_constant_body *body = ISEQ_BODY(iseq);
|
2015-08-12 15:18:51 +03:00
|
|
|
const struct iseq_compile_data *compile_data;
|
2009-06-17 02:23:53 +04:00
|
|
|
|
2015-12-02 16:58:07 +03:00
|
|
|
/* TODO: should we count original_iseq? */
|
2015-08-12 15:18:51 +03:00
|
|
|
|
2019-07-23 10:42:20 +03:00
|
|
|
if (ISEQ_EXECUTABLE_P(iseq) && body) {
|
|
|
|
size += sizeof(struct rb_iseq_constant_body);
|
|
|
|
size += body->iseq_size * sizeof(VALUE);
|
|
|
|
size += body->insns_info.size * (sizeof(struct iseq_insn_info_entry) + sizeof(unsigned int));
|
|
|
|
size += body->local_table_size * sizeof(ID);
|
2022-06-18 01:28:14 +03:00
|
|
|
size += ISEQ_MBITS_BUFLEN(body->iseq_size) * ISEQ_MBITS_SIZE;
|
2019-07-23 10:42:20 +03:00
|
|
|
if (body->catch_table) {
|
|
|
|
size += iseq_catch_table_bytes(body->catch_table->size);
|
|
|
|
}
|
|
|
|
size += (body->param.opt_num + 1) * sizeof(VALUE);
|
|
|
|
size += param_keyword_size(body->param.keyword);
|
2015-11-01 05:13:54 +03:00
|
|
|
|
2019-07-23 10:42:20 +03:00
|
|
|
/* body->is_entries */
|
2022-06-18 01:28:14 +03:00
|
|
|
size += ISEQ_IS_SIZE(body) * sizeof(union iseq_inline_storage_entry);
|
2015-11-01 05:13:54 +03:00
|
|
|
|
2022-12-04 23:23:09 +03:00
|
|
|
if (ISEQ_BODY(iseq)->is_entries) {
|
|
|
|
/* IC entries constant segments */
|
|
|
|
for (unsigned int ic_idx = 0; ic_idx < body->ic_size; ic_idx++) {
|
|
|
|
IC ic = &ISEQ_IS_IC_ENTRY(body, ic_idx);
|
|
|
|
const ID *ids = ic->segments;
|
|
|
|
if (!ids) continue;
|
|
|
|
while (*ids++) {
|
|
|
|
size += sizeof(ID);
|
|
|
|
}
|
|
|
|
size += sizeof(ID); // null terminator
|
New constant caching insn: opt_getconstant_path
Previously YARV bytecode implemented constant caching by having a pair
of instructions, opt_getinlinecache and opt_setinlinecache, wrapping a
series of getconstant calls (with putobject providing supporting
arguments).
This commit replaces that pattern with a new instruction,
opt_getconstant_path, handling both getting/setting the inline cache and
fetching the constant on a cache miss.
This is implemented by storing the full constant path as a
null-terminated array of IDs inside of the IC structure. idNULL is used
to signal an absolute constant reference.
$ ./miniruby --dump=insns -e '::Foo::Bar::Baz'
== disasm: #<ISeq:<main>@-e:1 (1,0)-(1,13)> (catch: FALSE)
0000 opt_getconstant_path <ic:0 ::Foo::Bar::Baz> ( 1)[Li]
0002 leave
The motivation for this is that we had increasingly found the need to
disassemble the instructions between the opt_getinlinecache and
opt_setinlinecache in order to determine the constant we are fetching,
or otherwise store metadata.
This disassembly was done:
* In opt_setinlinecache, to register the IC against the constant names
it is using for granular invalidation.
* In rb_iseq_free, to unregister the IC from the invalidation table.
* In YJIT to find the position of a opt_getinlinecache instruction to
invalidate it when the cache is populated
* In YJIT to register the constant names being used for invalidation.
With this change we no longe need disassemly for these (in fact
rb_iseq_each is now unused), as the list of constant names being
referenced is held in the IC. This should also make it possible to make
more optimizations in the future.
This may also reduce the size of iseqs, as previously each segment
required 32 bytes (on 64-bit platforms) for each constant segment. This
implementation only stores one ID per-segment.
There should be no significant performance change between this and the
previous implementation. Previously opt_getinlinecache was a "leaf"
instruction, but it included a jump (almost always to a separate cache
line). Now opt_getconstant_path is a non-leaf (it may
raise/autoload/call const_missing) but it does not jump. These seem to
even out.
2022-08-10 20:35:48 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-31 04:36:05 +03:00
|
|
|
/* body->call_data */
|
|
|
|
size += body->ci_size * sizeof(struct rb_call_data);
|
VALUE size packed callinfo (ci).
Now, rb_call_info contains how to call the method with tuple of
(mid, orig_argc, flags, kwarg). Most of cases, kwarg == NULL and
mid+argc+flags only requires 64bits. So this patch packed
rb_call_info to VALUE (1 word) on such cases. If we can not
represent it in VALUE, then use imemo_callinfo which contains
conventional callinfo (rb_callinfo, renamed from rb_call_info).
iseq->body->ci_kw_size is removed because all of callinfo is VALUE
size (packed ci or a pointer to imemo_callinfo).
To access ci information, we need to use these functions:
vm_ci_mid(ci), _flag(ci), _argc(ci), _kwarg(ci).
struct rb_call_info_kw_arg is renamed to rb_callinfo_kwarg.
rb_funcallv_with_cc() and rb_method_basic_definition_p_with_cc()
is temporary removed because cd->ci should be marked.
2020-01-08 02:20:36 +03:00
|
|
|
// TODO: should we count imemo_callinfo?
|
2015-08-12 15:18:51 +03:00
|
|
|
}
|
|
|
|
|
2015-12-02 10:52:12 +03:00
|
|
|
compile_data = ISEQ_COMPILE_DATA(iseq);
|
2015-08-12 15:18:51 +03:00
|
|
|
if (compile_data) {
|
|
|
|
struct iseq_compile_data_storage *cur;
|
|
|
|
|
|
|
|
size += sizeof(struct iseq_compile_data);
|
|
|
|
|
2019-09-13 01:21:18 +03:00
|
|
|
cur = compile_data->node.storage_head;
|
2015-08-12 15:18:51 +03:00
|
|
|
while (cur) {
|
2018-01-14 14:19:18 +03:00
|
|
|
size += cur->size + offsetof(struct iseq_compile_data_storage, buff);
|
2015-08-12 15:18:51 +03:00
|
|
|
cur = cur->next;
|
2009-06-17 02:23:53 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return size;
|
|
|
|
}
|
|
|
|
|
2019-12-09 09:22:48 +03:00
|
|
|
struct rb_iseq_constant_body *
|
|
|
|
rb_iseq_constant_body_alloc(void)
|
|
|
|
{
|
|
|
|
struct rb_iseq_constant_body *iseq_body;
|
|
|
|
iseq_body = ZALLOC(struct rb_iseq_constant_body);
|
|
|
|
return iseq_body;
|
|
|
|
}
|
|
|
|
|
2015-07-22 01:52:59 +03:00
|
|
|
static rb_iseq_t *
|
|
|
|
iseq_alloc(void)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2015-12-08 16:58:50 +03:00
|
|
|
rb_iseq_t *iseq = iseq_imemo_alloc();
|
2022-03-23 22:19:48 +03:00
|
|
|
ISEQ_BODY(iseq) = rb_iseq_constant_body_alloc();
|
2015-07-22 01:52:59 +03:00
|
|
|
return iseq;
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
2017-06-01 03:05:33 +03:00
|
|
|
VALUE
|
|
|
|
rb_iseq_pathobj_new(VALUE path, VALUE realpath)
|
2012-05-22 12:31:38 +04:00
|
|
|
{
|
2017-06-01 03:05:33 +03:00
|
|
|
VALUE pathobj;
|
|
|
|
VM_ASSERT(RB_TYPE_P(path, T_STRING));
|
2021-10-03 16:34:45 +03:00
|
|
|
VM_ASSERT(NIL_P(realpath) || RB_TYPE_P(realpath, T_STRING));
|
2017-06-01 03:05:33 +03:00
|
|
|
|
|
|
|
if (path == realpath ||
|
|
|
|
(!NIL_P(realpath) && rb_str_cmp(path, realpath) == 0)) {
|
|
|
|
pathobj = rb_fstring(path);
|
2013-06-19 10:26:01 +04:00
|
|
|
}
|
|
|
|
else {
|
2017-06-01 03:05:33 +03:00
|
|
|
if (!NIL_P(realpath)) realpath = rb_fstring(realpath);
|
|
|
|
pathobj = rb_ary_new_from_args(2, rb_fstring(path), realpath);
|
Resize arrays in `rb_ary_freeze` and use it for freezing arrays
While working on a separate issue we found that in some cases
`ary_heap_realloc` was being called on frozen arrays. To fix this, this
change does the following:
1) Updates `rb_ary_freeze` to assert the type is an array, return if
already frozen, and shrink the capacity if it is not embedded, shared
or a shared root.
2) Replaces `rb_obj_freeze` with `rb_ary_freeze` when the object is
always an array.
3) In `ary_heap_realloc`, ensure the new capa is set with
`ARY_SET_CAPA`. Previously the change in capa was not set.
4) Adds an assertion to `ary_heap_realloc` that the array is not frozen.
Some of this work was originally done in
https://github.com/ruby/ruby/pull/2640, referencing this issue
https://bugs.ruby-lang.org/issues/16291. There didn't appear to be any
objections to this PR, it appears to have simply lost traction.
The original PR made changes to arrays and strings at the same time,
this PR only does arrays. Also it was old enough that rather than revive
that branch I've made a new one. I added Lourens as co-author in addtion
to Aaron who helped me with this patch.
The original PR made this change for performance reasons, and while
that's still true for this PR, the goal of this PR is to avoid
calling `ary_heap_realloc` on frozen arrays. The capacity should be
shrunk _before_ the array is frozen, not after.
Co-authored-by: Aaron Patterson <tenderlove@ruby-lang.org>
Co-Authored-By: methodmissing <lourens@methodmissing.com>
2024-06-18 21:52:18 +03:00
|
|
|
rb_ary_freeze(pathobj);
|
2013-06-19 10:26:01 +04:00
|
|
|
}
|
2017-06-01 03:05:33 +03:00
|
|
|
return pathobj;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
rb_iseq_pathobj_set(const rb_iseq_t *iseq, VALUE path, VALUE realpath)
|
|
|
|
{
|
2022-03-23 22:19:48 +03:00
|
|
|
RB_OBJ_WRITE(iseq, &ISEQ_BODY(iseq)->location.pathobj,
|
2017-06-01 03:05:33 +03:00
|
|
|
rb_iseq_pathobj_new(path, realpath));
|
|
|
|
}
|
|
|
|
|
|
|
|
static rb_iseq_location_t *
|
2022-09-25 10:45:28 +03:00
|
|
|
iseq_location_setup(rb_iseq_t *iseq, VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_code_location_t *code_location, const int node_id)
|
2017-06-01 03:05:33 +03:00
|
|
|
{
|
2022-03-23 22:19:48 +03:00
|
|
|
rb_iseq_location_t *loc = &ISEQ_BODY(iseq)->location;
|
2017-06-01 03:05:33 +03:00
|
|
|
|
|
|
|
rb_iseq_pathobj_set(iseq, path, realpath);
|
2015-07-22 01:52:59 +03:00
|
|
|
RB_OBJ_WRITE(iseq, &loc->label, name);
|
|
|
|
RB_OBJ_WRITE(iseq, &loc->base_label, name);
|
2022-09-25 11:07:18 +03:00
|
|
|
loc->first_lineno = first_lineno;
|
2024-03-27 01:29:38 +03:00
|
|
|
|
|
|
|
if (ISEQ_BODY(iseq)->local_iseq == iseq && strcmp(RSTRING_PTR(name), "initialize") == 0) {
|
|
|
|
ISEQ_BODY(iseq)->param.flags.use_block = 1;
|
|
|
|
}
|
|
|
|
|
2018-01-09 11:45:35 +03:00
|
|
|
if (code_location) {
|
2018-11-05 05:13:45 +03:00
|
|
|
loc->node_id = node_id;
|
2018-01-09 11:45:35 +03:00
|
|
|
loc->code_location = *code_location;
|
2017-12-05 11:56:50 +03:00
|
|
|
}
|
|
|
|
else {
|
2018-01-09 11:45:35 +03:00
|
|
|
loc->code_location.beg_pos.lineno = 0;
|
|
|
|
loc->code_location.beg_pos.column = 0;
|
|
|
|
loc->code_location.end_pos.lineno = -1;
|
|
|
|
loc->code_location.end_pos.column = -1;
|
2017-12-05 11:56:50 +03:00
|
|
|
}
|
|
|
|
|
2012-05-22 12:31:38 +04:00
|
|
|
return loc;
|
|
|
|
}
|
|
|
|
|
2007-07-04 00:12:55 +04:00
|
|
|
static void
|
2015-07-22 01:52:59 +03:00
|
|
|
set_relation(rb_iseq_t *iseq, const rb_iseq_t *piseq)
|
2007-07-04 00:12:55 +04:00
|
|
|
{
|
2022-03-23 22:19:48 +03:00
|
|
|
struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
|
2018-05-12 04:24:18 +03:00
|
|
|
const VALUE type = body->type;
|
2007-07-04 00:12:55 +04:00
|
|
|
|
|
|
|
/* set class nest stack */
|
|
|
|
if (type == ISEQ_TYPE_TOP) {
|
2018-05-12 04:24:18 +03:00
|
|
|
body->local_iseq = iseq;
|
2007-07-04 00:12:55 +04:00
|
|
|
}
|
|
|
|
else if (type == ISEQ_TYPE_METHOD || type == ISEQ_TYPE_CLASS) {
|
2018-05-12 04:24:18 +03:00
|
|
|
body->local_iseq = iseq;
|
2007-07-04 00:12:55 +04:00
|
|
|
}
|
2015-07-22 01:52:59 +03:00
|
|
|
else if (piseq) {
|
2022-03-23 22:19:48 +03:00
|
|
|
body->local_iseq = ISEQ_BODY(piseq)->local_iseq;
|
2012-11-01 16:20:00 +04:00
|
|
|
}
|
|
|
|
|
2015-07-22 01:52:59 +03:00
|
|
|
if (piseq) {
|
2018-05-12 04:24:18 +03:00
|
|
|
body->parent_iseq = piseq;
|
2007-07-04 00:12:55 +04:00
|
|
|
}
|
2012-12-10 10:11:16 +04:00
|
|
|
|
|
|
|
if (type == ISEQ_TYPE_MAIN) {
|
2018-05-12 04:24:18 +03:00
|
|
|
body->local_iseq = iseq;
|
2012-12-10 10:11:16 +04:00
|
|
|
}
|
2007-07-04 00:12:55 +04:00
|
|
|
}
|
|
|
|
|
2019-09-13 01:15:43 +03:00
|
|
|
static struct iseq_compile_data_storage *
|
|
|
|
new_arena(void)
|
|
|
|
{
|
|
|
|
struct iseq_compile_data_storage * new_arena =
|
|
|
|
(struct iseq_compile_data_storage *)
|
|
|
|
ALLOC_N(char, INITIAL_ISEQ_COMPILE_DATA_STORAGE_BUFF_SIZE +
|
|
|
|
offsetof(struct iseq_compile_data_storage, buff));
|
|
|
|
|
|
|
|
new_arena->pos = 0;
|
|
|
|
new_arena->next = 0;
|
|
|
|
new_arena->size = INITIAL_ISEQ_COMPILE_DATA_STORAGE_BUFF_SIZE;
|
|
|
|
|
|
|
|
return new_arena;
|
|
|
|
}
|
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
static VALUE
|
* blockinlining.c, compile.c, compile.h, error.c, eval.c,
eval_intern.h, eval_jump.h, eval_load.c, eval_method.h,
eval_safe.h, gc.c, insnhelper.h, insns.def, iseq.c, proc.c,
process.c, signal.c, thread.c, thread_pthread.ci, thread_win32.ci,
vm.c, vm.h, vm_dump.c, vm_evalbody.ci, vm_macro.def,
yarv.h, yarvcore.h, yarvcore.c: change type and macro names:
* yarv_*_t -> rb_*_t
* yarv_*_struct -> rb_*_struct
* yarv_tag -> rb_vm_tag
* YARV_* -> RUBY_VM_*
* proc.c, vm.c: move functions about env object creation
from proc.c to vm.c.
* proc.c, yarvcore.c: fix rb_cVM initialization place.
* inits.c: change Init_ISeq() order (after Init_VM).
* ruby.h, proc.c: change declaration place of rb_cEnv
from proc.c to ruby.c.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@11651 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-02-06 22:00:03 +03:00
|
|
|
prepare_iseq_build(rb_iseq_t *iseq,
|
2022-09-25 10:45:28 +03:00
|
|
|
VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_code_location_t *code_location, const int node_id,
|
2022-07-22 10:57:25 +03:00
|
|
|
const rb_iseq_t *parent, int isolated_depth, enum rb_iseq_type type,
|
2021-09-30 10:58:46 +03:00
|
|
|
VALUE script_lines, const rb_compile_option_t *option)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2016-03-10 08:32:49 +03:00
|
|
|
VALUE coverage = Qfalse;
|
2017-01-12 10:41:35 +03:00
|
|
|
VALUE err_info = Qnil;
|
2022-03-23 22:19:48 +03:00
|
|
|
struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
|
2017-01-12 10:41:35 +03:00
|
|
|
|
|
|
|
if (parent && (type == ISEQ_TYPE_MAIN || type == ISEQ_TYPE_TOP))
|
|
|
|
err_info = Qfalse;
|
2016-03-10 08:32:49 +03:00
|
|
|
|
2018-05-12 04:24:18 +03:00
|
|
|
body->type = type;
|
2012-05-22 12:31:38 +04:00
|
|
|
set_relation(iseq, parent);
|
|
|
|
|
2013-11-27 03:30:25 +04:00
|
|
|
name = rb_fstring(name);
|
2018-11-05 05:13:45 +03:00
|
|
|
iseq_location_setup(iseq, name, path, realpath, first_lineno, code_location, node_id);
|
2018-05-12 04:24:18 +03:00
|
|
|
if (iseq != body->local_iseq) {
|
2022-03-23 22:19:48 +03:00
|
|
|
RB_OBJ_WRITE(iseq, &body->location.base_label, ISEQ_BODY(body->local_iseq)->location.label);
|
2012-05-22 12:31:38 +04:00
|
|
|
}
|
2018-03-19 21:21:54 +03:00
|
|
|
ISEQ_COVERAGE_SET(iseq, Qnil);
|
|
|
|
ISEQ_ORIGINAL_ISEQ_CLEAR(iseq);
|
2018-05-12 04:24:18 +03:00
|
|
|
body->variable.flip_count = 0;
|
2007-02-25 19:29:26 +03:00
|
|
|
|
2021-09-30 11:30:04 +03:00
|
|
|
if (NIL_P(script_lines)) {
|
|
|
|
RB_OBJ_WRITE(iseq, &body->variable.script_lines, Qnil);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
RB_OBJ_WRITE(iseq, &body->variable.script_lines, rb_ractor_make_shareable(script_lines));
|
|
|
|
}
|
2021-09-30 10:58:46 +03:00
|
|
|
|
2017-11-18 12:39:41 +03:00
|
|
|
ISEQ_COMPILE_DATA_ALLOC(iseq);
|
2017-01-12 10:41:35 +03:00
|
|
|
RB_OBJ_WRITE(iseq, &ISEQ_COMPILE_DATA(iseq)->err_info, err_info);
|
2019-04-10 15:05:15 +03:00
|
|
|
RB_OBJ_WRITE(iseq, &ISEQ_COMPILE_DATA(iseq)->catch_table_ary, Qnil);
|
2019-09-13 01:15:43 +03:00
|
|
|
|
2019-09-13 01:21:18 +03:00
|
|
|
ISEQ_COMPILE_DATA(iseq)->node.storage_head = ISEQ_COMPILE_DATA(iseq)->node.storage_current = new_arena();
|
|
|
|
ISEQ_COMPILE_DATA(iseq)->insn.storage_head = ISEQ_COMPILE_DATA(iseq)->insn.storage_current = new_arena();
|
2020-10-23 07:27:21 +03:00
|
|
|
ISEQ_COMPILE_DATA(iseq)->isolated_depth = isolated_depth;
|
2015-12-02 10:52:12 +03:00
|
|
|
ISEQ_COMPILE_DATA(iseq)->option = option;
|
2016-05-14 21:43:11 +03:00
|
|
|
ISEQ_COMPILE_DATA(iseq)->ivar_cache_table = NULL;
|
2019-11-07 10:58:00 +03:00
|
|
|
ISEQ_COMPILE_DATA(iseq)->builtin_function_table = GET_VM()->builtin_function_table;
|
2020-10-30 06:26:59 +03:00
|
|
|
|
2016-03-10 11:34:18 +03:00
|
|
|
if (option->coverage_enabled) {
|
2008-07-08 19:13:22 +04:00
|
|
|
VALUE coverages = rb_get_coverages();
|
2008-07-03 16:55:12 +04:00
|
|
|
if (RTEST(coverages)) {
|
2017-06-01 03:05:33 +03:00
|
|
|
coverage = rb_hash_lookup(coverages, rb_iseq_path(iseq));
|
2016-03-10 08:32:49 +03:00
|
|
|
if (NIL_P(coverage)) coverage = Qfalse;
|
2008-07-01 20:55:30 +04:00
|
|
|
}
|
|
|
|
}
|
2016-03-10 08:32:49 +03:00
|
|
|
ISEQ_COVERAGE_SET(iseq, coverage);
|
2018-10-20 13:45:48 +03:00
|
|
|
if (coverage && ISEQ_BRANCH_COVERAGE(iseq))
|
2022-07-25 17:40:45 +03:00
|
|
|
ISEQ_PC2BRANCHINDEX_SET(iseq, rb_ary_hidden_new(0));
|
2008-07-01 20:55:30 +04:00
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
return Qtrue;
|
|
|
|
}
|
|
|
|
|
2018-01-01 16:18:55 +03:00
|
|
|
#if VM_CHECK_MODE > 0 && VM_INSN_INFO_TABLE_IMPL > 0
|
2018-03-21 05:20:37 +03:00
|
|
|
static void validate_get_insn_info(const rb_iseq_t *iseq);
|
2017-12-20 10:38:24 +03:00
|
|
|
#endif
|
|
|
|
|
2018-01-09 17:05:23 +03:00
|
|
|
void
|
|
|
|
rb_iseq_insns_info_encode_positions(const rb_iseq_t *iseq)
|
|
|
|
{
|
|
|
|
#if VM_INSN_INFO_TABLE_IMPL == 2
|
2020-04-15 06:57:14 +03:00
|
|
|
/* create succ_index_table */
|
2022-03-23 22:19:48 +03:00
|
|
|
struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
|
2018-05-12 04:24:18 +03:00
|
|
|
int size = body->insns_info.size;
|
|
|
|
int max_pos = body->iseq_size;
|
|
|
|
int *data = (int *)body->insns_info.positions;
|
|
|
|
if (body->insns_info.succ_index_table) ruby_xfree(body->insns_info.succ_index_table);
|
|
|
|
body->insns_info.succ_index_table = succ_index_table_create(max_pos, data, size);
|
2018-01-09 17:05:23 +03:00
|
|
|
#if VM_CHECK_MODE == 0
|
2018-05-12 04:24:18 +03:00
|
|
|
ruby_xfree(body->insns_info.positions);
|
|
|
|
body->insns_info.positions = NULL;
|
2018-01-09 17:05:23 +03:00
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2018-06-13 07:51:43 +03:00
|
|
|
#if VM_INSN_INFO_TABLE_IMPL == 2
|
2018-04-05 10:04:39 +03:00
|
|
|
unsigned int *
|
|
|
|
rb_iseq_insns_info_decode_positions(const struct rb_iseq_constant_body *body)
|
2018-01-09 17:05:23 +03:00
|
|
|
{
|
2018-04-05 10:04:39 +03:00
|
|
|
int size = body->insns_info.size;
|
|
|
|
int max_pos = body->iseq_size;
|
|
|
|
struct succ_index_table *sd = body->insns_info.succ_index_table;
|
|
|
|
return succ_index_table_invert(max_pos, sd, size);
|
2018-01-09 17:05:23 +03:00
|
|
|
}
|
2018-06-13 07:51:43 +03:00
|
|
|
#endif
|
2018-01-09 17:05:23 +03:00
|
|
|
|
2018-08-23 07:12:14 +03:00
|
|
|
void
|
2018-09-13 16:59:25 +03:00
|
|
|
rb_iseq_init_trace(rb_iseq_t *iseq)
|
2018-08-23 07:12:14 +03:00
|
|
|
{
|
2018-12-06 13:52:27 +03:00
|
|
|
iseq->aux.exec.global_trace_events = 0;
|
2018-11-26 21:16:39 +03:00
|
|
|
if (ruby_vm_event_enabled_global_flags & ISEQ_TRACE_EVENTS) {
|
|
|
|
rb_iseq_trace_set(iseq, ruby_vm_event_enabled_global_flags & ISEQ_TRACE_EVENTS);
|
2018-08-23 07:12:14 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
static VALUE
|
2017-11-14 15:58:36 +03:00
|
|
|
finish_iseq_build(rb_iseq_t *iseq)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2015-12-02 10:52:12 +03:00
|
|
|
struct iseq_compile_data *data = ISEQ_COMPILE_DATA(iseq);
|
2022-03-23 22:19:48 +03:00
|
|
|
const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
|
* include/ruby/{intern,ruby}.h, compile.[ch], error.c, eval.c,
eval_load.c, gc.c, iseq.c, main.c, parse.y, re.c, ruby.c,
yarvcore.[ch] (ruby_eval_tree, ruby_sourcefile, ruby_sourceline,
ruby_nerrs): purge global variables.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12700 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-07-05 12:12:18 +04:00
|
|
|
VALUE err = data->err_info;
|
2017-11-18 12:39:41 +03:00
|
|
|
ISEQ_COMPILE_DATA_CLEAR(iseq);
|
2006-12-31 18:02:22 +03:00
|
|
|
compile_data_free(data);
|
2007-05-11 10:26:06 +04:00
|
|
|
|
2018-01-01 16:18:55 +03:00
|
|
|
#if VM_CHECK_MODE > 0 && VM_INSN_INFO_TABLE_IMPL > 0
|
2017-12-20 10:38:24 +03:00
|
|
|
validate_get_insn_info(iseq);
|
|
|
|
#endif
|
|
|
|
|
* include/ruby/{intern,ruby}.h, compile.[ch], error.c, eval.c,
eval_load.c, gc.c, iseq.c, main.c, parse.y, re.c, ruby.c,
yarvcore.[ch] (ruby_eval_tree, ruby_sourcefile, ruby_sourceline,
ruby_nerrs): purge global variables.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12700 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-07-05 12:12:18 +04:00
|
|
|
if (RTEST(err)) {
|
2018-05-12 04:24:18 +03:00
|
|
|
VALUE path = pathobj_path(body->location.pathobj);
|
2017-01-12 10:41:35 +03:00
|
|
|
if (err == Qtrue) err = rb_exc_new_cstr(rb_eSyntaxError, "compile error");
|
2017-06-01 03:05:33 +03:00
|
|
|
rb_funcallv(err, rb_intern("set_backtrace"), 1, &path);
|
* include/ruby/{intern,ruby}.h, compile.[ch], error.c, eval.c,
eval_load.c, gc.c, iseq.c, main.c, parse.y, re.c, ruby.c,
yarvcore.[ch] (ruby_eval_tree, ruby_sourcefile, ruby_sourceline,
ruby_nerrs): purge global variables.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12700 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-07-05 12:12:18 +04:00
|
|
|
rb_exc_raise(err);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
2017-11-14 15:58:36 +03:00
|
|
|
|
2020-01-08 10:14:01 +03:00
|
|
|
RB_DEBUG_COUNTER_INC(iseq_num);
|
2022-03-23 22:19:48 +03:00
|
|
|
RB_DEBUG_COUNTER_ADD(iseq_cd_num, ISEQ_BODY(iseq)->ci_size);
|
2020-01-08 10:14:01 +03:00
|
|
|
|
2018-09-13 16:59:25 +03:00
|
|
|
rb_iseq_init_trace(iseq);
|
2006-12-31 18:02:22 +03:00
|
|
|
return Qtrue;
|
|
|
|
}
|
|
|
|
|
* blockinlining.c, compile.c, compile.h, error.c, eval.c,
eval_intern.h, eval_jump.h, eval_load.c, eval_method.h,
eval_safe.h, gc.c, insnhelper.h, insns.def, iseq.c, proc.c,
process.c, signal.c, thread.c, thread_pthread.ci, thread_win32.ci,
vm.c, vm.h, vm_dump.c, vm_evalbody.ci, vm_macro.def,
yarv.h, yarvcore.h, yarvcore.c: change type and macro names:
* yarv_*_t -> rb_*_t
* yarv_*_struct -> rb_*_struct
* yarv_tag -> rb_vm_tag
* YARV_* -> RUBY_VM_*
* proc.c, vm.c: move functions about env object creation
from proc.c to vm.c.
* proc.c, yarvcore.c: fix rb_cVM initialization place.
* inits.c: change Init_ISeq() order (after Init_VM).
* ruby.h, proc.c: change declaration place of rb_cEnv
from proc.c to ruby.c.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@11651 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-02-06 22:00:03 +03:00
|
|
|
static rb_compile_option_t COMPILE_OPTION_DEFAULT = {
|
2023-12-01 13:33:00 +03:00
|
|
|
.inline_const_cache = OPT_INLINE_CONST_CACHE,
|
|
|
|
.peephole_optimization = OPT_PEEPHOLE_OPTIMIZATION,
|
|
|
|
.tailcall_optimization = OPT_TAILCALL_OPTIMIZATION,
|
|
|
|
.specialized_instruction = OPT_SPECIALISED_INSTRUCTION,
|
|
|
|
.operands_unification = OPT_OPERANDS_UNIFICATION,
|
|
|
|
.instructions_unification = OPT_INSTRUCTIONS_UNIFICATION,
|
|
|
|
.frozen_string_literal = OPT_FROZEN_STRING_LITERAL,
|
|
|
|
.debug_frozen_string_literal = OPT_DEBUG_FROZEN_STRING_LITERAL,
|
|
|
|
.coverage_enabled = TRUE,
|
2006-12-31 18:02:22 +03:00
|
|
|
};
|
2015-08-21 23:47:53 +03:00
|
|
|
|
2023-12-01 13:33:00 +03:00
|
|
|
static const rb_compile_option_t COMPILE_OPTION_FALSE = {
|
|
|
|
.frozen_string_literal = -1, // unspecified
|
|
|
|
};
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2024-03-15 14:38:39 +03:00
|
|
|
int
|
|
|
|
rb_iseq_opt_frozen_string_literal(void)
|
|
|
|
{
|
|
|
|
return COMPILE_OPTION_DEFAULT.frozen_string_literal;
|
|
|
|
}
|
|
|
|
|
2015-09-27 09:44:02 +03:00
|
|
|
static void
|
|
|
|
set_compile_option_from_hash(rb_compile_option_t *option, VALUE opt)
|
|
|
|
{
|
|
|
|
#define SET_COMPILE_OPTION(o, h, mem) \
|
|
|
|
{ VALUE flag = rb_hash_aref((h), ID2SYM(rb_intern(#mem))); \
|
|
|
|
if (flag == Qtrue) { (o)->mem = 1; } \
|
|
|
|
else if (flag == Qfalse) { (o)->mem = 0; } \
|
|
|
|
}
|
|
|
|
#define SET_COMPILE_OPTION_NUM(o, h, mem) \
|
2023-09-01 07:36:15 +03:00
|
|
|
{ VALUE num = rb_hash_aref((h), ID2SYM(rb_intern(#mem))); \
|
2015-09-27 09:44:02 +03:00
|
|
|
if (!NIL_P(num)) (o)->mem = NUM2INT(num); \
|
|
|
|
}
|
|
|
|
SET_COMPILE_OPTION(option, opt, inline_const_cache);
|
|
|
|
SET_COMPILE_OPTION(option, opt, peephole_optimization);
|
|
|
|
SET_COMPILE_OPTION(option, opt, tailcall_optimization);
|
|
|
|
SET_COMPILE_OPTION(option, opt, specialized_instruction);
|
|
|
|
SET_COMPILE_OPTION(option, opt, operands_unification);
|
|
|
|
SET_COMPILE_OPTION(option, opt, instructions_unification);
|
|
|
|
SET_COMPILE_OPTION(option, opt, frozen_string_literal);
|
2015-11-25 11:02:29 +03:00
|
|
|
SET_COMPILE_OPTION(option, opt, debug_frozen_string_literal);
|
2016-03-10 11:34:18 +03:00
|
|
|
SET_COMPILE_OPTION(option, opt, coverage_enabled);
|
2015-09-27 09:44:02 +03:00
|
|
|
SET_COMPILE_OPTION_NUM(option, opt, debug_level);
|
|
|
|
#undef SET_COMPILE_OPTION
|
|
|
|
#undef SET_COMPILE_OPTION_NUM
|
|
|
|
}
|
|
|
|
|
2023-09-01 08:06:42 +03:00
|
|
|
static rb_compile_option_t *
|
|
|
|
set_compile_option_from_ast(rb_compile_option_t *option, const rb_ast_body_t *ast)
|
2023-06-17 04:21:37 +03:00
|
|
|
{
|
2023-09-01 08:06:42 +03:00
|
|
|
#define SET_COMPILE_OPTION(o, a, mem) \
|
|
|
|
((a)->mem < 0 ? 0 : ((o)->mem = (a)->mem > 0))
|
|
|
|
SET_COMPILE_OPTION(option, ast, coverage_enabled);
|
|
|
|
#undef SET_COMPILE_OPTION
|
2023-12-01 13:33:00 +03:00
|
|
|
if (ast->frozen_string_literal >= 0) {
|
|
|
|
option->frozen_string_literal = ast->frozen_string_literal;
|
|
|
|
}
|
2023-09-01 08:06:42 +03:00
|
|
|
return option;
|
2015-09-27 09:44:02 +03:00
|
|
|
}
|
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
static void
|
* blockinlining.c, compile.c, compile.h, error.c, eval.c,
eval_intern.h, eval_jump.h, eval_load.c, eval_method.h,
eval_safe.h, gc.c, insnhelper.h, insns.def, iseq.c, proc.c,
process.c, signal.c, thread.c, thread_pthread.ci, thread_win32.ci,
vm.c, vm.h, vm_dump.c, vm_evalbody.ci, vm_macro.def,
yarv.h, yarvcore.h, yarvcore.c: change type and macro names:
* yarv_*_t -> rb_*_t
* yarv_*_struct -> rb_*_struct
* yarv_tag -> rb_vm_tag
* YARV_* -> RUBY_VM_*
* proc.c, vm.c: move functions about env object creation
from proc.c to vm.c.
* proc.c, yarvcore.c: fix rb_cVM initialization place.
* inits.c: change Init_ISeq() order (after Init_VM).
* ruby.h, proc.c: change declaration place of rb_cEnv
from proc.c to ruby.c.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@11651 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-02-06 22:00:03 +03:00
|
|
|
make_compile_option(rb_compile_option_t *option, VALUE opt)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2021-10-03 16:34:45 +03:00
|
|
|
if (NIL_P(opt)) {
|
2006-12-31 18:02:22 +03:00
|
|
|
*option = COMPILE_OPTION_DEFAULT;
|
|
|
|
}
|
|
|
|
else if (opt == Qfalse) {
|
|
|
|
*option = COMPILE_OPTION_FALSE;
|
|
|
|
}
|
|
|
|
else if (opt == Qtrue) {
|
2013-11-15 12:07:35 +04:00
|
|
|
int i;
|
|
|
|
for (i = 0; i < (int)(sizeof(rb_compile_option_t) / sizeof(int)); ++i)
|
|
|
|
((int *)option)[i] = 1;
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
2015-12-28 09:42:18 +03:00
|
|
|
else if (RB_TYPE_P(opt, T_HASH)) {
|
2007-05-21 08:46:51 +04:00
|
|
|
*option = COMPILE_OPTION_DEFAULT;
|
2015-09-27 09:44:02 +03:00
|
|
|
set_compile_option_from_hash(option, opt);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
rb_raise(rb_eTypeError, "Compile option must be Hash/true/false/nil");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
* blockinlining.c, compile.c, compile.h, error.c, eval.c,
eval_intern.h, eval_jump.h, eval_load.c, eval_method.h,
eval_safe.h, gc.c, insnhelper.h, insns.def, iseq.c, proc.c,
process.c, signal.c, thread.c, thread_pthread.ci, thread_win32.ci,
vm.c, vm.h, vm_dump.c, vm_evalbody.ci, vm_macro.def,
yarv.h, yarvcore.h, yarvcore.c: change type and macro names:
* yarv_*_t -> rb_*_t
* yarv_*_struct -> rb_*_struct
* yarv_tag -> rb_vm_tag
* YARV_* -> RUBY_VM_*
* proc.c, vm.c: move functions about env object creation
from proc.c to vm.c.
* proc.c, yarvcore.c: fix rb_cVM initialization place.
* inits.c: change Init_ISeq() order (after Init_VM).
* ruby.h, proc.c: change declaration place of rb_cEnv
from proc.c to ruby.c.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@11651 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-02-06 22:00:03 +03:00
|
|
|
make_compile_option_value(rb_compile_option_t *option)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2019-10-22 01:28:41 +03:00
|
|
|
VALUE opt = rb_hash_new_with_size(11);
|
2006-12-31 18:02:22 +03:00
|
|
|
#define SET_COMPILE_OPTION(o, h, mem) \
|
2021-08-31 14:30:35 +03:00
|
|
|
rb_hash_aset((h), ID2SYM(rb_intern(#mem)), RBOOL((o)->mem))
|
2008-04-14 09:34:04 +04:00
|
|
|
#define SET_COMPILE_OPTION_NUM(o, h, mem) \
|
2010-12-17 01:05:58 +03:00
|
|
|
rb_hash_aset((h), ID2SYM(rb_intern(#mem)), INT2NUM((o)->mem))
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
|
|
|
SET_COMPILE_OPTION(option, opt, inline_const_cache);
|
|
|
|
SET_COMPILE_OPTION(option, opt, peephole_optimization);
|
2007-05-21 08:46:51 +04:00
|
|
|
SET_COMPILE_OPTION(option, opt, tailcall_optimization);
|
2006-12-31 18:02:22 +03:00
|
|
|
SET_COMPILE_OPTION(option, opt, specialized_instruction);
|
|
|
|
SET_COMPILE_OPTION(option, opt, operands_unification);
|
|
|
|
SET_COMPILE_OPTION(option, opt, instructions_unification);
|
2015-11-25 11:02:29 +03:00
|
|
|
SET_COMPILE_OPTION(option, opt, debug_frozen_string_literal);
|
2016-03-10 11:34:18 +03:00
|
|
|
SET_COMPILE_OPTION(option, opt, coverage_enabled);
|
2008-04-14 09:34:04 +04:00
|
|
|
SET_COMPILE_OPTION_NUM(option, opt, debug_level);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
#undef SET_COMPILE_OPTION
|
2008-04-14 09:34:04 +04:00
|
|
|
#undef SET_COMPILE_OPTION_NUM
|
2023-12-01 13:33:00 +03:00
|
|
|
VALUE frozen_string_literal = option->frozen_string_literal == -1 ? Qnil : RBOOL(option->frozen_string_literal);
|
|
|
|
rb_hash_aset(opt, ID2SYM(rb_intern("frozen_string_literal")), frozen_string_literal);
|
2006-12-31 18:02:22 +03:00
|
|
|
return opt;
|
|
|
|
}
|
|
|
|
|
2015-07-22 01:52:59 +03:00
|
|
|
rb_iseq_t *
|
2024-05-03 02:57:55 +03:00
|
|
|
rb_iseq_new(const VALUE ast_value, VALUE name, VALUE path, VALUE realpath,
|
2022-07-22 10:57:25 +03:00
|
|
|
const rb_iseq_t *parent, enum rb_iseq_type type)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2024-05-03 02:57:55 +03:00
|
|
|
return rb_iseq_new_with_opt(ast_value, name, path, realpath, 0, parent,
|
2024-03-28 04:26:42 +03:00
|
|
|
0, type, &COMPILE_OPTION_DEFAULT,
|
|
|
|
Qnil);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
2021-06-17 19:31:50 +03:00
|
|
|
static int
|
2024-05-03 02:57:55 +03:00
|
|
|
ast_line_count(const VALUE ast_value)
|
2021-06-17 19:31:50 +03:00
|
|
|
{
|
2024-05-03 02:57:55 +03:00
|
|
|
rb_ast_t *ast = rb_ruby_ast_data_get(ast_value);
|
2024-04-26 15:43:35 +03:00
|
|
|
return ast->body.line_count;
|
2021-06-17 19:31:50 +03:00
|
|
|
}
|
|
|
|
|
2022-09-23 13:54:39 +03:00
|
|
|
static VALUE
|
2024-04-25 15:48:20 +03:00
|
|
|
iseq_setup_coverage(VALUE coverages, VALUE path, int line_count)
|
2022-09-23 13:54:39 +03:00
|
|
|
{
|
|
|
|
if (line_count >= 0) {
|
|
|
|
int len = (rb_get_coverage_mode() & COVERAGE_TARGET_ONESHOT_LINES) ? 0 : line_count;
|
|
|
|
|
|
|
|
VALUE coverage = rb_default_coverage(len);
|
|
|
|
rb_hash_aset(coverages, path, coverage);
|
|
|
|
|
|
|
|
return coverage;
|
|
|
|
}
|
|
|
|
|
|
|
|
return Qnil;
|
|
|
|
}
|
|
|
|
|
2023-01-22 00:51:59 +03:00
|
|
|
static inline void
|
2024-04-25 15:48:20 +03:00
|
|
|
iseq_new_setup_coverage(VALUE path, int line_count)
|
* include/ruby/node.h, vm_core.h: move definition of
RUBY_VM_METHOD_NODE to node.h.
* class.c, common.mk: remove useless inclusion.
* compile.h, iseq.h, vm_core.h: rename compile.h to iseq.h.
move some definitions from vm_core.h to iseq.h.
* compile.c, iseq.c, vm.c: ditto.
* eval.c, compile.c: move some functions for parser
from eval.c to compile.c.
* eval_intern.h, vm_core.h: move va_init_list() macro to
vm_core.h.
* iseq.c (rb_iseq_new_top, rb_iseq_first_lineno): added.
* load.c, ruby.c: use rb_iseq_new_top() instead of
rb_iseq_new() with ISEQ_TYPE_TOP constant directly.
* proc.c: use rb_iseq_first_lineno() instead of accessing
iseq structure.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@19472 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2008-09-23 11:49:45 +04:00
|
|
|
{
|
2018-08-22 13:38:56 +03:00
|
|
|
VALUE coverages = rb_get_coverages();
|
2023-01-22 00:51:59 +03:00
|
|
|
|
2018-08-22 13:38:56 +03:00
|
|
|
if (RTEST(coverages)) {
|
2024-04-25 15:48:20 +03:00
|
|
|
iseq_setup_coverage(coverages, path, line_count);
|
2018-08-22 13:38:56 +03:00
|
|
|
}
|
2023-01-22 00:51:59 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
rb_iseq_t *
|
2024-05-03 02:57:55 +03:00
|
|
|
rb_iseq_new_top(const VALUE ast_value, VALUE name, VALUE path, VALUE realpath, const rb_iseq_t *parent)
|
2023-01-22 00:51:59 +03:00
|
|
|
{
|
2024-05-03 02:57:55 +03:00
|
|
|
iseq_new_setup_coverage(path, ast_line_count(ast_value));
|
2018-08-22 13:38:56 +03:00
|
|
|
|
2024-05-03 02:57:55 +03:00
|
|
|
return rb_iseq_new_with_opt(ast_value, name, path, realpath, 0, parent, 0,
|
2024-03-28 04:26:42 +03:00
|
|
|
ISEQ_TYPE_TOP, &COMPILE_OPTION_DEFAULT,
|
|
|
|
Qnil);
|
2008-12-27 04:15:56 +03:00
|
|
|
}
|
|
|
|
|
2024-01-31 20:17:31 +03:00
|
|
|
/**
|
|
|
|
* The main entry-point into the prism compiler when a file is required.
|
|
|
|
*/
|
|
|
|
rb_iseq_t *
|
|
|
|
pm_iseq_new_top(pm_scope_node_t *node, VALUE name, VALUE path, VALUE realpath, const rb_iseq_t *parent)
|
|
|
|
{
|
2024-04-25 15:48:20 +03:00
|
|
|
iseq_new_setup_coverage(path, (int) (node->parser->newline_list.size - 1));
|
2024-01-31 20:17:31 +03:00
|
|
|
|
|
|
|
return pm_iseq_new_with_opt(node, name, path, realpath, 0, parent, 0,
|
|
|
|
ISEQ_TYPE_TOP, &COMPILE_OPTION_DEFAULT);
|
|
|
|
}
|
|
|
|
|
2015-07-22 01:52:59 +03:00
|
|
|
rb_iseq_t *
|
2024-05-03 02:57:55 +03:00
|
|
|
rb_iseq_new_main(const VALUE ast_value, VALUE path, VALUE realpath, const rb_iseq_t *parent, int opt)
|
2008-12-27 04:15:56 +03:00
|
|
|
{
|
2024-05-03 02:57:55 +03:00
|
|
|
iseq_new_setup_coverage(path, ast_line_count(ast_value));
|
2023-01-22 00:51:59 +03:00
|
|
|
|
2024-05-03 02:57:55 +03:00
|
|
|
return rb_iseq_new_with_opt(ast_value, rb_fstring_lit("<main>"),
|
2022-09-25 10:45:28 +03:00
|
|
|
path, realpath, 0,
|
2024-03-28 04:26:42 +03:00
|
|
|
parent, 0, ISEQ_TYPE_MAIN, opt ? &COMPILE_OPTION_DEFAULT : &COMPILE_OPTION_FALSE,
|
|
|
|
Qnil);
|
2020-10-23 07:27:21 +03:00
|
|
|
}
|
|
|
|
|
2024-01-31 20:17:31 +03:00
|
|
|
/**
|
|
|
|
* The main entry-point into the prism compiler when a file is executed as the
|
|
|
|
* main file in the program.
|
|
|
|
*/
|
|
|
|
rb_iseq_t *
|
|
|
|
pm_iseq_new_main(pm_scope_node_t *node, VALUE path, VALUE realpath, const rb_iseq_t *parent, int opt)
|
|
|
|
{
|
2024-04-25 15:48:20 +03:00
|
|
|
iseq_new_setup_coverage(path, (int) (node->parser->newline_list.size - 1));
|
2024-01-31 20:17:31 +03:00
|
|
|
|
|
|
|
return pm_iseq_new_with_opt(node, rb_fstring_lit("<main>"),
|
|
|
|
path, realpath, 0,
|
|
|
|
parent, 0, ISEQ_TYPE_MAIN, opt ? &COMPILE_OPTION_DEFAULT : &COMPILE_OPTION_FALSE);
|
|
|
|
}
|
|
|
|
|
2020-10-23 07:27:21 +03:00
|
|
|
rb_iseq_t *
|
2024-05-03 02:57:55 +03:00
|
|
|
rb_iseq_new_eval(const VALUE ast_value, VALUE name, VALUE path, VALUE realpath, int first_lineno, const rb_iseq_t *parent, int isolated_depth)
|
2020-10-23 07:27:21 +03:00
|
|
|
{
|
2022-09-28 13:35:42 +03:00
|
|
|
if (rb_get_coverage_mode() & COVERAGE_TARGET_EVAL) {
|
|
|
|
VALUE coverages = rb_get_coverages();
|
|
|
|
if (RTEST(coverages) && RTEST(path) && !RTEST(rb_hash_has_key(coverages, path))) {
|
2024-05-03 02:57:55 +03:00
|
|
|
iseq_setup_coverage(coverages, path, ast_line_count(ast_value) + first_lineno - 1);
|
2022-09-28 13:35:42 +03:00
|
|
|
}
|
2022-09-17 11:19:57 +03:00
|
|
|
}
|
|
|
|
|
2024-05-03 02:57:55 +03:00
|
|
|
return rb_iseq_new_with_opt(ast_value, name, path, realpath, first_lineno,
|
2024-03-28 04:26:42 +03:00
|
|
|
parent, isolated_depth, ISEQ_TYPE_EVAL, &COMPILE_OPTION_DEFAULT,
|
|
|
|
Qnil);
|
* include/ruby/node.h, vm_core.h: move definition of
RUBY_VM_METHOD_NODE to node.h.
* class.c, common.mk: remove useless inclusion.
* compile.h, iseq.h, vm_core.h: rename compile.h to iseq.h.
move some definitions from vm_core.h to iseq.h.
* compile.c, iseq.c, vm.c: ditto.
* eval.c, compile.c: move some functions for parser
from eval.c to compile.c.
* eval_intern.h, vm_core.h: move va_init_list() macro to
vm_core.h.
* iseq.c (rb_iseq_new_top, rb_iseq_first_lineno): added.
* load.c, ruby.c: use rb_iseq_new_top() instead of
rb_iseq_new() with ISEQ_TYPE_TOP constant directly.
* proc.c: use rb_iseq_first_lineno() instead of accessing
iseq structure.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@19472 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2008-09-23 11:49:45 +04:00
|
|
|
}
|
|
|
|
|
2024-01-31 23:56:08 +03:00
|
|
|
rb_iseq_t *
|
|
|
|
pm_iseq_new_eval(pm_scope_node_t *node, VALUE name, VALUE path, VALUE realpath,
|
|
|
|
int first_lineno, const rb_iseq_t *parent, int isolated_depth)
|
|
|
|
{
|
2024-04-25 15:50:23 +03:00
|
|
|
if (rb_get_coverage_mode() & COVERAGE_TARGET_EVAL) {
|
|
|
|
VALUE coverages = rb_get_coverages();
|
|
|
|
if (RTEST(coverages) && RTEST(path) && !RTEST(rb_hash_has_key(coverages, path))) {
|
|
|
|
iseq_setup_coverage(coverages, path, ((int) (node->parser->newline_list.size - 1)) + first_lineno - 1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return pm_iseq_new_with_opt(node, name, path, realpath, first_lineno,
|
|
|
|
parent, isolated_depth, ISEQ_TYPE_EVAL, &COMPILE_OPTION_DEFAULT);
|
2024-01-31 23:56:08 +03:00
|
|
|
}
|
|
|
|
|
2015-12-07 20:23:18 +03:00
|
|
|
static inline rb_iseq_t *
|
|
|
|
iseq_translate(rb_iseq_t *iseq)
|
|
|
|
{
|
|
|
|
if (rb_respond_to(rb_cISeq, rb_intern("translate"))) {
|
|
|
|
VALUE v1 = iseqw_new(iseq);
|
|
|
|
VALUE v2 = rb_funcall(rb_cISeq, rb_intern("translate"), 1, v1);
|
|
|
|
if (v1 != v2 && CLASS_OF(v2) == rb_cISeq) {
|
|
|
|
iseq = (rb_iseq_t *)iseqw_check(v2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return iseq;
|
|
|
|
}
|
|
|
|
|
2015-07-22 01:52:59 +03:00
|
|
|
rb_iseq_t *
|
2024-06-29 08:05:05 +03:00
|
|
|
rb_iseq_new_with_opt(VALUE ast_value, VALUE name, VALUE path, VALUE realpath,
|
2022-09-25 10:45:28 +03:00
|
|
|
int first_lineno, const rb_iseq_t *parent, int isolated_depth,
|
2024-03-28 04:26:42 +03:00
|
|
|
enum rb_iseq_type type, const rb_compile_option_t *option,
|
|
|
|
VALUE script_lines)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2024-05-03 02:57:55 +03:00
|
|
|
rb_ast_t *ast = rb_ruby_ast_data_get(ast_value);
|
[Universal parser] Decouple IMEMO from rb_ast_t
This patch removes the `VALUE flags` member from the `rb_ast_t` structure making `rb_ast_t` no longer an IMEMO object.
## Background
We are trying to make the Ruby parser generated from parse.y a universal parser that can be used by other implementations such as mruby.
To achieve this, it is necessary to exclude VALUE and IMEMO from parse.y, AST, and NODE.
## Summary (file by file)
- `rubyparser.h`
- Remove the `VALUE flags` member from `rb_ast_t`
- `ruby_parser.c` and `internal/ruby_parser.h`
- Use TypedData_Make_Struct VALUE which wraps `rb_ast_t` `in ast_alloc()` so that GC can manage it
- You can retrieve `rb_ast_t` from the VALUE by `rb_ruby_ast_data_get()`
- Change the return type of `rb_parser_compile_XXXX()` functions from `rb_ast_t *` to `VALUE`
- rb_ruby_ast_new() which internally `calls ast_alloc()` is to create VALUE vast outside ruby_parser.c
- `iseq.c` and `vm_core.h`
- Amend the first parameter of `rb_iseq_new_XXXX()` functions from `rb_ast_body_t *` to `VALUE`
- This keeps the VALUE of AST on the machine stack to prevent being removed by GC
- `ast.c`
- Almost all change is replacement `rb_ast_t *ast` with `VALUE vast` (sorry for the big diff)
- Fix `node_memsize()`
- Now it includes `rb_ast_local_table_link`, `tokens` and script_lines
- `compile.c`, `load.c`, `node.c`, `parse.y`, `proc.c`, `ruby.c`, `template/prelude.c.tmpl`, `vm.c` and `vm_eval.c`
- Follow-up due to the above changes
- `imemo.{c|h}`
- If an object with `imemo_ast` appears, considers it a bug
Co-authored-by: Nobuyoshi Nakada <nobu@ruby-lang.org>
2024-04-16 12:42:42 +03:00
|
|
|
rb_ast_body_t *body = ast ? &ast->body : NULL;
|
|
|
|
const NODE *node = body ? body->root : 0;
|
2014-12-05 05:10:29 +03:00
|
|
|
/* TODO: argument check */
|
2015-07-22 01:52:59 +03:00
|
|
|
rb_iseq_t *iseq = iseq_alloc();
|
2018-01-05 11:59:23 +03:00
|
|
|
rb_compile_option_t new_opt;
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2023-09-01 07:39:36 +03:00
|
|
|
if (!option) option = &COMPILE_OPTION_DEFAULT;
|
[Universal parser] Decouple IMEMO from rb_ast_t
This patch removes the `VALUE flags` member from the `rb_ast_t` structure making `rb_ast_t` no longer an IMEMO object.
## Background
We are trying to make the Ruby parser generated from parse.y a universal parser that can be used by other implementations such as mruby.
To achieve this, it is necessary to exclude VALUE and IMEMO from parse.y, AST, and NODE.
## Summary (file by file)
- `rubyparser.h`
- Remove the `VALUE flags` member from `rb_ast_t`
- `ruby_parser.c` and `internal/ruby_parser.h`
- Use TypedData_Make_Struct VALUE which wraps `rb_ast_t` `in ast_alloc()` so that GC can manage it
- You can retrieve `rb_ast_t` from the VALUE by `rb_ruby_ast_data_get()`
- Change the return type of `rb_parser_compile_XXXX()` functions from `rb_ast_t *` to `VALUE`
- rb_ruby_ast_new() which internally `calls ast_alloc()` is to create VALUE vast outside ruby_parser.c
- `iseq.c` and `vm_core.h`
- Amend the first parameter of `rb_iseq_new_XXXX()` functions from `rb_ast_body_t *` to `VALUE`
- This keeps the VALUE of AST on the machine stack to prevent being removed by GC
- `ast.c`
- Almost all change is replacement `rb_ast_t *ast` with `VALUE vast` (sorry for the big diff)
- Fix `node_memsize()`
- Now it includes `rb_ast_local_table_link`, `tokens` and script_lines
- `compile.c`, `load.c`, `node.c`, `parse.y`, `proc.c`, `ruby.c`, `template/prelude.c.tmpl`, `vm.c` and `vm_eval.c`
- Follow-up due to the above changes
- `imemo.{c|h}`
- If an object with `imemo_ast` appears, considers it a bug
Co-authored-by: Nobuyoshi Nakada <nobu@ruby-lang.org>
2024-04-16 12:42:42 +03:00
|
|
|
if (body) {
|
2020-01-31 09:25:09 +03:00
|
|
|
new_opt = *option;
|
[Universal parser] Decouple IMEMO from rb_ast_t
This patch removes the `VALUE flags` member from the `rb_ast_t` structure making `rb_ast_t` no longer an IMEMO object.
## Background
We are trying to make the Ruby parser generated from parse.y a universal parser that can be used by other implementations such as mruby.
To achieve this, it is necessary to exclude VALUE and IMEMO from parse.y, AST, and NODE.
## Summary (file by file)
- `rubyparser.h`
- Remove the `VALUE flags` member from `rb_ast_t`
- `ruby_parser.c` and `internal/ruby_parser.h`
- Use TypedData_Make_Struct VALUE which wraps `rb_ast_t` `in ast_alloc()` so that GC can manage it
- You can retrieve `rb_ast_t` from the VALUE by `rb_ruby_ast_data_get()`
- Change the return type of `rb_parser_compile_XXXX()` functions from `rb_ast_t *` to `VALUE`
- rb_ruby_ast_new() which internally `calls ast_alloc()` is to create VALUE vast outside ruby_parser.c
- `iseq.c` and `vm_core.h`
- Amend the first parameter of `rb_iseq_new_XXXX()` functions from `rb_ast_body_t *` to `VALUE`
- This keeps the VALUE of AST on the machine stack to prevent being removed by GC
- `ast.c`
- Almost all change is replacement `rb_ast_t *ast` with `VALUE vast` (sorry for the big diff)
- Fix `node_memsize()`
- Now it includes `rb_ast_local_table_link`, `tokens` and script_lines
- `compile.c`, `load.c`, `node.c`, `parse.y`, `proc.c`, `ruby.c`, `template/prelude.c.tmpl`, `vm.c` and `vm_eval.c`
- Follow-up due to the above changes
- `imemo.{c|h}`
- If an object with `imemo_ast` appears, considers it a bug
Co-authored-by: Nobuyoshi Nakada <nobu@ruby-lang.org>
2024-04-16 12:42:42 +03:00
|
|
|
option = set_compile_option_from_ast(&new_opt, body);
|
2020-01-31 09:25:09 +03:00
|
|
|
}
|
2018-01-05 11:59:23 +03:00
|
|
|
|
2024-03-28 04:26:42 +03:00
|
|
|
if (!NIL_P(script_lines)) {
|
|
|
|
// noop
|
|
|
|
}
|
2024-04-26 15:43:35 +03:00
|
|
|
else if (body && body->script_lines) {
|
[Universal parser] Decouple IMEMO from rb_ast_t
This patch removes the `VALUE flags` member from the `rb_ast_t` structure making `rb_ast_t` no longer an IMEMO object.
## Background
We are trying to make the Ruby parser generated from parse.y a universal parser that can be used by other implementations such as mruby.
To achieve this, it is necessary to exclude VALUE and IMEMO from parse.y, AST, and NODE.
## Summary (file by file)
- `rubyparser.h`
- Remove the `VALUE flags` member from `rb_ast_t`
- `ruby_parser.c` and `internal/ruby_parser.h`
- Use TypedData_Make_Struct VALUE which wraps `rb_ast_t` `in ast_alloc()` so that GC can manage it
- You can retrieve `rb_ast_t` from the VALUE by `rb_ruby_ast_data_get()`
- Change the return type of `rb_parser_compile_XXXX()` functions from `rb_ast_t *` to `VALUE`
- rb_ruby_ast_new() which internally `calls ast_alloc()` is to create VALUE vast outside ruby_parser.c
- `iseq.c` and `vm_core.h`
- Amend the first parameter of `rb_iseq_new_XXXX()` functions from `rb_ast_body_t *` to `VALUE`
- This keeps the VALUE of AST on the machine stack to prevent being removed by GC
- `ast.c`
- Almost all change is replacement `rb_ast_t *ast` with `VALUE vast` (sorry for the big diff)
- Fix `node_memsize()`
- Now it includes `rb_ast_local_table_link`, `tokens` and script_lines
- `compile.c`, `load.c`, `node.c`, `parse.y`, `proc.c`, `ruby.c`, `template/prelude.c.tmpl`, `vm.c` and `vm_eval.c`
- Follow-up due to the above changes
- `imemo.{c|h}`
- If an object with `imemo_ast` appears, considers it a bug
Co-authored-by: Nobuyoshi Nakada <nobu@ruby-lang.org>
2024-04-16 12:42:42 +03:00
|
|
|
script_lines = rb_parser_build_script_lines_from(body->script_lines);
|
2021-09-30 10:58:46 +03:00
|
|
|
}
|
|
|
|
else if (parent) {
|
2022-03-23 22:19:48 +03:00
|
|
|
script_lines = ISEQ_BODY(parent)->variable.script_lines;
|
2021-09-30 10:58:46 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
prepare_iseq_build(iseq, name, path, realpath, first_lineno, node ? &node->nd_loc : NULL, node ? nd_node_id(node) : -1,
|
2023-09-01 07:39:36 +03:00
|
|
|
parent, isolated_depth, type, script_lines, option);
|
2014-12-05 05:10:29 +03:00
|
|
|
|
2015-07-22 01:52:59 +03:00
|
|
|
rb_iseq_compile_node(iseq, node);
|
2017-11-14 15:58:36 +03:00
|
|
|
finish_iseq_build(iseq);
|
2024-06-29 08:05:05 +03:00
|
|
|
RB_GC_GUARD(ast_value);
|
2015-07-22 01:52:59 +03:00
|
|
|
|
2015-12-07 20:23:18 +03:00
|
|
|
return iseq_translate(iseq);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
2023-11-09 10:39:12 +03:00
|
|
|
/**
|
2024-01-31 20:17:31 +03:00
|
|
|
* This is a step in the prism compiler that is called once all of the various
|
|
|
|
* options have been established. It is called from one of the pm_iseq_new_*
|
|
|
|
* functions or from the RubyVM::InstructionSequence APIs. It is responsible for
|
|
|
|
* allocating the instruction sequence, calling into the compiler, and returning
|
|
|
|
* the built instruction sequence.
|
|
|
|
*
|
|
|
|
* Importantly, this is also the function where the compiler is re-entered to
|
|
|
|
* compile child instruction sequences. A child instruction sequence is always
|
|
|
|
* compiled using a scope node, which is why we cast it explicitly to that here
|
|
|
|
* in the parameters (as opposed to accepting a generic pm_node_t *).
|
2023-11-09 10:39:12 +03:00
|
|
|
*/
|
Compile more YARP node types (#8322)
* Add several more node simple types to YARP's compiler:
Nodes include: DefinedNode, EmbeddedStatementsNode,
LocalVariableReadNode, LocalVariableWriteNode, MultiWriteNode,
OptionalParameterNode, SplatNode, YieldNode
* Add AssocSplatNode, RangeNode
* Add RangeNode, other helpers for future nodes
* Add ArrayNode, HashNode, static literal helpers
* Add branch conditionals
* Add IfNode, UnlessNode
* Add ScopeNode
* NEW_ISEQ and NEW_CHILD_ISEQ implemented for YARP
* Add nodes that depend on ScopeNode
* Addressed PR comments
2023-08-29 23:13:15 +03:00
|
|
|
rb_iseq_t *
|
2024-01-31 20:17:31 +03:00
|
|
|
pm_iseq_new_with_opt(pm_scope_node_t *node, VALUE name, VALUE path, VALUE realpath,
|
Compile more YARP node types (#8322)
* Add several more node simple types to YARP's compiler:
Nodes include: DefinedNode, EmbeddedStatementsNode,
LocalVariableReadNode, LocalVariableWriteNode, MultiWriteNode,
OptionalParameterNode, SplatNode, YieldNode
* Add AssocSplatNode, RangeNode
* Add RangeNode, other helpers for future nodes
* Add ArrayNode, HashNode, static literal helpers
* Add branch conditionals
* Add IfNode, UnlessNode
* Add ScopeNode
* NEW_ISEQ and NEW_CHILD_ISEQ implemented for YARP
* Add nodes that depend on ScopeNode
* Addressed PR comments
2023-08-29 23:13:15 +03:00
|
|
|
int first_lineno, const rb_iseq_t *parent, int isolated_depth,
|
|
|
|
enum rb_iseq_type type, const rb_compile_option_t *option)
|
|
|
|
{
|
|
|
|
rb_iseq_t *iseq = iseq_alloc();
|
2024-02-12 22:40:07 +03:00
|
|
|
ISEQ_BODY(iseq)->prism = true;
|
|
|
|
|
2024-05-20 18:29:50 +03:00
|
|
|
rb_compile_option_t next_option;
|
2023-09-01 07:39:36 +03:00
|
|
|
if (!option) option = &COMPILE_OPTION_DEFAULT;
|
|
|
|
|
2024-05-20 18:29:50 +03:00
|
|
|
next_option = *option;
|
|
|
|
next_option.coverage_enabled = node->coverage_enabled < 0 ? 0 : node->coverage_enabled > 0;
|
|
|
|
option = &next_option;
|
|
|
|
|
2024-01-31 20:17:31 +03:00
|
|
|
pm_location_t *location = &node->base.location;
|
2024-02-14 22:17:32 +03:00
|
|
|
int32_t start_line = node->parser->start_line;
|
|
|
|
|
|
|
|
pm_line_column_t start = pm_newline_list_line_column(&node->parser->newline_list, location->start, start_line);
|
|
|
|
pm_line_column_t end = pm_newline_list_line_column(&node->parser->newline_list, location->end, start_line);
|
2024-01-31 20:17:31 +03:00
|
|
|
|
|
|
|
rb_code_location_t code_location = (rb_code_location_t) {
|
|
|
|
.beg_pos = { .lineno = (int) start.line, .column = (int) start.column },
|
|
|
|
.end_pos = { .lineno = (int) end.line, .column = (int) end.column }
|
|
|
|
};
|
Compile more YARP node types (#8322)
* Add several more node simple types to YARP's compiler:
Nodes include: DefinedNode, EmbeddedStatementsNode,
LocalVariableReadNode, LocalVariableWriteNode, MultiWriteNode,
OptionalParameterNode, SplatNode, YieldNode
* Add AssocSplatNode, RangeNode
* Add RangeNode, other helpers for future nodes
* Add ArrayNode, HashNode, static literal helpers
* Add branch conditionals
* Add IfNode, UnlessNode
* Add ScopeNode
* NEW_ISEQ and NEW_CHILD_ISEQ implemented for YARP
* Add nodes that depend on ScopeNode
* Addressed PR comments
2023-08-29 23:13:15 +03:00
|
|
|
|
2024-01-31 20:17:31 +03:00
|
|
|
prepare_iseq_build(iseq, name, path, realpath, first_lineno, &code_location, -1,
|
2024-08-29 21:26:08 +03:00
|
|
|
parent, isolated_depth, type, node->script_lines == NULL ? Qnil : *node->script_lines, option);
|
Compile more YARP node types (#8322)
* Add several more node simple types to YARP's compiler:
Nodes include: DefinedNode, EmbeddedStatementsNode,
LocalVariableReadNode, LocalVariableWriteNode, MultiWriteNode,
OptionalParameterNode, SplatNode, YieldNode
* Add AssocSplatNode, RangeNode
* Add RangeNode, other helpers for future nodes
* Add ArrayNode, HashNode, static literal helpers
* Add branch conditionals
* Add IfNode, UnlessNode
* Add ScopeNode
* NEW_ISEQ and NEW_CHILD_ISEQ implemented for YARP
* Add nodes that depend on ScopeNode
* Addressed PR comments
2023-08-29 23:13:15 +03:00
|
|
|
|
2024-01-31 20:17:31 +03:00
|
|
|
pm_iseq_compile_node(iseq, node);
|
Compile more YARP node types (#8322)
* Add several more node simple types to YARP's compiler:
Nodes include: DefinedNode, EmbeddedStatementsNode,
LocalVariableReadNode, LocalVariableWriteNode, MultiWriteNode,
OptionalParameterNode, SplatNode, YieldNode
* Add AssocSplatNode, RangeNode
* Add RangeNode, other helpers for future nodes
* Add ArrayNode, HashNode, static literal helpers
* Add branch conditionals
* Add IfNode, UnlessNode
* Add ScopeNode
* NEW_ISEQ and NEW_CHILD_ISEQ implemented for YARP
* Add nodes that depend on ScopeNode
* Addressed PR comments
2023-08-29 23:13:15 +03:00
|
|
|
finish_iseq_build(iseq);
|
|
|
|
|
|
|
|
return iseq_translate(iseq);
|
|
|
|
}
|
|
|
|
|
2018-01-04 10:07:49 +03:00
|
|
|
rb_iseq_t *
|
2019-08-26 08:25:53 +03:00
|
|
|
rb_iseq_new_with_callback(
|
|
|
|
const struct rb_iseq_new_with_callback_callback_func * ifunc,
|
|
|
|
VALUE name, VALUE path, VALUE realpath,
|
2022-09-25 10:45:28 +03:00
|
|
|
int first_lineno, const rb_iseq_t *parent,
|
2022-07-22 10:57:25 +03:00
|
|
|
enum rb_iseq_type type, const rb_compile_option_t *option)
|
2018-01-04 10:07:49 +03:00
|
|
|
{
|
|
|
|
/* TODO: argument check */
|
|
|
|
rb_iseq_t *iseq = iseq_alloc();
|
|
|
|
|
|
|
|
if (!option) option = &COMPILE_OPTION_DEFAULT;
|
2021-09-30 10:58:46 +03:00
|
|
|
prepare_iseq_build(iseq, name, path, realpath, first_lineno, NULL, -1, parent, 0, type, Qnil, option);
|
2018-01-04 10:07:49 +03:00
|
|
|
|
2019-08-26 08:25:53 +03:00
|
|
|
rb_iseq_compile_callback(iseq, ifunc);
|
2018-01-04 10:07:49 +03:00
|
|
|
finish_iseq_build(iseq);
|
|
|
|
|
2020-06-17 01:18:35 +03:00
|
|
|
return iseq;
|
2018-01-04 10:07:49 +03:00
|
|
|
}
|
|
|
|
|
2015-12-08 16:58:50 +03:00
|
|
|
const rb_iseq_t *
|
|
|
|
rb_iseq_load_iseq(VALUE fname)
|
|
|
|
{
|
2015-12-12 10:07:42 +03:00
|
|
|
VALUE iseqv = rb_check_funcall(rb_cISeq, rb_intern("load_iseq"), 1, &fname);
|
|
|
|
|
|
|
|
if (!SPECIAL_CONST_P(iseqv) && RBASIC_CLASS(iseqv) == rb_cISeq) {
|
|
|
|
return iseqw_check(iseqv);
|
2015-12-08 16:58:50 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2017-10-26 10:23:23 +03:00
|
|
|
#define CHECK_ARRAY(v) rb_to_array_type(v)
|
|
|
|
#define CHECK_HASH(v) rb_to_hash_type(v)
|
|
|
|
#define CHECK_STRING(v) rb_str_to_str(v)
|
|
|
|
#define CHECK_SYMBOL(v) rb_to_symbol_type(v)
|
2010-07-27 11:13:43 +04:00
|
|
|
static inline VALUE CHECK_INTEGER(VALUE v) {(void)NUM2LONG(v); return v;}
|
2014-01-12 11:49:26 +04:00
|
|
|
|
2022-07-22 10:57:25 +03:00
|
|
|
static enum rb_iseq_type
|
2014-07-28 12:15:42 +04:00
|
|
|
iseq_type_from_sym(VALUE type)
|
2014-01-12 11:49:26 +04:00
|
|
|
{
|
2014-07-28 12:15:42 +04:00
|
|
|
const ID id_top = rb_intern("top");
|
|
|
|
const ID id_method = rb_intern("method");
|
|
|
|
const ID id_block = rb_intern("block");
|
|
|
|
const ID id_class = rb_intern("class");
|
|
|
|
const ID id_rescue = rb_intern("rescue");
|
|
|
|
const ID id_ensure = rb_intern("ensure");
|
|
|
|
const ID id_eval = rb_intern("eval");
|
|
|
|
const ID id_main = rb_intern("main");
|
2018-01-05 03:49:41 +03:00
|
|
|
const ID id_plain = rb_intern("plain");
|
2014-07-28 12:15:42 +04:00
|
|
|
/* ensure all symbols are static or pinned down before
|
|
|
|
* conversion */
|
|
|
|
const ID typeid = rb_check_id(&type);
|
|
|
|
if (typeid == id_top) return ISEQ_TYPE_TOP;
|
|
|
|
if (typeid == id_method) return ISEQ_TYPE_METHOD;
|
|
|
|
if (typeid == id_block) return ISEQ_TYPE_BLOCK;
|
|
|
|
if (typeid == id_class) return ISEQ_TYPE_CLASS;
|
|
|
|
if (typeid == id_rescue) return ISEQ_TYPE_RESCUE;
|
|
|
|
if (typeid == id_ensure) return ISEQ_TYPE_ENSURE;
|
|
|
|
if (typeid == id_eval) return ISEQ_TYPE_EVAL;
|
|
|
|
if (typeid == id_main) return ISEQ_TYPE_MAIN;
|
2018-01-05 03:49:41 +03:00
|
|
|
if (typeid == id_plain) return ISEQ_TYPE_PLAIN;
|
2022-07-22 10:57:25 +03:00
|
|
|
return (enum rb_iseq_type)-1;
|
2014-01-12 11:49:26 +04:00
|
|
|
}
|
|
|
|
|
2008-12-05 06:35:48 +03:00
|
|
|
static VALUE
|
2015-07-22 01:52:59 +03:00
|
|
|
iseq_load(VALUE data, const rb_iseq_t *parent, VALUE opt)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2015-07-22 01:52:59 +03:00
|
|
|
rb_iseq_t *iseq = iseq_alloc();
|
2006-12-31 18:02:22 +03:00
|
|
|
|
|
|
|
VALUE magic, version1, version2, format_type, misc;
|
2022-09-25 10:45:28 +03:00
|
|
|
VALUE name, path, realpath, code_location, node_id;
|
2014-12-04 01:16:58 +03:00
|
|
|
VALUE type, body, locals, params, exception;
|
2006-12-31 18:02:22 +03:00
|
|
|
|
* compile.c (iseq_build_body), error.c (set_syserr, get_syserr),
(syserr_initialize), gc.c (define_final, rb_gc_copy_finalizer),
(run_final), hash.c (rb_hash_aref, rb_hash_lookup2),
(rb_hash_fetch_m, rb_hash_clear, rb_hash_aset, eql_i),
iseq.c (iseq_load, iseq_data_to_ary), marshal.c (r_symlink),
thread.c (rb_thread_local_aref),
variable.c (generic_ivar_remove, ivar_get, rb_const_get_0),
(rb_cvar_get), vm.c (rb_vm_check_redefinition_opt_method),
vm_insnhelper.c (vm_get_ev_const), vm_method.c (remove_method),
ext/iconv/iconv.c (map_charset): use st_data_t.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@29462 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2010-10-12 18:47:23 +04:00
|
|
|
st_data_t iseq_type;
|
* blockinlining.c, compile.c, compile.h, error.c, eval.c,
eval_intern.h, eval_jump.h, eval_load.c, eval_method.h,
eval_safe.h, gc.c, insnhelper.h, insns.def, iseq.c, proc.c,
process.c, signal.c, thread.c, thread_pthread.ci, thread_win32.ci,
vm.c, vm.h, vm_dump.c, vm_evalbody.ci, vm_macro.def,
yarv.h, yarvcore.h, yarvcore.c: change type and macro names:
* yarv_*_t -> rb_*_t
* yarv_*_struct -> rb_*_struct
* yarv_tag -> rb_vm_tag
* YARV_* -> RUBY_VM_*
* proc.c, vm.c: move functions about env object creation
from proc.c to vm.c.
* proc.c, yarvcore.c: fix rb_cVM initialization place.
* inits.c: change Init_ISeq() order (after Init_VM).
* ruby.h, proc.c: change declaration place of rb_cEnv
from proc.c to ruby.c.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@11651 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-02-06 22:00:03 +03:00
|
|
|
rb_compile_option_t option;
|
2008-01-08 07:05:59 +03:00
|
|
|
int i = 0;
|
2018-01-09 11:45:35 +03:00
|
|
|
rb_code_location_t tmp_loc = { {0, 0}, {-1, -1} };
|
2006-12-31 18:02:22 +03:00
|
|
|
|
|
|
|
/* [magic, major_version, minor_version, format_type, misc,
|
2012-06-04 06:49:37 +04:00
|
|
|
* label, path, first_lineno,
|
2006-12-31 18:02:22 +03:00
|
|
|
* type, locals, args, exception_table, body]
|
|
|
|
*/
|
|
|
|
|
|
|
|
data = CHECK_ARRAY(data);
|
2007-05-11 10:26:06 +04:00
|
|
|
|
2008-01-08 07:05:59 +03:00
|
|
|
magic = CHECK_STRING(rb_ary_entry(data, i++));
|
|
|
|
version1 = CHECK_INTEGER(rb_ary_entry(data, i++));
|
|
|
|
version2 = CHECK_INTEGER(rb_ary_entry(data, i++));
|
|
|
|
format_type = CHECK_INTEGER(rb_ary_entry(data, i++));
|
2014-12-04 01:16:58 +03:00
|
|
|
misc = CHECK_HASH(rb_ary_entry(data, i++));
|
|
|
|
((void)magic, (void)version1, (void)version2, (void)format_type);
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2008-01-08 07:05:59 +03:00
|
|
|
name = CHECK_STRING(rb_ary_entry(data, i++));
|
2012-06-04 06:49:37 +04:00
|
|
|
path = CHECK_STRING(rb_ary_entry(data, i++));
|
2017-06-01 03:05:33 +03:00
|
|
|
realpath = rb_ary_entry(data, i++);
|
|
|
|
realpath = NIL_P(realpath) ? Qnil : CHECK_STRING(realpath);
|
2022-09-25 10:45:28 +03:00
|
|
|
int first_lineno = RB_NUM2INT(rb_ary_entry(data, i++));
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2008-01-08 07:05:59 +03:00
|
|
|
type = CHECK_SYMBOL(rb_ary_entry(data, i++));
|
|
|
|
locals = CHECK_ARRAY(rb_ary_entry(data, i++));
|
2014-12-04 01:16:58 +03:00
|
|
|
params = CHECK_HASH(rb_ary_entry(data, i++));
|
2008-01-08 07:05:59 +03:00
|
|
|
exception = CHECK_ARRAY(rb_ary_entry(data, i++));
|
|
|
|
body = CHECK_ARRAY(rb_ary_entry(data, i++));
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2022-03-23 22:19:48 +03:00
|
|
|
ISEQ_BODY(iseq)->local_iseq = iseq;
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2014-07-28 12:15:42 +04:00
|
|
|
iseq_type = iseq_type_from_sym(type);
|
2022-07-22 10:57:25 +03:00
|
|
|
if (iseq_type == (enum rb_iseq_type)-1) {
|
2019-12-20 03:19:39 +03:00
|
|
|
rb_raise(rb_eTypeError, "unsupported type: :%"PRIsVALUE, rb_sym2str(type));
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
2018-11-05 05:13:45 +03:00
|
|
|
node_id = rb_hash_aref(misc, ID2SYM(rb_intern("node_id")));
|
|
|
|
|
2018-01-09 11:45:35 +03:00
|
|
|
code_location = rb_hash_aref(misc, ID2SYM(rb_intern("code_location")));
|
|
|
|
if (RB_TYPE_P(code_location, T_ARRAY) && RARRAY_LEN(code_location) == 4) {
|
|
|
|
tmp_loc.beg_pos.lineno = NUM2INT(rb_ary_entry(code_location, 0));
|
|
|
|
tmp_loc.beg_pos.column = NUM2INT(rb_ary_entry(code_location, 1));
|
|
|
|
tmp_loc.end_pos.lineno = NUM2INT(rb_ary_entry(code_location, 2));
|
|
|
|
tmp_loc.end_pos.column = NUM2INT(rb_ary_entry(code_location, 3));
|
2017-12-05 11:56:50 +03:00
|
|
|
}
|
|
|
|
|
2024-04-02 17:26:02 +03:00
|
|
|
if (SYM2ID(rb_hash_aref(misc, ID2SYM(rb_intern("parser")))) == rb_intern("prism")) {
|
2024-02-12 22:40:07 +03:00
|
|
|
ISEQ_BODY(iseq)->prism = true;
|
|
|
|
}
|
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
make_compile_option(&option, opt);
|
2015-12-07 21:46:27 +03:00
|
|
|
option.peephole_optimization = FALSE; /* because peephole optimization can modify original iseq */
|
2018-11-05 05:13:45 +03:00
|
|
|
prepare_iseq_build(iseq, name, path, realpath, first_lineno, &tmp_loc, NUM2INT(node_id),
|
2022-07-22 10:57:25 +03:00
|
|
|
parent, 0, (enum rb_iseq_type)iseq_type, Qnil, &option);
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2014-12-04 01:16:58 +03:00
|
|
|
rb_iseq_build_from_ary(iseq, misc, locals, params, exception, body);
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2017-11-14 15:58:36 +03:00
|
|
|
finish_iseq_build(iseq);
|
2015-07-22 01:52:59 +03:00
|
|
|
|
|
|
|
return iseqw_new(iseq);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
2012-07-25 01:29:24 +04:00
|
|
|
/*
|
|
|
|
* :nodoc:
|
|
|
|
*/
|
2006-12-31 18:02:22 +03:00
|
|
|
static VALUE
|
|
|
|
iseq_s_load(int argc, VALUE *argv, VALUE self)
|
|
|
|
{
|
|
|
|
VALUE data, opt=Qnil;
|
2015-12-08 17:05:44 +03:00
|
|
|
rb_scan_args(argc, argv, "11", &data, &opt);
|
2015-07-22 01:52:59 +03:00
|
|
|
return iseq_load(data, NULL, opt);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
2008-12-05 06:35:48 +03:00
|
|
|
VALUE
|
* iseq.c (rb_iseq_load): renamed from ruby_iseq_load, since it is
for C extensions or the ruby core. [ruby-core:21407]
Index: compile.c
===================================================================
--- compile.c (revision 21649)
+++ compile.c (working copy)
@@ -5078,5 +5078,5 @@ iseq_build_exception(rb_iseq_t *iseq, st
}
else {
- eiseqval = ruby_iseq_load(ptr[1], iseq->self, Qnil);
+ eiseqval = rb_iseq_load(ptr[1], iseq->self, Qnil);
}
@@ -5162,5 +5162,5 @@ iseq_build_body(rb_iseq_t *iseq, LINK_AN
if (op != Qnil) {
if (TYPE(op) == T_ARRAY) {
- argv[j] = ruby_iseq_load(op, iseq->self, Qnil);
+ argv[j] = rb_iseq_load(op, iseq->self, Qnil);
}
else if (CLASS_OF(op) == rb_cISeq) {
Index: iseq.c
===================================================================
--- iseq.c (revision 21649)
+++ iseq.c (working copy)
@@ -448,5 +448,5 @@ iseq_s_load(int argc, VALUE *argv, VALUE
VALUE
-ruby_iseq_load(VALUE data, VALUE parent, VALUE opt)
+rb_iseq_load(VALUE data, VALUE parent, VALUE opt)
{
return iseq_load(rb_cISeq, data, parent, opt);
Index: iseq.h
===================================================================
--- iseq.h (revision 21649)
+++ iseq.h (working copy)
@@ -21,5 +21,5 @@ VALUE ruby_iseq_build_from_ary(rb_iseq_t
/* iseq.c */
-VALUE ruby_iseq_load(VALUE data, VALUE parent, VALUE opt);
+VALUE rb_iseq_load(VALUE data, VALUE parent, VALUE opt);
struct st_table *ruby_insn_make_insn_table(void);
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@21650 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2009-01-18 22:05:15 +03:00
|
|
|
rb_iseq_load(VALUE data, VALUE parent, VALUE opt)
|
2008-12-05 06:35:48 +03:00
|
|
|
{
|
2015-07-22 01:52:59 +03:00
|
|
|
return iseq_load(data, RTEST(parent) ? (rb_iseq_t *)parent : NULL, opt);
|
2008-12-05 06:35:48 +03:00
|
|
|
}
|
|
|
|
|
2019-11-18 06:13:08 +03:00
|
|
|
static rb_iseq_t *
|
2019-10-04 15:30:32 +03:00
|
|
|
rb_iseq_compile_with_option(VALUE src, VALUE file, VALUE realpath, VALUE line, VALUE opt)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2015-07-22 01:52:59 +03:00
|
|
|
rb_iseq_t *iseq = NULL;
|
2015-12-24 17:15:14 +03:00
|
|
|
rb_compile_option_t option;
|
2016-01-08 13:52:24 +03:00
|
|
|
#if !defined(__GNUC__) || (__GNUC__ == 4 && __GNUC_MINOR__ == 8)
|
2016-01-08 10:47:49 +03:00
|
|
|
# define INITIALIZED volatile /* suppress warnings by gcc 4.8 */
|
|
|
|
#else
|
2016-01-08 13:52:24 +03:00
|
|
|
# define INITIALIZED /* volatile */
|
2016-01-08 10:47:49 +03:00
|
|
|
#endif
|
[Universal parser] Decouple IMEMO from rb_ast_t
This patch removes the `VALUE flags` member from the `rb_ast_t` structure making `rb_ast_t` no longer an IMEMO object.
## Background
We are trying to make the Ruby parser generated from parse.y a universal parser that can be used by other implementations such as mruby.
To achieve this, it is necessary to exclude VALUE and IMEMO from parse.y, AST, and NODE.
## Summary (file by file)
- `rubyparser.h`
- Remove the `VALUE flags` member from `rb_ast_t`
- `ruby_parser.c` and `internal/ruby_parser.h`
- Use TypedData_Make_Struct VALUE which wraps `rb_ast_t` `in ast_alloc()` so that GC can manage it
- You can retrieve `rb_ast_t` from the VALUE by `rb_ruby_ast_data_get()`
- Change the return type of `rb_parser_compile_XXXX()` functions from `rb_ast_t *` to `VALUE`
- rb_ruby_ast_new() which internally `calls ast_alloc()` is to create VALUE vast outside ruby_parser.c
- `iseq.c` and `vm_core.h`
- Amend the first parameter of `rb_iseq_new_XXXX()` functions from `rb_ast_body_t *` to `VALUE`
- This keeps the VALUE of AST on the machine stack to prevent being removed by GC
- `ast.c`
- Almost all change is replacement `rb_ast_t *ast` with `VALUE vast` (sorry for the big diff)
- Fix `node_memsize()`
- Now it includes `rb_ast_local_table_link`, `tokens` and script_lines
- `compile.c`, `load.c`, `node.c`, `parse.y`, `proc.c`, `ruby.c`, `template/prelude.c.tmpl`, `vm.c` and `vm_eval.c`
- Follow-up due to the above changes
- `imemo.{c|h}`
- If an object with `imemo_ast` appears, considers it a bug
Co-authored-by: Nobuyoshi Nakada <nobu@ruby-lang.org>
2024-04-16 12:42:42 +03:00
|
|
|
VALUE (*parse)(VALUE vparser, VALUE fname, VALUE file, int start);
|
2016-03-29 00:39:24 +03:00
|
|
|
int ln;
|
2024-05-03 02:57:55 +03:00
|
|
|
VALUE INITIALIZED ast_value;
|
[Universal parser] Decouple IMEMO from rb_ast_t
This patch removes the `VALUE flags` member from the `rb_ast_t` structure making `rb_ast_t` no longer an IMEMO object.
## Background
We are trying to make the Ruby parser generated from parse.y a universal parser that can be used by other implementations such as mruby.
To achieve this, it is necessary to exclude VALUE and IMEMO from parse.y, AST, and NODE.
## Summary (file by file)
- `rubyparser.h`
- Remove the `VALUE flags` member from `rb_ast_t`
- `ruby_parser.c` and `internal/ruby_parser.h`
- Use TypedData_Make_Struct VALUE which wraps `rb_ast_t` `in ast_alloc()` so that GC can manage it
- You can retrieve `rb_ast_t` from the VALUE by `rb_ruby_ast_data_get()`
- Change the return type of `rb_parser_compile_XXXX()` functions from `rb_ast_t *` to `VALUE`
- rb_ruby_ast_new() which internally `calls ast_alloc()` is to create VALUE vast outside ruby_parser.c
- `iseq.c` and `vm_core.h`
- Amend the first parameter of `rb_iseq_new_XXXX()` functions from `rb_ast_body_t *` to `VALUE`
- This keeps the VALUE of AST on the machine stack to prevent being removed by GC
- `ast.c`
- Almost all change is replacement `rb_ast_t *ast` with `VALUE vast` (sorry for the big diff)
- Fix `node_memsize()`
- Now it includes `rb_ast_local_table_link`, `tokens` and script_lines
- `compile.c`, `load.c`, `node.c`, `parse.y`, `proc.c`, `ruby.c`, `template/prelude.c.tmpl`, `vm.c` and `vm_eval.c`
- Follow-up due to the above changes
- `imemo.{c|h}`
- If an object with `imemo_ast` appears, considers it a bug
Co-authored-by: Nobuyoshi Nakada <nobu@ruby-lang.org>
2024-04-16 12:42:42 +03:00
|
|
|
rb_ast_t *ast;
|
2022-06-17 17:27:16 +03:00
|
|
|
VALUE name = rb_fstring_lit("<compiled>");
|
2016-03-29 00:39:24 +03:00
|
|
|
|
2016-01-08 10:47:49 +03:00
|
|
|
/* safe results first */
|
2016-03-29 00:39:24 +03:00
|
|
|
make_compile_option(&option, opt);
|
|
|
|
ln = NUM2INT(line);
|
|
|
|
StringValueCStr(file);
|
|
|
|
if (RB_TYPE_P(src, T_FILE)) {
|
|
|
|
parse = rb_parser_compile_file_path;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
parse = rb_parser_compile_string_path;
|
|
|
|
StringValue(src);
|
|
|
|
}
|
|
|
|
{
|
|
|
|
const VALUE parser = rb_parser_new();
|
[Universal parser] Decouple IMEMO from rb_ast_t
This patch removes the `VALUE flags` member from the `rb_ast_t` structure making `rb_ast_t` no longer an IMEMO object.
## Background
We are trying to make the Ruby parser generated from parse.y a universal parser that can be used by other implementations such as mruby.
To achieve this, it is necessary to exclude VALUE and IMEMO from parse.y, AST, and NODE.
## Summary (file by file)
- `rubyparser.h`
- Remove the `VALUE flags` member from `rb_ast_t`
- `ruby_parser.c` and `internal/ruby_parser.h`
- Use TypedData_Make_Struct VALUE which wraps `rb_ast_t` `in ast_alloc()` so that GC can manage it
- You can retrieve `rb_ast_t` from the VALUE by `rb_ruby_ast_data_get()`
- Change the return type of `rb_parser_compile_XXXX()` functions from `rb_ast_t *` to `VALUE`
- rb_ruby_ast_new() which internally `calls ast_alloc()` is to create VALUE vast outside ruby_parser.c
- `iseq.c` and `vm_core.h`
- Amend the first parameter of `rb_iseq_new_XXXX()` functions from `rb_ast_body_t *` to `VALUE`
- This keeps the VALUE of AST on the machine stack to prevent being removed by GC
- `ast.c`
- Almost all change is replacement `rb_ast_t *ast` with `VALUE vast` (sorry for the big diff)
- Fix `node_memsize()`
- Now it includes `rb_ast_local_table_link`, `tokens` and script_lines
- `compile.c`, `load.c`, `node.c`, `parse.y`, `proc.c`, `ruby.c`, `template/prelude.c.tmpl`, `vm.c` and `vm_eval.c`
- Follow-up due to the above changes
- `imemo.{c|h}`
- If an object with `imemo_ast` appears, considers it a bug
Co-authored-by: Nobuyoshi Nakada <nobu@ruby-lang.org>
2024-04-16 12:42:42 +03:00
|
|
|
const rb_iseq_t *outer_scope = rb_iseq_new(Qnil, name, name, Qnil, 0, ISEQ_TYPE_TOP);
|
2019-10-03 20:35:10 +03:00
|
|
|
VALUE outer_scope_v = (VALUE)outer_scope;
|
|
|
|
rb_parser_set_context(parser, outer_scope, FALSE);
|
2024-03-28 04:26:42 +03:00
|
|
|
if (ruby_vm_keep_script_lines) rb_parser_set_script_lines(parser);
|
2019-10-03 20:35:10 +03:00
|
|
|
RB_GC_GUARD(outer_scope_v);
|
2024-05-03 02:57:55 +03:00
|
|
|
ast_value = (*parse)(parser, file, src, ln);
|
2016-03-29 00:39:24 +03:00
|
|
|
}
|
|
|
|
|
2024-05-03 02:57:55 +03:00
|
|
|
ast = rb_ruby_ast_data_get(ast_value);
|
[Universal parser] Decouple IMEMO from rb_ast_t
This patch removes the `VALUE flags` member from the `rb_ast_t` structure making `rb_ast_t` no longer an IMEMO object.
## Background
We are trying to make the Ruby parser generated from parse.y a universal parser that can be used by other implementations such as mruby.
To achieve this, it is necessary to exclude VALUE and IMEMO from parse.y, AST, and NODE.
## Summary (file by file)
- `rubyparser.h`
- Remove the `VALUE flags` member from `rb_ast_t`
- `ruby_parser.c` and `internal/ruby_parser.h`
- Use TypedData_Make_Struct VALUE which wraps `rb_ast_t` `in ast_alloc()` so that GC can manage it
- You can retrieve `rb_ast_t` from the VALUE by `rb_ruby_ast_data_get()`
- Change the return type of `rb_parser_compile_XXXX()` functions from `rb_ast_t *` to `VALUE`
- rb_ruby_ast_new() which internally `calls ast_alloc()` is to create VALUE vast outside ruby_parser.c
- `iseq.c` and `vm_core.h`
- Amend the first parameter of `rb_iseq_new_XXXX()` functions from `rb_ast_body_t *` to `VALUE`
- This keeps the VALUE of AST on the machine stack to prevent being removed by GC
- `ast.c`
- Almost all change is replacement `rb_ast_t *ast` with `VALUE vast` (sorry for the big diff)
- Fix `node_memsize()`
- Now it includes `rb_ast_local_table_link`, `tokens` and script_lines
- `compile.c`, `load.c`, `node.c`, `parse.y`, `proc.c`, `ruby.c`, `template/prelude.c.tmpl`, `vm.c` and `vm_eval.c`
- Follow-up due to the above changes
- `imemo.{c|h}`
- If an object with `imemo_ast` appears, considers it a bug
Co-authored-by: Nobuyoshi Nakada <nobu@ruby-lang.org>
2024-04-16 12:42:42 +03:00
|
|
|
|
|
|
|
if (!ast || !ast->body.root) {
|
2017-10-27 19:44:57 +03:00
|
|
|
rb_ast_dispose(ast);
|
2017-11-07 08:08:09 +03:00
|
|
|
rb_exc_raise(GET_EC()->errinfo);
|
2016-03-29 00:39:24 +03:00
|
|
|
}
|
|
|
|
else {
|
2024-05-03 02:57:55 +03:00
|
|
|
iseq = rb_iseq_new_with_opt(ast_value, name, file, realpath, ln,
|
2024-03-28 04:26:42 +03:00
|
|
|
NULL, 0, ISEQ_TYPE_TOP, &option,
|
|
|
|
Qnil);
|
2017-10-27 19:44:57 +03:00
|
|
|
rb_ast_dispose(ast);
|
* blockinlining.c: remove "yarv" prefix.
* array.c, numeric.c: ditto.
* insnhelper.ci, insns.def, vm_evalbody.ci: ditto.
* yarvcore.c: removed.
* yarvcore.h: renamed to core.h.
* cont.c, debug.c, error.c, process.c, signal.c : ditto.
* ext/probeprofiler/probeprofiler.c: ditto.
* id.c, id.h: added.
* inits.c: ditto.
* compile.c: rename internal functions.
* compile.h: fix debug flag.
* eval.c, object.c, vm.c: remove ruby_top_self.
use rb_vm_top_self() instead.
* eval_intern.h, eval_load: ditto.
* gc.c: rename yarv_machine_stack_mark() to
rb_gc_mark_machine_stack().
* insnhelper.h: remove unused macros.
* iseq.c: add iseq_compile() to create iseq object
from source string.
* proc.c: rename a internal function.
* template/insns.inc.tmpl: remove YARV prefix.
* thread.c:
* vm.c (rb_iseq_eval): added.
* vm.c: move some functions from yarvcore.c.
* vm_dump.c: fix to remove compiler warning.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12741 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-07-12 08:25:46 +04:00
|
|
|
}
|
* iseq.c, vm_eval.c: set th->base_block properly.
th->base_block is information for (a) parsing, (b) compiling
and (c) setting up the frame to execute the program passed by
`eval' method. For example, (1) parser need to know up-level
variables to detect it is variable or method without paren.
Befor (a), (b) and (c), VM set th->base_block by passed bindng
(or previous frame information). After execute (a), (b) and (c),
VM should clear th->base_block. However, if (a), (b) or (c)
raises an exception, then th->base_block is not cleared.
Problem is that the uncleared value th->balo_block is used for
irrelevant iseq compilation. It causes SEGV or critical error.
I tried to solve this problem: to clear them before exception,
but finally I found out that it is difficult to do it (Ruby
program can be run in many places).
Because of this background, I set th->base_block before
compiling iseq and restore it after compiling.
Basically, th->base_block is dirty hack (similar to global
variable) and this patch is also dirty.
* bootstraptest/test_eval.rb: add a test for above.
* internal.h: remove unused decl.
* iseq.c (rb_iseq_compile_with_option): add base_block parameter.
set th->base_block before compation and restore it after
compilation.
* ruby.c (require_libraries): pass 0 as base_block instead of
setting th->base_block
* tool/compile_prelude.rb (prelude_eval): apply above changes.
* vm.c, vm_eval.c: ditto.
* vm_core.h: add comments.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@36179 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-06-22 13:32:56 +04:00
|
|
|
|
2015-07-22 01:52:59 +03:00
|
|
|
return iseq;
|
* blockinlining.c: remove "yarv" prefix.
* array.c, numeric.c: ditto.
* insnhelper.ci, insns.def, vm_evalbody.ci: ditto.
* yarvcore.c: removed.
* yarvcore.h: renamed to core.h.
* cont.c, debug.c, error.c, process.c, signal.c : ditto.
* ext/probeprofiler/probeprofiler.c: ditto.
* id.c, id.h: added.
* inits.c: ditto.
* compile.c: rename internal functions.
* compile.h: fix debug flag.
* eval.c, object.c, vm.c: remove ruby_top_self.
use rb_vm_top_self() instead.
* eval_intern.h, eval_load: ditto.
* gc.c: rename yarv_machine_stack_mark() to
rb_gc_mark_machine_stack().
* insnhelper.h: remove unused macros.
* iseq.c: add iseq_compile() to create iseq object
from source string.
* proc.c: rename a internal function.
* template/insns.inc.tmpl: remove YARV prefix.
* thread.c:
* vm.c (rb_iseq_eval): added.
* vm.c: move some functions from yarvcore.c.
* vm_dump.c: fix to remove compiler warning.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12741 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-07-12 08:25:46 +04:00
|
|
|
}
|
|
|
|
|
2024-01-31 20:17:31 +03:00
|
|
|
static rb_iseq_t *
|
|
|
|
pm_iseq_compile_with_option(VALUE src, VALUE file, VALUE realpath, VALUE line, VALUE opt)
|
|
|
|
{
|
|
|
|
rb_iseq_t *iseq = NULL;
|
|
|
|
rb_compile_option_t option;
|
|
|
|
int ln;
|
|
|
|
VALUE name = rb_fstring_lit("<compiled>");
|
|
|
|
|
|
|
|
/* safe results first */
|
|
|
|
make_compile_option(&option, opt);
|
|
|
|
ln = NUM2INT(line);
|
|
|
|
StringValueCStr(file);
|
|
|
|
|
|
|
|
pm_parse_result_t result = { 0 };
|
2024-02-14 22:17:32 +03:00
|
|
|
pm_options_line_set(&result.options, NUM2INT(line));
|
2024-09-16 18:03:38 +03:00
|
|
|
pm_options_scopes_init(&result.options, 1);
|
2024-05-20 18:29:50 +03:00
|
|
|
result.node.coverage_enabled = 1;
|
2024-01-31 20:17:31 +03:00
|
|
|
|
2024-05-02 01:26:01 +03:00
|
|
|
switch (option.frozen_string_literal) {
|
|
|
|
case ISEQ_FROZEN_STRING_LITERAL_UNSET:
|
|
|
|
break;
|
|
|
|
case ISEQ_FROZEN_STRING_LITERAL_DISABLED:
|
|
|
|
pm_options_frozen_string_literal_set(&result.options, false);
|
|
|
|
break;
|
|
|
|
case ISEQ_FROZEN_STRING_LITERAL_ENABLED:
|
|
|
|
pm_options_frozen_string_literal_set(&result.options, true);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
rb_bug("pm_iseq_compile_with_option: invalid frozen_string_literal=%d", option.frozen_string_literal);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2024-08-29 21:26:08 +03:00
|
|
|
VALUE script_lines;
|
2024-02-14 22:17:32 +03:00
|
|
|
VALUE error;
|
2024-08-29 21:26:08 +03:00
|
|
|
|
2024-01-31 20:17:31 +03:00
|
|
|
if (RB_TYPE_P(src, T_FILE)) {
|
|
|
|
VALUE filepath = rb_io_path(src);
|
2024-08-29 21:26:08 +03:00
|
|
|
error = pm_load_parse_file(&result, filepath, ruby_vm_keep_script_lines ? &script_lines : NULL);
|
2024-01-31 20:17:31 +03:00
|
|
|
RB_GC_GUARD(filepath);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
src = StringValue(src);
|
2024-08-29 21:26:08 +03:00
|
|
|
error = pm_parse_string(&result, src, file, ruby_vm_keep_script_lines ? &script_lines : NULL);
|
2024-01-31 20:17:31 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (error == Qnil) {
|
|
|
|
iseq = pm_iseq_new_with_opt(&result.node, name, file, realpath, ln, NULL, 0, ISEQ_TYPE_TOP, &option);
|
|
|
|
pm_parse_result_free(&result);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
pm_parse_result_free(&result);
|
|
|
|
rb_exc_raise(error);
|
|
|
|
}
|
|
|
|
|
|
|
|
return iseq;
|
|
|
|
}
|
|
|
|
|
2015-07-22 01:52:59 +03:00
|
|
|
VALUE
|
|
|
|
rb_iseq_path(const rb_iseq_t *iseq)
|
|
|
|
{
|
2022-03-23 22:19:48 +03:00
|
|
|
return pathobj_path(ISEQ_BODY(iseq)->location.pathobj);
|
2017-06-01 03:05:33 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
VALUE
|
|
|
|
rb_iseq_realpath(const rb_iseq_t *iseq)
|
|
|
|
{
|
2022-03-23 22:19:48 +03:00
|
|
|
return pathobj_realpath(ISEQ_BODY(iseq)->location.pathobj);
|
2015-07-22 01:52:59 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
VALUE
|
|
|
|
rb_iseq_absolute_path(const rb_iseq_t *iseq)
|
|
|
|
{
|
2017-06-01 03:05:33 +03:00
|
|
|
return rb_iseq_realpath(iseq);
|
2015-07-22 01:52:59 +03:00
|
|
|
}
|
|
|
|
|
2021-05-21 21:01:06 +03:00
|
|
|
int
|
|
|
|
rb_iseq_from_eval_p(const rb_iseq_t *iseq)
|
|
|
|
{
|
|
|
|
return NIL_P(rb_iseq_realpath(iseq));
|
|
|
|
}
|
|
|
|
|
2015-07-22 01:52:59 +03:00
|
|
|
VALUE
|
|
|
|
rb_iseq_label(const rb_iseq_t *iseq)
|
|
|
|
{
|
2022-03-23 22:19:48 +03:00
|
|
|
return ISEQ_BODY(iseq)->location.label;
|
2015-07-22 01:52:59 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
VALUE
|
|
|
|
rb_iseq_base_label(const rb_iseq_t *iseq)
|
|
|
|
{
|
2022-03-23 22:19:48 +03:00
|
|
|
return ISEQ_BODY(iseq)->location.base_label;
|
2015-07-22 01:52:59 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
VALUE
|
|
|
|
rb_iseq_first_lineno(const rb_iseq_t *iseq)
|
|
|
|
{
|
2022-09-25 11:07:18 +03:00
|
|
|
return RB_INT2NUM(ISEQ_BODY(iseq)->location.first_lineno);
|
2015-07-22 01:52:59 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
VALUE
|
|
|
|
rb_iseq_method_name(const rb_iseq_t *iseq)
|
|
|
|
{
|
2022-03-23 22:19:48 +03:00
|
|
|
struct rb_iseq_constant_body *const body = ISEQ_BODY(ISEQ_BODY(iseq)->local_iseq);
|
2015-07-22 01:52:59 +03:00
|
|
|
|
2018-05-12 04:24:18 +03:00
|
|
|
if (body->type == ISEQ_TYPE_METHOD) {
|
|
|
|
return body->location.base_label;
|
2015-07-22 01:52:59 +03:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
return Qnil;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-21 09:40:28 +03:00
|
|
|
void
|
2018-01-09 11:45:35 +03:00
|
|
|
rb_iseq_code_location(const rb_iseq_t *iseq, int *beg_pos_lineno, int *beg_pos_column, int *end_pos_lineno, int *end_pos_column)
|
2017-12-21 09:40:28 +03:00
|
|
|
{
|
2022-03-23 22:19:48 +03:00
|
|
|
const rb_code_location_t *loc = &ISEQ_BODY(iseq)->location.code_location;
|
2018-05-12 04:24:18 +03:00
|
|
|
if (beg_pos_lineno) *beg_pos_lineno = loc->beg_pos.lineno;
|
|
|
|
if (beg_pos_column) *beg_pos_column = loc->beg_pos.column;
|
|
|
|
if (end_pos_lineno) *end_pos_lineno = loc->end_pos.lineno;
|
|
|
|
if (end_pos_column) *end_pos_column = loc->end_pos.column;
|
2017-12-21 09:40:28 +03:00
|
|
|
}
|
|
|
|
|
2022-07-22 10:57:25 +03:00
|
|
|
static ID iseq_type_id(enum rb_iseq_type type);
|
2021-12-18 21:20:00 +03:00
|
|
|
|
|
|
|
VALUE
|
|
|
|
rb_iseq_type(const rb_iseq_t *iseq)
|
|
|
|
{
|
2022-03-23 22:19:48 +03:00
|
|
|
return ID2SYM(iseq_type_id(ISEQ_BODY(iseq)->type));
|
2021-12-18 21:20:00 +03:00
|
|
|
}
|
|
|
|
|
2015-12-02 11:05:36 +03:00
|
|
|
VALUE
|
|
|
|
rb_iseq_coverage(const rb_iseq_t *iseq)
|
|
|
|
{
|
|
|
|
return ISEQ_COVERAGE(iseq);
|
|
|
|
}
|
|
|
|
|
2018-08-22 08:24:50 +03:00
|
|
|
static int
|
|
|
|
remove_coverage_i(void *vstart, void *vend, size_t stride, void *data)
|
|
|
|
{
|
|
|
|
VALUE v = (VALUE)vstart;
|
|
|
|
for (; v != (VALUE)vend; v += stride) {
|
2019-05-23 11:02:07 +03:00
|
|
|
void *ptr = asan_poisoned_object_p(v);
|
|
|
|
asan_unpoison_object(v, false);
|
2019-04-02 01:52:35 +03:00
|
|
|
|
2018-08-22 08:24:50 +03:00
|
|
|
if (rb_obj_is_iseq(v)) {
|
|
|
|
rb_iseq_t *iseq = (rb_iseq_t *)v;
|
|
|
|
ISEQ_COVERAGE_SET(iseq, Qnil);
|
|
|
|
}
|
2019-04-02 01:52:35 +03:00
|
|
|
|
2019-05-29 07:12:15 +03:00
|
|
|
asan_poison_object_if(ptr, v);
|
2018-08-22 08:24:50 +03:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2018-08-22 13:22:02 +03:00
|
|
|
rb_iseq_remove_coverage_all(void)
|
2018-08-22 08:24:50 +03:00
|
|
|
{
|
|
|
|
rb_objspace_each_objects(remove_coverage_i, NULL);
|
|
|
|
}
|
|
|
|
|
2015-07-22 01:52:59 +03:00
|
|
|
/* define wrapper class methods (RubyVM::InstructionSequence) */
|
|
|
|
|
2015-08-12 15:18:51 +03:00
|
|
|
static void
|
|
|
|
iseqw_mark(void *ptr)
|
|
|
|
{
|
2024-04-01 22:46:51 +03:00
|
|
|
rb_gc_mark_movable(*(VALUE *)ptr);
|
2015-08-12 15:18:51 +03:00
|
|
|
}
|
|
|
|
|
2015-09-03 22:48:12 +03:00
|
|
|
static size_t
|
|
|
|
iseqw_memsize(const void *ptr)
|
|
|
|
{
|
2024-04-01 22:46:51 +03:00
|
|
|
return rb_iseq_memsize(*(const rb_iseq_t **)ptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
iseqw_ref_update(void *ptr)
|
|
|
|
{
|
|
|
|
VALUE *vptr = ptr;
|
|
|
|
*vptr = rb_gc_location(*vptr);
|
2015-09-03 22:48:12 +03:00
|
|
|
}
|
|
|
|
|
2015-08-12 15:18:51 +03:00
|
|
|
static const rb_data_type_t iseqw_data_type = {
|
|
|
|
"T_IMEMO/iseq",
|
2024-04-01 22:46:51 +03:00
|
|
|
{
|
|
|
|
iseqw_mark,
|
|
|
|
RUBY_TYPED_DEFAULT_FREE,
|
|
|
|
iseqw_memsize,
|
|
|
|
iseqw_ref_update,
|
|
|
|
},
|
2015-08-12 15:18:51 +03:00
|
|
|
0, 0, RUBY_TYPED_FREE_IMMEDIATELY|RUBY_TYPED_WB_PROTECTED
|
|
|
|
};
|
|
|
|
|
2015-07-22 01:52:59 +03:00
|
|
|
static VALUE
|
|
|
|
iseqw_new(const rb_iseq_t *iseq)
|
|
|
|
{
|
2018-12-06 13:52:27 +03:00
|
|
|
if (iseq->wrapper) {
|
2024-04-01 22:46:51 +03:00
|
|
|
if (*(const rb_iseq_t **)rb_check_typeddata(iseq->wrapper, &iseqw_data_type) != iseq) {
|
2024-03-27 05:45:01 +03:00
|
|
|
rb_raise(rb_eTypeError, "wrong iseq wrapper: %" PRIsVALUE " for %p",
|
|
|
|
iseq->wrapper, (void *)iseq);
|
|
|
|
}
|
2018-12-06 13:52:27 +03:00
|
|
|
return iseq->wrapper;
|
|
|
|
}
|
|
|
|
else {
|
2024-04-01 22:46:51 +03:00
|
|
|
rb_iseq_t **ptr;
|
|
|
|
VALUE obj = TypedData_Make_Struct(rb_cISeq, rb_iseq_t *, &iseqw_data_type, ptr);
|
|
|
|
RB_OBJ_WRITE(obj, ptr, iseq);
|
2015-08-12 15:18:51 +03:00
|
|
|
|
2018-12-06 13:52:27 +03:00
|
|
|
/* cache a wrapper object */
|
|
|
|
RB_OBJ_WRITE((VALUE)iseq, &iseq->wrapper, obj);
|
|
|
|
RB_OBJ_FREEZE((VALUE)iseq);
|
2015-08-12 15:18:51 +03:00
|
|
|
|
2018-12-06 13:52:27 +03:00
|
|
|
return obj;
|
|
|
|
}
|
2015-07-22 01:52:59 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
VALUE
|
|
|
|
rb_iseqw_new(const rb_iseq_t *iseq)
|
|
|
|
{
|
|
|
|
return iseqw_new(iseq);
|
|
|
|
}
|
|
|
|
|
2024-03-29 18:16:45 +03:00
|
|
|
/**
|
|
|
|
* Accept the options given to InstructionSequence.compile and
|
|
|
|
* InstructionSequence.compile_prism and share the logic for creating the
|
|
|
|
* instruction sequence.
|
|
|
|
*/
|
|
|
|
static VALUE
|
|
|
|
iseqw_s_compile_parser(int argc, VALUE *argv, VALUE self, bool prism)
|
|
|
|
{
|
|
|
|
VALUE src, file = Qnil, path = Qnil, line = Qnil, opt = Qnil;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
i = rb_scan_args(argc, argv, "1*:", &src, NULL, &opt);
|
|
|
|
if (i > 4+NIL_P(opt)) rb_error_arity(argc, 1, 5);
|
|
|
|
switch (i) {
|
|
|
|
case 5: opt = argv[--i];
|
|
|
|
case 4: line = argv[--i];
|
|
|
|
case 3: path = argv[--i];
|
|
|
|
case 2: file = argv[--i];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (NIL_P(file)) file = rb_fstring_lit("<compiled>");
|
|
|
|
if (NIL_P(path)) path = file;
|
|
|
|
if (NIL_P(line)) line = INT2FIX(1);
|
|
|
|
|
|
|
|
Check_Type(path, T_STRING);
|
|
|
|
Check_Type(file, T_STRING);
|
|
|
|
|
|
|
|
rb_iseq_t *iseq;
|
|
|
|
if (prism) {
|
|
|
|
iseq = pm_iseq_compile_with_option(src, file, path, line, opt);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
iseq = rb_iseq_compile_with_option(src, file, path, line, opt);
|
|
|
|
}
|
|
|
|
|
|
|
|
return iseqw_new(iseq);
|
|
|
|
}
|
|
|
|
|
2012-07-25 01:29:24 +04:00
|
|
|
/*
|
|
|
|
* call-seq:
|
|
|
|
* InstructionSequence.compile(source[, file[, path[, line[, options]]]]) -> iseq
|
|
|
|
* InstructionSequence.new(source[, file[, path[, line[, options]]]]) -> iseq
|
|
|
|
*
|
[ci skip] More docs for InstructionSequence.compile
This commit documents that you can also pass a `File` object to
`RubyVM::InstructionSequence.compile`, instead of a string, and this
will behave in a similar way to
`RubyVM::InstructionSequence.compile_file`
e.g.
```
❯ ./ruby -e "puts RubyVM::InstructionSequence.compile(File.open('test.rb')).disasm"
== disasm: #<ISeq:<compiled>@<compiled>:1 (1,0)-(2,21)>
local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
[ 1] name@0
0000 putstring "Ruby" ( 1)[Li]
0002 setlocal_WC_0 name@0
0004 putself ( 2)[Li]
0005 putobject "Hello, "
0007 getlocal_WC_0 name@0
0009 dup
0010 objtostring <calldata!mid:to_s, argc:0, FCALL|ARGS_SIMPLE>
0012 anytostring
0013 concatstrings 2
0015 opt_send_without_block <calldata!mid:puts, argc:1, FCALL|ARGS_SIMPLE>
0017 leave
~/git/ruby master* ≡ ⇡
❯ ./ruby -e "puts RubyVM::InstructionSequence.compile(File.open('test.rb').read).disasm"
== disasm: #<ISeq:<compiled>@<compiled>:1 (1,0)-(2,21)>
local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
[ 1] name@0
0000 putstring "Ruby" ( 1)[Li]
0002 setlocal_WC_0 name@0
0004 putself ( 2)[Li]
0005 putobject "Hello, "
0007 getlocal_WC_0 name@0
0009 dup
0010 objtostring <calldata!mid:to_s, argc:0, FCALL|ARGS_SIMPLE>
0012 anytostring
0013 concatstrings 2
0015 opt_send_without_block <calldata!mid:puts, argc:1, FCALL|ARGS_SIMPLE>
0017 leave
```
This is explicitly allowed by this code path in
`rb_iseq_compile_with_option` so we should document it.
```
if (RB_TYPE_P(src, T_FILE)) {
parse = rb_parser_compile_file_path;
}
else {
parse = rb_parser_compile_string_path;
StringValue(src);
}
```
2023-09-29 12:17:10 +03:00
|
|
|
* Takes +source+, which can be a string of Ruby code, or an open +File+ object.
|
|
|
|
* that contains Ruby source code.
|
2012-07-25 01:29:24 +04:00
|
|
|
*
|
2019-11-19 05:40:00 +03:00
|
|
|
* Optionally takes +file+, +path+, and +line+ which describe the file path,
|
|
|
|
* real path and first line number of the ruby code in +source+ which are
|
2012-07-25 01:29:24 +04:00
|
|
|
* metadata attached to the returned +iseq+.
|
|
|
|
*
|
2019-11-19 05:40:00 +03:00
|
|
|
* +file+ is used for `__FILE__` and exception backtrace. +path+ is used for
|
|
|
|
* +require_relative+ base. It is recommended these should be the same full
|
|
|
|
* path.
|
|
|
|
*
|
2012-07-25 01:29:24 +04:00
|
|
|
* +options+, which can be +true+, +false+ or a +Hash+, is used to
|
2012-12-29 11:44:54 +04:00
|
|
|
* modify the default behavior of the Ruby iseq compiler.
|
|
|
|
*
|
|
|
|
* For details regarding valid compile options see ::compile_option=.
|
2012-07-25 01:29:24 +04:00
|
|
|
*
|
|
|
|
* RubyVM::InstructionSequence.compile("a = 1 + 2")
|
|
|
|
* #=> <RubyVM::InstructionSequence:<compiled>@<compiled>>
|
|
|
|
*
|
2019-11-19 05:40:00 +03:00
|
|
|
* path = "test.rb"
|
|
|
|
* RubyVM::InstructionSequence.compile(File.read(path), path, File.expand_path(path))
|
|
|
|
* #=> <RubyVM::InstructionSequence:<compiled>@test.rb:1>
|
|
|
|
*
|
[ci skip] More docs for InstructionSequence.compile
This commit documents that you can also pass a `File` object to
`RubyVM::InstructionSequence.compile`, instead of a string, and this
will behave in a similar way to
`RubyVM::InstructionSequence.compile_file`
e.g.
```
❯ ./ruby -e "puts RubyVM::InstructionSequence.compile(File.open('test.rb')).disasm"
== disasm: #<ISeq:<compiled>@<compiled>:1 (1,0)-(2,21)>
local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
[ 1] name@0
0000 putstring "Ruby" ( 1)[Li]
0002 setlocal_WC_0 name@0
0004 putself ( 2)[Li]
0005 putobject "Hello, "
0007 getlocal_WC_0 name@0
0009 dup
0010 objtostring <calldata!mid:to_s, argc:0, FCALL|ARGS_SIMPLE>
0012 anytostring
0013 concatstrings 2
0015 opt_send_without_block <calldata!mid:puts, argc:1, FCALL|ARGS_SIMPLE>
0017 leave
~/git/ruby master* ≡ ⇡
❯ ./ruby -e "puts RubyVM::InstructionSequence.compile(File.open('test.rb').read).disasm"
== disasm: #<ISeq:<compiled>@<compiled>:1 (1,0)-(2,21)>
local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
[ 1] name@0
0000 putstring "Ruby" ( 1)[Li]
0002 setlocal_WC_0 name@0
0004 putself ( 2)[Li]
0005 putobject "Hello, "
0007 getlocal_WC_0 name@0
0009 dup
0010 objtostring <calldata!mid:to_s, argc:0, FCALL|ARGS_SIMPLE>
0012 anytostring
0013 concatstrings 2
0015 opt_send_without_block <calldata!mid:puts, argc:1, FCALL|ARGS_SIMPLE>
0017 leave
```
This is explicitly allowed by this code path in
`rb_iseq_compile_with_option` so we should document it.
```
if (RB_TYPE_P(src, T_FILE)) {
parse = rb_parser_compile_file_path;
}
else {
parse = rb_parser_compile_string_path;
StringValue(src);
}
```
2023-09-29 12:17:10 +03:00
|
|
|
* file = File.open("test.rb")
|
|
|
|
* RubyVM::InstructionSequence.compile(file)
|
|
|
|
* #=> <RubyVM::InstructionSequence:<compiled>@<compiled>:1>
|
|
|
|
*
|
2019-11-19 05:40:00 +03:00
|
|
|
* path = File.expand_path("test.rb")
|
|
|
|
* RubyVM::InstructionSequence.compile(File.read(path), path, path)
|
|
|
|
* #=> <RubyVM::InstructionSequence:<compiled>@/absolute/path/to/test.rb:1>
|
|
|
|
*
|
2012-07-25 01:29:24 +04:00
|
|
|
*/
|
* blockinlining.c: remove "yarv" prefix.
* array.c, numeric.c: ditto.
* insnhelper.ci, insns.def, vm_evalbody.ci: ditto.
* yarvcore.c: removed.
* yarvcore.h: renamed to core.h.
* cont.c, debug.c, error.c, process.c, signal.c : ditto.
* ext/probeprofiler/probeprofiler.c: ditto.
* id.c, id.h: added.
* inits.c: ditto.
* compile.c: rename internal functions.
* compile.h: fix debug flag.
* eval.c, object.c, vm.c: remove ruby_top_self.
use rb_vm_top_self() instead.
* eval_intern.h, eval_load: ditto.
* gc.c: rename yarv_machine_stack_mark() to
rb_gc_mark_machine_stack().
* insnhelper.h: remove unused macros.
* iseq.c: add iseq_compile() to create iseq object
from source string.
* proc.c: rename a internal function.
* template/insns.inc.tmpl: remove YARV prefix.
* thread.c:
* vm.c (rb_iseq_eval): added.
* vm.c: move some functions from yarvcore.c.
* vm_dump.c: fix to remove compiler warning.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12741 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-07-12 08:25:46 +04:00
|
|
|
static VALUE
|
2015-07-22 01:52:59 +03:00
|
|
|
iseqw_s_compile(int argc, VALUE *argv, VALUE self)
|
* blockinlining.c: remove "yarv" prefix.
* array.c, numeric.c: ditto.
* insnhelper.ci, insns.def, vm_evalbody.ci: ditto.
* yarvcore.c: removed.
* yarvcore.h: renamed to core.h.
* cont.c, debug.c, error.c, process.c, signal.c : ditto.
* ext/probeprofiler/probeprofiler.c: ditto.
* id.c, id.h: added.
* inits.c: ditto.
* compile.c: rename internal functions.
* compile.h: fix debug flag.
* eval.c, object.c, vm.c: remove ruby_top_self.
use rb_vm_top_self() instead.
* eval_intern.h, eval_load: ditto.
* gc.c: rename yarv_machine_stack_mark() to
rb_gc_mark_machine_stack().
* insnhelper.h: remove unused macros.
* iseq.c: add iseq_compile() to create iseq object
from source string.
* proc.c: rename a internal function.
* template/insns.inc.tmpl: remove YARV prefix.
* thread.c:
* vm.c (rb_iseq_eval): added.
* vm.c: move some functions from yarvcore.c.
* vm_dump.c: fix to remove compiler warning.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12741 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-07-12 08:25:46 +04:00
|
|
|
{
|
2024-03-29 18:16:45 +03:00
|
|
|
return iseqw_s_compile_parser(argc, argv, self, *rb_ruby_prism_ptr());
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
2024-01-31 20:17:31 +03:00
|
|
|
/*
|
|
|
|
* call-seq:
|
|
|
|
* InstructionSequence.compile_prism(source[, file[, path[, line[, options]]]]) -> iseq
|
|
|
|
*
|
|
|
|
* Takes +source+, which can be a string of Ruby code, or an open +File+ object.
|
|
|
|
* that contains Ruby source code. It parses and compiles using prism.
|
|
|
|
*
|
|
|
|
* Optionally takes +file+, +path+, and +line+ which describe the file path,
|
|
|
|
* real path and first line number of the ruby code in +source+ which are
|
|
|
|
* metadata attached to the returned +iseq+.
|
|
|
|
*
|
|
|
|
* +file+ is used for `__FILE__` and exception backtrace. +path+ is used for
|
|
|
|
* +require_relative+ base. It is recommended these should be the same full
|
|
|
|
* path.
|
|
|
|
*
|
|
|
|
* +options+, which can be +true+, +false+ or a +Hash+, is used to
|
|
|
|
* modify the default behavior of the Ruby iseq compiler.
|
|
|
|
*
|
|
|
|
* For details regarding valid compile options see ::compile_option=.
|
|
|
|
*
|
|
|
|
* RubyVM::InstructionSequence.compile("a = 1 + 2")
|
|
|
|
* #=> <RubyVM::InstructionSequence:<compiled>@<compiled>>
|
|
|
|
*
|
|
|
|
* path = "test.rb"
|
|
|
|
* RubyVM::InstructionSequence.compile(File.read(path), path, File.expand_path(path))
|
|
|
|
* #=> <RubyVM::InstructionSequence:<compiled>@test.rb:1>
|
|
|
|
*
|
|
|
|
* file = File.open("test.rb")
|
|
|
|
* RubyVM::InstructionSequence.compile(file)
|
|
|
|
* #=> <RubyVM::InstructionSequence:<compiled>@<compiled>:1>
|
|
|
|
*
|
|
|
|
* path = File.expand_path("test.rb")
|
|
|
|
* RubyVM::InstructionSequence.compile(File.read(path), path, path)
|
|
|
|
* #=> <RubyVM::InstructionSequence:<compiled>@/absolute/path/to/test.rb:1>
|
|
|
|
*
|
|
|
|
*/
|
2023-08-28 23:55:58 +03:00
|
|
|
static VALUE
|
2023-09-27 19:39:53 +03:00
|
|
|
iseqw_s_compile_prism(int argc, VALUE *argv, VALUE self)
|
2023-08-28 23:55:58 +03:00
|
|
|
{
|
2024-03-29 18:16:45 +03:00
|
|
|
return iseqw_s_compile_parser(argc, argv, self, true);
|
2023-12-08 02:47:36 +03:00
|
|
|
}
|
|
|
|
|
2012-07-25 01:29:24 +04:00
|
|
|
/*
|
|
|
|
* call-seq:
|
|
|
|
* InstructionSequence.compile_file(file[, options]) -> iseq
|
|
|
|
*
|
|
|
|
* Takes +file+, a String with the location of a Ruby source file, reads,
|
|
|
|
* parses and compiles the file, and returns +iseq+, the compiled
|
|
|
|
* InstructionSequence with source location metadata set.
|
|
|
|
*
|
|
|
|
* Optionally takes +options+, which can be +true+, +false+ or a +Hash+, to
|
2012-12-29 11:44:54 +04:00
|
|
|
* modify the default behavior of the Ruby iseq compiler.
|
|
|
|
*
|
|
|
|
* For details regarding valid compile options see ::compile_option=.
|
2012-07-25 01:29:24 +04:00
|
|
|
*
|
|
|
|
* # /tmp/hello.rb
|
|
|
|
* puts "Hello, world!"
|
|
|
|
*
|
|
|
|
* # elsewhere
|
|
|
|
* RubyVM::InstructionSequence.compile_file("/tmp/hello.rb")
|
|
|
|
* #=> <RubyVM::InstructionSequence:<main>@/tmp/hello.rb>
|
|
|
|
*/
|
2006-12-31 18:02:22 +03:00
|
|
|
static VALUE
|
2015-07-22 01:52:59 +03:00
|
|
|
iseqw_s_compile_file(int argc, VALUE *argv, VALUE self)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2022-09-25 10:45:28 +03:00
|
|
|
VALUE file, opt = Qnil;
|
2017-10-27 19:44:57 +03:00
|
|
|
VALUE parser, f, exc = Qnil, ret;
|
2017-10-29 18:51:23 +03:00
|
|
|
rb_ast_t *ast;
|
2024-05-03 02:57:55 +03:00
|
|
|
VALUE ast_value;
|
* blockinlining.c, compile.c, compile.h, error.c, eval.c,
eval_intern.h, eval_jump.h, eval_load.c, eval_method.h,
eval_safe.h, gc.c, insnhelper.h, insns.def, iseq.c, proc.c,
process.c, signal.c, thread.c, thread_pthread.ci, thread_win32.ci,
vm.c, vm.h, vm_dump.c, vm_evalbody.ci, vm_macro.def,
yarv.h, yarvcore.h, yarvcore.c: change type and macro names:
* yarv_*_t -> rb_*_t
* yarv_*_struct -> rb_*_struct
* yarv_tag -> rb_vm_tag
* YARV_* -> RUBY_VM_*
* proc.c, vm.c: move functions about env object creation
from proc.c to vm.c.
* proc.c, yarvcore.c: fix rb_cVM initialization place.
* inits.c: change Init_ISeq() order (after Init_VM).
* ruby.h, proc.c: change declaration place of rb_cEnv
from proc.c to ruby.c.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@11651 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-02-06 22:00:03 +03:00
|
|
|
rb_compile_option_t option;
|
2016-09-27 09:23:36 +03:00
|
|
|
int i;
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2016-09-27 09:23:36 +03:00
|
|
|
i = rb_scan_args(argc, argv, "1*:", &file, NULL, &opt);
|
|
|
|
if (i > 1+NIL_P(opt)) rb_error_arity(argc, 1, 2);
|
|
|
|
switch (i) {
|
|
|
|
case 2: opt = argv[--i];
|
|
|
|
}
|
2008-09-12 22:32:19 +04:00
|
|
|
FilePathValue(file);
|
2016-01-26 09:23:47 +03:00
|
|
|
file = rb_fstring(file); /* rb_io_t->pathv gets frozen anyways */
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2008-09-12 22:32:19 +04:00
|
|
|
f = rb_file_open_str(file, "r");
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2022-10-17 11:50:42 +03:00
|
|
|
rb_execution_context_t *ec = GET_EC();
|
|
|
|
VALUE v = rb_vm_push_frame_fname(ec, file);
|
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
parser = rb_parser_new();
|
2016-09-27 11:35:31 +03:00
|
|
|
rb_parser_set_context(parser, NULL, FALSE);
|
2024-05-03 02:57:55 +03:00
|
|
|
ast_value = rb_parser_load_file(parser, file);
|
|
|
|
ast = rb_ruby_ast_data_get(ast_value);
|
2018-01-05 11:59:20 +03:00
|
|
|
if (!ast->body.root) exc = GET_EC()->errinfo;
|
2014-11-26 05:03:06 +03:00
|
|
|
|
|
|
|
rb_io_close(f);
|
2018-01-05 11:59:20 +03:00
|
|
|
if (!ast->body.root) {
|
2017-10-27 19:44:57 +03:00
|
|
|
rb_ast_dispose(ast);
|
|
|
|
rb_exc_raise(exc);
|
|
|
|
}
|
2014-11-26 05:03:06 +03:00
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
make_compile_option(&option, opt);
|
2015-07-22 01:52:59 +03:00
|
|
|
|
2024-05-03 02:57:55 +03:00
|
|
|
ret = iseqw_new(rb_iseq_new_with_opt(ast_value, rb_fstring_lit("<main>"),
|
2017-10-27 19:44:57 +03:00
|
|
|
file,
|
|
|
|
rb_realpath_internal(Qnil, file, 1),
|
2024-03-28 04:26:42 +03:00
|
|
|
1, NULL, 0, ISEQ_TYPE_TOP, &option,
|
|
|
|
Qnil));
|
2017-10-27 19:44:57 +03:00
|
|
|
rb_ast_dispose(ast);
|
2022-10-17 11:50:42 +03:00
|
|
|
|
|
|
|
rb_vm_pop_frame(ec);
|
|
|
|
RB_GC_GUARD(v);
|
2017-10-27 19:44:57 +03:00
|
|
|
return ret;
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
2024-01-31 20:17:31 +03:00
|
|
|
/*
|
|
|
|
* call-seq:
|
|
|
|
* InstructionSequence.compile_file_prism(file[, options]) -> iseq
|
|
|
|
*
|
|
|
|
* Takes +file+, a String with the location of a Ruby source file, reads,
|
|
|
|
* parses and compiles the file, and returns +iseq+, the compiled
|
|
|
|
* InstructionSequence with source location metadata set. It parses and
|
|
|
|
* compiles using prism.
|
|
|
|
*
|
|
|
|
* Optionally takes +options+, which can be +true+, +false+ or a +Hash+, to
|
|
|
|
* modify the default behavior of the Ruby iseq compiler.
|
|
|
|
*
|
|
|
|
* For details regarding valid compile options see ::compile_option=.
|
|
|
|
*
|
|
|
|
* # /tmp/hello.rb
|
|
|
|
* puts "Hello, world!"
|
|
|
|
*
|
|
|
|
* # elsewhere
|
|
|
|
* RubyVM::InstructionSequence.compile_file_prism("/tmp/hello.rb")
|
|
|
|
* #=> <RubyVM::InstructionSequence:<main>@/tmp/hello.rb>
|
|
|
|
*/
|
|
|
|
static VALUE
|
|
|
|
iseqw_s_compile_file_prism(int argc, VALUE *argv, VALUE self)
|
|
|
|
{
|
|
|
|
VALUE file, opt = Qnil, ret;
|
|
|
|
rb_compile_option_t option;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
i = rb_scan_args(argc, argv, "1*:", &file, NULL, &opt);
|
|
|
|
if (i > 1+NIL_P(opt)) rb_error_arity(argc, 1, 2);
|
|
|
|
switch (i) {
|
|
|
|
case 2: opt = argv[--i];
|
|
|
|
}
|
|
|
|
FilePathValue(file);
|
|
|
|
file = rb_fstring(file); /* rb_io_t->pathv gets frozen anyways */
|
|
|
|
|
|
|
|
rb_execution_context_t *ec = GET_EC();
|
|
|
|
VALUE v = rb_vm_push_frame_fname(ec, file);
|
|
|
|
|
|
|
|
pm_parse_result_t result = { 0 };
|
2024-02-14 22:17:32 +03:00
|
|
|
result.options.line = 1;
|
2024-05-20 18:29:50 +03:00
|
|
|
result.node.coverage_enabled = 1;
|
2024-02-14 22:17:32 +03:00
|
|
|
|
2024-08-29 21:26:08 +03:00
|
|
|
VALUE script_lines;
|
|
|
|
VALUE error = pm_load_parse_file(&result, file, ruby_vm_keep_script_lines ? &script_lines : NULL);
|
2024-01-31 20:17:31 +03:00
|
|
|
|
|
|
|
if (error == Qnil) {
|
|
|
|
make_compile_option(&option, opt);
|
|
|
|
|
|
|
|
ret = iseqw_new(pm_iseq_new_with_opt(&result.node, rb_fstring_lit("<main>"),
|
|
|
|
file,
|
|
|
|
rb_realpath_internal(Qnil, file, 1),
|
|
|
|
1, NULL, 0, ISEQ_TYPE_TOP, &option));
|
|
|
|
pm_parse_result_free(&result);
|
|
|
|
rb_vm_pop_frame(ec);
|
|
|
|
RB_GC_GUARD(v);
|
|
|
|
return ret;
|
|
|
|
} else {
|
|
|
|
pm_parse_result_free(&result);
|
|
|
|
rb_vm_pop_frame(ec);
|
|
|
|
RB_GC_GUARD(v);
|
|
|
|
rb_exc_raise(error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2012-07-25 01:29:24 +04:00
|
|
|
/*
|
|
|
|
* call-seq:
|
|
|
|
* InstructionSequence.compile_option = options
|
|
|
|
*
|
|
|
|
* Sets the default values for various optimizations in the Ruby iseq
|
2012-12-29 11:44:54 +04:00
|
|
|
* compiler.
|
|
|
|
*
|
|
|
|
* Possible values for +options+ include +true+, which enables all options,
|
|
|
|
* +false+ which disables all options, and +nil+ which leaves all options
|
|
|
|
* unchanged.
|
|
|
|
*
|
|
|
|
* You can also pass a +Hash+ of +options+ that you want to change, any
|
|
|
|
* options not present in the hash will be left unchanged.
|
2012-07-25 01:29:24 +04:00
|
|
|
*
|
|
|
|
* Possible option names (which are keys in +options+) which can be set to
|
|
|
|
* +true+ or +false+ include:
|
|
|
|
*
|
|
|
|
* * +:inline_const_cache+
|
|
|
|
* * +:instructions_unification+
|
|
|
|
* * +:operands_unification+
|
|
|
|
* * +:peephole_optimization+
|
|
|
|
* * +:specialized_instruction+
|
|
|
|
* * +:tailcall_optimization+
|
|
|
|
*
|
|
|
|
* Additionally, +:debug_level+ can be set to an integer.
|
|
|
|
*
|
|
|
|
* These default options can be overwritten for a single run of the iseq
|
|
|
|
* compiler by passing any of the above values as the +options+ parameter to
|
2012-12-29 11:44:54 +04:00
|
|
|
* ::new, ::compile and ::compile_file.
|
2012-07-25 01:29:24 +04:00
|
|
|
*/
|
2006-12-31 18:02:22 +03:00
|
|
|
static VALUE
|
2015-07-22 01:52:59 +03:00
|
|
|
iseqw_s_compile_option_set(VALUE self, VALUE opt)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
* blockinlining.c, compile.c, compile.h, error.c, eval.c,
eval_intern.h, eval_jump.h, eval_load.c, eval_method.h,
eval_safe.h, gc.c, insnhelper.h, insns.def, iseq.c, proc.c,
process.c, signal.c, thread.c, thread_pthread.ci, thread_win32.ci,
vm.c, vm.h, vm_dump.c, vm_evalbody.ci, vm_macro.def,
yarv.h, yarvcore.h, yarvcore.c: change type and macro names:
* yarv_*_t -> rb_*_t
* yarv_*_struct -> rb_*_struct
* yarv_tag -> rb_vm_tag
* YARV_* -> RUBY_VM_*
* proc.c, vm.c: move functions about env object creation
from proc.c to vm.c.
* proc.c, yarvcore.c: fix rb_cVM initialization place.
* inits.c: change Init_ISeq() order (after Init_VM).
* ruby.h, proc.c: change declaration place of rb_cEnv
from proc.c to ruby.c.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@11651 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-02-06 22:00:03 +03:00
|
|
|
rb_compile_option_t option;
|
2006-12-31 18:02:22 +03:00
|
|
|
make_compile_option(&option, opt);
|
|
|
|
COMPILE_OPTION_DEFAULT = option;
|
2007-08-06 08:34:11 +04:00
|
|
|
return opt;
|
|
|
|
}
|
|
|
|
|
2012-07-25 01:29:24 +04:00
|
|
|
/*
|
|
|
|
* call-seq:
|
|
|
|
* InstructionSequence.compile_option -> options
|
|
|
|
*
|
|
|
|
* Returns a hash of default options used by the Ruby iseq compiler.
|
2012-12-29 11:44:54 +04:00
|
|
|
*
|
2012-07-25 01:29:24 +04:00
|
|
|
* For details, see InstructionSequence.compile_option=.
|
|
|
|
*/
|
2007-08-06 08:34:11 +04:00
|
|
|
static VALUE
|
2015-07-22 01:52:59 +03:00
|
|
|
iseqw_s_compile_option_get(VALUE self)
|
2007-08-06 08:34:11 +04:00
|
|
|
{
|
|
|
|
return make_compile_option_value(&COMPILE_OPTION_DEFAULT);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
2015-07-22 01:52:59 +03:00
|
|
|
static const rb_iseq_t *
|
|
|
|
iseqw_check(VALUE iseqw)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2024-04-01 22:46:51 +03:00
|
|
|
rb_iseq_t **iseq_ptr;
|
|
|
|
TypedData_Get_Struct(iseqw, rb_iseq_t *, &iseqw_data_type, iseq_ptr);
|
|
|
|
rb_iseq_t *iseq = *iseq_ptr;
|
2015-12-08 16:58:50 +03:00
|
|
|
|
2022-03-23 22:19:48 +03:00
|
|
|
if (!ISEQ_BODY(iseq)) {
|
2018-09-13 16:59:25 +03:00
|
|
|
rb_ibf_load_iseq_complete(iseq);
|
2015-12-08 16:58:50 +03:00
|
|
|
}
|
2015-07-22 01:52:59 +03:00
|
|
|
|
2022-03-23 22:19:48 +03:00
|
|
|
if (!ISEQ_BODY(iseq)->location.label) {
|
2006-12-31 18:02:22 +03:00
|
|
|
rb_raise(rb_eTypeError, "uninitialized InstructionSequence");
|
|
|
|
}
|
|
|
|
return iseq;
|
|
|
|
}
|
|
|
|
|
2015-07-22 01:52:59 +03:00
|
|
|
const rb_iseq_t *
|
|
|
|
rb_iseqw_to_iseq(VALUE iseqw)
|
|
|
|
{
|
|
|
|
return iseqw_check(iseqw);
|
|
|
|
}
|
|
|
|
|
2012-07-25 01:29:24 +04:00
|
|
|
/*
|
|
|
|
* call-seq:
|
|
|
|
* iseq.eval -> obj
|
|
|
|
*
|
|
|
|
* Evaluates the instruction sequence and returns the result.
|
|
|
|
*
|
|
|
|
* RubyVM::InstructionSequence.compile("1 + 2").eval #=> 3
|
|
|
|
*/
|
2006-12-31 18:02:22 +03:00
|
|
|
static VALUE
|
2015-07-22 01:52:59 +03:00
|
|
|
iseqw_eval(VALUE self)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2015-07-22 01:52:59 +03:00
|
|
|
return rb_iseq_eval(iseqw_check(self));
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
2012-07-25 01:29:24 +04:00
|
|
|
/*
|
2012-12-29 10:29:47 +04:00
|
|
|
* Returns a human-readable string representation of this instruction
|
|
|
|
* sequence, including the #label and #path.
|
2012-07-25 01:29:24 +04:00
|
|
|
*/
|
2006-12-31 18:02:22 +03:00
|
|
|
static VALUE
|
2015-07-22 01:52:59 +03:00
|
|
|
iseqw_inspect(VALUE self)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2015-07-22 01:52:59 +03:00
|
|
|
const rb_iseq_t *iseq = iseqw_check(self);
|
2022-03-23 22:19:48 +03:00
|
|
|
const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
|
2017-06-12 07:35:53 +03:00
|
|
|
VALUE klass = rb_class_name(rb_obj_class(self));
|
2015-07-22 01:52:59 +03:00
|
|
|
|
2018-05-12 04:24:18 +03:00
|
|
|
if (!body->location.label) {
|
2017-06-12 07:35:53 +03:00
|
|
|
return rb_sprintf("#<%"PRIsVALUE": uninitialized>", klass);
|
2008-08-21 21:25:43 +04:00
|
|
|
}
|
2015-07-22 01:52:59 +03:00
|
|
|
else {
|
2017-12-23 12:10:34 +03:00
|
|
|
return rb_sprintf("<%"PRIsVALUE":%"PRIsVALUE"@%"PRIsVALUE":%d>",
|
2017-06-12 07:35:53 +03:00
|
|
|
klass,
|
2018-05-12 04:24:18 +03:00
|
|
|
body->location.label, rb_iseq_path(iseq),
|
2017-12-23 12:10:34 +03:00
|
|
|
FIX2INT(rb_iseq_first_lineno(iseq)));
|
2015-07-22 01:52:59 +03:00
|
|
|
}
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
2012-12-29 10:29:47 +04:00
|
|
|
/*
|
|
|
|
* Returns the path of this instruction sequence.
|
|
|
|
*
|
2012-12-29 11:44:54 +04:00
|
|
|
* <code><compiled></code> if the iseq was evaluated from a string.
|
|
|
|
*
|
2012-12-29 10:29:47 +04:00
|
|
|
* For example, using irb:
|
|
|
|
*
|
2012-12-29 11:44:54 +04:00
|
|
|
* iseq = RubyVM::InstructionSequence.compile('num = 1 + 2')
|
2012-12-29 10:29:47 +04:00
|
|
|
* #=> <RubyVM::InstructionSequence:<compiled>@<compiled>>
|
2012-12-29 11:44:54 +04:00
|
|
|
* iseq.path
|
2012-12-29 10:29:47 +04:00
|
|
|
* #=> "<compiled>"
|
|
|
|
*
|
|
|
|
* Using ::compile_file:
|
|
|
|
*
|
|
|
|
* # /tmp/method.rb
|
|
|
|
* def hello
|
|
|
|
* puts "hello, world"
|
|
|
|
* end
|
|
|
|
*
|
|
|
|
* # in irb
|
2012-12-29 11:44:54 +04:00
|
|
|
* > iseq = RubyVM::InstructionSequence.compile_file('/tmp/method.rb')
|
|
|
|
* > iseq.path #=> /tmp/method.rb
|
2012-12-29 10:29:47 +04:00
|
|
|
*/
|
2015-07-22 01:52:59 +03:00
|
|
|
static VALUE
|
|
|
|
iseqw_path(VALUE self)
|
2012-11-30 22:02:43 +04:00
|
|
|
{
|
2015-07-22 01:52:59 +03:00
|
|
|
return rb_iseq_path(iseqw_check(self));
|
2012-11-30 22:02:43 +04:00
|
|
|
}
|
|
|
|
|
2012-12-29 10:29:47 +04:00
|
|
|
/*
|
|
|
|
* Returns the absolute path of this instruction sequence.
|
|
|
|
*
|
2012-12-29 11:44:54 +04:00
|
|
|
* +nil+ if the iseq was evaluated from a string.
|
|
|
|
*
|
2012-12-29 10:29:47 +04:00
|
|
|
* For example, using ::compile_file:
|
|
|
|
*
|
|
|
|
* # /tmp/method.rb
|
|
|
|
* def hello
|
|
|
|
* puts "hello, world"
|
|
|
|
* end
|
|
|
|
*
|
|
|
|
* # in irb
|
2012-12-29 11:44:54 +04:00
|
|
|
* > iseq = RubyVM::InstructionSequence.compile_file('/tmp/method.rb')
|
|
|
|
* > iseq.absolute_path #=> /tmp/method.rb
|
2012-12-29 10:29:47 +04:00
|
|
|
*/
|
2015-07-22 01:52:59 +03:00
|
|
|
static VALUE
|
|
|
|
iseqw_absolute_path(VALUE self)
|
2012-11-30 22:02:43 +04:00
|
|
|
{
|
2017-06-01 03:05:33 +03:00
|
|
|
return rb_iseq_realpath(iseqw_check(self));
|
2012-11-30 22:02:43 +04:00
|
|
|
}
|
|
|
|
|
2012-12-29 10:29:47 +04:00
|
|
|
/* Returns the label of this instruction sequence.
|
2012-12-29 11:44:54 +04:00
|
|
|
*
|
|
|
|
* <code><main></code> if it's at the top level, <code><compiled></code> if it
|
|
|
|
* was evaluated from a string.
|
2012-12-29 10:29:47 +04:00
|
|
|
*
|
|
|
|
* For example, using irb:
|
|
|
|
*
|
2012-12-29 11:44:54 +04:00
|
|
|
* iseq = RubyVM::InstructionSequence.compile('num = 1 + 2')
|
2012-12-29 10:29:47 +04:00
|
|
|
* #=> <RubyVM::InstructionSequence:<compiled>@<compiled>>
|
2012-12-29 11:44:54 +04:00
|
|
|
* iseq.label
|
2012-12-29 10:29:47 +04:00
|
|
|
* #=> "<compiled>"
|
|
|
|
*
|
|
|
|
* Using ::compile_file:
|
|
|
|
*
|
|
|
|
* # /tmp/method.rb
|
|
|
|
* def hello
|
|
|
|
* puts "hello, world"
|
|
|
|
* end
|
|
|
|
*
|
|
|
|
* # in irb
|
2012-12-29 11:44:54 +04:00
|
|
|
* > iseq = RubyVM::InstructionSequence.compile_file('/tmp/method.rb')
|
|
|
|
* > iseq.label #=> <main>
|
2012-12-29 10:29:47 +04:00
|
|
|
*/
|
2015-12-08 18:07:41 +03:00
|
|
|
static VALUE
|
2015-07-22 01:52:59 +03:00
|
|
|
iseqw_label(VALUE self)
|
2012-11-30 22:02:43 +04:00
|
|
|
{
|
2015-07-22 01:52:59 +03:00
|
|
|
return rb_iseq_label(iseqw_check(self));
|
2012-11-30 22:02:43 +04:00
|
|
|
}
|
|
|
|
|
2012-12-29 10:29:47 +04:00
|
|
|
/* Returns the base label of this instruction sequence.
|
|
|
|
*
|
|
|
|
* For example, using irb:
|
|
|
|
*
|
2012-12-29 11:44:54 +04:00
|
|
|
* iseq = RubyVM::InstructionSequence.compile('num = 1 + 2')
|
2012-12-29 10:29:47 +04:00
|
|
|
* #=> <RubyVM::InstructionSequence:<compiled>@<compiled>>
|
2012-12-29 11:44:54 +04:00
|
|
|
* iseq.base_label
|
2012-12-29 10:29:47 +04:00
|
|
|
* #=> "<compiled>"
|
|
|
|
*
|
|
|
|
* Using ::compile_file:
|
|
|
|
*
|
|
|
|
* # /tmp/method.rb
|
|
|
|
* def hello
|
|
|
|
* puts "hello, world"
|
|
|
|
* end
|
|
|
|
*
|
|
|
|
* # in irb
|
2012-12-29 11:44:54 +04:00
|
|
|
* > iseq = RubyVM::InstructionSequence.compile_file('/tmp/method.rb')
|
|
|
|
* > iseq.base_label #=> <main>
|
2012-12-29 10:29:47 +04:00
|
|
|
*/
|
2015-07-22 01:52:59 +03:00
|
|
|
static VALUE
|
|
|
|
iseqw_base_label(VALUE self)
|
2012-11-30 22:02:43 +04:00
|
|
|
{
|
2015-07-22 01:52:59 +03:00
|
|
|
return rb_iseq_base_label(iseqw_check(self));
|
2012-11-30 22:02:43 +04:00
|
|
|
}
|
|
|
|
|
2012-12-29 11:44:54 +04:00
|
|
|
/* Returns the number of the first source line where the instruction sequence
|
|
|
|
* was loaded from.
|
2012-12-29 10:29:47 +04:00
|
|
|
*
|
|
|
|
* For example, using irb:
|
|
|
|
*
|
2012-12-29 11:44:54 +04:00
|
|
|
* iseq = RubyVM::InstructionSequence.compile('num = 1 + 2')
|
2012-12-29 10:29:47 +04:00
|
|
|
* #=> <RubyVM::InstructionSequence:<compiled>@<compiled>>
|
2012-12-29 11:44:54 +04:00
|
|
|
* iseq.first_lineno
|
2012-12-29 10:29:47 +04:00
|
|
|
* #=> 1
|
|
|
|
*/
|
2015-12-08 18:07:41 +03:00
|
|
|
static VALUE
|
2015-07-22 01:52:59 +03:00
|
|
|
iseqw_first_lineno(VALUE self)
|
2012-11-30 22:02:43 +04:00
|
|
|
{
|
2015-07-22 01:52:59 +03:00
|
|
|
return rb_iseq_first_lineno(iseqw_check(self));
|
2013-10-08 16:08:20 +04:00
|
|
|
}
|
|
|
|
|
2015-07-22 00:28:43 +03:00
|
|
|
static VALUE iseq_data_to_ary(const rb_iseq_t *iseq);
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2012-07-25 01:29:24 +04:00
|
|
|
/*
|
|
|
|
* call-seq:
|
|
|
|
* iseq.to_a -> ary
|
|
|
|
*
|
2012-12-29 11:44:54 +04:00
|
|
|
* Returns an Array with 14 elements representing the instruction sequence
|
|
|
|
* with the following data:
|
2012-07-25 01:29:24 +04:00
|
|
|
*
|
|
|
|
* [magic]
|
2012-12-29 11:44:54 +04:00
|
|
|
* A string identifying the data format. <b>Always
|
|
|
|
* +YARVInstructionSequence/SimpleDataFormat+.</b>
|
2012-07-25 01:29:24 +04:00
|
|
|
*
|
|
|
|
* [major_version]
|
|
|
|
* The major version of the instruction sequence.
|
|
|
|
*
|
|
|
|
* [minor_version]
|
|
|
|
* The minor version of the instruction sequence.
|
|
|
|
*
|
|
|
|
* [format_type]
|
2012-12-29 11:44:54 +04:00
|
|
|
* A number identifying the data format. <b>Always 1</b>.
|
2012-07-25 01:29:24 +04:00
|
|
|
*
|
|
|
|
* [misc]
|
2012-12-29 11:44:54 +04:00
|
|
|
* A hash containing:
|
|
|
|
*
|
|
|
|
* [+:arg_size+]
|
|
|
|
* the total number of arguments taken by the method or the block (0 if
|
|
|
|
* _iseq_ doesn't represent a method or block)
|
|
|
|
* [+:local_size+]
|
|
|
|
* the number of local variables + 1
|
|
|
|
* [+:stack_max+]
|
|
|
|
* used in calculating the stack depth at which a SystemStackError is
|
|
|
|
* thrown.
|
|
|
|
*
|
|
|
|
* [#label]
|
2012-07-25 01:29:24 +04:00
|
|
|
* The name of the context (block, method, class, module, etc.) that this
|
2012-12-29 11:44:54 +04:00
|
|
|
* instruction sequence belongs to.
|
|
|
|
*
|
|
|
|
* <code><main></code> if it's at the top level, <code><compiled></code> if
|
|
|
|
* it was evaluated from a string.
|
2012-07-25 01:29:24 +04:00
|
|
|
*
|
2012-12-29 11:44:54 +04:00
|
|
|
* [#path]
|
2012-07-25 01:29:24 +04:00
|
|
|
* The relative path to the Ruby file where the instruction sequence was
|
2012-12-29 11:44:54 +04:00
|
|
|
* loaded from.
|
|
|
|
*
|
|
|
|
* <code><compiled></code> if the iseq was evaluated from a string.
|
2012-07-25 01:29:24 +04:00
|
|
|
*
|
2012-12-29 11:44:54 +04:00
|
|
|
* [#absolute_path]
|
2012-07-25 01:29:24 +04:00
|
|
|
* The absolute path to the Ruby file where the instruction sequence was
|
2012-12-29 11:44:54 +04:00
|
|
|
* loaded from.
|
2012-07-25 01:29:24 +04:00
|
|
|
*
|
2012-12-29 11:44:54 +04:00
|
|
|
* +nil+ if the iseq was evaluated from a string.
|
|
|
|
*
|
|
|
|
* [#first_lineno]
|
2012-07-25 01:29:24 +04:00
|
|
|
* The number of the first source line where the instruction sequence was
|
|
|
|
* loaded from.
|
|
|
|
*
|
|
|
|
* [type]
|
2012-12-29 11:44:54 +04:00
|
|
|
* The type of the instruction sequence.
|
|
|
|
*
|
|
|
|
* Valid values are +:top+, +:method+, +:block+, +:class+, +:rescue+,
|
2018-01-05 03:49:41 +03:00
|
|
|
* +:ensure+, +:eval+, +:main+, and +plain+.
|
2012-07-25 01:29:24 +04:00
|
|
|
*
|
|
|
|
* [locals]
|
|
|
|
* An array containing the names of all arguments and local variables as
|
|
|
|
* symbols.
|
|
|
|
*
|
2014-11-03 02:14:21 +03:00
|
|
|
* [params]
|
|
|
|
* An Hash object containing parameter information.
|
2012-07-25 01:29:24 +04:00
|
|
|
*
|
|
|
|
* More info about these values can be found in +vm_core.h+.
|
|
|
|
*
|
|
|
|
* [catch_table]
|
|
|
|
* A list of exceptions and control flow operators (rescue, next, redo,
|
|
|
|
* break, etc.).
|
|
|
|
*
|
|
|
|
* [bytecode]
|
|
|
|
* An array of arrays containing the instruction names and operands that
|
|
|
|
* make up the body of the instruction sequence.
|
|
|
|
*
|
2014-11-03 02:14:21 +03:00
|
|
|
* Note that this format is MRI specific and version dependent.
|
|
|
|
*
|
2012-07-25 01:29:24 +04:00
|
|
|
*/
|
2006-12-31 18:02:22 +03:00
|
|
|
static VALUE
|
2015-07-22 01:52:59 +03:00
|
|
|
iseqw_to_a(VALUE self)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2015-07-22 01:52:59 +03:00
|
|
|
const rb_iseq_t *iseq = iseqw_check(self);
|
2006-12-31 18:02:22 +03:00
|
|
|
return iseq_data_to_ary(iseq);
|
|
|
|
}
|
|
|
|
|
2018-01-01 16:18:55 +03:00
|
|
|
#if VM_INSN_INFO_TABLE_IMPL == 1 /* binary search */
|
2017-12-20 10:38:24 +03:00
|
|
|
static const struct iseq_insn_info_entry *
|
|
|
|
get_insn_info_binary_search(const rb_iseq_t *iseq, size_t pos)
|
|
|
|
{
|
2022-03-23 22:19:48 +03:00
|
|
|
const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
|
2018-05-12 04:24:18 +03:00
|
|
|
size_t size = body->insns_info.size;
|
|
|
|
const struct iseq_insn_info_entry *insns_info = body->insns_info.body;
|
|
|
|
const unsigned int *positions = body->insns_info.positions;
|
2017-12-20 10:38:24 +03:00
|
|
|
const int debug = 0;
|
|
|
|
|
|
|
|
if (debug) {
|
|
|
|
printf("size: %"PRIuSIZE"\n", size);
|
|
|
|
printf("insns_info[%"PRIuSIZE"]: position: %d, line: %d, pos: %"PRIuSIZE"\n",
|
2018-01-01 15:51:21 +03:00
|
|
|
(size_t)0, positions[0], insns_info[0].line_no, pos);
|
2017-12-20 10:38:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (size == 0) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
else if (size == 1) {
|
|
|
|
return &insns_info[0];
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
size_t l = 1, r = size - 1;
|
|
|
|
while (l <= r) {
|
|
|
|
size_t m = l + (r - l) / 2;
|
2018-01-01 15:51:21 +03:00
|
|
|
if (positions[m] == pos) {
|
2017-12-20 10:38:24 +03:00
|
|
|
return &insns_info[m];
|
|
|
|
}
|
2018-01-01 15:51:21 +03:00
|
|
|
if (positions[m] < pos) {
|
2017-12-20 10:38:24 +03:00
|
|
|
l = m + 1;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
r = m - 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (l >= size) {
|
|
|
|
return &insns_info[size-1];
|
|
|
|
}
|
2018-01-01 15:51:21 +03:00
|
|
|
if (positions[l] > pos) {
|
2017-12-20 10:38:24 +03:00
|
|
|
return &insns_info[l-1];
|
|
|
|
}
|
|
|
|
return &insns_info[l];
|
|
|
|
}
|
|
|
|
}
|
2007-12-20 00:39:08 +03:00
|
|
|
|
2018-01-01 16:18:55 +03:00
|
|
|
static const struct iseq_insn_info_entry *
|
|
|
|
get_insn_info(const rb_iseq_t *iseq, size_t pos)
|
|
|
|
{
|
|
|
|
return get_insn_info_binary_search(iseq, pos);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2018-01-09 17:05:23 +03:00
|
|
|
#if VM_INSN_INFO_TABLE_IMPL == 2 /* succinct bitvector */
|
|
|
|
static const struct iseq_insn_info_entry *
|
|
|
|
get_insn_info_succinct_bitvector(const rb_iseq_t *iseq, size_t pos)
|
|
|
|
{
|
2022-03-23 22:19:48 +03:00
|
|
|
const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
|
2018-05-12 04:24:18 +03:00
|
|
|
size_t size = body->insns_info.size;
|
|
|
|
const struct iseq_insn_info_entry *insns_info = body->insns_info.body;
|
2018-01-09 17:05:23 +03:00
|
|
|
const int debug = 0;
|
|
|
|
|
|
|
|
if (debug) {
|
2018-05-19 06:43:00 +03:00
|
|
|
#if VM_CHECK_MODE > 0
|
|
|
|
const unsigned int *positions = body->insns_info.positions;
|
|
|
|
printf("size: %"PRIuSIZE"\n", size);
|
|
|
|
printf("insns_info[%"PRIuSIZE"]: position: %d, line: %d, pos: %"PRIuSIZE"\n",
|
|
|
|
(size_t)0, positions[0], insns_info[0].line_no, pos);
|
|
|
|
#else
|
|
|
|
printf("size: %"PRIuSIZE"\n", size);
|
|
|
|
printf("insns_info[%"PRIuSIZE"]: line: %d, pos: %"PRIuSIZE"\n",
|
|
|
|
(size_t)0, insns_info[0].line_no, pos);
|
|
|
|
#endif
|
2018-01-09 17:05:23 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (size == 0) {
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
else if (size == 1) {
|
|
|
|
return &insns_info[0];
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
int index;
|
2018-05-12 04:24:18 +03:00
|
|
|
VM_ASSERT(body->insns_info.succ_index_table != NULL);
|
|
|
|
index = succ_index_lookup(body->insns_info.succ_index_table, (int)pos);
|
2018-01-09 17:05:23 +03:00
|
|
|
return &insns_info[index-1];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct iseq_insn_info_entry *
|
|
|
|
get_insn_info(const rb_iseq_t *iseq, size_t pos)
|
|
|
|
{
|
|
|
|
return get_insn_info_succinct_bitvector(iseq, pos);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2018-01-01 16:18:55 +03:00
|
|
|
#if VM_CHECK_MODE > 0 || VM_INSN_INFO_TABLE_IMPL == 0
|
2017-11-09 09:57:24 +03:00
|
|
|
static const struct iseq_insn_info_entry *
|
2017-12-20 10:38:24 +03:00
|
|
|
get_insn_info_linear_search(const rb_iseq_t *iseq, size_t pos)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2022-03-23 22:19:48 +03:00
|
|
|
const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
|
2018-05-12 04:24:18 +03:00
|
|
|
size_t i = 0, size = body->insns_info.size;
|
|
|
|
const struct iseq_insn_info_entry *insns_info = body->insns_info.body;
|
|
|
|
const unsigned int *positions = body->insns_info.positions;
|
2011-08-24 10:31:15 +04:00
|
|
|
const int debug = 0;
|
|
|
|
|
|
|
|
if (debug) {
|
2016-09-13 15:33:13 +03:00
|
|
|
printf("size: %"PRIuSIZE"\n", size);
|
2017-11-09 09:57:24 +03:00
|
|
|
printf("insns_info[%"PRIuSIZE"]: position: %d, line: %d, pos: %"PRIuSIZE"\n",
|
2018-01-01 15:51:21 +03:00
|
|
|
i, positions[i], insns_info[i].line_no, pos);
|
2011-08-24 10:31:15 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (size == 0) {
|
2017-11-09 09:57:24 +03:00
|
|
|
return NULL;
|
2011-08-24 10:31:15 +04:00
|
|
|
}
|
|
|
|
else if (size == 1) {
|
2017-11-09 09:57:24 +03:00
|
|
|
return &insns_info[0];
|
2011-08-24 10:31:15 +04:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
for (i=1; i<size; i++) {
|
2017-11-09 09:57:24 +03:00
|
|
|
if (debug) printf("insns_info[%"PRIuSIZE"]: position: %d, line: %d, pos: %"PRIuSIZE"\n",
|
2018-01-01 15:51:21 +03:00
|
|
|
i, positions[i], insns_info[i].line_no, pos);
|
2022-07-21 19:23:58 +03:00
|
|
|
|
2018-01-01 15:51:21 +03:00
|
|
|
if (positions[i] == pos) {
|
2017-11-09 09:57:24 +03:00
|
|
|
return &insns_info[i];
|
2011-09-15 09:24:42 +04:00
|
|
|
}
|
2018-01-01 15:51:21 +03:00
|
|
|
if (positions[i] > pos) {
|
2017-11-09 09:57:24 +03:00
|
|
|
return &insns_info[i-1];
|
2011-09-15 09:24:42 +04:00
|
|
|
}
|
2011-08-24 10:31:15 +04:00
|
|
|
}
|
|
|
|
}
|
2017-11-09 09:57:24 +03:00
|
|
|
return &insns_info[i-1];
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
2018-01-01 16:18:55 +03:00
|
|
|
#endif
|
|
|
|
|
|
|
|
#if VM_INSN_INFO_TABLE_IMPL == 0 /* linear search */
|
|
|
|
static const struct iseq_insn_info_entry *
|
|
|
|
get_insn_info(const rb_iseq_t *iseq, size_t pos)
|
|
|
|
{
|
|
|
|
return get_insn_info_linear_search(iseq, pos);
|
|
|
|
}
|
|
|
|
#endif
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2018-01-01 16:18:55 +03:00
|
|
|
#if VM_CHECK_MODE > 0 && VM_INSN_INFO_TABLE_IMPL > 0
|
2017-12-20 10:38:24 +03:00
|
|
|
static void
|
2018-03-21 05:20:37 +03:00
|
|
|
validate_get_insn_info(const rb_iseq_t *iseq)
|
2017-12-20 10:38:24 +03:00
|
|
|
{
|
2022-03-23 22:19:48 +03:00
|
|
|
const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
|
2017-12-20 10:38:24 +03:00
|
|
|
size_t i;
|
2018-05-12 04:24:18 +03:00
|
|
|
for (i = 0; i < body->iseq_size; i++) {
|
2018-01-01 16:18:55 +03:00
|
|
|
if (get_insn_info_linear_search(iseq, i) != get_insn_info(iseq, i)) {
|
|
|
|
rb_bug("validate_get_insn_info: get_insn_info_linear_search(iseq, %"PRIuSIZE") != get_insn_info(iseq, %"PRIuSIZE")", i, i);
|
2017-12-20 10:38:24 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2017-11-10 08:26:52 +03:00
|
|
|
unsigned int
|
|
|
|
rb_iseq_line_no(const rb_iseq_t *iseq, size_t pos)
|
2007-12-20 00:39:08 +03:00
|
|
|
{
|
2018-01-01 16:18:55 +03:00
|
|
|
const struct iseq_insn_info_entry *entry = get_insn_info(iseq, pos);
|
2015-07-25 00:01:09 +03:00
|
|
|
|
2007-12-20 00:39:08 +03:00
|
|
|
if (entry) {
|
|
|
|
return entry->line_no;
|
|
|
|
}
|
2007-12-20 10:44:03 +03:00
|
|
|
else {
|
|
|
|
return 0;
|
|
|
|
}
|
2007-12-20 00:39:08 +03:00
|
|
|
}
|
|
|
|
|
2021-06-08 11:57:44 +03:00
|
|
|
#ifdef USE_ISEQ_NODE_ID
|
2021-04-30 12:54:46 +03:00
|
|
|
int
|
|
|
|
rb_iseq_node_id(const rb_iseq_t *iseq, size_t pos)
|
|
|
|
{
|
|
|
|
const struct iseq_insn_info_entry *entry = get_insn_info(iseq, pos);
|
|
|
|
|
|
|
|
if (entry) {
|
|
|
|
return entry->node_id;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2023-03-07 08:34:31 +03:00
|
|
|
rb_event_flag_t
|
2017-11-14 15:58:36 +03:00
|
|
|
rb_iseq_event_flags(const rb_iseq_t *iseq, size_t pos)
|
|
|
|
{
|
2018-01-01 16:18:55 +03:00
|
|
|
const struct iseq_insn_info_entry *entry = get_insn_info(iseq, pos);
|
2017-11-14 15:58:36 +03:00
|
|
|
if (entry) {
|
|
|
|
return entry->events;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-20 08:33:04 +03:00
|
|
|
void
|
|
|
|
rb_iseq_clear_event_flags(const rb_iseq_t *iseq, size_t pos, rb_event_flag_t reset)
|
|
|
|
{
|
|
|
|
struct iseq_insn_info_entry *entry = (struct iseq_insn_info_entry *)get_insn_info(iseq, pos);
|
|
|
|
if (entry) {
|
2018-10-20 08:33:13 +03:00
|
|
|
entry->events &= ~reset;
|
2018-12-06 13:52:27 +03:00
|
|
|
if (!(entry->events & iseq->aux.exec.global_trace_events)) {
|
2018-10-20 08:44:12 +03:00
|
|
|
void rb_iseq_trace_flag_cleared(const rb_iseq_t *iseq, size_t pos);
|
2018-10-20 08:33:04 +03:00
|
|
|
rb_iseq_trace_flag_cleared(iseq, pos);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-10 02:08:01 +03:00
|
|
|
static VALUE
|
|
|
|
local_var_name(const rb_iseq_t *diseq, VALUE level, VALUE op)
|
|
|
|
{
|
|
|
|
VALUE i;
|
2018-01-30 05:08:30 +03:00
|
|
|
VALUE name;
|
2017-11-10 02:08:01 +03:00
|
|
|
ID lid;
|
2018-03-19 03:32:52 +03:00
|
|
|
int idx;
|
2017-11-10 02:08:01 +03:00
|
|
|
|
|
|
|
for (i = 0; i < level; i++) {
|
2022-03-23 22:19:48 +03:00
|
|
|
diseq = ISEQ_BODY(diseq)->parent_iseq;
|
2017-11-10 02:08:01 +03:00
|
|
|
}
|
2022-03-23 22:19:48 +03:00
|
|
|
idx = ISEQ_BODY(diseq)->local_table_size - (int)op - 1;
|
|
|
|
lid = ISEQ_BODY(diseq)->local_table[idx];
|
2018-01-30 05:08:30 +03:00
|
|
|
name = rb_id2str(lid);
|
|
|
|
if (!name) {
|
2018-03-19 03:32:52 +03:00
|
|
|
name = rb_str_new_cstr("?");
|
2018-01-30 05:08:30 +03:00
|
|
|
}
|
2023-11-14 16:10:08 +03:00
|
|
|
else if (!rb_is_local_id(lid)) {
|
2018-01-30 05:08:30 +03:00
|
|
|
name = rb_str_inspect(name);
|
|
|
|
}
|
2018-03-19 03:32:52 +03:00
|
|
|
else {
|
|
|
|
name = rb_str_dup(name);
|
|
|
|
}
|
|
|
|
rb_str_catf(name, "@%d", idx);
|
2018-01-30 05:08:30 +03:00
|
|
|
return name;
|
2017-11-10 02:08:01 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
int rb_insn_unified_local_var_level(VALUE);
|
2020-10-20 13:32:10 +03:00
|
|
|
VALUE rb_dump_literal(VALUE lit);
|
2017-11-10 02:08:01 +03:00
|
|
|
|
2012-10-04 16:31:05 +04:00
|
|
|
VALUE
|
2014-06-18 10:16:39 +04:00
|
|
|
rb_insn_operand_intern(const rb_iseq_t *iseq,
|
2013-03-06 10:30:03 +04:00
|
|
|
VALUE insn, int op_no, VALUE op,
|
2014-06-18 10:16:39 +04:00
|
|
|
int len, size_t pos, const VALUE *pnop, VALUE child)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2008-04-03 04:00:48 +04:00
|
|
|
const char *types = insn_op_types(insn);
|
2006-12-31 18:02:22 +03:00
|
|
|
char type = types[op_no];
|
2015-12-08 16:58:50 +03:00
|
|
|
VALUE ret = Qundef;
|
2006-12-31 18:02:22 +03:00
|
|
|
|
|
|
|
switch (type) {
|
2007-05-11 10:26:06 +04:00
|
|
|
case TS_OFFSET: /* LONG */
|
2010-10-15 18:11:26 +04:00
|
|
|
ret = rb_sprintf("%"PRIdVALUE, (VALUE)(pos + len + op));
|
2006-12-31 18:02:22 +03:00
|
|
|
break;
|
|
|
|
|
2007-05-11 10:26:06 +04:00
|
|
|
case TS_NUM: /* ULONG */
|
2018-01-17 09:23:57 +03:00
|
|
|
if (insn == BIN(defined) && op_no == 0) {
|
|
|
|
enum defined_type deftype = (enum defined_type)op;
|
2019-11-06 09:47:32 +03:00
|
|
|
switch (deftype) {
|
|
|
|
case DEFINED_FUNC:
|
|
|
|
ret = rb_fstring_lit("func");
|
|
|
|
break;
|
|
|
|
case DEFINED_REF:
|
|
|
|
ret = rb_fstring_lit("ref");
|
|
|
|
break;
|
|
|
|
case DEFINED_CONST_FROM:
|
|
|
|
ret = rb_fstring_lit("constant-from");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
ret = rb_iseq_defined_string(deftype);
|
|
|
|
break;
|
iseq.c: dump type of branchiftype on disasm
This makes easier to debug scripts related to r59950.
* before
$ ./ruby --dump=insns -e '"#{a}"'
== disasm: #<ISeq:<main>@-e>============================================
0000 putobject "" ( 1)[Li]
0002 putself
0003 opt_send_without_block <callinfo!mid:a, argc:0, FCALL|VCALL|ARGS_SIMPLE>, <callcache>
0006 dup
0007 branchiftype 5, 15
0010 dup
0011 opt_send_without_block <callinfo!mid:to_s, argc:0, FCALL|ARGS_SIMPLE>, <callcache>
0014 tostring
0015 concatstrings 2
0017 leave
* after
$ ./ruby --dump=insns -e '"#{a}"'
== disasm: #<ISeq:<main>@-e>============================================
0000 putobject "" ( 1)[Li]
0002 putself
0003 opt_send_without_block <callinfo!mid:a, argc:0, FCALL|VCALL|ARGS_SIMPLE>, <callcache>
0006 dup
0007 branchiftype T_STRING, 15
0010 dup
0011 opt_send_without_block <callinfo!mid:to_s, argc:0, FCALL|ARGS_SIMPLE>, <callcache>
0014 tostring
0015 concatstrings 2
0017 leave
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@61217 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2017-12-13 19:07:52 +03:00
|
|
|
}
|
2018-01-17 09:23:57 +03:00
|
|
|
if (ret) break;
|
|
|
|
}
|
2018-04-21 13:52:52 +03:00
|
|
|
else if (insn == BIN(checktype) && op_no == 0) {
|
2018-01-17 09:23:57 +03:00
|
|
|
const char *type_str = rb_type_str((enum ruby_value_type)op);
|
|
|
|
if (type_str) {
|
|
|
|
ret = rb_str_new_cstr(type_str); break;
|
iseq.c: dump type of branchiftype on disasm
This makes easier to debug scripts related to r59950.
* before
$ ./ruby --dump=insns -e '"#{a}"'
== disasm: #<ISeq:<main>@-e>============================================
0000 putobject "" ( 1)[Li]
0002 putself
0003 opt_send_without_block <callinfo!mid:a, argc:0, FCALL|VCALL|ARGS_SIMPLE>, <callcache>
0006 dup
0007 branchiftype 5, 15
0010 dup
0011 opt_send_without_block <callinfo!mid:to_s, argc:0, FCALL|ARGS_SIMPLE>, <callcache>
0014 tostring
0015 concatstrings 2
0017 leave
* after
$ ./ruby --dump=insns -e '"#{a}"'
== disasm: #<ISeq:<main>@-e>============================================
0000 putobject "" ( 1)[Li]
0002 putself
0003 opt_send_without_block <callinfo!mid:a, argc:0, FCALL|VCALL|ARGS_SIMPLE>, <callcache>
0006 dup
0007 branchiftype T_STRING, 15
0010 dup
0011 opt_send_without_block <callinfo!mid:to_s, argc:0, FCALL|ARGS_SIMPLE>, <callcache>
0014 tostring
0015 concatstrings 2
0017 leave
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@61217 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2017-12-13 19:07:52 +03:00
|
|
|
}
|
|
|
|
}
|
2018-01-17 09:23:57 +03:00
|
|
|
ret = rb_sprintf("%"PRIuVALUE, op);
|
2006-12-31 18:02:22 +03:00
|
|
|
break;
|
|
|
|
|
2012-10-04 17:52:20 +04:00
|
|
|
case TS_LINDEX:{
|
2017-11-10 02:08:01 +03:00
|
|
|
int level;
|
2017-12-23 03:55:29 +03:00
|
|
|
if (types[op_no+1] == TS_NUM && pnop) {
|
2018-01-30 05:08:30 +03:00
|
|
|
ret = local_var_name(iseq, *pnop, op - VM_ENV_DATA_SIZE);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
2017-11-10 02:08:01 +03:00
|
|
|
else if ((level = rb_insn_unified_local_var_level(insn)) >= 0) {
|
2018-01-30 05:08:30 +03:00
|
|
|
ret = local_var_name(iseq, (VALUE)level, op - VM_ENV_DATA_SIZE);
|
2017-11-10 02:08:01 +03:00
|
|
|
}
|
2007-05-11 10:26:06 +04:00
|
|
|
else {
|
|
|
|
ret = rb_inspect(INT2FIX(op));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case TS_ID: /* ID (symbol) */
|
2017-12-22 03:29:38 +03:00
|
|
|
ret = rb_inspect(ID2SYM(op));
|
|
|
|
break;
|
2008-05-14 07:48:39 +04:00
|
|
|
|
2007-05-11 10:26:06 +04:00
|
|
|
case TS_VALUE: /* VALUE */
|
2009-02-18 08:33:36 +03:00
|
|
|
op = obj_resurrect(op);
|
2017-12-22 03:29:38 +03:00
|
|
|
if (insn == BIN(defined) && op_no == 1 && FIXNUM_P(op)) {
|
|
|
|
/* should be DEFINED_REF */
|
|
|
|
int type = NUM2INT(op);
|
|
|
|
if (type) {
|
|
|
|
if (type & 1) {
|
|
|
|
ret = rb_sprintf(":$%c", (type >> 1));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ret = rb_sprintf(":$%d", (type >> 1));
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2020-10-20 13:32:10 +03:00
|
|
|
ret = rb_dump_literal(op);
|
2008-05-14 07:48:39 +04:00
|
|
|
if (CLASS_OF(op) == rb_cISeq) {
|
2012-10-04 16:31:05 +04:00
|
|
|
if (child) {
|
|
|
|
rb_ary_push(child, op);
|
|
|
|
}
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2007-05-11 10:26:06 +04:00
|
|
|
case TS_ISEQ: /* iseq */
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2015-12-08 16:58:50 +03:00
|
|
|
if (op) {
|
|
|
|
const rb_iseq_t *iseq = rb_iseq_check((rb_iseq_t *)op);
|
2022-03-23 22:19:48 +03:00
|
|
|
ret = ISEQ_BODY(iseq)->location.label;
|
2006-12-31 18:02:22 +03:00
|
|
|
if (child) {
|
2015-07-22 01:52:59 +03:00
|
|
|
rb_ary_push(child, (VALUE)iseq);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
ret = rb_str_new2("nil");
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2007-05-11 10:26:06 +04:00
|
|
|
case TS_IC:
|
New constant caching insn: opt_getconstant_path
Previously YARV bytecode implemented constant caching by having a pair
of instructions, opt_getinlinecache and opt_setinlinecache, wrapping a
series of getconstant calls (with putobject providing supporting
arguments).
This commit replaces that pattern with a new instruction,
opt_getconstant_path, handling both getting/setting the inline cache and
fetching the constant on a cache miss.
This is implemented by storing the full constant path as a
null-terminated array of IDs inside of the IC structure. idNULL is used
to signal an absolute constant reference.
$ ./miniruby --dump=insns -e '::Foo::Bar::Baz'
== disasm: #<ISeq:<main>@-e:1 (1,0)-(1,13)> (catch: FALSE)
0000 opt_getconstant_path <ic:0 ::Foo::Bar::Baz> ( 1)[Li]
0002 leave
The motivation for this is that we had increasingly found the need to
disassemble the instructions between the opt_getinlinecache and
opt_setinlinecache in order to determine the constant we are fetching,
or otherwise store metadata.
This disassembly was done:
* In opt_setinlinecache, to register the IC against the constant names
it is using for granular invalidation.
* In rb_iseq_free, to unregister the IC from the invalidation table.
* In YJIT to find the position of a opt_getinlinecache instruction to
invalidate it when the cache is populated
* In YJIT to register the constant names being used for invalidation.
With this change we no longe need disassemly for these (in fact
rb_iseq_each is now unused), as the list of constant names being
referenced is held in the IC. This should also make it possible to make
more optimizations in the future.
This may also reduce the size of iseqs, as previously each segment
required 32 bytes (on 64-bit platforms) for each constant segment. This
implementation only stores one ID per-segment.
There should be no significant performance change between this and the
previous implementation. Previously opt_getinlinecache was a "leaf"
instruction, but it included a jump (almost always to a separate cache
line). Now opt_getconstant_path is a non-leaf (it may
raise/autoload/call const_missing) but it does not jump. These seem to
even out.
2022-08-10 20:35:48 +03:00
|
|
|
{
|
|
|
|
ret = rb_sprintf("<ic:%"PRIdPTRDIFF" ", (union iseq_inline_storage_entry *)op - ISEQ_BODY(iseq)->is_entries);
|
|
|
|
const ID *segments = ((IC)op)->segments;
|
|
|
|
rb_str_cat2(ret, rb_id2name(*segments++));
|
|
|
|
while (*segments) {
|
|
|
|
rb_str_catf(ret, "::%s", rb_id2name(*segments++));
|
|
|
|
}
|
|
|
|
rb_str_cat2(ret, ">");
|
|
|
|
}
|
|
|
|
break;
|
2019-10-12 03:06:41 +03:00
|
|
|
case TS_IVC:
|
2022-02-02 17:14:59 +03:00
|
|
|
case TS_ICVARC:
|
2018-03-19 21:21:54 +03:00
|
|
|
case TS_ISE:
|
2022-03-23 22:19:48 +03:00
|
|
|
ret = rb_sprintf("<is:%"PRIdPTRDIFF">", (union iseq_inline_storage_entry *)op - ISEQ_BODY(iseq)->is_entries);
|
2006-12-31 18:02:22 +03:00
|
|
|
break;
|
|
|
|
|
2019-07-31 04:36:05 +03:00
|
|
|
case TS_CALLDATA:
|
* insns.def (send, invokesuper, invokeblock, opt_*), vm_core.h:
use only a `ci' (rb_call_info_t) parameter instead of using
parameters such as `op_id', 'op_argc', `blockiseq' and flag.
These information are stored in rb_call_info_t at the compile
time.
This technique simplifies parameter passings at related
function calls (~10% speedups for simple mehtod invocation at
my machine).
`rb_call_info_t' also has new function pointer variable `call'.
This `call' variable enables to customize method (block)
invocation process for each place. However, it always call
`vm_call_general()' at this changes.
`rb_call_info_t' also has temporary variables for method
(block) invocation.
* vm_core.h, compile.c, insns.def: introduce VM_CALL_ARGS_SKIP_SETUP
VM_CALL macro. This flag indicates that this call can skip
caller_setup (block arg and splat arg).
* compile.c: catch up above changes.
* iseq.c: catch up above changes (especially for TS_CALLINFO).
* tool/instruction.rb: catch up above chagnes.
* vm_insnhelper.c, vm_insnhelper.h: ditto. Macros and functions
parameters are changed.
* vm_eval.c (vm_call0): ditto (it will be rewriten soon).
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37180 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-10-14 20:59:05 +04:00
|
|
|
{
|
2019-07-31 04:36:05 +03:00
|
|
|
struct rb_call_data *cd = (struct rb_call_data *)op;
|
VALUE size packed callinfo (ci).
Now, rb_call_info contains how to call the method with tuple of
(mid, orig_argc, flags, kwarg). Most of cases, kwarg == NULL and
mid+argc+flags only requires 64bits. So this patch packed
rb_call_info to VALUE (1 word) on such cases. If we can not
represent it in VALUE, then use imemo_callinfo which contains
conventional callinfo (rb_callinfo, renamed from rb_call_info).
iseq->body->ci_kw_size is removed because all of callinfo is VALUE
size (packed ci or a pointer to imemo_callinfo).
To access ci information, we need to use these functions:
vm_ci_mid(ci), _flag(ci), _argc(ci), _kwarg(ci).
struct rb_call_info_kw_arg is renamed to rb_callinfo_kwarg.
rb_funcallv_with_cc() and rb_method_basic_definition_p_with_cc()
is temporary removed because cd->ci should be marked.
2020-01-08 02:20:36 +03:00
|
|
|
const struct rb_callinfo *ci = cd->ci;
|
* insns.def (send, invokesuper, invokeblock, opt_*), vm_core.h:
use only a `ci' (rb_call_info_t) parameter instead of using
parameters such as `op_id', 'op_argc', `blockiseq' and flag.
These information are stored in rb_call_info_t at the compile
time.
This technique simplifies parameter passings at related
function calls (~10% speedups for simple mehtod invocation at
my machine).
`rb_call_info_t' also has new function pointer variable `call'.
This `call' variable enables to customize method (block)
invocation process for each place. However, it always call
`vm_call_general()' at this changes.
`rb_call_info_t' also has temporary variables for method
(block) invocation.
* vm_core.h, compile.c, insns.def: introduce VM_CALL_ARGS_SKIP_SETUP
VM_CALL macro. This flag indicates that this call can skip
caller_setup (block arg and splat arg).
* compile.c: catch up above changes.
* iseq.c: catch up above changes (especially for TS_CALLINFO).
* tool/instruction.rb: catch up above chagnes.
* vm_insnhelper.c, vm_insnhelper.h: ditto. Macros and functions
parameters are changed.
* vm_eval.c (vm_call0): ditto (it will be rewriten soon).
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37180 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-10-14 20:59:05 +04:00
|
|
|
VALUE ary = rb_ary_new();
|
VALUE size packed callinfo (ci).
Now, rb_call_info contains how to call the method with tuple of
(mid, orig_argc, flags, kwarg). Most of cases, kwarg == NULL and
mid+argc+flags only requires 64bits. So this patch packed
rb_call_info to VALUE (1 word) on such cases. If we can not
represent it in VALUE, then use imemo_callinfo which contains
conventional callinfo (rb_callinfo, renamed from rb_call_info).
iseq->body->ci_kw_size is removed because all of callinfo is VALUE
size (packed ci or a pointer to imemo_callinfo).
To access ci information, we need to use these functions:
vm_ci_mid(ci), _flag(ci), _argc(ci), _kwarg(ci).
struct rb_call_info_kw_arg is renamed to rb_callinfo_kwarg.
rb_funcallv_with_cc() and rb_method_basic_definition_p_with_cc()
is temporary removed because cd->ci should be marked.
2020-01-08 02:20:36 +03:00
|
|
|
ID mid = vm_ci_mid(ci);
|
* insns.def (send, invokesuper, invokeblock, opt_*), vm_core.h:
use only a `ci' (rb_call_info_t) parameter instead of using
parameters such as `op_id', 'op_argc', `blockiseq' and flag.
These information are stored in rb_call_info_t at the compile
time.
This technique simplifies parameter passings at related
function calls (~10% speedups for simple mehtod invocation at
my machine).
`rb_call_info_t' also has new function pointer variable `call'.
This `call' variable enables to customize method (block)
invocation process for each place. However, it always call
`vm_call_general()' at this changes.
`rb_call_info_t' also has temporary variables for method
(block) invocation.
* vm_core.h, compile.c, insns.def: introduce VM_CALL_ARGS_SKIP_SETUP
VM_CALL macro. This flag indicates that this call can skip
caller_setup (block arg and splat arg).
* compile.c: catch up above changes.
* iseq.c: catch up above changes (especially for TS_CALLINFO).
* tool/instruction.rb: catch up above chagnes.
* vm_insnhelper.c, vm_insnhelper.h: ditto. Macros and functions
parameters are changed.
* vm_eval.c (vm_call0): ditto (it will be rewriten soon).
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37180 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-10-14 20:59:05 +04:00
|
|
|
|
VALUE size packed callinfo (ci).
Now, rb_call_info contains how to call the method with tuple of
(mid, orig_argc, flags, kwarg). Most of cases, kwarg == NULL and
mid+argc+flags only requires 64bits. So this patch packed
rb_call_info to VALUE (1 word) on such cases. If we can not
represent it in VALUE, then use imemo_callinfo which contains
conventional callinfo (rb_callinfo, renamed from rb_call_info).
iseq->body->ci_kw_size is removed because all of callinfo is VALUE
size (packed ci or a pointer to imemo_callinfo).
To access ci information, we need to use these functions:
vm_ci_mid(ci), _flag(ci), _argc(ci), _kwarg(ci).
struct rb_call_info_kw_arg is renamed to rb_callinfo_kwarg.
rb_funcallv_with_cc() and rb_method_basic_definition_p_with_cc()
is temporary removed because cd->ci should be marked.
2020-01-08 02:20:36 +03:00
|
|
|
if (mid) {
|
|
|
|
rb_ary_push(ary, rb_sprintf("mid:%"PRIsVALUE, rb_id2str(mid)));
|
* insns.def (send, invokesuper, invokeblock, opt_*), vm_core.h:
use only a `ci' (rb_call_info_t) parameter instead of using
parameters such as `op_id', 'op_argc', `blockiseq' and flag.
These information are stored in rb_call_info_t at the compile
time.
This technique simplifies parameter passings at related
function calls (~10% speedups for simple mehtod invocation at
my machine).
`rb_call_info_t' also has new function pointer variable `call'.
This `call' variable enables to customize method (block)
invocation process for each place. However, it always call
`vm_call_general()' at this changes.
`rb_call_info_t' also has temporary variables for method
(block) invocation.
* vm_core.h, compile.c, insns.def: introduce VM_CALL_ARGS_SKIP_SETUP
VM_CALL macro. This flag indicates that this call can skip
caller_setup (block arg and splat arg).
* compile.c: catch up above changes.
* iseq.c: catch up above changes (especially for TS_CALLINFO).
* tool/instruction.rb: catch up above chagnes.
* vm_insnhelper.c, vm_insnhelper.h: ditto. Macros and functions
parameters are changed.
* vm_eval.c (vm_call0): ditto (it will be rewriten soon).
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37180 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-10-14 20:59:05 +04:00
|
|
|
}
|
|
|
|
|
VALUE size packed callinfo (ci).
Now, rb_call_info contains how to call the method with tuple of
(mid, orig_argc, flags, kwarg). Most of cases, kwarg == NULL and
mid+argc+flags only requires 64bits. So this patch packed
rb_call_info to VALUE (1 word) on such cases. If we can not
represent it in VALUE, then use imemo_callinfo which contains
conventional callinfo (rb_callinfo, renamed from rb_call_info).
iseq->body->ci_kw_size is removed because all of callinfo is VALUE
size (packed ci or a pointer to imemo_callinfo).
To access ci information, we need to use these functions:
vm_ci_mid(ci), _flag(ci), _argc(ci), _kwarg(ci).
struct rb_call_info_kw_arg is renamed to rb_callinfo_kwarg.
rb_funcallv_with_cc() and rb_method_basic_definition_p_with_cc()
is temporary removed because cd->ci should be marked.
2020-01-08 02:20:36 +03:00
|
|
|
rb_ary_push(ary, rb_sprintf("argc:%d", vm_ci_argc(ci)));
|
* insns.def (send, invokesuper, invokeblock, opt_*), vm_core.h:
use only a `ci' (rb_call_info_t) parameter instead of using
parameters such as `op_id', 'op_argc', `blockiseq' and flag.
These information are stored in rb_call_info_t at the compile
time.
This technique simplifies parameter passings at related
function calls (~10% speedups for simple mehtod invocation at
my machine).
`rb_call_info_t' also has new function pointer variable `call'.
This `call' variable enables to customize method (block)
invocation process for each place. However, it always call
`vm_call_general()' at this changes.
`rb_call_info_t' also has temporary variables for method
(block) invocation.
* vm_core.h, compile.c, insns.def: introduce VM_CALL_ARGS_SKIP_SETUP
VM_CALL macro. This flag indicates that this call can skip
caller_setup (block arg and splat arg).
* compile.c: catch up above changes.
* iseq.c: catch up above changes (especially for TS_CALLINFO).
* tool/instruction.rb: catch up above chagnes.
* vm_insnhelper.c, vm_insnhelper.h: ditto. Macros and functions
parameters are changed.
* vm_eval.c (vm_call0): ditto (it will be rewriten soon).
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37180 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-10-14 20:59:05 +04:00
|
|
|
|
VALUE size packed callinfo (ci).
Now, rb_call_info contains how to call the method with tuple of
(mid, orig_argc, flags, kwarg). Most of cases, kwarg == NULL and
mid+argc+flags only requires 64bits. So this patch packed
rb_call_info to VALUE (1 word) on such cases. If we can not
represent it in VALUE, then use imemo_callinfo which contains
conventional callinfo (rb_callinfo, renamed from rb_call_info).
iseq->body->ci_kw_size is removed because all of callinfo is VALUE
size (packed ci or a pointer to imemo_callinfo).
To access ci information, we need to use these functions:
vm_ci_mid(ci), _flag(ci), _argc(ci), _kwarg(ci).
struct rb_call_info_kw_arg is renamed to rb_callinfo_kwarg.
rb_funcallv_with_cc() and rb_method_basic_definition_p_with_cc()
is temporary removed because cd->ci should be marked.
2020-01-08 02:20:36 +03:00
|
|
|
if (vm_ci_flag(ci) & VM_CALL_KWARG) {
|
|
|
|
const struct rb_callinfo_kwarg *kw_args = vm_ci_kwarg(ci);
|
|
|
|
VALUE kw_ary = rb_ary_new_from_values(kw_args->keyword_len, kw_args->keywords);
|
|
|
|
rb_ary_push(ary, rb_sprintf("kw:[%"PRIsVALUE"]", rb_ary_join(kw_ary, rb_str_new2(","))));
|
* rewrite method/block parameter fitting logic to optimize
keyword arguments/parameters and a splat argument.
[Feature #10440] (Details are described in this ticket)
Most of complex part is moved to vm_args.c.
Now, ISeq#to_a does not catch up new instruction format.
* vm_core.h: change iseq data structures.
* introduce rb_call_info_kw_arg_t to represent keyword arguments.
* add rb_call_info_t::kw_arg.
* rename rb_iseq_t::arg_post_len to rb_iseq_t::arg_post_num.
* rename rb_iseq_t::arg_keywords to arg_keyword_num.
* rename rb_iseq_t::arg_keyword to rb_iseq_t::arg_keyword_bits.
to represent keyword bitmap parameter index.
This bitmap parameter shows that which keyword parameters are given
or not given (0 for given).
It is refered by `checkkeyword' instruction described bellow.
* rename rb_iseq_t::arg_keyword_check to rb_iseq_t::arg_keyword_rest
to represent keyword rest parameter index.
* add rb_iseq_t::arg_keyword_default_values to represent default
keyword values.
* rename VM_CALL_ARGS_SKIP_SETUP to VM_CALL_ARGS_SIMPLE
to represent
(ci->flag & (SPLAT|BLOCKARG)) &&
ci->blockiseq == NULL &&
ci->kw_arg == NULL.
* vm_insnhelper.c, vm_args.c: rewrite with refactoring.
* rewrite splat argument code.
* rewrite keyword arguments/parameters code.
* merge method and block parameter fitting code into one code base.
* vm.c, vm_eval.c: catch up these changes.
* compile.c (new_callinfo): callinfo requires kw_arg parameter.
* compile.c (compile_array_): check the last argument Hash object or
not. If Hash object and all keys are Symbol literals, they are
compiled to keyword arguments.
* insns.def (checkkeyword): add new instruction.
This instruction check the availability of corresponding keyword.
For example, a method "def foo k1: 'v1'; end" is cimpiled to the
following instructions.
0000 checkkeyword 2, 0 # check k1 is given.
0003 branchif 9 # if given, jump to address #9
0005 putstring "v1"
0007 setlocal_OP__WC__0 3 # k1 = 'v1'
0009 trace 8
0011 putnil
0012 trace 16
0014 leave
* insns.def (opt_send_simple): removed and add new instruction
"opt_send_without_block".
* parse.y (new_args_tail_gen): reorder variables.
Before this patch, a method "def foo(k1: 1, kr1:, k2: 2, **krest, &b)"
has parameter variables "k1, kr1, k2, &b, internal_id, krest",
but this patch reorders to "kr1, k1, k2, internal_id, krest, &b".
(locate a block variable at last)
* parse.y (vtable_pop): added.
This function remove latest `n' variables from vtable.
* iseq.c: catch up iseq data changes.
* proc.c: ditto.
* class.c (keyword_error): export as rb_keyword_error().
* common.mk: depend vm_args.c for vm.o.
* hash.c (rb_hash_has_key): export.
* internal.h: ditto.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@48239 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2014-11-02 21:02:55 +03:00
|
|
|
}
|
* insns.def (send, invokesuper, invokeblock, opt_*), vm_core.h:
use only a `ci' (rb_call_info_t) parameter instead of using
parameters such as `op_id', 'op_argc', `blockiseq' and flag.
These information are stored in rb_call_info_t at the compile
time.
This technique simplifies parameter passings at related
function calls (~10% speedups for simple mehtod invocation at
my machine).
`rb_call_info_t' also has new function pointer variable `call'.
This `call' variable enables to customize method (block)
invocation process for each place. However, it always call
`vm_call_general()' at this changes.
`rb_call_info_t' also has temporary variables for method
(block) invocation.
* vm_core.h, compile.c, insns.def: introduce VM_CALL_ARGS_SKIP_SETUP
VM_CALL macro. This flag indicates that this call can skip
caller_setup (block arg and splat arg).
* compile.c: catch up above changes.
* iseq.c: catch up above changes (especially for TS_CALLINFO).
* tool/instruction.rb: catch up above chagnes.
* vm_insnhelper.c, vm_insnhelper.h: ditto. Macros and functions
parameters are changed.
* vm_eval.c (vm_call0): ditto (it will be rewriten soon).
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37180 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-10-14 20:59:05 +04:00
|
|
|
|
VALUE size packed callinfo (ci).
Now, rb_call_info contains how to call the method with tuple of
(mid, orig_argc, flags, kwarg). Most of cases, kwarg == NULL and
mid+argc+flags only requires 64bits. So this patch packed
rb_call_info to VALUE (1 word) on such cases. If we can not
represent it in VALUE, then use imemo_callinfo which contains
conventional callinfo (rb_callinfo, renamed from rb_call_info).
iseq->body->ci_kw_size is removed because all of callinfo is VALUE
size (packed ci or a pointer to imemo_callinfo).
To access ci information, we need to use these functions:
vm_ci_mid(ci), _flag(ci), _argc(ci), _kwarg(ci).
struct rb_call_info_kw_arg is renamed to rb_callinfo_kwarg.
rb_funcallv_with_cc() and rb_method_basic_definition_p_with_cc()
is temporary removed because cd->ci should be marked.
2020-01-08 02:20:36 +03:00
|
|
|
if (vm_ci_flag(ci)) {
|
* insns.def (send, invokesuper, invokeblock, opt_*), vm_core.h:
use only a `ci' (rb_call_info_t) parameter instead of using
parameters such as `op_id', 'op_argc', `blockiseq' and flag.
These information are stored in rb_call_info_t at the compile
time.
This technique simplifies parameter passings at related
function calls (~10% speedups for simple mehtod invocation at
my machine).
`rb_call_info_t' also has new function pointer variable `call'.
This `call' variable enables to customize method (block)
invocation process for each place. However, it always call
`vm_call_general()' at this changes.
`rb_call_info_t' also has temporary variables for method
(block) invocation.
* vm_core.h, compile.c, insns.def: introduce VM_CALL_ARGS_SKIP_SETUP
VM_CALL macro. This flag indicates that this call can skip
caller_setup (block arg and splat arg).
* compile.c: catch up above changes.
* iseq.c: catch up above changes (especially for TS_CALLINFO).
* tool/instruction.rb: catch up above chagnes.
* vm_insnhelper.c, vm_insnhelper.h: ditto. Macros and functions
parameters are changed.
* vm_eval.c (vm_call0): ditto (it will be rewriten soon).
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37180 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-10-14 20:59:05 +04:00
|
|
|
VALUE flags = rb_ary_new();
|
VALUE size packed callinfo (ci).
Now, rb_call_info contains how to call the method with tuple of
(mid, orig_argc, flags, kwarg). Most of cases, kwarg == NULL and
mid+argc+flags only requires 64bits. So this patch packed
rb_call_info to VALUE (1 word) on such cases. If we can not
represent it in VALUE, then use imemo_callinfo which contains
conventional callinfo (rb_callinfo, renamed from rb_call_info).
iseq->body->ci_kw_size is removed because all of callinfo is VALUE
size (packed ci or a pointer to imemo_callinfo).
To access ci information, we need to use these functions:
vm_ci_mid(ci), _flag(ci), _argc(ci), _kwarg(ci).
struct rb_call_info_kw_arg is renamed to rb_callinfo_kwarg.
rb_funcallv_with_cc() and rb_method_basic_definition_p_with_cc()
is temporary removed because cd->ci should be marked.
2020-01-08 02:20:36 +03:00
|
|
|
# define CALL_FLAG(n) if (vm_ci_flag(ci) & VM_CALL_##n) rb_ary_push(flags, rb_str_new2(#n))
|
2017-12-22 03:29:38 +03:00
|
|
|
CALL_FLAG(ARGS_SPLAT);
|
Add VM_CALL_ARGS_SPLAT_MUT callinfo flag
This flag is set when the caller has already created a new array to
handle a splat, such as for `f(*a, b)` and `f(*a, *b)`. Previously,
if `f` was defined as `def f(*a)`, these calls would create an extra
array on the callee side, instead of using the new array created
by the caller.
This modifies `setup_args_core` to set the flag whenver it would add
a `splatarray true` instruction. However, when `splatarray true` is
changed to `splatarray false` in the peephole optimizer, to avoid
unnecessary allocations on the caller side, the flag must be removed.
Add `optimize_args_splat_no_copy` and have the peephole optimizer call
that. This significantly simplifies the related peephole optimizer
code.
On the callee side, in `setup_parameters_complex`, set
`args->rest_dupped` to true if the flag is set.
This takes a similar approach for optimizing regular splats that was
previiously used for keyword splats in
d2c41b1bff1f3102544bb0d03d4e82356d034d33 (via VM_CALL_KW_SPLAT_MUT).
2023-11-23 21:47:24 +03:00
|
|
|
CALL_FLAG(ARGS_SPLAT_MUT);
|
2017-12-22 03:29:38 +03:00
|
|
|
CALL_FLAG(ARGS_BLOCKARG);
|
|
|
|
CALL_FLAG(FCALL);
|
|
|
|
CALL_FLAG(VCALL);
|
|
|
|
CALL_FLAG(ARGS_SIMPLE);
|
|
|
|
CALL_FLAG(TAILCALL);
|
|
|
|
CALL_FLAG(SUPER);
|
2018-08-10 10:45:16 +03:00
|
|
|
CALL_FLAG(ZSUPER);
|
2017-12-22 03:29:38 +03:00
|
|
|
CALL_FLAG(KWARG);
|
|
|
|
CALL_FLAG(KW_SPLAT);
|
Reduce allocations for keyword argument hashes
Previously, passing a keyword splat to a method always allocated
a hash on the caller side, and accepting arbitrary keywords in
a method allocated a separate hash on the callee side. Passing
explicit keywords to a method that accepted a keyword splat
did not allocate a hash on the caller side, but resulted in two
hashes allocated on the callee side.
This commit makes passing a single keyword splat to a method not
allocate a hash on the caller side. Passing multiple keyword
splats or a mix of explicit keywords and a keyword splat still
generates a hash on the caller side. On the callee side,
if arbitrary keywords are not accepted, it does not allocate a
hash. If arbitrary keywords are accepted, it will allocate a
hash, but this commit uses a callinfo flag to indicate whether
the caller already allocated a hash, and if so, the callee can
use the passed hash without duplicating it. So this commit
should make it so that a maximum of a single hash is allocated
during method calls.
To set the callinfo flag appropriately, method call argument
compilation checks if only a single keyword splat is given.
If only one keyword splat is given, the VM_CALL_KW_SPLAT_MUT
callinfo flag is not set, since in that case the keyword
splat is passed directly and not mutable. If more than one
splat is used, a new hash needs to be generated on the caller
side, and in that case the callinfo flag is set, indicating
the keyword splat is mutable by the callee.
In compile_hash, used for both hash and keyword argument
compilation, if compiling keyword arguments and only a
single keyword splat is used, pass the argument directly.
On the caller side, in vm_args.c, the callinfo flag needs to
be recognized and handled. Because the keyword splat
argument may not be a hash, it needs to be converted to a
hash first if not. Then, unless the callinfo flag is set,
the hash needs to be duplicated. The temporary copy of the
callinfo flag, kw_flag, is updated if a hash was duplicated,
to prevent the need to duplicate it again. If we are
converting to a hash or duplicating a hash, we need to update
the argument array, which can including duplicating the
positional splat array if one was passed. CALLER_SETUP_ARG
and a couple other places needs to be modified to handle
similar issues for other types of calls.
This includes fairly comprehensive tests for different ways
keywords are handled internally, checking that you get equal
results but that keyword splats on the caller side result in
distinct objects for keyword rest parameters.
Included are benchmarks for keyword argument calls.
Brief results when compiled without optimization:
def kw(a: 1) a end
def kws(**kw) kw end
h = {a: 1}
kw(a: 1) # about same
kw(**h) # 2.37x faster
kws(a: 1) # 1.30x faster
kws(**h) # 2.19x faster
kw(a: 1, **h) # 1.03x slower
kw(**h, **h) # about same
kws(a: 1, **h) # 1.16x faster
kws(**h, **h) # 1.14x faster
2020-02-24 23:05:07 +03:00
|
|
|
CALL_FLAG(KW_SPLAT_MUT);
|
Optimized forwarding callers and callees
This patch optimizes forwarding callers and callees. It only optimizes methods that only take `...` as their parameter, and then pass `...` to other calls.
Calls it optimizes look like this:
```ruby
def bar(a) = a
def foo(...) = bar(...) # optimized
foo(123)
```
```ruby
def bar(a) = a
def foo(...) = bar(1, 2, ...) # optimized
foo(123)
```
```ruby
def bar(*a) = a
def foo(...)
list = [1, 2]
bar(*list, ...) # optimized
end
foo(123)
```
All variants of the above but using `super` are also optimized, including a bare super like this:
```ruby
def foo(...)
super
end
```
This patch eliminates intermediate allocations made when calling methods that accept `...`.
We can observe allocation elimination like this:
```ruby
def m
x = GC.stat(:total_allocated_objects)
yield
GC.stat(:total_allocated_objects) - x
end
def bar(a) = a
def foo(...) = bar(...)
def test
m { foo(123) }
end
test
p test # allocates 1 object on master, but 0 objects with this patch
```
```ruby
def bar(a, b:) = a + b
def foo(...) = bar(...)
def test
m { foo(1, b: 2) }
end
test
p test # allocates 2 objects on master, but 0 objects with this patch
```
How does it work?
-----------------
This patch works by using a dynamic stack size when passing forwarded parameters to callees.
The caller's info object (known as the "CI") contains the stack size of the
parameters, so we pass the CI object itself as a parameter to the callee.
When forwarding parameters, the forwarding ISeq uses the caller's CI to determine how much stack to copy, then copies the caller's stack before calling the callee.
The CI at the forwarded call site is adjusted using information from the caller's CI.
I think this description is kind of confusing, so let's walk through an example with code.
```ruby
def delegatee(a, b) = a + b
def delegator(...)
delegatee(...) # CI2 (FORWARDING)
end
def caller
delegator(1, 2) # CI1 (argc: 2)
end
```
Before we call the delegator method, the stack looks like this:
```
Executing Line | Code | Stack
---------------+---------------------------------------+--------
1| def delegatee(a, b) = a + b | self
2| | 1
3| def delegator(...) | 2
4| # |
5| delegatee(...) # CI2 (FORWARDING) |
6| end |
7| |
8| def caller |
-> 9| delegator(1, 2) # CI1 (argc: 2) |
10| end |
```
The ISeq for `delegator` is tagged as "forwardable", so when `caller` calls in
to `delegator`, it writes `CI1` on to the stack as a local variable for the
`delegator` method. The `delegator` method has a special local called `...`
that holds the caller's CI object.
Here is the ISeq disasm fo `delegator`:
```
== disasm: #<ISeq:delegator@-e:1 (1,0)-(1,39)>
local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
[ 1] "..."@0
0000 putself ( 1)[LiCa]
0001 getlocal_WC_0 "..."@0
0003 send <calldata!mid:delegatee, argc:0, FCALL|FORWARDING>, nil
0006 leave [Re]
```
The local called `...` will contain the caller's CI: CI1.
Here is the stack when we enter `delegator`:
```
Executing Line | Code | Stack
---------------+---------------------------------------+--------
1| def delegatee(a, b) = a + b | self
2| | 1
3| def delegator(...) | 2
-> 4| # | CI1 (argc: 2)
5| delegatee(...) # CI2 (FORWARDING) | cref_or_me
6| end | specval
7| | type
8| def caller |
9| delegator(1, 2) # CI1 (argc: 2) |
10| end |
```
The CI at `delegatee` on line 5 is tagged as "FORWARDING", so it knows to
memcopy the caller's stack before calling `delegatee`. In this case, it will
memcopy self, 1, and 2 to the stack before calling `delegatee`. It knows how much
memory to copy from the caller because `CI1` contains stack size information
(argc: 2).
Before executing the `send` instruction, we push `...` on the stack. The
`send` instruction pops `...`, and because it is tagged with `FORWARDING`, it
knows to memcopy (using the information in the CI it just popped):
```
== disasm: #<ISeq:delegator@-e:1 (1,0)-(1,39)>
local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
[ 1] "..."@0
0000 putself ( 1)[LiCa]
0001 getlocal_WC_0 "..."@0
0003 send <calldata!mid:delegatee, argc:0, FCALL|FORWARDING>, nil
0006 leave [Re]
```
Instruction 001 puts the caller's CI on the stack. `send` is tagged with
FORWARDING, so it reads the CI and _copies_ the callers stack to this stack:
```
Executing Line | Code | Stack
---------------+---------------------------------------+--------
1| def delegatee(a, b) = a + b | self
2| | 1
3| def delegator(...) | 2
4| # | CI1 (argc: 2)
-> 5| delegatee(...) # CI2 (FORWARDING) | cref_or_me
6| end | specval
7| | type
8| def caller | self
9| delegator(1, 2) # CI1 (argc: 2) | 1
10| end | 2
```
The "FORWARDING" call site combines information from CI1 with CI2 in order
to support passing other values in addition to the `...` value, as well as
perfectly forward splat args, kwargs, etc.
Since we're able to copy the stack from `caller` in to `delegator`'s stack, we
can avoid allocating objects.
I want to do this to eliminate object allocations for delegate methods.
My long term goal is to implement `Class#new` in Ruby and it uses `...`.
I was able to implement `Class#new` in Ruby
[here](https://github.com/ruby/ruby/pull/9289).
If we adopt the technique in this patch, then we can optimize allocating
objects that take keyword parameters for `initialize`.
For example, this code will allocate 2 objects: one for `SomeObject`, and one
for the kwargs:
```ruby
SomeObject.new(foo: 1)
```
If we combine this technique, plus implement `Class#new` in Ruby, then we can
reduce allocations for this common operation.
Co-Authored-By: John Hawthorn <john@hawthorn.email>
Co-Authored-By: Alan Wu <XrXr@users.noreply.github.com>
2024-04-15 20:48:53 +03:00
|
|
|
CALL_FLAG(FORWARDING);
|
2017-12-22 03:29:38 +03:00
|
|
|
CALL_FLAG(OPT_SEND); /* maybe not reachable */
|
* insns.def (send, invokesuper, invokeblock, opt_*), vm_core.h:
use only a `ci' (rb_call_info_t) parameter instead of using
parameters such as `op_id', 'op_argc', `blockiseq' and flag.
These information are stored in rb_call_info_t at the compile
time.
This technique simplifies parameter passings at related
function calls (~10% speedups for simple mehtod invocation at
my machine).
`rb_call_info_t' also has new function pointer variable `call'.
This `call' variable enables to customize method (block)
invocation process for each place. However, it always call
`vm_call_general()' at this changes.
`rb_call_info_t' also has temporary variables for method
(block) invocation.
* vm_core.h, compile.c, insns.def: introduce VM_CALL_ARGS_SKIP_SETUP
VM_CALL macro. This flag indicates that this call can skip
caller_setup (block arg and splat arg).
* compile.c: catch up above changes.
* iseq.c: catch up above changes (especially for TS_CALLINFO).
* tool/instruction.rb: catch up above chagnes.
* vm_insnhelper.c, vm_insnhelper.h: ditto. Macros and functions
parameters are changed.
* vm_eval.c (vm_call0): ditto (it will be rewriten soon).
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37180 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-10-14 20:59:05 +04:00
|
|
|
rb_ary_push(ary, rb_ary_join(flags, rb_str_new2("|")));
|
|
|
|
}
|
2012-10-10 21:52:24 +04:00
|
|
|
|
2019-07-31 04:36:05 +03:00
|
|
|
ret = rb_sprintf("<calldata!%"PRIsVALUE">", rb_ary_join(ary, rb_str_new2(", ")));
|
|
|
|
}
|
2015-09-19 20:59:58 +03:00
|
|
|
break;
|
|
|
|
|
2007-05-11 10:26:06 +04:00
|
|
|
case TS_CDHASH:
|
2006-12-31 18:02:22 +03:00
|
|
|
ret = rb_str_new2("<cdhash>");
|
|
|
|
break;
|
|
|
|
|
2007-08-12 23:09:15 +04:00
|
|
|
case TS_FUNCPTR:
|
2014-12-16 04:14:27 +03:00
|
|
|
{
|
|
|
|
#ifdef HAVE_DLADDR
|
|
|
|
Dl_info info;
|
|
|
|
if (dladdr((void *)op, &info) && info.dli_sname) {
|
|
|
|
ret = rb_str_new_cstr(info.dli_sname);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
ret = rb_str_new2("<funcptr>");
|
|
|
|
}
|
2007-08-12 23:09:15 +04:00
|
|
|
break;
|
|
|
|
|
2019-11-07 10:58:00 +03:00
|
|
|
case TS_BUILTIN:
|
|
|
|
{
|
|
|
|
const struct rb_builtin_function *bf = (const struct rb_builtin_function *)op;
|
|
|
|
ret = rb_sprintf("<builtin!%s/%d>",
|
|
|
|
bf->name, bf->argc);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
2007-05-11 10:26:06 +04:00
|
|
|
default:
|
2018-04-27 16:14:09 +03:00
|
|
|
rb_bug("unknown operand type: %c", type);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2018-04-04 07:28:47 +03:00
|
|
|
static VALUE
|
|
|
|
right_strip(VALUE str)
|
|
|
|
{
|
|
|
|
const char *beg = RSTRING_PTR(str), *end = RSTRING_END(str);
|
|
|
|
while (end-- > beg && *end == ' ');
|
|
|
|
rb_str_set_len(str, end - beg + 1);
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
/**
|
|
|
|
* Disassemble a instruction
|
|
|
|
* Iseq -> Iseq inspect object
|
|
|
|
*/
|
* compile.c, cont.c, gc.c, insns.def, iseq.c, iseq.h, process.c,
thread.c, vm.c, vm_core.h, vm_dump.c, vm_eval.c,
vm_insnhelper.c, vm_method.c, template/insns_info.inc.tmpl,
tool/instruction.rb: fixed types.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@25030 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2009-09-22 00:58:26 +04:00
|
|
|
int
|
2015-07-16 15:50:25 +03:00
|
|
|
rb_iseq_disasm_insn(VALUE ret, const VALUE *code, size_t pos,
|
|
|
|
const rb_iseq_t *iseq, VALUE child)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2015-07-16 15:50:25 +03:00
|
|
|
VALUE insn = code[pos];
|
2006-12-31 18:02:22 +03:00
|
|
|
int len = insn_len(insn);
|
* compile.c (insn_data_to_s_detail), file.c (rb_stat_inspect),
iseq.c (ruby_iseq_disasm_insn, ruby_iseq_disasm),
process.c (pst_message), re.c (match_inspect): use rb_str_catf.
* dir.c (dir_inspect), iseq.c (iseq_inspect, insn_operand_intern): use
rb_sprintf.
* error.c (rb_name_error, rb_raise, rb_loaderror, rb_fatal): use
rb_vsprintf.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@18158 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2008-07-22 12:53:34 +04:00
|
|
|
int j;
|
2008-04-03 04:00:48 +04:00
|
|
|
const char *types = insn_op_types(insn);
|
2006-12-31 18:02:22 +03:00
|
|
|
VALUE str = rb_str_new(0, 0);
|
* compile.c (insn_data_to_s_detail), file.c (rb_stat_inspect),
iseq.c (ruby_iseq_disasm_insn, ruby_iseq_disasm),
process.c (pst_message), re.c (match_inspect): use rb_str_catf.
* dir.c (dir_inspect), iseq.c (iseq_inspect, insn_operand_intern): use
rb_sprintf.
* error.c (rb_name_error, rb_raise, rb_loaderror, rb_fatal): use
rb_vsprintf.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@18158 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2008-07-22 12:53:34 +04:00
|
|
|
const char *insn_name_buff;
|
2006-12-31 18:02:22 +03:00
|
|
|
|
* compile.c (insn_data_to_s_detail), file.c (rb_stat_inspect),
iseq.c (ruby_iseq_disasm_insn, ruby_iseq_disasm),
process.c (pst_message), re.c (match_inspect): use rb_str_catf.
* dir.c (dir_inspect), iseq.c (iseq_inspect, insn_operand_intern): use
rb_sprintf.
* error.c (rb_name_error, rb_raise, rb_loaderror, rb_fatal): use
rb_vsprintf.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@18158 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2008-07-22 12:53:34 +04:00
|
|
|
insn_name_buff = insn_name(insn);
|
|
|
|
if (1) {
|
2018-05-02 03:57:50 +03:00
|
|
|
extern const int rb_vm_max_insn_name_size;
|
|
|
|
rb_str_catf(str, "%04"PRIuSIZE" %-*s ", pos, rb_vm_max_insn_name_size, insn_name_buff);
|
* compile.c (insn_data_to_s_detail), file.c (rb_stat_inspect),
iseq.c (ruby_iseq_disasm_insn, ruby_iseq_disasm),
process.c (pst_message), re.c (match_inspect): use rb_str_catf.
* dir.c (dir_inspect), iseq.c (iseq_inspect, insn_operand_intern): use
rb_sprintf.
* error.c (rb_name_error, rb_raise, rb_loaderror, rb_fatal): use
rb_vsprintf.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@18158 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2008-07-22 12:53:34 +04:00
|
|
|
}
|
|
|
|
else {
|
2018-05-02 03:57:47 +03:00
|
|
|
rb_str_catf(str, "%04"PRIuSIZE" %-28.*s ", pos,
|
2008-07-31 05:51:44 +04:00
|
|
|
(int)strcspn(insn_name_buff, "_"), insn_name_buff);
|
* compile.c (insn_data_to_s_detail), file.c (rb_stat_inspect),
iseq.c (ruby_iseq_disasm_insn, ruby_iseq_disasm),
process.c (pst_message), re.c (match_inspect): use rb_str_catf.
* dir.c (dir_inspect), iseq.c (iseq_inspect, insn_operand_intern): use
rb_sprintf.
* error.c (rb_name_error, rb_raise, rb_loaderror, rb_fatal): use
rb_vsprintf.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@18158 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2008-07-22 12:53:34 +04:00
|
|
|
}
|
2006-12-31 18:02:22 +03:00
|
|
|
|
|
|
|
for (j = 0; types[j]; j++) {
|
2015-07-16 15:50:25 +03:00
|
|
|
VALUE opstr = rb_insn_operand_intern(iseq, insn, j, code[pos + j + 1],
|
|
|
|
len, pos, &code[pos + j + 2],
|
2013-03-06 10:30:03 +04:00
|
|
|
child);
|
2006-12-31 18:02:22 +03:00
|
|
|
rb_str_concat(str, opstr);
|
|
|
|
|
|
|
|
if (types[j + 1]) {
|
|
|
|
rb_str_cat2(str, ", ");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-08-24 10:31:15 +04:00
|
|
|
{
|
2017-11-10 08:26:52 +03:00
|
|
|
unsigned int line_no = rb_iseq_line_no(iseq, pos);
|
|
|
|
unsigned int prev = pos == 0 ? 0 : rb_iseq_line_no(iseq, pos - 1);
|
2006-12-31 18:02:22 +03:00
|
|
|
if (line_no && line_no != prev) {
|
* compile.c (insn_data_to_s_detail), file.c (rb_stat_inspect),
iseq.c (ruby_iseq_disasm_insn, ruby_iseq_disasm),
process.c (pst_message), re.c (match_inspect): use rb_str_catf.
* dir.c (dir_inspect), iseq.c (iseq_inspect, insn_operand_intern): use
rb_sprintf.
* error.c (rb_name_error, rb_raise, rb_loaderror, rb_fatal): use
rb_vsprintf.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@18158 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2008-07-22 12:53:34 +04:00
|
|
|
long slen = RSTRING_LEN(str);
|
|
|
|
slen = (slen > 70) ? 0 : (70 - slen);
|
|
|
|
str = rb_str_catf(str, "%*s(%4d)", (int)slen, "", line_no);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
}
|
2007-12-20 00:39:08 +03:00
|
|
|
|
2017-11-14 15:58:36 +03:00
|
|
|
{
|
|
|
|
rb_event_flag_t events = rb_iseq_event_flags(iseq, pos);
|
|
|
|
if (events) {
|
2023-08-01 11:25:20 +03:00
|
|
|
str = rb_str_catf(str, "[%s%s%s%s%s%s%s%s%s%s%s%s]",
|
2017-11-14 15:58:36 +03:00
|
|
|
events & RUBY_EVENT_LINE ? "Li" : "",
|
|
|
|
events & RUBY_EVENT_CLASS ? "Cl" : "",
|
|
|
|
events & RUBY_EVENT_END ? "En" : "",
|
|
|
|
events & RUBY_EVENT_CALL ? "Ca" : "",
|
|
|
|
events & RUBY_EVENT_RETURN ? "Re" : "",
|
|
|
|
events & RUBY_EVENT_C_CALL ? "Cc" : "",
|
|
|
|
events & RUBY_EVENT_C_RETURN ? "Cr" : "",
|
|
|
|
events & RUBY_EVENT_B_CALL ? "Bc" : "",
|
2018-10-20 13:45:55 +03:00
|
|
|
events & RUBY_EVENT_B_RETURN ? "Br" : "",
|
2023-08-01 11:25:20 +03:00
|
|
|
events & RUBY_EVENT_RESCUE ? "Rs" : "",
|
2018-10-20 13:45:55 +03:00
|
|
|
events & RUBY_EVENT_COVERAGE_LINE ? "Cli" : "",
|
2018-11-26 21:16:39 +03:00
|
|
|
events & RUBY_EVENT_COVERAGE_BRANCH ? "Cbr" : "");
|
2017-11-14 15:58:36 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-04 07:28:47 +03:00
|
|
|
right_strip(str);
|
2006-12-31 18:02:22 +03:00
|
|
|
if (ret) {
|
|
|
|
rb_str_cat2(str, "\n");
|
|
|
|
rb_str_concat(ret, str);
|
|
|
|
}
|
|
|
|
else {
|
2018-04-04 07:28:47 +03:00
|
|
|
printf("%.*s\n", (int)RSTRING_LEN(str), RSTRING_PTR(str));
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2008-05-31 13:28:20 +04:00
|
|
|
static const char *
|
2006-12-31 18:02:22 +03:00
|
|
|
catch_type(int type)
|
|
|
|
{
|
|
|
|
switch (type) {
|
2007-05-11 10:26:06 +04:00
|
|
|
case CATCH_TYPE_RESCUE:
|
2006-12-31 18:02:22 +03:00
|
|
|
return "rescue";
|
2007-05-11 10:26:06 +04:00
|
|
|
case CATCH_TYPE_ENSURE:
|
2006-12-31 18:02:22 +03:00
|
|
|
return "ensure";
|
2007-05-11 10:26:06 +04:00
|
|
|
case CATCH_TYPE_RETRY:
|
2006-12-31 18:02:22 +03:00
|
|
|
return "retry";
|
2007-05-11 10:26:06 +04:00
|
|
|
case CATCH_TYPE_BREAK:
|
2006-12-31 18:02:22 +03:00
|
|
|
return "break";
|
2007-05-11 10:26:06 +04:00
|
|
|
case CATCH_TYPE_REDO:
|
2006-12-31 18:02:22 +03:00
|
|
|
return "redo";
|
2007-05-11 10:26:06 +04:00
|
|
|
case CATCH_TYPE_NEXT:
|
2006-12-31 18:02:22 +03:00
|
|
|
return "next";
|
2007-05-11 10:26:06 +04:00
|
|
|
default:
|
2018-04-27 16:14:09 +03:00
|
|
|
rb_bug("unknown catch type: %d", type);
|
2006-12-31 18:02:22 +03:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-22 01:52:59 +03:00
|
|
|
static VALUE
|
|
|
|
iseq_inspect(const rb_iseq_t *iseq)
|
|
|
|
{
|
2022-03-23 22:19:48 +03:00
|
|
|
const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
|
2018-05-12 04:24:18 +03:00
|
|
|
if (!body->location.label) {
|
2015-07-22 01:52:59 +03:00
|
|
|
return rb_sprintf("#<ISeq: uninitialized>");
|
|
|
|
}
|
|
|
|
else {
|
2018-05-12 04:24:18 +03:00
|
|
|
const rb_code_location_t *loc = &body->location.code_location;
|
2018-01-11 11:34:55 +03:00
|
|
|
return rb_sprintf("#<ISeq:%"PRIsVALUE"@%"PRIsVALUE":%d (%d,%d)-(%d,%d)>",
|
2018-05-12 04:24:18 +03:00
|
|
|
body->location.label, rb_iseq_path(iseq),
|
|
|
|
loc->beg_pos.lineno,
|
|
|
|
loc->beg_pos.lineno,
|
|
|
|
loc->beg_pos.column,
|
|
|
|
loc->end_pos.lineno,
|
|
|
|
loc->end_pos.column);
|
2015-07-22 01:52:59 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-08 23:35:24 +03:00
|
|
|
static const rb_data_type_t tmp_set = {
|
|
|
|
"tmpset",
|
|
|
|
{(void (*)(void *))rb_mark_set, (void (*)(void *))st_free_table, 0, 0,},
|
|
|
|
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
|
|
|
|
};
|
|
|
|
|
2018-04-04 14:00:39 +03:00
|
|
|
static VALUE
|
|
|
|
rb_iseq_disasm_recursive(const rb_iseq_t *iseq, VALUE indent)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2022-03-23 22:19:48 +03:00
|
|
|
const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
|
2015-07-16 15:50:25 +03:00
|
|
|
VALUE *code;
|
2006-12-31 18:02:22 +03:00
|
|
|
VALUE str = rb_str_new(0, 0);
|
2022-07-25 17:40:45 +03:00
|
|
|
VALUE child = rb_ary_hidden_new(3);
|
2014-07-26 11:30:26 +04:00
|
|
|
unsigned int size;
|
2015-07-25 00:44:14 +03:00
|
|
|
unsigned int i;
|
2009-03-11 23:19:24 +03:00
|
|
|
long l;
|
* compile.c, cont.c, gc.c, insns.def, iseq.c, iseq.h, process.c,
thread.c, vm.c, vm_core.h, vm_dump.c, vm_eval.c,
vm_insnhelper.c, vm_method.c, template/insns_info.inc.tmpl,
tool/instruction.rb: fixed types.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@25030 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2009-09-22 00:58:26 +04:00
|
|
|
size_t n;
|
2007-08-06 08:34:11 +04:00
|
|
|
enum {header_minlen = 72};
|
2017-11-10 11:26:44 +03:00
|
|
|
st_table *done_iseq = 0;
|
2019-10-08 23:35:24 +03:00
|
|
|
VALUE done_iseq_wrapper = Qnil;
|
2018-04-04 14:00:39 +03:00
|
|
|
const char *indent_str;
|
|
|
|
long indent_len;
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2018-05-12 04:24:18 +03:00
|
|
|
size = body->iseq_size;
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2018-04-04 14:00:39 +03:00
|
|
|
indent_len = RSTRING_LEN(indent);
|
|
|
|
indent_str = RSTRING_PTR(indent);
|
|
|
|
|
|
|
|
rb_str_cat(str, indent_str, indent_len);
|
2006-12-31 18:02:22 +03:00
|
|
|
rb_str_cat2(str, "== disasm: ");
|
|
|
|
|
2018-04-04 14:00:39 +03:00
|
|
|
rb_str_append(str, iseq_inspect(iseq));
|
|
|
|
if ((l = RSTRING_LEN(str) - indent_len) < header_minlen) {
|
|
|
|
rb_str_modify_expand(str, header_minlen - l);
|
|
|
|
memset(RSTRING_END(str), '=', header_minlen - l);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
2024-01-24 01:54:39 +03:00
|
|
|
if (iseq->body->builtin_attrs) {
|
|
|
|
#define disasm_builtin_attr(str, iseq, attr) \
|
|
|
|
if (iseq->body->builtin_attrs & BUILTIN_ATTR_ ## attr) { \
|
|
|
|
rb_str_cat2(str, " " #attr); \
|
|
|
|
}
|
|
|
|
disasm_builtin_attr(str, iseq, LEAF);
|
|
|
|
disasm_builtin_attr(str, iseq, SINGLE_NOARG_LEAF);
|
|
|
|
disasm_builtin_attr(str, iseq, INLINE_BLOCK);
|
|
|
|
}
|
2006-12-31 18:02:22 +03:00
|
|
|
rb_str_cat2(str, "\n");
|
|
|
|
|
|
|
|
/* show catch table information */
|
2018-05-12 04:24:18 +03:00
|
|
|
if (body->catch_table) {
|
2018-04-04 14:00:39 +03:00
|
|
|
rb_str_cat(str, indent_str, indent_len);
|
2006-12-31 18:02:22 +03:00
|
|
|
rb_str_cat2(str, "== catch table\n");
|
|
|
|
}
|
2018-05-12 04:24:18 +03:00
|
|
|
if (body->catch_table) {
|
2018-04-04 14:00:39 +03:00
|
|
|
rb_str_cat_cstr(indent, "| ");
|
|
|
|
indent_str = RSTRING_PTR(indent);
|
2018-05-12 04:24:18 +03:00
|
|
|
for (i = 0; i < body->catch_table->size; i++) {
|
2019-05-31 09:58:50 +03:00
|
|
|
const struct iseq_catch_table_entry *entry =
|
|
|
|
UNALIGNED_MEMBER_PTR(body->catch_table, entries[i]);
|
2018-04-04 14:00:39 +03:00
|
|
|
rb_str_cat(str, indent_str, indent_len);
|
2015-07-25 00:44:14 +03:00
|
|
|
rb_str_catf(str,
|
|
|
|
"| catch type: %-6s st: %04d ed: %04d sp: %04d cont: %04d\n",
|
|
|
|
catch_type((int)entry->type), (int)entry->start,
|
|
|
|
(int)entry->end, (int)entry->sp, (int)entry->cont);
|
2017-11-10 11:26:44 +03:00
|
|
|
if (entry->iseq && !(done_iseq && st_is_member(done_iseq, (st_data_t)entry->iseq))) {
|
2018-04-04 14:00:39 +03:00
|
|
|
rb_str_concat(str, rb_iseq_disasm_recursive(rb_iseq_check(entry->iseq), indent));
|
2019-10-08 23:35:24 +03:00
|
|
|
if (!done_iseq) {
|
|
|
|
done_iseq = st_init_numtable();
|
|
|
|
done_iseq_wrapper = TypedData_Wrap_Struct(0, &tmp_set, done_iseq);
|
|
|
|
}
|
2017-11-10 11:26:44 +03:00
|
|
|
st_insert(done_iseq, (st_data_t)entry->iseq, (st_data_t)0);
|
2018-04-04 14:00:39 +03:00
|
|
|
indent_str = RSTRING_PTR(indent);
|
2015-07-25 00:44:14 +03:00
|
|
|
}
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
2018-04-04 14:00:39 +03:00
|
|
|
rb_str_resize(indent, indent_len);
|
|
|
|
indent_str = RSTRING_PTR(indent);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
2018-05-12 04:24:18 +03:00
|
|
|
if (body->catch_table) {
|
2018-04-04 14:00:39 +03:00
|
|
|
rb_str_cat(str, indent_str, indent_len);
|
2006-12-31 18:02:22 +03:00
|
|
|
rb_str_cat2(str, "|-------------------------------------"
|
|
|
|
"-----------------------------------\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* show local table information */
|
2018-05-12 04:24:18 +03:00
|
|
|
if (body->local_table) {
|
|
|
|
const struct rb_iseq_param_keyword *const keyword = body->param.keyword;
|
2018-04-04 14:00:39 +03:00
|
|
|
rb_str_cat(str, indent_str, indent_len);
|
* compile.c (insn_data_to_s_detail), file.c (rb_stat_inspect),
iseq.c (ruby_iseq_disasm_insn, ruby_iseq_disasm),
process.c (pst_message), re.c (match_inspect): use rb_str_catf.
* dir.c (dir_inspect), iseq.c (iseq_inspect, insn_operand_intern): use
rb_sprintf.
* error.c (rb_name_error, rb_raise, rb_loaderror, rb_fatal): use
rb_vsprintf.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@18158 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2008-07-22 12:53:34 +04:00
|
|
|
rb_str_catf(str,
|
|
|
|
"local table (size: %d, argc: %d "
|
2014-11-03 02:14:21 +03:00
|
|
|
"[opts: %d, rest: %d, post: %d, block: %d, kw: %d@%d, kwrest: %d])\n",
|
2018-05-12 04:24:18 +03:00
|
|
|
body->local_table_size,
|
|
|
|
body->param.lead_num,
|
|
|
|
body->param.opt_num,
|
|
|
|
body->param.flags.has_rest ? body->param.rest_start : -1,
|
|
|
|
body->param.post_num,
|
|
|
|
body->param.flags.has_block ? body->param.block_start : -1,
|
|
|
|
body->param.flags.has_kw ? keyword->num : -1,
|
|
|
|
body->param.flags.has_kw ? keyword->required_num : -1,
|
|
|
|
body->param.flags.has_kwrest ? keyword->rest_start : -1);
|
2022-07-21 19:23:58 +03:00
|
|
|
|
2018-05-12 04:24:18 +03:00
|
|
|
for (i = body->local_table_size; i > 0;) {
|
|
|
|
int li = body->local_table_size - --i - 1;
|
2012-06-09 18:36:56 +04:00
|
|
|
long width;
|
2018-01-30 05:08:30 +03:00
|
|
|
VALUE name = local_var_name(iseq, 0, i);
|
2019-04-29 05:31:18 +03:00
|
|
|
char argi[0x100];
|
|
|
|
char opti[0x100];
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2019-04-29 05:31:18 +03:00
|
|
|
opti[0] = '\0';
|
2018-05-12 04:24:18 +03:00
|
|
|
if (body->param.flags.has_opt) {
|
|
|
|
int argc = body->param.lead_num;
|
|
|
|
int opts = body->param.opt_num;
|
2015-07-25 00:44:14 +03:00
|
|
|
if (li >= argc && li < argc + opts) {
|
2010-10-13 18:07:22 +04:00
|
|
|
snprintf(opti, sizeof(opti), "Opt=%"PRIdVALUE,
|
2018-05-12 04:24:18 +03:00
|
|
|
body->param.opt_table[li - argc]);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
}
|
2022-07-21 19:23:58 +03:00
|
|
|
|
2018-03-19 03:32:51 +03:00
|
|
|
snprintf(argi, sizeof(argi), "%s%s%s%s%s%s", /* arg, opts, rest, post, kwrest, block */
|
2024-08-15 19:59:30 +03:00
|
|
|
(body->param.lead_num > li) ? (body->param.flags.ambiguous_param0 ? "AmbiguousArg" : "Arg") : "",
|
2006-12-31 18:02:22 +03:00
|
|
|
opti,
|
2024-08-15 19:59:30 +03:00
|
|
|
(body->param.flags.has_rest && body->param.rest_start == li) ? (body->param.flags.anon_rest ? "AnonRest" : "Rest") : "",
|
2018-05-12 04:24:18 +03:00
|
|
|
(body->param.flags.has_post && body->param.post_start <= li && li < body->param.post_start + body->param.post_num) ? "Post" : "",
|
2024-08-15 19:59:30 +03:00
|
|
|
(body->param.flags.has_kwrest && keyword->rest_start == li) ? (body->param.flags.anon_kwrest ? "AnonKwrest" : "Kwrest") : "",
|
2018-05-12 04:24:18 +03:00
|
|
|
(body->param.flags.has_block && body->param.block_start == li) ? "Block" : "");
|
2022-07-21 19:23:58 +03:00
|
|
|
|
2018-04-04 14:00:39 +03:00
|
|
|
rb_str_cat(str, indent_str, indent_len);
|
2018-01-30 05:08:30 +03:00
|
|
|
rb_str_catf(str, "[%2d] ", i + 1);
|
2012-06-09 18:36:56 +04:00
|
|
|
width = RSTRING_LEN(str) + 11;
|
2018-01-30 05:08:30 +03:00
|
|
|
rb_str_append(str, name);
|
2012-06-09 18:36:56 +04:00
|
|
|
if (*argi) rb_str_catf(str, "<%s>", argi);
|
|
|
|
if ((width -= RSTRING_LEN(str)) > 0) rb_str_catf(str, "%*s", (int)width, "");
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
2018-04-04 07:28:47 +03:00
|
|
|
rb_str_cat_cstr(right_strip(str), "\n");
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* show each line */
|
2015-07-16 15:50:25 +03:00
|
|
|
code = rb_iseq_original_iseq(iseq);
|
* compile.c, cont.c, gc.c, insns.def, iseq.c, iseq.h, process.c,
thread.c, vm.c, vm_core.h, vm_dump.c, vm_eval.c,
vm_insnhelper.c, vm_method.c, template/insns_info.inc.tmpl,
tool/instruction.rb: fixed types.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@25030 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2009-09-22 00:58:26 +04:00
|
|
|
for (n = 0; n < size;) {
|
2018-04-04 14:00:39 +03:00
|
|
|
rb_str_cat(str, indent_str, indent_len);
|
2015-07-16 15:50:25 +03:00
|
|
|
n += rb_iseq_disasm_insn(str, code, n, iseq, child);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
2015-07-25 05:18:32 +03:00
|
|
|
for (l = 0; l < RARRAY_LEN(child); l++) {
|
|
|
|
VALUE isv = rb_ary_entry(child, l);
|
2017-11-10 11:26:44 +03:00
|
|
|
if (done_iseq && st_is_member(done_iseq, (st_data_t)isv)) continue;
|
2018-04-04 14:00:39 +03:00
|
|
|
rb_str_cat_cstr(str, "\n");
|
|
|
|
rb_str_concat(str, rb_iseq_disasm_recursive(rb_iseq_check((rb_iseq_t *)isv), indent));
|
|
|
|
indent_str = RSTRING_PTR(indent);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
2019-10-08 23:35:24 +03:00
|
|
|
RB_GC_GUARD(done_iseq_wrapper);
|
2006-12-31 18:02:22 +03:00
|
|
|
|
|
|
|
return str;
|
|
|
|
}
|
|
|
|
|
2018-04-04 14:00:39 +03:00
|
|
|
VALUE
|
|
|
|
rb_iseq_disasm(const rb_iseq_t *iseq)
|
|
|
|
{
|
2020-04-15 06:17:45 +03:00
|
|
|
VALUE str = rb_iseq_disasm_recursive(iseq, rb_str_new(0, 0));
|
|
|
|
rb_str_resize(str, RSTRING_LEN(str));
|
|
|
|
return str;
|
2018-04-04 14:00:39 +03:00
|
|
|
}
|
|
|
|
|
2022-11-22 23:28:14 +03:00
|
|
|
/*
|
|
|
|
* Estimates the number of instance variables that will be set on
|
|
|
|
* a given `class` with the initialize method defined in
|
|
|
|
* `initialize_iseq`
|
|
|
|
*/
|
|
|
|
attr_index_t
|
|
|
|
rb_estimate_iv_count(VALUE klass, const rb_iseq_t * initialize_iseq)
|
|
|
|
{
|
|
|
|
struct rb_id_table * iv_names = rb_id_table_create(0);
|
|
|
|
|
2022-12-06 22:52:11 +03:00
|
|
|
for (unsigned int i = 0; i < ISEQ_BODY(initialize_iseq)->ivc_size; i++) {
|
|
|
|
IVC cache = (IVC)&ISEQ_BODY(initialize_iseq)->is_entries[i];
|
2022-11-22 23:28:14 +03:00
|
|
|
|
2022-12-06 22:52:11 +03:00
|
|
|
if (cache->iv_set_name) {
|
|
|
|
rb_id_table_insert(iv_names, cache->iv_set_name, Qtrue);
|
2022-11-22 23:28:14 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
attr_index_t count = (attr_index_t)rb_id_table_size(iv_names);
|
|
|
|
|
2022-12-06 22:52:11 +03:00
|
|
|
VALUE superclass = rb_class_superclass(klass);
|
|
|
|
count += RCLASS_EXT(superclass)->max_iv_count;
|
2022-11-22 23:28:14 +03:00
|
|
|
|
2022-11-23 00:54:30 +03:00
|
|
|
rb_id_table_free(iv_names);
|
|
|
|
|
2022-11-22 23:28:14 +03:00
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
2015-07-22 01:52:59 +03:00
|
|
|
/*
|
|
|
|
* call-seq:
|
|
|
|
* iseq.disasm -> str
|
|
|
|
* iseq.disassemble -> str
|
|
|
|
*
|
|
|
|
* Returns the instruction sequence as a +String+ in human readable form.
|
|
|
|
*
|
|
|
|
* puts RubyVM::InstructionSequence.compile('1 + 2').disasm
|
|
|
|
*
|
|
|
|
* Produces:
|
|
|
|
*
|
|
|
|
* == disasm: <RubyVM::InstructionSequence:<compiled>@<compiled>>==========
|
|
|
|
* 0000 trace 1 ( 1)
|
|
|
|
* 0002 putobject 1
|
|
|
|
* 0004 putobject 2
|
|
|
|
* 0006 opt_plus <ic:1>
|
|
|
|
* 0008 leave
|
|
|
|
*/
|
|
|
|
static VALUE
|
|
|
|
iseqw_disasm(VALUE self)
|
|
|
|
{
|
|
|
|
return rb_iseq_disasm(iseqw_check(self));
|
|
|
|
}
|
|
|
|
|
2017-12-23 15:48:24 +03:00
|
|
|
static int
|
2018-11-26 21:16:39 +03:00
|
|
|
iseq_iterate_children(const rb_iseq_t *iseq, void (*iter_func)(const rb_iseq_t *child_iseq, void *data), void *data)
|
2017-12-23 15:48:24 +03:00
|
|
|
{
|
2018-11-26 21:16:39 +03:00
|
|
|
unsigned int i;
|
|
|
|
VALUE *code = rb_iseq_original_iseq(iseq);
|
2022-03-23 22:19:48 +03:00
|
|
|
const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
|
2018-11-26 21:16:39 +03:00
|
|
|
const rb_iseq_t *child;
|
|
|
|
VALUE all_children = rb_obj_hide(rb_ident_hash_new());
|
|
|
|
|
|
|
|
if (body->catch_table) {
|
|
|
|
for (i = 0; i < body->catch_table->size; i++) {
|
2019-05-31 09:58:50 +03:00
|
|
|
const struct iseq_catch_table_entry *entry =
|
|
|
|
UNALIGNED_MEMBER_PTR(body->catch_table, entries[i]);
|
2018-11-26 21:16:39 +03:00
|
|
|
child = entry->iseq;
|
|
|
|
if (child) {
|
2021-10-03 16:34:45 +03:00
|
|
|
if (NIL_P(rb_hash_aref(all_children, (VALUE)child))) {
|
2018-11-26 21:16:39 +03:00
|
|
|
rb_hash_aset(all_children, (VALUE)child, Qtrue);
|
|
|
|
(*iter_func)(child, data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i=0; i<body->iseq_size;) {
|
|
|
|
VALUE insn = code[i];
|
|
|
|
int len = insn_len(insn);
|
|
|
|
const char *types = insn_op_types(insn);
|
|
|
|
int j;
|
|
|
|
|
|
|
|
for (j=0; types[j]; j++) {
|
|
|
|
switch (types[j]) {
|
|
|
|
case TS_ISEQ:
|
|
|
|
child = (const rb_iseq_t *)code[i+j+1];
|
|
|
|
if (child) {
|
2021-10-03 16:34:45 +03:00
|
|
|
if (NIL_P(rb_hash_aref(all_children, (VALUE)child))) {
|
2018-11-26 21:16:39 +03:00
|
|
|
rb_hash_aset(all_children, (VALUE)child, Qtrue);
|
|
|
|
(*iter_func)(child, data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
i += len;
|
|
|
|
}
|
|
|
|
|
2018-11-26 21:27:02 +03:00
|
|
|
return (int)RHASH_SIZE(all_children);
|
2018-11-26 21:16:39 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
yield_each_children(const rb_iseq_t *child_iseq, void *data)
|
|
|
|
{
|
|
|
|
rb_yield(iseqw_new(child_iseq));
|
2017-12-23 15:48:24 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
|
|
|
* iseq.each_child{|child_iseq| ...} -> iseq
|
|
|
|
*
|
|
|
|
* Iterate all direct child instruction sequences.
|
|
|
|
* Iteration order is implementation/version defined
|
|
|
|
* so that people should not rely on the order.
|
|
|
|
*/
|
|
|
|
static VALUE
|
|
|
|
iseqw_each_child(VALUE self)
|
|
|
|
{
|
|
|
|
const rb_iseq_t *iseq = iseqw_check(self);
|
2018-11-26 21:16:39 +03:00
|
|
|
iseq_iterate_children(iseq, yield_each_children, NULL);
|
2017-12-23 15:48:24 +03:00
|
|
|
return self;
|
|
|
|
}
|
|
|
|
|
2017-12-23 17:46:59 +03:00
|
|
|
static void
|
|
|
|
push_event_info(const rb_iseq_t *iseq, rb_event_flag_t events, int line, VALUE ary)
|
|
|
|
{
|
|
|
|
#define C(ev, cstr, l) if (events & ev) rb_ary_push(ary, rb_ary_new_from_args(2, l, ID2SYM(rb_intern(cstr))));
|
|
|
|
C(RUBY_EVENT_CLASS, "class", rb_iseq_first_lineno(iseq));
|
|
|
|
C(RUBY_EVENT_CALL, "call", rb_iseq_first_lineno(iseq));
|
|
|
|
C(RUBY_EVENT_B_CALL, "b_call", rb_iseq_first_lineno(iseq));
|
|
|
|
C(RUBY_EVENT_LINE, "line", INT2FIX(line));
|
|
|
|
C(RUBY_EVENT_END, "end", INT2FIX(line));
|
|
|
|
C(RUBY_EVENT_RETURN, "return", INT2FIX(line));
|
|
|
|
C(RUBY_EVENT_B_RETURN, "b_return", INT2FIX(line));
|
2023-08-01 11:25:20 +03:00
|
|
|
C(RUBY_EVENT_RESCUE, "rescue", INT2FIX(line));
|
2017-12-23 17:46:59 +03:00
|
|
|
#undef C
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
|
|
|
* iseq.trace_points -> ary
|
|
|
|
*
|
|
|
|
* Return trace points in the instruction sequence.
|
|
|
|
* Return an array of [line, event_symbol] pair.
|
|
|
|
*/
|
|
|
|
static VALUE
|
|
|
|
iseqw_trace_points(VALUE self)
|
|
|
|
{
|
|
|
|
const rb_iseq_t *iseq = iseqw_check(self);
|
2022-03-23 22:19:48 +03:00
|
|
|
const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
|
2017-12-23 17:46:59 +03:00
|
|
|
unsigned int i;
|
|
|
|
VALUE ary = rb_ary_new();
|
|
|
|
|
2018-05-12 04:24:18 +03:00
|
|
|
for (i=0; i<body->insns_info.size; i++) {
|
|
|
|
const struct iseq_insn_info_entry *entry = &body->insns_info.body[i];
|
2017-12-23 17:46:59 +03:00
|
|
|
if (entry->events) {
|
|
|
|
push_event_info(iseq, entry->events, entry->line_no, ary);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return ary;
|
|
|
|
}
|
|
|
|
|
2012-12-29 10:29:47 +04:00
|
|
|
/*
|
|
|
|
* Returns the instruction sequence containing the given proc or method.
|
|
|
|
*
|
|
|
|
* For example, using irb:
|
|
|
|
*
|
|
|
|
* # a proc
|
|
|
|
* > p = proc { num = 1 + 2 }
|
|
|
|
* > RubyVM::InstructionSequence.of(p)
|
|
|
|
* > #=> <RubyVM::InstructionSequence:block in irb_binding@(irb)>
|
|
|
|
*
|
|
|
|
* # for a method
|
|
|
|
* > def foo(bar); puts bar; end
|
|
|
|
* > RubyVM::InstructionSequence.of(method(:foo))
|
|
|
|
* > #=> <RubyVM::InstructionSequence:foo@(irb)>
|
|
|
|
*
|
|
|
|
* Using ::compile_file:
|
|
|
|
*
|
|
|
|
* # /tmp/iseq_of.rb
|
|
|
|
* def hello
|
|
|
|
* puts "hello, world"
|
|
|
|
* end
|
|
|
|
*
|
|
|
|
* $a_global_proc = proc { str = 'a' + 'b' }
|
|
|
|
*
|
|
|
|
* # in irb
|
|
|
|
* > require '/tmp/iseq_of.rb'
|
|
|
|
*
|
|
|
|
* # first the method hello
|
|
|
|
* > RubyVM::InstructionSequence.of(method(:hello))
|
|
|
|
* > #=> #<RubyVM::InstructionSequence:0x007fb73d7cb1d0>
|
|
|
|
*
|
|
|
|
* # then the global proc
|
|
|
|
* > RubyVM::InstructionSequence.of($a_global_proc)
|
|
|
|
* > #=> #<RubyVM::InstructionSequence:0x007fb73d7caf78>
|
|
|
|
*/
|
2012-11-30 22:02:43 +04:00
|
|
|
static VALUE
|
2015-07-22 01:52:59 +03:00
|
|
|
iseqw_s_of(VALUE klass, VALUE body)
|
2012-11-30 22:02:43 +04:00
|
|
|
{
|
2015-07-22 01:52:59 +03:00
|
|
|
const rb_iseq_t *iseq = NULL;
|
2012-11-30 22:02:43 +04:00
|
|
|
|
|
|
|
if (rb_obj_is_proc(body)) {
|
2018-11-27 05:45:25 +03:00
|
|
|
iseq = vm_proc_iseq(body);
|
2015-07-22 01:52:59 +03:00
|
|
|
|
2018-11-27 06:02:41 +03:00
|
|
|
if (!rb_obj_is_iseq((VALUE)iseq)) {
|
|
|
|
iseq = NULL;
|
2018-11-27 05:45:25 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (rb_obj_is_method(body)) {
|
2018-11-27 06:02:41 +03:00
|
|
|
iseq = rb_method_iseq(body);
|
2018-11-27 05:45:25 +03:00
|
|
|
}
|
2018-11-27 06:19:06 +03:00
|
|
|
else if (rb_typeddata_is_instance_of(body, &iseqw_data_type)) {
|
2018-11-27 05:45:25 +03:00
|
|
|
return body;
|
2012-11-30 22:02:43 +04:00
|
|
|
}
|
2018-11-27 06:02:41 +03:00
|
|
|
|
|
|
|
return iseq ? iseqw_new(iseq) : Qnil;
|
2012-11-30 22:02:43 +04:00
|
|
|
}
|
|
|
|
|
2012-07-25 01:29:24 +04:00
|
|
|
/*
|
|
|
|
* call-seq:
|
|
|
|
* InstructionSequence.disasm(body) -> str
|
|
|
|
* InstructionSequence.disassemble(body) -> str
|
|
|
|
*
|
|
|
|
* Takes +body+, a Method or Proc object, and returns a String with the
|
|
|
|
* human readable instructions for +body+.
|
|
|
|
*
|
|
|
|
* For a Method object:
|
|
|
|
*
|
|
|
|
* # /tmp/method.rb
|
|
|
|
* def hello
|
|
|
|
* puts "hello, world"
|
|
|
|
* end
|
|
|
|
*
|
|
|
|
* puts RubyVM::InstructionSequence.disasm(method(:hello))
|
|
|
|
*
|
|
|
|
* Produces:
|
|
|
|
*
|
|
|
|
* == disasm: <RubyVM::InstructionSequence:hello@/tmp/method.rb>============
|
|
|
|
* 0000 trace 8 ( 1)
|
|
|
|
* 0002 trace 1 ( 2)
|
|
|
|
* 0004 putself
|
|
|
|
* 0005 putstring "hello, world"
|
|
|
|
* 0007 send :puts, 1, nil, 8, <ic:0>
|
|
|
|
* 0013 trace 16 ( 3)
|
|
|
|
* 0015 leave ( 2)
|
|
|
|
*
|
|
|
|
* For a Proc:
|
|
|
|
*
|
|
|
|
* # /tmp/proc.rb
|
|
|
|
* p = proc { num = 1 + 2 }
|
|
|
|
* puts RubyVM::InstructionSequence.disasm(p)
|
|
|
|
*
|
|
|
|
* Produces:
|
|
|
|
*
|
|
|
|
* == disasm: <RubyVM::InstructionSequence:block in <main>@/tmp/proc.rb>===
|
|
|
|
* == catch table
|
|
|
|
* | catch type: redo st: 0000 ed: 0012 sp: 0000 cont: 0000
|
|
|
|
* | catch type: next st: 0000 ed: 0012 sp: 0000 cont: 0012
|
|
|
|
* |------------------------------------------------------------------------
|
|
|
|
* local table (size: 2, argc: 0 [opts: 0, rest: -1, post: 0, block: -1] s1)
|
|
|
|
* [ 2] num
|
|
|
|
* 0000 trace 1 ( 1)
|
|
|
|
* 0002 putobject 1
|
|
|
|
* 0004 putobject 2
|
|
|
|
* 0006 opt_plus <ic:1>
|
|
|
|
* 0008 dup
|
2012-10-04 17:52:20 +04:00
|
|
|
* 0009 setlocal num, 0
|
2012-07-25 01:29:24 +04:00
|
|
|
* 0012 leave
|
|
|
|
*
|
|
|
|
*/
|
2007-12-24 12:09:21 +03:00
|
|
|
static VALUE
|
2015-07-22 01:52:59 +03:00
|
|
|
iseqw_s_disasm(VALUE klass, VALUE body)
|
2007-12-24 12:09:21 +03:00
|
|
|
{
|
2015-07-22 01:52:59 +03:00
|
|
|
VALUE iseqw = iseqw_s_of(klass, body);
|
|
|
|
return NIL_P(iseqw) ? Qnil : rb_iseq_disasm(iseqw_check(iseqw));
|
2007-12-24 12:09:21 +03:00
|
|
|
}
|
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
static VALUE
|
* compile.c, cont.c, gc.c, insns.def, iseq.c, iseq.h, process.c,
thread.c, vm.c, vm_core.h, vm_dump.c, vm_eval.c,
vm_insnhelper.c, vm_method.c, template/insns_info.inc.tmpl,
tool/instruction.rb: fixed types.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@25030 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2009-09-22 00:58:26 +04:00
|
|
|
register_label(struct st_table *table, unsigned long idx)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2014-10-14 11:23:01 +04:00
|
|
|
VALUE sym = rb_str_intern(rb_sprintf("label_%lu", idx));
|
2006-12-31 18:02:22 +03:00
|
|
|
st_insert(table, idx, sym);
|
|
|
|
return sym;
|
|
|
|
}
|
|
|
|
|
|
|
|
static VALUE
|
|
|
|
exception_type2symbol(VALUE type)
|
|
|
|
{
|
|
|
|
ID id;
|
2012-12-29 16:22:04 +04:00
|
|
|
switch (type) {
|
2008-06-09 13:25:32 +04:00
|
|
|
case CATCH_TYPE_RESCUE: CONST_ID(id, "rescue"); break;
|
|
|
|
case CATCH_TYPE_ENSURE: CONST_ID(id, "ensure"); break;
|
|
|
|
case CATCH_TYPE_RETRY: CONST_ID(id, "retry"); break;
|
|
|
|
case CATCH_TYPE_BREAK: CONST_ID(id, "break"); break;
|
|
|
|
case CATCH_TYPE_REDO: CONST_ID(id, "redo"); break;
|
|
|
|
case CATCH_TYPE_NEXT: CONST_ID(id, "next"); break;
|
2006-12-31 18:02:22 +03:00
|
|
|
default:
|
2018-04-27 16:14:09 +03:00
|
|
|
rb_bug("unknown exception type: %d", (int)type);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
return ID2SYM(id);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
cdhash_each(VALUE key, VALUE value, VALUE ary)
|
|
|
|
{
|
2009-02-26 07:23:21 +03:00
|
|
|
rb_ary_push(ary, obj_resurrect(key));
|
2006-12-31 18:02:22 +03:00
|
|
|
rb_ary_push(ary, value);
|
|
|
|
return ST_CONTINUE;
|
|
|
|
}
|
|
|
|
|
2019-10-08 23:35:24 +03:00
|
|
|
static const rb_data_type_t label_wrapper = {
|
|
|
|
"label_wrapper",
|
|
|
|
{(void (*)(void *))rb_mark_tbl, (void (*)(void *))st_free_table, 0, 0,},
|
|
|
|
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
|
|
|
|
};
|
|
|
|
|
2021-12-18 23:35:16 +03:00
|
|
|
#define DECL_ID(name) \
|
|
|
|
static ID id_##name
|
|
|
|
|
|
|
|
#define INIT_ID(name) \
|
|
|
|
id_##name = rb_intern(#name)
|
|
|
|
|
2021-12-18 21:20:00 +03:00
|
|
|
static VALUE
|
2022-07-22 10:57:25 +03:00
|
|
|
iseq_type_id(enum rb_iseq_type type)
|
2021-12-18 23:35:16 +03:00
|
|
|
{
|
|
|
|
DECL_ID(top);
|
|
|
|
DECL_ID(method);
|
|
|
|
DECL_ID(block);
|
|
|
|
DECL_ID(class);
|
|
|
|
DECL_ID(rescue);
|
|
|
|
DECL_ID(ensure);
|
|
|
|
DECL_ID(eval);
|
|
|
|
DECL_ID(main);
|
|
|
|
DECL_ID(plain);
|
|
|
|
|
|
|
|
if (id_top == 0) {
|
|
|
|
INIT_ID(top);
|
|
|
|
INIT_ID(method);
|
|
|
|
INIT_ID(block);
|
|
|
|
INIT_ID(class);
|
|
|
|
INIT_ID(rescue);
|
|
|
|
INIT_ID(ensure);
|
|
|
|
INIT_ID(eval);
|
|
|
|
INIT_ID(main);
|
|
|
|
INIT_ID(plain);
|
2021-12-18 21:20:00 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
switch (type) {
|
2021-12-18 23:35:16 +03:00
|
|
|
case ISEQ_TYPE_TOP: return id_top;
|
|
|
|
case ISEQ_TYPE_METHOD: return id_method;
|
|
|
|
case ISEQ_TYPE_BLOCK: return id_block;
|
|
|
|
case ISEQ_TYPE_CLASS: return id_class;
|
|
|
|
case ISEQ_TYPE_RESCUE: return id_rescue;
|
|
|
|
case ISEQ_TYPE_ENSURE: return id_ensure;
|
|
|
|
case ISEQ_TYPE_EVAL: return id_eval;
|
|
|
|
case ISEQ_TYPE_MAIN: return id_main;
|
|
|
|
case ISEQ_TYPE_PLAIN: return id_plain;
|
2021-12-18 21:20:00 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
rb_bug("unsupported iseq type: %d", (int)type);
|
|
|
|
}
|
|
|
|
|
2008-09-04 22:23:27 +04:00
|
|
|
static VALUE
|
2015-07-22 00:28:43 +03:00
|
|
|
iseq_data_to_ary(const rb_iseq_t *iseq)
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2015-07-25 05:18:32 +03:00
|
|
|
unsigned int i;
|
|
|
|
long l;
|
2022-03-23 22:19:48 +03:00
|
|
|
const struct rb_iseq_constant_body *const iseq_body = ISEQ_BODY(iseq);
|
2018-01-09 17:05:21 +03:00
|
|
|
const struct iseq_insn_info_entry *prev_insn_info;
|
2011-08-26 23:04:39 +04:00
|
|
|
unsigned int pos;
|
2017-11-14 15:58:36 +03:00
|
|
|
int last_line = 0;
|
compile: translate iseq in-place
running "ruby -rpp -e 'pp GC.stat'", a reduction in
malloc usage is shown:
before:
:malloc_increase=>118784,
:oldmalloc_increase=>1178736,
after:
:malloc_increase=>99832,
:oldmalloc_increase=>1031976,
For "ruby -e exit", valgrind reports over 300K reduction in
overall allocations (and unnecessary memory copies).
before:
total heap usage: 49,622 allocs, 20,492 frees, 8,697,493 bytes allocated
after:
total heap usage: 48,935 allocs, 19,805 frees, 8,373,773 bytes allocated
(numbers from x86-64)
v2 changes based on ko1 recommendations [ruby-core:64883]:
- squashed in-place direct thread translation to avoid alloc+copy
- renamed rb_iseq_untranslate_threaded_code to rb_iseq_original_iseq,
cache new iseq->iseq_original field.
* compile.c (rb_iseq_translate_threaded_code): modify in-place w/o copy
(rb_vm_addr2insn): new function for debug
(rb_iseq_original_iseq): ditto
(iseq_set_sequence): assign iseq_encoded directly
[Feature #10185]
* vm_core (rb_iseq_t): move original ->iseq to bottom
* iseq.c (iseq_free, iseq_free): adjust for new layout
(rb_iseq_disasm): use original iseq for dump
(iseq_data_to_ary): ditto
(rb_iseq_line_trace_each): ditto
(rb_iseq_build_for_ruby2cext): use iseq_encoded directly
* vm_dump.c (rb_vmdebug_debug_print_pre): use original iseq
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@47508 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2014-09-10 10:14:07 +04:00
|
|
|
VALUE *seq, *iseq_original;
|
2006-12-31 18:02:22 +03:00
|
|
|
|
|
|
|
VALUE val = rb_ary_new();
|
2019-04-23 02:23:04 +03:00
|
|
|
ID type; /* Symbol */
|
2006-12-31 18:02:22 +03:00
|
|
|
VALUE locals = rb_ary_new();
|
2014-11-03 02:14:21 +03:00
|
|
|
VALUE params = rb_hash_new();
|
2006-12-31 18:02:22 +03:00
|
|
|
VALUE body = rb_ary_new(); /* [[:insn1, ...], ...] */
|
|
|
|
VALUE nbody;
|
|
|
|
VALUE exception = rb_ary_new(); /* [[....]] */
|
2007-07-02 01:43:30 +04:00
|
|
|
VALUE misc = rb_hash_new();
|
2007-05-11 10:26:06 +04:00
|
|
|
|
2019-04-23 02:23:04 +03:00
|
|
|
static ID insn_syms[VM_INSTRUCTION_SIZE/2]; /* w/o-trace only */
|
2006-12-31 18:02:22 +03:00
|
|
|
struct st_table *labels_table = st_init_numtable();
|
2019-10-08 23:35:24 +03:00
|
|
|
VALUE labels_wrapper = TypedData_Wrap_Struct(0, &label_wrapper, labels_table);
|
2007-05-11 10:26:06 +04:00
|
|
|
|
2021-12-18 21:20:00 +03:00
|
|
|
if (insn_syms[0] == 0) {
|
|
|
|
int i;
|
|
|
|
for (i=0; i<numberof(insn_syms); i++) {
|
2019-04-23 04:19:47 +03:00
|
|
|
insn_syms[i] = rb_intern(insn_name(i));
|
2021-12-18 21:20:00 +03:00
|
|
|
}
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
2007-05-11 10:26:06 +04:00
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
/* type */
|
2021-12-18 23:35:16 +03:00
|
|
|
type = iseq_type_id(iseq_body->type);
|
2006-12-31 18:02:22 +03:00
|
|
|
|
|
|
|
/* locals */
|
2018-05-12 04:24:18 +03:00
|
|
|
for (i=0; i<iseq_body->local_table_size; i++) {
|
|
|
|
ID lid = iseq_body->local_table[i];
|
2006-12-31 18:02:22 +03:00
|
|
|
if (lid) {
|
2014-12-02 01:32:56 +03:00
|
|
|
if (rb_id2str(lid)) {
|
|
|
|
rb_ary_push(locals, ID2SYM(lid));
|
|
|
|
}
|
|
|
|
else { /* hidden variable from id_internal() */
|
2018-05-12 04:24:18 +03:00
|
|
|
rb_ary_push(locals, ULONG2NUM(iseq_body->local_table_size-i+1));
|
2014-12-02 01:32:56 +03:00
|
|
|
}
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
rb_ary_push(locals, ID2SYM(rb_intern("#arg_rest")));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-03 02:14:21 +03:00
|
|
|
/* params */
|
2006-12-31 18:02:22 +03:00
|
|
|
{
|
2018-05-12 04:24:18 +03:00
|
|
|
const struct rb_iseq_param_keyword *const keyword = iseq_body->param.keyword;
|
2006-12-31 18:02:22 +03:00
|
|
|
int j;
|
2022-07-21 19:23:58 +03:00
|
|
|
|
2018-05-12 04:24:18 +03:00
|
|
|
if (iseq_body->param.flags.has_opt) {
|
|
|
|
int len = iseq_body->param.opt_num + 1;
|
|
|
|
VALUE arg_opt_labels = rb_ary_new2(len);
|
2022-07-21 19:23:58 +03:00
|
|
|
|
2018-05-12 04:24:18 +03:00
|
|
|
for (j = 0; j < len; j++) {
|
|
|
|
VALUE l = register_label(labels_table, iseq_body->param.opt_table[j]);
|
|
|
|
rb_ary_push(arg_opt_labels, l);
|
|
|
|
}
|
|
|
|
rb_hash_aset(params, ID2SYM(rb_intern("opt")), arg_opt_labels);
|
2014-12-04 01:16:58 +03:00
|
|
|
}
|
2022-07-21 19:23:58 +03:00
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
/* commit */
|
2018-05-12 04:24:18 +03:00
|
|
|
if (iseq_body->param.flags.has_lead) rb_hash_aset(params, ID2SYM(rb_intern("lead_num")), INT2FIX(iseq_body->param.lead_num));
|
|
|
|
if (iseq_body->param.flags.has_post) rb_hash_aset(params, ID2SYM(rb_intern("post_num")), INT2FIX(iseq_body->param.post_num));
|
|
|
|
if (iseq_body->param.flags.has_post) rb_hash_aset(params, ID2SYM(rb_intern("post_start")), INT2FIX(iseq_body->param.post_start));
|
|
|
|
if (iseq_body->param.flags.has_rest) rb_hash_aset(params, ID2SYM(rb_intern("rest_start")), INT2FIX(iseq_body->param.rest_start));
|
|
|
|
if (iseq_body->param.flags.has_block) rb_hash_aset(params, ID2SYM(rb_intern("block_start")), INT2FIX(iseq_body->param.block_start));
|
|
|
|
if (iseq_body->param.flags.has_kw) {
|
2014-11-03 02:14:21 +03:00
|
|
|
VALUE keywords = rb_ary_new();
|
|
|
|
int i, j;
|
2018-05-12 04:24:18 +03:00
|
|
|
for (i=0; i<keyword->required_num; i++) {
|
|
|
|
rb_ary_push(keywords, ID2SYM(keyword->table[i]));
|
2014-11-03 02:14:21 +03:00
|
|
|
}
|
2018-05-12 04:24:18 +03:00
|
|
|
for (j=0; i<keyword->num; i++, j++) {
|
|
|
|
VALUE key = rb_ary_new_from_args(1, ID2SYM(keyword->table[i]));
|
2022-11-15 07:24:08 +03:00
|
|
|
if (!UNDEF_P(keyword->default_values[j])) {
|
2018-05-12 04:24:18 +03:00
|
|
|
rb_ary_push(key, keyword->default_values[j]);
|
2014-11-03 02:14:21 +03:00
|
|
|
}
|
|
|
|
rb_ary_push(keywords, key);
|
|
|
|
}
|
2022-07-21 19:23:58 +03:00
|
|
|
|
2014-12-04 01:16:58 +03:00
|
|
|
rb_hash_aset(params, ID2SYM(rb_intern("kwbits")),
|
2018-05-12 04:24:18 +03:00
|
|
|
INT2FIX(keyword->bits_start));
|
2014-11-03 02:14:21 +03:00
|
|
|
rb_hash_aset(params, ID2SYM(rb_intern("keyword")), keywords);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
2018-05-12 04:24:18 +03:00
|
|
|
if (iseq_body->param.flags.has_kwrest) rb_hash_aset(params, ID2SYM(rb_intern("kwrest")), INT2FIX(keyword->rest_start));
|
|
|
|
if (iseq_body->param.flags.ambiguous_param0) rb_hash_aset(params, ID2SYM(rb_intern("ambiguous_param0")), Qtrue);
|
2024-04-17 10:30:47 +03:00
|
|
|
if (iseq_body->param.flags.use_block) rb_hash_aset(params, ID2SYM(rb_intern("use_block")), Qtrue);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/* body */
|
2015-07-22 00:28:43 +03:00
|
|
|
iseq_original = rb_iseq_original_iseq((rb_iseq_t *)iseq);
|
compile: translate iseq in-place
running "ruby -rpp -e 'pp GC.stat'", a reduction in
malloc usage is shown:
before:
:malloc_increase=>118784,
:oldmalloc_increase=>1178736,
after:
:malloc_increase=>99832,
:oldmalloc_increase=>1031976,
For "ruby -e exit", valgrind reports over 300K reduction in
overall allocations (and unnecessary memory copies).
before:
total heap usage: 49,622 allocs, 20,492 frees, 8,697,493 bytes allocated
after:
total heap usage: 48,935 allocs, 19,805 frees, 8,373,773 bytes allocated
(numbers from x86-64)
v2 changes based on ko1 recommendations [ruby-core:64883]:
- squashed in-place direct thread translation to avoid alloc+copy
- renamed rb_iseq_untranslate_threaded_code to rb_iseq_original_iseq,
cache new iseq->iseq_original field.
* compile.c (rb_iseq_translate_threaded_code): modify in-place w/o copy
(rb_vm_addr2insn): new function for debug
(rb_iseq_original_iseq): ditto
(iseq_set_sequence): assign iseq_encoded directly
[Feature #10185]
* vm_core (rb_iseq_t): move original ->iseq to bottom
* iseq.c (iseq_free, iseq_free): adjust for new layout
(rb_iseq_disasm): use original iseq for dump
(iseq_data_to_ary): ditto
(rb_iseq_line_trace_each): ditto
(rb_iseq_build_for_ruby2cext): use iseq_encoded directly
* vm_dump.c (rb_vmdebug_debug_print_pre): use original iseq
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@47508 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2014-09-10 10:14:07 +04:00
|
|
|
|
2018-05-12 04:24:18 +03:00
|
|
|
for (seq = iseq_original; seq < iseq_original + iseq_body->iseq_size; ) {
|
2006-12-31 18:02:22 +03:00
|
|
|
VALUE insn = *seq++;
|
|
|
|
int j, len = insn_len(insn);
|
|
|
|
VALUE *nseq = seq + len - 1;
|
2007-05-11 10:26:06 +04:00
|
|
|
VALUE ary = rb_ary_new2(len);
|
|
|
|
|
2019-04-23 04:19:47 +03:00
|
|
|
rb_ary_push(ary, ID2SYM(insn_syms[insn%numberof(insn_syms)]));
|
2006-12-31 18:02:22 +03:00
|
|
|
for (j=0; j<len-1; j++, seq++) {
|
2022-06-28 00:31:15 +03:00
|
|
|
enum ruby_insn_type_chars op_type = insn_op_type(insn, j);
|
2022-07-21 19:23:58 +03:00
|
|
|
|
2022-06-28 00:31:15 +03:00
|
|
|
switch (op_type) {
|
2006-12-31 18:02:22 +03:00
|
|
|
case TS_OFFSET: {
|
compile: translate iseq in-place
running "ruby -rpp -e 'pp GC.stat'", a reduction in
malloc usage is shown:
before:
:malloc_increase=>118784,
:oldmalloc_increase=>1178736,
after:
:malloc_increase=>99832,
:oldmalloc_increase=>1031976,
For "ruby -e exit", valgrind reports over 300K reduction in
overall allocations (and unnecessary memory copies).
before:
total heap usage: 49,622 allocs, 20,492 frees, 8,697,493 bytes allocated
after:
total heap usage: 48,935 allocs, 19,805 frees, 8,373,773 bytes allocated
(numbers from x86-64)
v2 changes based on ko1 recommendations [ruby-core:64883]:
- squashed in-place direct thread translation to avoid alloc+copy
- renamed rb_iseq_untranslate_threaded_code to rb_iseq_original_iseq,
cache new iseq->iseq_original field.
* compile.c (rb_iseq_translate_threaded_code): modify in-place w/o copy
(rb_vm_addr2insn): new function for debug
(rb_iseq_original_iseq): ditto
(iseq_set_sequence): assign iseq_encoded directly
[Feature #10185]
* vm_core (rb_iseq_t): move original ->iseq to bottom
* iseq.c (iseq_free, iseq_free): adjust for new layout
(rb_iseq_disasm): use original iseq for dump
(iseq_data_to_ary): ditto
(rb_iseq_line_trace_each): ditto
(rb_iseq_build_for_ruby2cext): use iseq_encoded directly
* vm_dump.c (rb_vmdebug_debug_print_pre): use original iseq
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@47508 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2014-09-10 10:14:07 +04:00
|
|
|
unsigned long idx = nseq - iseq_original + *seq;
|
2007-05-11 10:26:06 +04:00
|
|
|
rb_ary_push(ary, register_label(labels_table, idx));
|
|
|
|
break;
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
case TS_LINDEX:
|
|
|
|
case TS_NUM:
|
|
|
|
rb_ary_push(ary, INT2FIX(*seq));
|
|
|
|
break;
|
|
|
|
case TS_VALUE:
|
2009-02-18 08:33:36 +03:00
|
|
|
rb_ary_push(ary, obj_resurrect(*seq));
|
2006-12-31 18:02:22 +03:00
|
|
|
break;
|
|
|
|
case TS_ISEQ:
|
|
|
|
{
|
2015-07-22 00:28:43 +03:00
|
|
|
const rb_iseq_t *iseq = (rb_iseq_t *)*seq;
|
2006-12-31 18:02:22 +03:00
|
|
|
if (iseq) {
|
2015-12-08 16:58:50 +03:00
|
|
|
VALUE val = iseq_data_to_ary(rb_iseq_check(iseq));
|
2006-12-31 18:02:22 +03:00
|
|
|
rb_ary_push(ary, val);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
rb_ary_push(ary, Qnil);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
2013-08-20 21:41:13 +04:00
|
|
|
case TS_IC:
|
New constant caching insn: opt_getconstant_path
Previously YARV bytecode implemented constant caching by having a pair
of instructions, opt_getinlinecache and opt_setinlinecache, wrapping a
series of getconstant calls (with putobject providing supporting
arguments).
This commit replaces that pattern with a new instruction,
opt_getconstant_path, handling both getting/setting the inline cache and
fetching the constant on a cache miss.
This is implemented by storing the full constant path as a
null-terminated array of IDs inside of the IC structure. idNULL is used
to signal an absolute constant reference.
$ ./miniruby --dump=insns -e '::Foo::Bar::Baz'
== disasm: #<ISeq:<main>@-e:1 (1,0)-(1,13)> (catch: FALSE)
0000 opt_getconstant_path <ic:0 ::Foo::Bar::Baz> ( 1)[Li]
0002 leave
The motivation for this is that we had increasingly found the need to
disassemble the instructions between the opt_getinlinecache and
opt_setinlinecache in order to determine the constant we are fetching,
or otherwise store metadata.
This disassembly was done:
* In opt_setinlinecache, to register the IC against the constant names
it is using for granular invalidation.
* In rb_iseq_free, to unregister the IC from the invalidation table.
* In YJIT to find the position of a opt_getinlinecache instruction to
invalidate it when the cache is populated
* In YJIT to register the constant names being used for invalidation.
With this change we no longe need disassemly for these (in fact
rb_iseq_each is now unused), as the list of constant names being
referenced is held in the IC. This should also make it possible to make
more optimizations in the future.
This may also reduce the size of iseqs, as previously each segment
required 32 bytes (on 64-bit platforms) for each constant segment. This
implementation only stores one ID per-segment.
There should be no significant performance change between this and the
previous implementation. Previously opt_getinlinecache was a "leaf"
instruction, but it included a jump (almost always to a separate cache
line). Now opt_getconstant_path is a non-leaf (it may
raise/autoload/call const_missing) but it does not jump. These seem to
even out.
2022-08-10 20:35:48 +03:00
|
|
|
{
|
|
|
|
VALUE list = rb_ary_new();
|
|
|
|
const ID *ids = ((IC)*seq)->segments;
|
|
|
|
while (*ids) {
|
|
|
|
rb_ary_push(list, ID2SYM(*ids++));
|
|
|
|
}
|
|
|
|
rb_ary_push(ary, list);
|
|
|
|
}
|
|
|
|
break;
|
2019-10-12 03:06:41 +03:00
|
|
|
case TS_IVC:
|
2022-02-03 05:21:41 +03:00
|
|
|
case TS_ICVARC:
|
2018-03-19 21:21:54 +03:00
|
|
|
case TS_ISE:
|
2013-08-20 21:41:13 +04:00
|
|
|
{
|
|
|
|
union iseq_inline_storage_entry *is = (union iseq_inline_storage_entry *)*seq;
|
2022-06-28 00:31:15 +03:00
|
|
|
rb_ary_push(ary, INT2FIX(is - ISEQ_IS_ENTRY_START(ISEQ_BODY(iseq), op_type)));
|
2013-08-20 21:41:13 +04:00
|
|
|
}
|
2006-12-31 18:02:22 +03:00
|
|
|
break;
|
2019-07-31 04:36:05 +03:00
|
|
|
case TS_CALLDATA:
|
* insns.def (send, invokesuper, invokeblock, opt_*), vm_core.h:
use only a `ci' (rb_call_info_t) parameter instead of using
parameters such as `op_id', 'op_argc', `blockiseq' and flag.
These information are stored in rb_call_info_t at the compile
time.
This technique simplifies parameter passings at related
function calls (~10% speedups for simple mehtod invocation at
my machine).
`rb_call_info_t' also has new function pointer variable `call'.
This `call' variable enables to customize method (block)
invocation process for each place. However, it always call
`vm_call_general()' at this changes.
`rb_call_info_t' also has temporary variables for method
(block) invocation.
* vm_core.h, compile.c, insns.def: introduce VM_CALL_ARGS_SKIP_SETUP
VM_CALL macro. This flag indicates that this call can skip
caller_setup (block arg and splat arg).
* compile.c: catch up above changes.
* iseq.c: catch up above changes (especially for TS_CALLINFO).
* tool/instruction.rb: catch up above chagnes.
* vm_insnhelper.c, vm_insnhelper.h: ditto. Macros and functions
parameters are changed.
* vm_eval.c (vm_call0): ditto (it will be rewriten soon).
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37180 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-10-14 20:59:05 +04:00
|
|
|
{
|
2019-07-31 04:36:05 +03:00
|
|
|
struct rb_call_data *cd = (struct rb_call_data *)*seq;
|
VALUE size packed callinfo (ci).
Now, rb_call_info contains how to call the method with tuple of
(mid, orig_argc, flags, kwarg). Most of cases, kwarg == NULL and
mid+argc+flags only requires 64bits. So this patch packed
rb_call_info to VALUE (1 word) on such cases. If we can not
represent it in VALUE, then use imemo_callinfo which contains
conventional callinfo (rb_callinfo, renamed from rb_call_info).
iseq->body->ci_kw_size is removed because all of callinfo is VALUE
size (packed ci or a pointer to imemo_callinfo).
To access ci information, we need to use these functions:
vm_ci_mid(ci), _flag(ci), _argc(ci), _kwarg(ci).
struct rb_call_info_kw_arg is renamed to rb_callinfo_kwarg.
rb_funcallv_with_cc() and rb_method_basic_definition_p_with_cc()
is temporary removed because cd->ci should be marked.
2020-01-08 02:20:36 +03:00
|
|
|
const struct rb_callinfo *ci = cd->ci;
|
* insns.def (send, invokesuper, invokeblock, opt_*), vm_core.h:
use only a `ci' (rb_call_info_t) parameter instead of using
parameters such as `op_id', 'op_argc', `blockiseq' and flag.
These information are stored in rb_call_info_t at the compile
time.
This technique simplifies parameter passings at related
function calls (~10% speedups for simple mehtod invocation at
my machine).
`rb_call_info_t' also has new function pointer variable `call'.
This `call' variable enables to customize method (block)
invocation process for each place. However, it always call
`vm_call_general()' at this changes.
`rb_call_info_t' also has temporary variables for method
(block) invocation.
* vm_core.h, compile.c, insns.def: introduce VM_CALL_ARGS_SKIP_SETUP
VM_CALL macro. This flag indicates that this call can skip
caller_setup (block arg and splat arg).
* compile.c: catch up above changes.
* iseq.c: catch up above changes (especially for TS_CALLINFO).
* tool/instruction.rb: catch up above chagnes.
* vm_insnhelper.c, vm_insnhelper.h: ditto. Macros and functions
parameters are changed.
* vm_eval.c (vm_call0): ditto (it will be rewriten soon).
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37180 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-10-14 20:59:05 +04:00
|
|
|
VALUE e = rb_hash_new();
|
VALUE size packed callinfo (ci).
Now, rb_call_info contains how to call the method with tuple of
(mid, orig_argc, flags, kwarg). Most of cases, kwarg == NULL and
mid+argc+flags only requires 64bits. So this patch packed
rb_call_info to VALUE (1 word) on such cases. If we can not
represent it in VALUE, then use imemo_callinfo which contains
conventional callinfo (rb_callinfo, renamed from rb_call_info).
iseq->body->ci_kw_size is removed because all of callinfo is VALUE
size (packed ci or a pointer to imemo_callinfo).
To access ci information, we need to use these functions:
vm_ci_mid(ci), _flag(ci), _argc(ci), _kwarg(ci).
struct rb_call_info_kw_arg is renamed to rb_callinfo_kwarg.
rb_funcallv_with_cc() and rb_method_basic_definition_p_with_cc()
is temporary removed because cd->ci should be marked.
2020-01-08 02:20:36 +03:00
|
|
|
int argc = vm_ci_argc(ci);
|
2014-12-04 01:16:58 +03:00
|
|
|
|
VALUE size packed callinfo (ci).
Now, rb_call_info contains how to call the method with tuple of
(mid, orig_argc, flags, kwarg). Most of cases, kwarg == NULL and
mid+argc+flags only requires 64bits. So this patch packed
rb_call_info to VALUE (1 word) on such cases. If we can not
represent it in VALUE, then use imemo_callinfo which contains
conventional callinfo (rb_callinfo, renamed from rb_call_info).
iseq->body->ci_kw_size is removed because all of callinfo is VALUE
size (packed ci or a pointer to imemo_callinfo).
To access ci information, we need to use these functions:
vm_ci_mid(ci), _flag(ci), _argc(ci), _kwarg(ci).
struct rb_call_info_kw_arg is renamed to rb_callinfo_kwarg.
rb_funcallv_with_cc() and rb_method_basic_definition_p_with_cc()
is temporary removed because cd->ci should be marked.
2020-01-08 02:20:36 +03:00
|
|
|
ID mid = vm_ci_mid(ci);
|
|
|
|
rb_hash_aset(e, ID2SYM(rb_intern("mid")), mid ? ID2SYM(mid) : Qnil);
|
|
|
|
rb_hash_aset(e, ID2SYM(rb_intern("flag")), UINT2NUM(vm_ci_flag(ci)));
|
2014-12-04 01:16:58 +03:00
|
|
|
|
VALUE size packed callinfo (ci).
Now, rb_call_info contains how to call the method with tuple of
(mid, orig_argc, flags, kwarg). Most of cases, kwarg == NULL and
mid+argc+flags only requires 64bits. So this patch packed
rb_call_info to VALUE (1 word) on such cases. If we can not
represent it in VALUE, then use imemo_callinfo which contains
conventional callinfo (rb_callinfo, renamed from rb_call_info).
iseq->body->ci_kw_size is removed because all of callinfo is VALUE
size (packed ci or a pointer to imemo_callinfo).
To access ci information, we need to use these functions:
vm_ci_mid(ci), _flag(ci), _argc(ci), _kwarg(ci).
struct rb_call_info_kw_arg is renamed to rb_callinfo_kwarg.
rb_funcallv_with_cc() and rb_method_basic_definition_p_with_cc()
is temporary removed because cd->ci should be marked.
2020-01-08 02:20:36 +03:00
|
|
|
if (vm_ci_flag(ci) & VM_CALL_KWARG) {
|
|
|
|
const struct rb_callinfo_kwarg *kwarg = vm_ci_kwarg(ci);
|
|
|
|
int i;
|
|
|
|
VALUE kw = rb_ary_new2((long)kwarg->keyword_len);
|
2014-12-04 01:16:58 +03:00
|
|
|
|
VALUE size packed callinfo (ci).
Now, rb_call_info contains how to call the method with tuple of
(mid, orig_argc, flags, kwarg). Most of cases, kwarg == NULL and
mid+argc+flags only requires 64bits. So this patch packed
rb_call_info to VALUE (1 word) on such cases. If we can not
represent it in VALUE, then use imemo_callinfo which contains
conventional callinfo (rb_callinfo, renamed from rb_call_info).
iseq->body->ci_kw_size is removed because all of callinfo is VALUE
size (packed ci or a pointer to imemo_callinfo).
To access ci information, we need to use these functions:
vm_ci_mid(ci), _flag(ci), _argc(ci), _kwarg(ci).
struct rb_call_info_kw_arg is renamed to rb_callinfo_kwarg.
rb_funcallv_with_cc() and rb_method_basic_definition_p_with_cc()
is temporary removed because cd->ci should be marked.
2020-01-08 02:20:36 +03:00
|
|
|
argc -= kwarg->keyword_len;
|
|
|
|
for (i = 0; i < kwarg->keyword_len; i++) {
|
|
|
|
rb_ary_push(kw, kwarg->keywords[i]);
|
2014-12-04 01:16:58 +03:00
|
|
|
}
|
|
|
|
rb_hash_aset(e, ID2SYM(rb_intern("kw_arg")), kw);
|
|
|
|
}
|
2022-07-21 19:23:58 +03:00
|
|
|
|
2014-12-04 01:16:58 +03:00
|
|
|
rb_hash_aset(e, ID2SYM(rb_intern("orig_argc")),
|
VALUE size packed callinfo (ci).
Now, rb_call_info contains how to call the method with tuple of
(mid, orig_argc, flags, kwarg). Most of cases, kwarg == NULL and
mid+argc+flags only requires 64bits. So this patch packed
rb_call_info to VALUE (1 word) on such cases. If we can not
represent it in VALUE, then use imemo_callinfo which contains
conventional callinfo (rb_callinfo, renamed from rb_call_info).
iseq->body->ci_kw_size is removed because all of callinfo is VALUE
size (packed ci or a pointer to imemo_callinfo).
To access ci information, we need to use these functions:
vm_ci_mid(ci), _flag(ci), _argc(ci), _kwarg(ci).
struct rb_call_info_kw_arg is renamed to rb_callinfo_kwarg.
rb_funcallv_with_cc() and rb_method_basic_definition_p_with_cc()
is temporary removed because cd->ci should be marked.
2020-01-08 02:20:36 +03:00
|
|
|
INT2FIX(argc));
|
* insns.def (send, invokesuper, invokeblock, opt_*), vm_core.h:
use only a `ci' (rb_call_info_t) parameter instead of using
parameters such as `op_id', 'op_argc', `blockiseq' and flag.
These information are stored in rb_call_info_t at the compile
time.
This technique simplifies parameter passings at related
function calls (~10% speedups for simple mehtod invocation at
my machine).
`rb_call_info_t' also has new function pointer variable `call'.
This `call' variable enables to customize method (block)
invocation process for each place. However, it always call
`vm_call_general()' at this changes.
`rb_call_info_t' also has temporary variables for method
(block) invocation.
* vm_core.h, compile.c, insns.def: introduce VM_CALL_ARGS_SKIP_SETUP
VM_CALL macro. This flag indicates that this call can skip
caller_setup (block arg and splat arg).
* compile.c: catch up above changes.
* iseq.c: catch up above changes (especially for TS_CALLINFO).
* tool/instruction.rb: catch up above chagnes.
* vm_insnhelper.c, vm_insnhelper.h: ditto. Macros and functions
parameters are changed.
* vm_eval.c (vm_call0): ditto (it will be rewriten soon).
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@37180 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2012-10-14 20:59:05 +04:00
|
|
|
rb_ary_push(ary, e);
|
|
|
|
}
|
|
|
|
break;
|
2006-12-31 18:02:22 +03:00
|
|
|
case TS_ID:
|
|
|
|
rb_ary_push(ary, ID2SYM(*seq));
|
|
|
|
break;
|
|
|
|
case TS_CDHASH:
|
|
|
|
{
|
|
|
|
VALUE hash = *seq;
|
|
|
|
VALUE val = rb_ary_new();
|
|
|
|
int i;
|
2022-07-21 19:23:58 +03:00
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
rb_hash_foreach(hash, cdhash_each, val);
|
2022-07-21 19:23:58 +03:00
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
for (i=0; i<RARRAY_LEN(val); i+=2) {
|
|
|
|
VALUE pos = FIX2INT(rb_ary_entry(val, i+1));
|
compile: translate iseq in-place
running "ruby -rpp -e 'pp GC.stat'", a reduction in
malloc usage is shown:
before:
:malloc_increase=>118784,
:oldmalloc_increase=>1178736,
after:
:malloc_increase=>99832,
:oldmalloc_increase=>1031976,
For "ruby -e exit", valgrind reports over 300K reduction in
overall allocations (and unnecessary memory copies).
before:
total heap usage: 49,622 allocs, 20,492 frees, 8,697,493 bytes allocated
after:
total heap usage: 48,935 allocs, 19,805 frees, 8,373,773 bytes allocated
(numbers from x86-64)
v2 changes based on ko1 recommendations [ruby-core:64883]:
- squashed in-place direct thread translation to avoid alloc+copy
- renamed rb_iseq_untranslate_threaded_code to rb_iseq_original_iseq,
cache new iseq->iseq_original field.
* compile.c (rb_iseq_translate_threaded_code): modify in-place w/o copy
(rb_vm_addr2insn): new function for debug
(rb_iseq_original_iseq): ditto
(iseq_set_sequence): assign iseq_encoded directly
[Feature #10185]
* vm_core (rb_iseq_t): move original ->iseq to bottom
* iseq.c (iseq_free, iseq_free): adjust for new layout
(rb_iseq_disasm): use original iseq for dump
(iseq_data_to_ary): ditto
(rb_iseq_line_trace_each): ditto
(rb_iseq_build_for_ruby2cext): use iseq_encoded directly
* vm_dump.c (rb_vmdebug_debug_print_pre): use original iseq
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@47508 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2014-09-10 10:14:07 +04:00
|
|
|
unsigned long idx = nseq - iseq_original + pos;
|
2022-07-21 19:23:58 +03:00
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
rb_ary_store(val, i+1,
|
|
|
|
register_label(labels_table, idx));
|
|
|
|
}
|
|
|
|
rb_ary_push(ary, val);
|
|
|
|
}
|
|
|
|
break;
|
2014-12-16 04:14:20 +03:00
|
|
|
case TS_FUNCPTR:
|
2014-12-16 08:54:32 +03:00
|
|
|
{
|
|
|
|
#if SIZEOF_VALUE <= SIZEOF_LONG
|
|
|
|
VALUE val = LONG2NUM((SIGNED_VALUE)*seq);
|
|
|
|
#else
|
|
|
|
VALUE val = LL2NUM((SIGNED_VALUE)*seq);
|
|
|
|
#endif
|
|
|
|
rb_ary_push(ary, val);
|
|
|
|
}
|
2014-12-16 04:14:20 +03:00
|
|
|
break;
|
2019-11-10 08:40:38 +03:00
|
|
|
case TS_BUILTIN:
|
|
|
|
{
|
|
|
|
VALUE val = rb_hash_new();
|
|
|
|
#if SIZEOF_VALUE <= SIZEOF_LONG
|
|
|
|
VALUE func_ptr = LONG2NUM((SIGNED_VALUE)((RB_BUILTIN)*seq)->func_ptr);
|
|
|
|
#else
|
|
|
|
VALUE func_ptr = LL2NUM((SIGNED_VALUE)((RB_BUILTIN)*seq)->func_ptr);
|
|
|
|
#endif
|
|
|
|
rb_hash_aset(val, ID2SYM(rb_intern("func_ptr")), func_ptr);
|
|
|
|
rb_hash_aset(val, ID2SYM(rb_intern("argc")), INT2NUM(((RB_BUILTIN)*seq)->argc));
|
|
|
|
rb_hash_aset(val, ID2SYM(rb_intern("index")), INT2NUM(((RB_BUILTIN)*seq)->index));
|
|
|
|
rb_hash_aset(val, ID2SYM(rb_intern("name")), rb_str_new_cstr(((RB_BUILTIN)*seq)->name));
|
|
|
|
rb_ary_push(ary, val);
|
|
|
|
}
|
|
|
|
break;
|
2006-12-31 18:02:22 +03:00
|
|
|
default:
|
|
|
|
rb_bug("unknown operand: %c", insn_op_type(insn, j));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
rb_ary_push(body, ary);
|
|
|
|
}
|
|
|
|
|
|
|
|
nbody = body;
|
|
|
|
|
|
|
|
/* exception */
|
2018-05-12 04:24:18 +03:00
|
|
|
if (iseq_body->catch_table) for (i=0; i<iseq_body->catch_table->size; i++) {
|
2006-12-31 18:02:22 +03:00
|
|
|
VALUE ary = rb_ary_new();
|
2019-05-31 09:58:50 +03:00
|
|
|
const struct iseq_catch_table_entry *entry =
|
|
|
|
UNALIGNED_MEMBER_PTR(iseq_body->catch_table, entries[i]);
|
2006-12-31 18:02:22 +03:00
|
|
|
rb_ary_push(ary, exception_type2symbol(entry->type));
|
|
|
|
if (entry->iseq) {
|
2015-12-08 16:58:50 +03:00
|
|
|
rb_ary_push(ary, iseq_data_to_ary(rb_iseq_check(entry->iseq)));
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
rb_ary_push(ary, Qnil);
|
|
|
|
}
|
|
|
|
rb_ary_push(ary, register_label(labels_table, entry->start));
|
|
|
|
rb_ary_push(ary, register_label(labels_table, entry->end));
|
|
|
|
rb_ary_push(ary, register_label(labels_table, entry->cont));
|
2014-12-04 01:16:58 +03:00
|
|
|
rb_ary_push(ary, UINT2NUM(entry->sp));
|
2006-12-31 18:02:22 +03:00
|
|
|
rb_ary_push(exception, ary);
|
|
|
|
}
|
|
|
|
|
2007-07-02 01:43:30 +04:00
|
|
|
/* make body with labels and insert line number */
|
2006-12-31 18:02:22 +03:00
|
|
|
body = rb_ary_new();
|
2018-01-09 17:05:21 +03:00
|
|
|
prev_insn_info = NULL;
|
2021-06-08 11:57:44 +03:00
|
|
|
#ifdef USE_ISEQ_NODE_ID
|
2021-04-30 12:54:46 +03:00
|
|
|
VALUE node_ids = rb_ary_new();
|
|
|
|
#endif
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2015-07-25 05:18:32 +03:00
|
|
|
for (l=0, pos=0; l<RARRAY_LEN(nbody); l++) {
|
2018-01-09 17:05:21 +03:00
|
|
|
const struct iseq_insn_info_entry *info;
|
2015-07-25 05:18:32 +03:00
|
|
|
VALUE ary = RARRAY_AREF(nbody, l);
|
* compile.c (iseq_build_body), error.c (set_syserr, get_syserr),
(syserr_initialize), gc.c (define_final, rb_gc_copy_finalizer),
(run_final), hash.c (rb_hash_aref, rb_hash_lookup2),
(rb_hash_fetch_m, rb_hash_clear, rb_hash_aset, eql_i),
iseq.c (iseq_load, iseq_data_to_ary), marshal.c (r_symlink),
thread.c (rb_thread_local_aref),
variable.c (generic_ivar_remove, ivar_get, rb_const_get_0),
(rb_cvar_get), vm.c (rb_vm_check_redefinition_opt_method),
vm_insnhelper.c (vm_get_ev_const), vm_method.c (remove_method),
ext/iconv/iconv.c (map_charset): use st_data_t.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@29462 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2010-10-12 18:47:23 +04:00
|
|
|
st_data_t label;
|
2006-12-31 18:02:22 +03:00
|
|
|
|
|
|
|
if (st_lookup(labels_table, pos, &label)) {
|
2010-10-12 18:35:40 +04:00
|
|
|
rb_ary_push(body, (VALUE)label);
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
|
|
|
|
2018-01-09 17:05:21 +03:00
|
|
|
info = get_insn_info(iseq, pos);
|
2021-06-08 11:57:44 +03:00
|
|
|
#ifdef USE_ISEQ_NODE_ID
|
2021-04-30 12:54:46 +03:00
|
|
|
rb_ary_push(node_ids, INT2FIX(info->node_id));
|
|
|
|
#endif
|
2017-11-14 15:58:36 +03:00
|
|
|
|
2018-01-09 17:05:21 +03:00
|
|
|
if (prev_insn_info != info) {
|
|
|
|
int line = info->line_no;
|
|
|
|
rb_event_flag_t events = info->events;
|
|
|
|
|
|
|
|
if (line > 0 && last_line != line) {
|
|
|
|
rb_ary_push(body, INT2FIX(line));
|
|
|
|
last_line = line;
|
|
|
|
}
|
2017-11-14 15:58:36 +03:00
|
|
|
#define CHECK_EVENT(ev) if (events & ev) rb_ary_push(body, ID2SYM(rb_intern(#ev)));
|
2018-01-09 17:05:21 +03:00
|
|
|
CHECK_EVENT(RUBY_EVENT_LINE);
|
|
|
|
CHECK_EVENT(RUBY_EVENT_CLASS);
|
|
|
|
CHECK_EVENT(RUBY_EVENT_END);
|
|
|
|
CHECK_EVENT(RUBY_EVENT_CALL);
|
|
|
|
CHECK_EVENT(RUBY_EVENT_RETURN);
|
|
|
|
CHECK_EVENT(RUBY_EVENT_B_CALL);
|
|
|
|
CHECK_EVENT(RUBY_EVENT_B_RETURN);
|
2023-08-01 11:25:20 +03:00
|
|
|
CHECK_EVENT(RUBY_EVENT_RESCUE);
|
2017-11-14 15:58:36 +03:00
|
|
|
#undef CHECK_EVENT
|
2018-01-09 17:05:21 +03:00
|
|
|
prev_insn_info = info;
|
2007-07-02 01:43:30 +04:00
|
|
|
}
|
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
rb_ary_push(body, ary);
|
2011-09-25 11:42:38 +04:00
|
|
|
pos += RARRAY_LENINT(ary); /* reject too huge data */
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|
2014-11-27 05:48:14 +03:00
|
|
|
RB_GC_GUARD(nbody);
|
2019-10-08 23:35:24 +03:00
|
|
|
RB_GC_GUARD(labels_wrapper);
|
2006-12-31 18:02:22 +03:00
|
|
|
|
2018-05-12 04:24:18 +03:00
|
|
|
rb_hash_aset(misc, ID2SYM(rb_intern("arg_size")), INT2FIX(iseq_body->param.size));
|
|
|
|
rb_hash_aset(misc, ID2SYM(rb_intern("local_size")), INT2FIX(iseq_body->local_table_size));
|
|
|
|
rb_hash_aset(misc, ID2SYM(rb_intern("stack_max")), INT2FIX(iseq_body->stack_max));
|
2018-11-05 05:13:45 +03:00
|
|
|
rb_hash_aset(misc, ID2SYM(rb_intern("node_id")), INT2FIX(iseq_body->location.node_id));
|
2018-01-09 11:45:35 +03:00
|
|
|
rb_hash_aset(misc, ID2SYM(rb_intern("code_location")),
|
2017-12-05 11:56:50 +03:00
|
|
|
rb_ary_new_from_args(4,
|
2018-05-12 04:24:18 +03:00
|
|
|
INT2FIX(iseq_body->location.code_location.beg_pos.lineno),
|
|
|
|
INT2FIX(iseq_body->location.code_location.beg_pos.column),
|
|
|
|
INT2FIX(iseq_body->location.code_location.end_pos.lineno),
|
|
|
|
INT2FIX(iseq_body->location.code_location.end_pos.column)));
|
2021-06-08 11:57:44 +03:00
|
|
|
#ifdef USE_ISEQ_NODE_ID
|
2021-06-08 11:34:08 +03:00
|
|
|
rb_hash_aset(misc, ID2SYM(rb_intern("node_ids")), node_ids);
|
2021-04-30 12:54:46 +03:00
|
|
|
#endif
|
2024-02-27 16:31:47 +03:00
|
|
|
rb_hash_aset(misc, ID2SYM(rb_intern("parser")), iseq_body->prism ? ID2SYM(rb_intern("prism")) : ID2SYM(rb_intern("parse.y")));
|
2007-05-11 10:26:06 +04:00
|
|
|
|
2009-02-22 17:23:33 +03:00
|
|
|
/*
|
2007-07-02 01:43:30 +04:00
|
|
|
* [:magic, :major_version, :minor_version, :format_type, :misc,
|
2012-06-04 06:49:37 +04:00
|
|
|
* :name, :path, :absolute_path, :start_lineno, :type, :locals, :args,
|
2007-07-02 01:43:30 +04:00
|
|
|
* :catch_table, :bytecode]
|
2006-12-31 18:02:22 +03:00
|
|
|
*/
|
* blockinlining.c: remove "yarv" prefix.
* array.c, numeric.c: ditto.
* insnhelper.ci, insns.def, vm_evalbody.ci: ditto.
* yarvcore.c: removed.
* yarvcore.h: renamed to core.h.
* cont.c, debug.c, error.c, process.c, signal.c : ditto.
* ext/probeprofiler/probeprofiler.c: ditto.
* id.c, id.h: added.
* inits.c: ditto.
* compile.c: rename internal functions.
* compile.h: fix debug flag.
* eval.c, object.c, vm.c: remove ruby_top_self.
use rb_vm_top_self() instead.
* eval_intern.h, eval_load: ditto.
* gc.c: rename yarv_machine_stack_mark() to
rb_gc_mark_machine_stack().
* insnhelper.h: remove unused macros.
* iseq.c: add iseq_compile() to create iseq object
from source string.
* proc.c: rename a internal function.
* template/insns.inc.tmpl: remove YARV prefix.
* thread.c:
* vm.c (rb_iseq_eval): added.
* vm.c: move some functions from yarvcore.c.
* vm_dump.c: fix to remove compiler warning.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@12741 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2007-07-12 08:25:46 +04:00
|
|
|
rb_ary_push(val, rb_str_new2("YARVInstructionSequence/SimpleDataFormat"));
|
2010-10-31 04:42:54 +03:00
|
|
|
rb_ary_push(val, INT2FIX(ISEQ_MAJOR_VERSION)); /* major */
|
|
|
|
rb_ary_push(val, INT2FIX(ISEQ_MINOR_VERSION)); /* minor */
|
2006-12-31 18:02:22 +03:00
|
|
|
rb_ary_push(val, INT2FIX(1));
|
2007-07-02 01:43:30 +04:00
|
|
|
rb_ary_push(val, misc);
|
2018-05-12 04:24:18 +03:00
|
|
|
rb_ary_push(val, iseq_body->location.label);
|
2017-06-01 03:05:33 +03:00
|
|
|
rb_ary_push(val, rb_iseq_path(iseq));
|
|
|
|
rb_ary_push(val, rb_iseq_realpath(iseq));
|
2022-09-25 11:07:18 +03:00
|
|
|
rb_ary_push(val, RB_INT2NUM(iseq_body->location.first_lineno));
|
2019-04-23 02:23:04 +03:00
|
|
|
rb_ary_push(val, ID2SYM(type));
|
2006-12-31 18:02:22 +03:00
|
|
|
rb_ary_push(val, locals);
|
2014-11-03 02:14:21 +03:00
|
|
|
rb_ary_push(val, params);
|
2006-12-31 18:02:22 +03:00
|
|
|
rb_ary_push(val, exception);
|
|
|
|
rb_ary_push(val, body);
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
2008-11-28 07:19:37 +03:00
|
|
|
VALUE
|
2008-12-05 07:05:48 +03:00
|
|
|
rb_iseq_parameters(const rb_iseq_t *iseq, int is_proc)
|
2008-11-28 07:19:37 +03:00
|
|
|
{
|
2011-12-05 13:50:12 +04:00
|
|
|
int i, r;
|
2022-03-23 22:19:48 +03:00
|
|
|
const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
|
2018-05-16 04:40:44 +03:00
|
|
|
const struct rb_iseq_param_keyword *const keyword = body->param.keyword;
|
2018-05-12 04:24:18 +03:00
|
|
|
VALUE a, args = rb_ary_new2(body->param.size);
|
2011-12-26 18:20:15 +04:00
|
|
|
ID req, opt, rest, block, key, keyrest;
|
2008-11-28 07:19:37 +03:00
|
|
|
#define PARAM_TYPE(type) rb_ary_push(a = rb_ary_new2(2), ID2SYM(type))
|
2018-05-12 04:24:18 +03:00
|
|
|
#define PARAM_ID(i) body->local_table[(i)]
|
2008-11-28 07:19:37 +03:00
|
|
|
#define PARAM(i, type) ( \
|
|
|
|
PARAM_TYPE(type), \
|
2012-06-09 18:36:56 +04:00
|
|
|
rb_id2str(PARAM_ID(i)) ? \
|
2008-11-28 07:19:37 +03:00
|
|
|
rb_ary_push(a, ID2SYM(PARAM_ID(i))) : \
|
|
|
|
a)
|
|
|
|
|
|
|
|
CONST_ID(req, "req");
|
2008-12-05 07:05:48 +03:00
|
|
|
CONST_ID(opt, "opt");
|
Optimized forwarding callers and callees
This patch optimizes forwarding callers and callees. It only optimizes methods that only take `...` as their parameter, and then pass `...` to other calls.
Calls it optimizes look like this:
```ruby
def bar(a) = a
def foo(...) = bar(...) # optimized
foo(123)
```
```ruby
def bar(a) = a
def foo(...) = bar(1, 2, ...) # optimized
foo(123)
```
```ruby
def bar(*a) = a
def foo(...)
list = [1, 2]
bar(*list, ...) # optimized
end
foo(123)
```
All variants of the above but using `super` are also optimized, including a bare super like this:
```ruby
def foo(...)
super
end
```
This patch eliminates intermediate allocations made when calling methods that accept `...`.
We can observe allocation elimination like this:
```ruby
def m
x = GC.stat(:total_allocated_objects)
yield
GC.stat(:total_allocated_objects) - x
end
def bar(a) = a
def foo(...) = bar(...)
def test
m { foo(123) }
end
test
p test # allocates 1 object on master, but 0 objects with this patch
```
```ruby
def bar(a, b:) = a + b
def foo(...) = bar(...)
def test
m { foo(1, b: 2) }
end
test
p test # allocates 2 objects on master, but 0 objects with this patch
```
How does it work?
-----------------
This patch works by using a dynamic stack size when passing forwarded parameters to callees.
The caller's info object (known as the "CI") contains the stack size of the
parameters, so we pass the CI object itself as a parameter to the callee.
When forwarding parameters, the forwarding ISeq uses the caller's CI to determine how much stack to copy, then copies the caller's stack before calling the callee.
The CI at the forwarded call site is adjusted using information from the caller's CI.
I think this description is kind of confusing, so let's walk through an example with code.
```ruby
def delegatee(a, b) = a + b
def delegator(...)
delegatee(...) # CI2 (FORWARDING)
end
def caller
delegator(1, 2) # CI1 (argc: 2)
end
```
Before we call the delegator method, the stack looks like this:
```
Executing Line | Code | Stack
---------------+---------------------------------------+--------
1| def delegatee(a, b) = a + b | self
2| | 1
3| def delegator(...) | 2
4| # |
5| delegatee(...) # CI2 (FORWARDING) |
6| end |
7| |
8| def caller |
-> 9| delegator(1, 2) # CI1 (argc: 2) |
10| end |
```
The ISeq for `delegator` is tagged as "forwardable", so when `caller` calls in
to `delegator`, it writes `CI1` on to the stack as a local variable for the
`delegator` method. The `delegator` method has a special local called `...`
that holds the caller's CI object.
Here is the ISeq disasm fo `delegator`:
```
== disasm: #<ISeq:delegator@-e:1 (1,0)-(1,39)>
local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
[ 1] "..."@0
0000 putself ( 1)[LiCa]
0001 getlocal_WC_0 "..."@0
0003 send <calldata!mid:delegatee, argc:0, FCALL|FORWARDING>, nil
0006 leave [Re]
```
The local called `...` will contain the caller's CI: CI1.
Here is the stack when we enter `delegator`:
```
Executing Line | Code | Stack
---------------+---------------------------------------+--------
1| def delegatee(a, b) = a + b | self
2| | 1
3| def delegator(...) | 2
-> 4| # | CI1 (argc: 2)
5| delegatee(...) # CI2 (FORWARDING) | cref_or_me
6| end | specval
7| | type
8| def caller |
9| delegator(1, 2) # CI1 (argc: 2) |
10| end |
```
The CI at `delegatee` on line 5 is tagged as "FORWARDING", so it knows to
memcopy the caller's stack before calling `delegatee`. In this case, it will
memcopy self, 1, and 2 to the stack before calling `delegatee`. It knows how much
memory to copy from the caller because `CI1` contains stack size information
(argc: 2).
Before executing the `send` instruction, we push `...` on the stack. The
`send` instruction pops `...`, and because it is tagged with `FORWARDING`, it
knows to memcopy (using the information in the CI it just popped):
```
== disasm: #<ISeq:delegator@-e:1 (1,0)-(1,39)>
local table (size: 1, argc: 0 [opts: 0, rest: -1, post: 0, block: -1, kw: -1@-1, kwrest: -1])
[ 1] "..."@0
0000 putself ( 1)[LiCa]
0001 getlocal_WC_0 "..."@0
0003 send <calldata!mid:delegatee, argc:0, FCALL|FORWARDING>, nil
0006 leave [Re]
```
Instruction 001 puts the caller's CI on the stack. `send` is tagged with
FORWARDING, so it reads the CI and _copies_ the callers stack to this stack:
```
Executing Line | Code | Stack
---------------+---------------------------------------+--------
1| def delegatee(a, b) = a + b | self
2| | 1
3| def delegator(...) | 2
4| # | CI1 (argc: 2)
-> 5| delegatee(...) # CI2 (FORWARDING) | cref_or_me
6| end | specval
7| | type
8| def caller | self
9| delegator(1, 2) # CI1 (argc: 2) | 1
10| end | 2
```
The "FORWARDING" call site combines information from CI1 with CI2 in order
to support passing other values in addition to the `...` value, as well as
perfectly forward splat args, kwargs, etc.
Since we're able to copy the stack from `caller` in to `delegator`'s stack, we
can avoid allocating objects.
I want to do this to eliminate object allocations for delegate methods.
My long term goal is to implement `Class#new` in Ruby and it uses `...`.
I was able to implement `Class#new` in Ruby
[here](https://github.com/ruby/ruby/pull/9289).
If we adopt the technique in this patch, then we can optimize allocating
objects that take keyword parameters for `initialize`.
For example, this code will allocate 2 objects: one for `SomeObject`, and one
for the kwargs:
```ruby
SomeObject.new(foo: 1)
```
If we combine this technique, plus implement `Class#new` in Ruby, then we can
reduce allocations for this common operation.
Co-Authored-By: John Hawthorn <john@hawthorn.email>
Co-Authored-By: Alan Wu <XrXr@users.noreply.github.com>
2024-04-15 20:48:53 +03:00
|
|
|
|
|
|
|
if (body->param.flags.forwardable) {
|
|
|
|
// [[:rest, :*], [:keyrest, :**], [:block, :&]]
|
|
|
|
CONST_ID(rest, "rest");
|
|
|
|
CONST_ID(keyrest, "keyrest");
|
|
|
|
CONST_ID(block, "block");
|
|
|
|
rb_ary_push(args, rb_ary_new_from_args(2, ID2SYM(rest), ID2SYM(idMULT)));
|
|
|
|
rb_ary_push(args, rb_ary_new_from_args(2, ID2SYM(keyrest), ID2SYM(idPow)));
|
|
|
|
rb_ary_push(args, rb_ary_new_from_args(2, ID2SYM(block), ID2SYM(idAnd)));
|
|
|
|
}
|
|
|
|
|
2008-12-05 07:05:48 +03:00
|
|
|
if (is_proc) {
|
2018-05-12 04:24:18 +03:00
|
|
|
for (i = 0; i < body->param.lead_num; i++) {
|
2008-12-05 07:05:48 +03:00
|
|
|
PARAM_TYPE(opt);
|
2012-06-09 18:36:56 +04:00
|
|
|
rb_ary_push(a, rb_id2str(PARAM_ID(i)) ? ID2SYM(PARAM_ID(i)) : Qnil);
|
2008-12-05 07:05:48 +03:00
|
|
|
rb_ary_push(args, a);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2018-05-12 04:24:18 +03:00
|
|
|
for (i = 0; i < body->param.lead_num; i++) {
|
2008-12-05 07:05:48 +03:00
|
|
|
rb_ary_push(args, PARAM(i, req));
|
|
|
|
}
|
2008-11-28 07:19:37 +03:00
|
|
|
}
|
2018-05-12 04:24:18 +03:00
|
|
|
r = body->param.lead_num + body->param.opt_num;
|
2011-12-05 13:50:12 +04:00
|
|
|
for (; i < r; i++) {
|
2008-11-28 07:19:37 +03:00
|
|
|
PARAM_TYPE(opt);
|
2012-06-09 18:36:56 +04:00
|
|
|
if (rb_id2str(PARAM_ID(i))) {
|
2008-11-28 07:19:37 +03:00
|
|
|
rb_ary_push(a, ID2SYM(PARAM_ID(i)));
|
|
|
|
}
|
|
|
|
rb_ary_push(args, a);
|
|
|
|
}
|
2018-05-12 04:24:18 +03:00
|
|
|
if (body->param.flags.has_rest) {
|
2008-11-28 07:19:37 +03:00
|
|
|
CONST_ID(rest, "rest");
|
2018-05-12 04:24:18 +03:00
|
|
|
rb_ary_push(args, PARAM(body->param.rest_start, rest));
|
2008-11-28 07:19:37 +03:00
|
|
|
}
|
2018-05-12 04:24:18 +03:00
|
|
|
r = body->param.post_start + body->param.post_num;
|
2008-12-05 07:05:48 +03:00
|
|
|
if (is_proc) {
|
2018-05-12 04:24:18 +03:00
|
|
|
for (i = body->param.post_start; i < r; i++) {
|
2008-12-05 07:05:48 +03:00
|
|
|
PARAM_TYPE(opt);
|
2012-06-09 18:36:56 +04:00
|
|
|
rb_ary_push(a, rb_id2str(PARAM_ID(i)) ? ID2SYM(PARAM_ID(i)) : Qnil);
|
2008-12-05 07:05:48 +03:00
|
|
|
rb_ary_push(args, a);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
2018-05-12 04:24:18 +03:00
|
|
|
for (i = body->param.post_start; i < r; i++) {
|
2008-12-05 07:05:48 +03:00
|
|
|
rb_ary_push(args, PARAM(i, req));
|
|
|
|
}
|
2008-11-28 07:19:37 +03:00
|
|
|
}
|
2019-04-24 22:06:39 +03:00
|
|
|
if (body->param.flags.accepts_no_kwarg) {
|
|
|
|
ID nokey;
|
|
|
|
CONST_ID(nokey, "nokey");
|
|
|
|
PARAM_TYPE(nokey);
|
|
|
|
rb_ary_push(args, a);
|
|
|
|
}
|
2018-05-12 04:24:18 +03:00
|
|
|
if (body->param.flags.has_kw) {
|
2013-03-12 17:20:50 +04:00
|
|
|
i = 0;
|
2018-05-16 04:40:44 +03:00
|
|
|
if (keyword->required_num > 0) {
|
2013-03-12 17:20:50 +04:00
|
|
|
ID keyreq;
|
|
|
|
CONST_ID(keyreq, "keyreq");
|
2018-05-16 04:40:44 +03:00
|
|
|
for (; i < keyword->required_num; i++) {
|
2013-03-12 17:20:50 +04:00
|
|
|
PARAM_TYPE(keyreq);
|
2018-05-16 04:40:44 +03:00
|
|
|
if (rb_id2str(keyword->table[i])) {
|
|
|
|
rb_ary_push(a, ID2SYM(keyword->table[i]));
|
2013-03-12 17:20:50 +04:00
|
|
|
}
|
|
|
|
rb_ary_push(args, a);
|
|
|
|
}
|
|
|
|
}
|
2011-12-26 18:20:15 +04:00
|
|
|
CONST_ID(key, "key");
|
2018-05-16 04:40:44 +03:00
|
|
|
for (; i < keyword->num; i++) {
|
2011-12-26 18:20:15 +04:00
|
|
|
PARAM_TYPE(key);
|
2018-05-16 04:40:44 +03:00
|
|
|
if (rb_id2str(keyword->table[i])) {
|
|
|
|
rb_ary_push(a, ID2SYM(keyword->table[i]));
|
2011-12-26 18:20:15 +04:00
|
|
|
}
|
|
|
|
rb_ary_push(args, a);
|
|
|
|
}
|
* rewrite method/block parameter fitting logic to optimize
keyword arguments/parameters and a splat argument.
[Feature #10440] (Details are described in this ticket)
Most of complex part is moved to vm_args.c.
Now, ISeq#to_a does not catch up new instruction format.
* vm_core.h: change iseq data structures.
* introduce rb_call_info_kw_arg_t to represent keyword arguments.
* add rb_call_info_t::kw_arg.
* rename rb_iseq_t::arg_post_len to rb_iseq_t::arg_post_num.
* rename rb_iseq_t::arg_keywords to arg_keyword_num.
* rename rb_iseq_t::arg_keyword to rb_iseq_t::arg_keyword_bits.
to represent keyword bitmap parameter index.
This bitmap parameter shows that which keyword parameters are given
or not given (0 for given).
It is refered by `checkkeyword' instruction described bellow.
* rename rb_iseq_t::arg_keyword_check to rb_iseq_t::arg_keyword_rest
to represent keyword rest parameter index.
* add rb_iseq_t::arg_keyword_default_values to represent default
keyword values.
* rename VM_CALL_ARGS_SKIP_SETUP to VM_CALL_ARGS_SIMPLE
to represent
(ci->flag & (SPLAT|BLOCKARG)) &&
ci->blockiseq == NULL &&
ci->kw_arg == NULL.
* vm_insnhelper.c, vm_args.c: rewrite with refactoring.
* rewrite splat argument code.
* rewrite keyword arguments/parameters code.
* merge method and block parameter fitting code into one code base.
* vm.c, vm_eval.c: catch up these changes.
* compile.c (new_callinfo): callinfo requires kw_arg parameter.
* compile.c (compile_array_): check the last argument Hash object or
not. If Hash object and all keys are Symbol literals, they are
compiled to keyword arguments.
* insns.def (checkkeyword): add new instruction.
This instruction check the availability of corresponding keyword.
For example, a method "def foo k1: 'v1'; end" is cimpiled to the
following instructions.
0000 checkkeyword 2, 0 # check k1 is given.
0003 branchif 9 # if given, jump to address #9
0005 putstring "v1"
0007 setlocal_OP__WC__0 3 # k1 = 'v1'
0009 trace 8
0011 putnil
0012 trace 16
0014 leave
* insns.def (opt_send_simple): removed and add new instruction
"opt_send_without_block".
* parse.y (new_args_tail_gen): reorder variables.
Before this patch, a method "def foo(k1: 1, kr1:, k2: 2, **krest, &b)"
has parameter variables "k1, kr1, k2, &b, internal_id, krest",
but this patch reorders to "kr1, k1, k2, internal_id, krest, &b".
(locate a block variable at last)
* parse.y (vtable_pop): added.
This function remove latest `n' variables from vtable.
* iseq.c: catch up iseq data changes.
* proc.c: ditto.
* class.c (keyword_error): export as rb_keyword_error().
* common.mk: depend vm_args.c for vm.o.
* hash.c (rb_hash_has_key): export.
* internal.h: ditto.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@48239 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2014-11-02 21:02:55 +03:00
|
|
|
}
|
2021-07-15 15:25:43 +03:00
|
|
|
if (body->param.flags.has_kwrest || body->param.flags.ruby2_keywords) {
|
|
|
|
ID param;
|
* rewrite method/block parameter fitting logic to optimize
keyword arguments/parameters and a splat argument.
[Feature #10440] (Details are described in this ticket)
Most of complex part is moved to vm_args.c.
Now, ISeq#to_a does not catch up new instruction format.
* vm_core.h: change iseq data structures.
* introduce rb_call_info_kw_arg_t to represent keyword arguments.
* add rb_call_info_t::kw_arg.
* rename rb_iseq_t::arg_post_len to rb_iseq_t::arg_post_num.
* rename rb_iseq_t::arg_keywords to arg_keyword_num.
* rename rb_iseq_t::arg_keyword to rb_iseq_t::arg_keyword_bits.
to represent keyword bitmap parameter index.
This bitmap parameter shows that which keyword parameters are given
or not given (0 for given).
It is refered by `checkkeyword' instruction described bellow.
* rename rb_iseq_t::arg_keyword_check to rb_iseq_t::arg_keyword_rest
to represent keyword rest parameter index.
* add rb_iseq_t::arg_keyword_default_values to represent default
keyword values.
* rename VM_CALL_ARGS_SKIP_SETUP to VM_CALL_ARGS_SIMPLE
to represent
(ci->flag & (SPLAT|BLOCKARG)) &&
ci->blockiseq == NULL &&
ci->kw_arg == NULL.
* vm_insnhelper.c, vm_args.c: rewrite with refactoring.
* rewrite splat argument code.
* rewrite keyword arguments/parameters code.
* merge method and block parameter fitting code into one code base.
* vm.c, vm_eval.c: catch up these changes.
* compile.c (new_callinfo): callinfo requires kw_arg parameter.
* compile.c (compile_array_): check the last argument Hash object or
not. If Hash object and all keys are Symbol literals, they are
compiled to keyword arguments.
* insns.def (checkkeyword): add new instruction.
This instruction check the availability of corresponding keyword.
For example, a method "def foo k1: 'v1'; end" is cimpiled to the
following instructions.
0000 checkkeyword 2, 0 # check k1 is given.
0003 branchif 9 # if given, jump to address #9
0005 putstring "v1"
0007 setlocal_OP__WC__0 3 # k1 = 'v1'
0009 trace 8
0011 putnil
0012 trace 16
0014 leave
* insns.def (opt_send_simple): removed and add new instruction
"opt_send_without_block".
* parse.y (new_args_tail_gen): reorder variables.
Before this patch, a method "def foo(k1: 1, kr1:, k2: 2, **krest, &b)"
has parameter variables "k1, kr1, k2, &b, internal_id, krest",
but this patch reorders to "kr1, k1, k2, internal_id, krest, &b".
(locate a block variable at last)
* parse.y (vtable_pop): added.
This function remove latest `n' variables from vtable.
* iseq.c: catch up iseq data changes.
* proc.c: ditto.
* class.c (keyword_error): export as rb_keyword_error().
* common.mk: depend vm_args.c for vm.o.
* hash.c (rb_hash_has_key): export.
* internal.h: ditto.
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@48239 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2014-11-02 21:02:55 +03:00
|
|
|
CONST_ID(keyrest, "keyrest");
|
2021-07-15 15:25:43 +03:00
|
|
|
PARAM_TYPE(keyrest);
|
|
|
|
if (body->param.flags.has_kwrest &&
|
|
|
|
rb_id2str(param = PARAM_ID(keyword->rest_start))) {
|
|
|
|
rb_ary_push(a, ID2SYM(param));
|
|
|
|
}
|
|
|
|
else if (body->param.flags.ruby2_keywords) {
|
|
|
|
rb_ary_push(a, ID2SYM(idPow));
|
|
|
|
}
|
|
|
|
rb_ary_push(args, a);
|
2011-12-26 18:20:15 +04:00
|
|
|
}
|
2018-05-12 04:24:18 +03:00
|
|
|
if (body->param.flags.has_block) {
|
2008-11-28 07:19:37 +03:00
|
|
|
CONST_ID(block, "block");
|
2018-05-12 04:24:18 +03:00
|
|
|
rb_ary_push(args, PARAM(body->param.block_start, block));
|
2008-11-28 07:19:37 +03:00
|
|
|
}
|
|
|
|
return args;
|
|
|
|
}
|
|
|
|
|
2012-09-24 12:36:53 +04:00
|
|
|
VALUE
|
|
|
|
rb_iseq_defined_string(enum defined_type type)
|
|
|
|
{
|
|
|
|
static const char expr_names[][18] = {
|
|
|
|
"nil",
|
|
|
|
"instance-variable",
|
|
|
|
"local-variable",
|
|
|
|
"global-variable",
|
|
|
|
"class variable",
|
|
|
|
"constant",
|
|
|
|
"method",
|
|
|
|
"yield",
|
|
|
|
"super",
|
|
|
|
"self",
|
|
|
|
"true",
|
|
|
|
"false",
|
|
|
|
"assignment",
|
|
|
|
"expression",
|
|
|
|
};
|
|
|
|
const char *estr;
|
|
|
|
|
2021-03-17 01:12:37 +03:00
|
|
|
if ((unsigned)(type - 1) >= (unsigned)numberof(expr_names)) rb_bug("unknown defined type %d", type);
|
2012-09-24 12:36:53 +04:00
|
|
|
estr = expr_names[type - 1];
|
2021-03-17 01:30:47 +03:00
|
|
|
return rb_fstring_cstr(estr);
|
2012-09-24 12:36:53 +04:00
|
|
|
}
|
|
|
|
|
2018-08-23 11:32:30 +03:00
|
|
|
/* A map from encoded_insn to insn_data: decoded insn number, its len,
|
|
|
|
* non-trace version of encoded insn, and trace version. */
|
|
|
|
|
2021-10-06 22:49:56 +03:00
|
|
|
static st_table *encoded_insn_data;
|
2018-08-23 11:32:30 +03:00
|
|
|
typedef struct insn_data_struct {
|
|
|
|
int insn;
|
|
|
|
int insn_len;
|
|
|
|
void *notrace_encoded_insn;
|
|
|
|
void *trace_encoded_insn;
|
|
|
|
} insn_data_t;
|
|
|
|
static insn_data_t insn_data[VM_INSTRUCTION_SIZE/2];
|
|
|
|
|
2023-10-12 21:15:53 +03:00
|
|
|
void
|
|
|
|
rb_free_encoded_insn_data(void)
|
|
|
|
{
|
|
|
|
st_free_table(encoded_insn_data);
|
|
|
|
}
|
|
|
|
|
2018-08-23 11:32:30 +03:00
|
|
|
void
|
|
|
|
rb_vm_encoded_insn_data_table_init(void)
|
|
|
|
{
|
|
|
|
#if OPT_DIRECT_THREADED_CODE || OPT_CALL_THREADED_CODE
|
|
|
|
const void * const *table = rb_vm_get_insns_address_table();
|
|
|
|
#define INSN_CODE(insn) ((VALUE)table[insn])
|
|
|
|
#else
|
|
|
|
#define INSN_CODE(insn) (insn)
|
|
|
|
#endif
|
|
|
|
st_data_t insn;
|
2021-10-06 22:49:56 +03:00
|
|
|
encoded_insn_data = st_init_numtable_with_size(VM_INSTRUCTION_SIZE / 2);
|
2018-08-23 11:32:30 +03:00
|
|
|
|
|
|
|
for (insn = 0; insn < VM_INSTRUCTION_SIZE/2; insn++) {
|
|
|
|
st_data_t key1 = (st_data_t)INSN_CODE(insn);
|
2020-06-26 04:21:56 +03:00
|
|
|
st_data_t key2 = (st_data_t)INSN_CODE(insn + VM_INSTRUCTION_SIZE/2);
|
2018-08-23 11:32:30 +03:00
|
|
|
|
2018-08-23 11:49:23 +03:00
|
|
|
insn_data[insn].insn = (int)insn;
|
2018-08-23 11:32:30 +03:00
|
|
|
insn_data[insn].insn_len = insn_len(insn);
|
2020-06-26 04:21:56 +03:00
|
|
|
|
|
|
|
if (insn != BIN(opt_invokebuiltin_delegate_leave)) {
|
|
|
|
insn_data[insn].notrace_encoded_insn = (void *) key1;
|
|
|
|
insn_data[insn].trace_encoded_insn = (void *) key2;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
insn_data[insn].notrace_encoded_insn = (void *) INSN_CODE(BIN(opt_invokebuiltin_delegate));
|
|
|
|
insn_data[insn].trace_encoded_insn = (void *) INSN_CODE(BIN(opt_invokebuiltin_delegate) + VM_INSTRUCTION_SIZE/2);
|
|
|
|
}
|
2018-08-23 11:32:30 +03:00
|
|
|
|
2021-10-06 22:49:56 +03:00
|
|
|
st_add_direct(encoded_insn_data, key1, (st_data_t)&insn_data[insn]);
|
|
|
|
st_add_direct(encoded_insn_data, key2, (st_data_t)&insn_data[insn]);
|
2018-08-23 11:32:30 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
rb_vm_insn_addr2insn(const void *addr)
|
|
|
|
{
|
|
|
|
st_data_t key = (st_data_t)addr;
|
|
|
|
st_data_t val;
|
|
|
|
|
2021-10-06 22:49:56 +03:00
|
|
|
if (st_lookup(encoded_insn_data, key, &val)) {
|
2018-08-23 11:32:30 +03:00
|
|
|
insn_data_t *e = (insn_data_t *)val;
|
|
|
|
return (int)e->insn;
|
|
|
|
}
|
|
|
|
|
|
|
|
rb_bug("rb_vm_insn_addr2insn: invalid insn address: %p", addr);
|
|
|
|
}
|
|
|
|
|
2020-10-19 16:47:39 +03:00
|
|
|
// Unlike rb_vm_insn_addr2insn, this function can return trace opcode variants.
|
|
|
|
int
|
|
|
|
rb_vm_insn_addr2opcode(const void *addr)
|
|
|
|
{
|
|
|
|
st_data_t key = (st_data_t)addr;
|
|
|
|
st_data_t val;
|
|
|
|
|
2021-10-06 22:49:56 +03:00
|
|
|
if (st_lookup(encoded_insn_data, key, &val)) {
|
2020-10-19 16:47:39 +03:00
|
|
|
insn_data_t *e = (insn_data_t *)val;
|
|
|
|
int opcode = e->insn;
|
|
|
|
if (addr == e->trace_encoded_insn) {
|
|
|
|
opcode += VM_INSTRUCTION_SIZE/2;
|
|
|
|
}
|
|
|
|
return opcode;
|
|
|
|
}
|
|
|
|
|
|
|
|
rb_bug("rb_vm_insn_addr2opcode: invalid insn address: %p", addr);
|
|
|
|
}
|
|
|
|
|
2022-03-23 22:19:48 +03:00
|
|
|
// Decode `ISEQ_BODY(iseq)->iseq_encoded[i]` to an insn.
|
2021-06-02 11:16:49 +03:00
|
|
|
int
|
|
|
|
rb_vm_insn_decode(const VALUE encoded)
|
|
|
|
{
|
|
|
|
#if OPT_DIRECT_THREADED_CODE || OPT_CALL_THREADED_CODE
|
|
|
|
int insn = rb_vm_insn_addr2insn((void *)encoded);
|
|
|
|
#else
|
|
|
|
int insn = (int)encoded;
|
|
|
|
#endif
|
|
|
|
return insn;
|
|
|
|
}
|
|
|
|
|
2018-08-23 11:32:31 +03:00
|
|
|
static inline int
|
2020-11-16 10:40:04 +03:00
|
|
|
encoded_iseq_trace_instrument(VALUE *iseq_encoded_insn, rb_event_flag_t turnon, bool remain_current_trace)
|
2018-08-23 11:32:31 +03:00
|
|
|
{
|
|
|
|
st_data_t key = (st_data_t)*iseq_encoded_insn;
|
|
|
|
st_data_t val;
|
2012-11-30 21:00:30 +04:00
|
|
|
|
2021-10-06 22:49:56 +03:00
|
|
|
if (st_lookup(encoded_insn_data, key, &val)) {
|
2018-08-23 11:32:31 +03:00
|
|
|
insn_data_t *e = (insn_data_t *)val;
|
2020-11-16 10:40:04 +03:00
|
|
|
if (remain_current_trace && key == (st_data_t)e->trace_encoded_insn) {
|
|
|
|
turnon = 1;
|
|
|
|
}
|
2018-08-23 11:32:31 +03:00
|
|
|
*iseq_encoded_insn = (VALUE) (turnon ? e->trace_encoded_insn : e->notrace_encoded_insn);
|
|
|
|
return e->insn_len;
|
|
|
|
}
|
2018-03-15 02:27:10 +03:00
|
|
|
|
2018-08-23 11:32:31 +03:00
|
|
|
rb_bug("trace_instrument: invalid insn address: %p", (void *)*iseq_encoded_insn);
|
|
|
|
}
|
2017-11-14 15:58:36 +03:00
|
|
|
|
2018-10-20 08:33:04 +03:00
|
|
|
void
|
2018-10-20 08:44:12 +03:00
|
|
|
rb_iseq_trace_flag_cleared(const rb_iseq_t *iseq, size_t pos)
|
2018-10-20 08:33:04 +03:00
|
|
|
{
|
2022-03-23 22:19:48 +03:00
|
|
|
const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
|
2018-10-20 08:33:04 +03:00
|
|
|
VALUE *iseq_encoded = (VALUE *)body->iseq_encoded;
|
2020-11-16 10:40:04 +03:00
|
|
|
encoded_iseq_trace_instrument(&iseq_encoded[pos], 0, false);
|
2018-10-20 08:33:04 +03:00
|
|
|
}
|
|
|
|
|
2021-07-14 02:01:09 +03:00
|
|
|
// We need to fire call events on instructions with b_call events if the block
|
|
|
|
// is running as a method. So, if we are listening for call events, then
|
|
|
|
// instructions that have b_call events need to become trace variants.
|
|
|
|
// Use this function when making decisions about recompiling to trace variants.
|
|
|
|
static inline rb_event_flag_t
|
|
|
|
add_bmethod_events(rb_event_flag_t events)
|
|
|
|
{
|
|
|
|
if (events & RUBY_EVENT_CALL) {
|
|
|
|
events |= RUBY_EVENT_B_CALL;
|
|
|
|
}
|
|
|
|
if (events & RUBY_EVENT_RETURN) {
|
|
|
|
events |= RUBY_EVENT_B_RETURN;
|
|
|
|
}
|
|
|
|
return events;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Note, to support call/return events for bmethods, turnon_event can have more events than tpval.
|
2018-11-26 21:16:39 +03:00
|
|
|
static int
|
2018-11-26 23:16:14 +03:00
|
|
|
iseq_add_local_tracepoint(const rb_iseq_t *iseq, rb_event_flag_t turnon_events, VALUE tpval, unsigned int target_line)
|
2018-11-26 21:16:39 +03:00
|
|
|
{
|
|
|
|
unsigned int pc;
|
|
|
|
int n = 0;
|
2022-03-23 22:19:48 +03:00
|
|
|
const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
|
2018-11-26 21:16:39 +03:00
|
|
|
VALUE *iseq_encoded = (VALUE *)body->iseq_encoded;
|
|
|
|
|
2018-12-06 13:52:27 +03:00
|
|
|
VM_ASSERT(ISEQ_EXECUTABLE_P(iseq));
|
2018-11-26 21:16:39 +03:00
|
|
|
|
|
|
|
for (pc=0; pc<body->iseq_size;) {
|
2018-11-26 23:16:14 +03:00
|
|
|
const struct iseq_insn_info_entry *entry = get_insn_info(iseq, pc);
|
|
|
|
rb_event_flag_t pc_events = entry->events;
|
|
|
|
rb_event_flag_t target_events = turnon_events;
|
|
|
|
unsigned int line = (int)entry->line_no;
|
|
|
|
|
|
|
|
if (target_line == 0 || target_line == line) {
|
|
|
|
/* ok */
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
target_events &= ~RUBY_EVENT_LINE;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (pc_events & target_events) {
|
2018-11-26 21:16:39 +03:00
|
|
|
n++;
|
|
|
|
}
|
2020-11-16 10:40:04 +03:00
|
|
|
pc += encoded_iseq_trace_instrument(&iseq_encoded[pc], pc_events & (target_events | iseq->aux.exec.global_trace_events), true);
|
2018-11-26 21:16:39 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
if (n > 0) {
|
2018-12-06 13:52:27 +03:00
|
|
|
if (iseq->aux.exec.local_hooks == NULL) {
|
|
|
|
((rb_iseq_t *)iseq)->aux.exec.local_hooks = RB_ZALLOC(rb_hook_list_t);
|
2021-12-12 20:15:05 +03:00
|
|
|
iseq->aux.exec.local_hooks->is_local = true;
|
2018-11-26 21:16:39 +03:00
|
|
|
}
|
2018-12-06 13:52:27 +03:00
|
|
|
rb_hook_list_connect_tracepoint((VALUE)iseq, iseq->aux.exec.local_hooks, tpval, target_line);
|
2018-11-26 21:16:39 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct trace_set_local_events_struct {
|
|
|
|
rb_event_flag_t turnon_events;
|
|
|
|
VALUE tpval;
|
2018-11-26 23:16:14 +03:00
|
|
|
unsigned int target_line;
|
2018-11-26 21:16:39 +03:00
|
|
|
int n;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void
|
|
|
|
iseq_add_local_tracepoint_i(const rb_iseq_t *iseq, void *p)
|
|
|
|
{
|
|
|
|
struct trace_set_local_events_struct *data = (struct trace_set_local_events_struct *)p;
|
2018-11-26 23:16:14 +03:00
|
|
|
data->n += iseq_add_local_tracepoint(iseq, data->turnon_events, data->tpval, data->target_line);
|
2018-11-26 21:16:39 +03:00
|
|
|
iseq_iterate_children(iseq, iseq_add_local_tracepoint_i, p);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
2021-07-14 02:01:09 +03:00
|
|
|
rb_iseq_add_local_tracepoint_recursively(const rb_iseq_t *iseq, rb_event_flag_t turnon_events, VALUE tpval, unsigned int target_line, bool target_bmethod)
|
2018-11-26 21:16:39 +03:00
|
|
|
{
|
2018-11-26 21:48:48 +03:00
|
|
|
struct trace_set_local_events_struct data;
|
2021-07-14 02:01:09 +03:00
|
|
|
if (target_bmethod) {
|
|
|
|
turnon_events = add_bmethod_events(turnon_events);
|
|
|
|
}
|
2018-11-26 21:48:48 +03:00
|
|
|
data.turnon_events = turnon_events;
|
|
|
|
data.tpval = tpval;
|
2018-11-26 23:16:14 +03:00
|
|
|
data.target_line = target_line;
|
2018-11-26 21:48:48 +03:00
|
|
|
data.n = 0;
|
|
|
|
|
2018-11-26 21:16:39 +03:00
|
|
|
iseq_add_local_tracepoint_i(iseq, (void *)&data);
|
|
|
|
if (0) rb_funcall(Qnil, rb_intern("puts"), 1, rb_iseq_disasm(iseq)); /* for debug */
|
|
|
|
return data.n;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
iseq_remove_local_tracepoint(const rb_iseq_t *iseq, VALUE tpval)
|
|
|
|
{
|
|
|
|
int n = 0;
|
|
|
|
|
2018-12-06 13:52:27 +03:00
|
|
|
if (iseq->aux.exec.local_hooks) {
|
2018-11-26 21:16:39 +03:00
|
|
|
unsigned int pc;
|
2022-03-23 22:19:48 +03:00
|
|
|
const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
|
2018-11-26 21:16:39 +03:00
|
|
|
VALUE *iseq_encoded = (VALUE *)body->iseq_encoded;
|
|
|
|
rb_event_flag_t local_events = 0;
|
|
|
|
|
2018-12-06 13:52:27 +03:00
|
|
|
rb_hook_list_remove_tracepoint(iseq->aux.exec.local_hooks, tpval);
|
|
|
|
local_events = iseq->aux.exec.local_hooks->events;
|
2018-11-26 21:16:39 +03:00
|
|
|
|
|
|
|
if (local_events == 0) {
|
2021-12-12 20:15:05 +03:00
|
|
|
rb_hook_list_free(iseq->aux.exec.local_hooks);
|
2018-12-06 13:52:27 +03:00
|
|
|
((rb_iseq_t *)iseq)->aux.exec.local_hooks = NULL;
|
2018-11-26 21:16:39 +03:00
|
|
|
}
|
|
|
|
|
2021-07-14 02:01:09 +03:00
|
|
|
local_events = add_bmethod_events(local_events);
|
2018-11-26 21:16:39 +03:00
|
|
|
for (pc = 0; pc<body->iseq_size;) {
|
|
|
|
rb_event_flag_t pc_events = rb_iseq_event_flags(iseq, pc);
|
2020-11-16 10:40:04 +03:00
|
|
|
pc += encoded_iseq_trace_instrument(&iseq_encoded[pc], pc_events & (local_events | iseq->aux.exec.global_trace_events), false);
|
2018-11-26 21:16:39 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return n;
|
|
|
|
}
|
|
|
|
|
|
|
|
struct trace_clear_local_events_struct {
|
|
|
|
VALUE tpval;
|
|
|
|
int n;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void
|
|
|
|
iseq_remove_local_tracepoint_i(const rb_iseq_t *iseq, void *p)
|
|
|
|
{
|
|
|
|
struct trace_clear_local_events_struct *data = (struct trace_clear_local_events_struct *)p;
|
|
|
|
data->n += iseq_remove_local_tracepoint(iseq, data->tpval);
|
|
|
|
iseq_iterate_children(iseq, iseq_remove_local_tracepoint_i, p);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
rb_iseq_remove_local_tracepoint_recursively(const rb_iseq_t *iseq, VALUE tpval)
|
|
|
|
{
|
2018-11-26 21:48:48 +03:00
|
|
|
struct trace_clear_local_events_struct data;
|
|
|
|
data.tpval = tpval;
|
|
|
|
data.n = 0;
|
|
|
|
|
2018-11-26 21:16:39 +03:00
|
|
|
iseq_remove_local_tracepoint_i(iseq, (void *)&data);
|
|
|
|
return data.n;
|
|
|
|
}
|
|
|
|
|
2017-11-17 09:24:55 +03:00
|
|
|
void
|
2017-11-14 15:58:36 +03:00
|
|
|
rb_iseq_trace_set(const rb_iseq_t *iseq, rb_event_flag_t turnon_events)
|
2012-11-30 21:00:30 +04:00
|
|
|
{
|
2018-12-06 13:52:27 +03:00
|
|
|
if (iseq->aux.exec.global_trace_events == turnon_events) {
|
2017-11-18 12:39:41 +03:00
|
|
|
return;
|
|
|
|
}
|
2018-12-06 13:52:27 +03:00
|
|
|
|
|
|
|
if (!ISEQ_EXECUTABLE_P(iseq)) {
|
2017-11-27 03:43:23 +03:00
|
|
|
/* this is building ISeq */
|
|
|
|
return;
|
|
|
|
}
|
2017-11-18 12:39:41 +03:00
|
|
|
else {
|
2018-11-26 21:16:54 +03:00
|
|
|
unsigned int pc;
|
2022-03-23 22:19:48 +03:00
|
|
|
const struct rb_iseq_constant_body *const body = ISEQ_BODY(iseq);
|
2018-05-12 04:24:18 +03:00
|
|
|
VALUE *iseq_encoded = (VALUE *)body->iseq_encoded;
|
2018-11-26 21:16:39 +03:00
|
|
|
rb_event_flag_t enabled_events;
|
2018-12-06 13:52:27 +03:00
|
|
|
rb_event_flag_t local_events = iseq->aux.exec.local_hooks ? iseq->aux.exec.local_hooks->events : 0;
|
|
|
|
((rb_iseq_t *)iseq)->aux.exec.global_trace_events = turnon_events;
|
2021-07-14 02:01:09 +03:00
|
|
|
enabled_events = add_bmethod_events(turnon_events | local_events);
|
2018-11-26 21:16:39 +03:00
|
|
|
|
|
|
|
for (pc=0; pc<body->iseq_size;) {
|
2018-11-26 21:16:54 +03:00
|
|
|
rb_event_flag_t pc_events = rb_iseq_event_flags(iseq, pc);
|
2020-11-16 10:40:04 +03:00
|
|
|
pc += encoded_iseq_trace_instrument(&iseq_encoded[pc], pc_events & enabled_events, true);
|
2012-11-30 21:00:30 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-24 01:22:14 +03:00
|
|
|
void rb_vm_cc_general(const struct rb_callcache *cc);
|
|
|
|
|
2023-03-09 19:30:30 +03:00
|
|
|
static bool
|
|
|
|
clear_attr_cc(VALUE v)
|
|
|
|
{
|
|
|
|
if (imemo_type_p(v, imemo_callcache) && vm_cc_ivar_p((const struct rb_callcache *)v)) {
|
|
|
|
rb_vm_cc_general((struct rb_callcache *)v);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool
|
|
|
|
clear_bf_cc(VALUE v)
|
|
|
|
{
|
|
|
|
if (imemo_type_p(v, imemo_callcache) && vm_cc_bf_p((const struct rb_callcache *)v)) {
|
|
|
|
rb_vm_cc_general((struct rb_callcache *)v);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-24 01:22:14 +03:00
|
|
|
static int
|
|
|
|
clear_attr_ccs_i(void *vstart, void *vend, size_t stride, void *data)
|
|
|
|
{
|
|
|
|
VALUE v = (VALUE)vstart;
|
|
|
|
for (; v != (VALUE)vend; v += stride) {
|
|
|
|
void *ptr = asan_poisoned_object_p(v);
|
|
|
|
asan_unpoison_object(v, false);
|
2023-03-09 19:30:30 +03:00
|
|
|
clear_attr_cc(v);
|
2021-08-24 01:22:14 +03:00
|
|
|
asan_poison_object_if(ptr, v);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
rb_clear_attr_ccs(void)
|
|
|
|
{
|
|
|
|
rb_objspace_each_objects(clear_attr_ccs_i, NULL);
|
|
|
|
}
|
|
|
|
|
2023-03-09 19:30:30 +03:00
|
|
|
static int
|
|
|
|
clear_bf_ccs_i(void *vstart, void *vend, size_t stride, void *data)
|
|
|
|
{
|
|
|
|
VALUE v = (VALUE)vstart;
|
|
|
|
for (; v != (VALUE)vend; v += stride) {
|
|
|
|
void *ptr = asan_poisoned_object_p(v);
|
|
|
|
asan_unpoison_object(v, false);
|
|
|
|
clear_bf_cc(v);
|
|
|
|
asan_poison_object_if(ptr, v);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
rb_clear_bf_ccs(void)
|
|
|
|
{
|
|
|
|
rb_objspace_each_objects(clear_bf_ccs_i, NULL);
|
|
|
|
}
|
|
|
|
|
2012-11-30 21:00:30 +04:00
|
|
|
static int
|
2017-11-14 15:58:36 +03:00
|
|
|
trace_set_i(void *vstart, void *vend, size_t stride, void *data)
|
2012-11-30 21:00:30 +04:00
|
|
|
{
|
2017-11-14 15:58:36 +03:00
|
|
|
rb_event_flag_t turnon_events = *(rb_event_flag_t *)data;
|
2012-11-30 21:00:30 +04:00
|
|
|
|
2017-11-14 15:58:36 +03:00
|
|
|
VALUE v = (VALUE)vstart;
|
|
|
|
for (; v != (VALUE)vend; v += stride) {
|
2019-05-23 11:02:07 +03:00
|
|
|
void *ptr = asan_poisoned_object_p(v);
|
|
|
|
asan_unpoison_object(v, false);
|
2019-04-02 01:52:35 +03:00
|
|
|
|
2017-11-14 15:58:36 +03:00
|
|
|
if (rb_obj_is_iseq(v)) {
|
|
|
|
rb_iseq_trace_set(rb_iseq_check((rb_iseq_t *)v), turnon_events);
|
2012-11-30 21:00:30 +04:00
|
|
|
}
|
2023-03-09 19:30:30 +03:00
|
|
|
else if (clear_attr_cc(v)) {
|
|
|
|
}
|
|
|
|
else if (clear_bf_cc(v)) {
|
2021-08-24 01:22:14 +03:00
|
|
|
}
|
2019-04-02 01:52:35 +03:00
|
|
|
|
2019-05-29 07:12:15 +03:00
|
|
|
asan_poison_object_if(ptr, v);
|
2012-11-30 21:00:30 +04:00
|
|
|
}
|
2017-11-14 15:58:36 +03:00
|
|
|
return 0;
|
2012-11-30 21:00:30 +04:00
|
|
|
}
|
|
|
|
|
2017-11-14 15:58:36 +03:00
|
|
|
void
|
|
|
|
rb_iseq_trace_set_all(rb_event_flag_t turnon_events)
|
2012-11-30 21:00:30 +04:00
|
|
|
{
|
2017-11-14 15:58:36 +03:00
|
|
|
rb_objspace_each_objects(trace_set_i, &turnon_events);
|
|
|
|
}
|
2012-11-30 21:00:30 +04:00
|
|
|
|
2015-12-08 08:27:10 +03:00
|
|
|
VALUE
|
|
|
|
rb_iseqw_local_variables(VALUE iseqval)
|
|
|
|
{
|
|
|
|
return rb_iseq_local_variables(iseqw_check(iseqval));
|
|
|
|
}
|
|
|
|
|
2015-12-08 16:58:50 +03:00
|
|
|
/*
|
|
|
|
* call-seq:
|
2015-12-09 18:10:57 +03:00
|
|
|
* iseq.to_binary(extra_data = nil) -> binary str
|
2015-12-08 16:58:50 +03:00
|
|
|
*
|
|
|
|
* Returns serialized iseq binary format data as a String object.
|
2015-12-14 05:52:14 +03:00
|
|
|
* A corresponding iseq object is created by
|
2015-12-09 18:10:57 +03:00
|
|
|
* RubyVM::InstructionSequence.load_from_binary() method.
|
2015-12-08 16:58:50 +03:00
|
|
|
*
|
|
|
|
* String extra_data will be saved with binary data.
|
|
|
|
* You can access this data with
|
2015-12-09 18:10:57 +03:00
|
|
|
* RubyVM::InstructionSequence.load_from_binary_extra_data(binary).
|
2015-12-09 05:41:14 +03:00
|
|
|
*
|
|
|
|
* Note that the translated binary data is not portable.
|
|
|
|
* You can not move this binary data to another machine.
|
2015-12-09 18:10:57 +03:00
|
|
|
* You can not use the binary data which is created by another
|
2015-12-09 05:41:14 +03:00
|
|
|
* version/another architecture of Ruby.
|
2015-12-08 16:58:50 +03:00
|
|
|
*/
|
|
|
|
static VALUE
|
2015-12-09 18:10:57 +03:00
|
|
|
iseqw_to_binary(int argc, VALUE *argv, VALUE self)
|
2015-12-08 16:58:50 +03:00
|
|
|
{
|
2018-12-06 10:49:24 +03:00
|
|
|
VALUE opt = !rb_check_arity(argc, 0, 1) ? Qnil : argv[0];
|
2018-09-13 16:59:25 +03:00
|
|
|
return rb_iseq_ibf_dump(iseqw_check(self), opt);
|
2015-12-08 16:58:50 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
2015-12-09 18:10:57 +03:00
|
|
|
* RubyVM::InstructionSequence.load_from_binary(binary) -> iseq
|
2015-12-08 16:58:50 +03:00
|
|
|
*
|
|
|
|
* Load an iseq object from binary format String object
|
2015-12-09 18:10:57 +03:00
|
|
|
* created by RubyVM::InstructionSequence.to_binary.
|
2015-12-09 05:41:14 +03:00
|
|
|
*
|
|
|
|
* This loader does not have a verifier, so that loading broken/modified
|
|
|
|
* binary causes critical problem.
|
|
|
|
*
|
|
|
|
* You should not load binary data provided by others.
|
|
|
|
* You should use binary data translated by yourself.
|
2015-12-08 16:58:50 +03:00
|
|
|
*/
|
|
|
|
static VALUE
|
2015-12-09 18:10:57 +03:00
|
|
|
iseqw_s_load_from_binary(VALUE self, VALUE str)
|
2015-12-08 16:58:50 +03:00
|
|
|
{
|
2018-09-13 16:59:25 +03:00
|
|
|
return iseqw_new(rb_iseq_ibf_load(str));
|
2015-12-08 16:58:50 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
2015-12-09 18:10:57 +03:00
|
|
|
* RubyVM::InstructionSequence.load_from_binary_extra_data(binary) -> str
|
2015-12-08 16:58:50 +03:00
|
|
|
*
|
|
|
|
* Load extra data embed into binary format String object.
|
|
|
|
*/
|
|
|
|
static VALUE
|
2015-12-09 18:10:57 +03:00
|
|
|
iseqw_s_load_from_binary_extra_data(VALUE self, VALUE str)
|
2015-12-08 16:58:50 +03:00
|
|
|
{
|
2018-09-13 16:59:25 +03:00
|
|
|
return rb_iseq_ibf_load_extra_data(str);
|
2015-12-08 16:58:50 +03:00
|
|
|
}
|
|
|
|
|
2018-01-09 17:05:23 +03:00
|
|
|
#if VM_INSN_INFO_TABLE_IMPL == 2
|
|
|
|
|
|
|
|
/* An implementation of succinct bit-vector for insn_info table.
|
|
|
|
*
|
|
|
|
* A succinct bit-vector is a small and efficient data structure that provides
|
|
|
|
* a bit-vector augmented with an index for O(1) rank operation:
|
|
|
|
*
|
|
|
|
* rank(bv, n): the number of 1's within a range from index 0 to index n
|
|
|
|
*
|
|
|
|
* This can be used to lookup insn_info table from PC.
|
|
|
|
* For example, consider the following iseq and insn_info_table:
|
|
|
|
*
|
|
|
|
* iseq insn_info_table
|
|
|
|
* PC insn+operand position lineno event
|
|
|
|
* 0: insn1 0: 1 [Li]
|
|
|
|
* 2: insn2 2: 2 [Li] <= (A)
|
|
|
|
* 5: insn3 8: 3 [Li] <= (B)
|
|
|
|
* 8: insn4
|
|
|
|
*
|
|
|
|
* In this case, a succinct bit-vector whose indexes 0, 2, 8 is "1" and
|
|
|
|
* other indexes is "0", i.e., "101000001", is created.
|
|
|
|
* To lookup the lineno of insn2, calculate rank("10100001", 2) = 2, so
|
|
|
|
* the line (A) is the entry in question.
|
|
|
|
* To lookup the lineno of insn4, calculate rank("10100001", 8) = 3, so
|
|
|
|
* the line (B) is the entry in question.
|
|
|
|
*
|
2019-12-20 03:19:39 +03:00
|
|
|
* A naive implementation of succinct bit-vector works really well
|
2018-01-09 17:05:23 +03:00
|
|
|
* not only for large size but also for small size. However, it has
|
|
|
|
* tiny overhead for very small size. So, this implementation consist
|
|
|
|
* of two parts: one part is the "immediate" table that keeps rank result
|
|
|
|
* as a raw table, and the other part is a normal succinct bit-vector.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define IMMEDIATE_TABLE_SIZE 54 /* a multiple of 9, and < 128 */
|
|
|
|
|
|
|
|
struct succ_index_table {
|
|
|
|
uint64_t imm_part[IMMEDIATE_TABLE_SIZE / 9];
|
|
|
|
struct succ_dict_block {
|
|
|
|
unsigned int rank;
|
|
|
|
uint64_t small_block_ranks; /* 9 bits * 7 = 63 bits */
|
|
|
|
uint64_t bits[512/64];
|
2018-01-13 14:45:25 +03:00
|
|
|
} succ_part[FLEX_ARY_LEN];
|
2018-01-19 06:19:58 +03:00
|
|
|
};
|
2018-01-09 17:05:23 +03:00
|
|
|
|
|
|
|
#define imm_block_rank_set(v, i, r) (v) |= (uint64_t)(r) << (7 * (i))
|
2018-01-10 08:57:15 +03:00
|
|
|
#define imm_block_rank_get(v, i) (((int)((v) >> ((i) * 7))) & 0x7f)
|
2018-01-09 17:05:23 +03:00
|
|
|
#define small_block_rank_set(v, i, r) (v) |= (uint64_t)(r) << (9 * ((i) - 1))
|
2018-01-10 08:57:15 +03:00
|
|
|
#define small_block_rank_get(v, i) ((i) == 0 ? 0 : (((int)((v) >> (((i) - 1) * 9))) & 0x1ff))
|
2021-10-06 22:49:56 +03:00
|
|
|
|
2018-01-09 17:05:23 +03:00
|
|
|
static struct succ_index_table *
|
|
|
|
succ_index_table_create(int max_pos, int *data, int size)
|
|
|
|
{
|
|
|
|
const int imm_size = (max_pos < IMMEDIATE_TABLE_SIZE ? max_pos + 8 : IMMEDIATE_TABLE_SIZE) / 9;
|
|
|
|
const int succ_size = (max_pos < IMMEDIATE_TABLE_SIZE ? 0 : (max_pos - IMMEDIATE_TABLE_SIZE + 511)) / 512;
|
2019-10-07 10:56:08 +03:00
|
|
|
struct succ_index_table *sd =
|
|
|
|
rb_xcalloc_mul_add_mul(
|
|
|
|
imm_size, sizeof(uint64_t),
|
|
|
|
succ_size, sizeof(struct succ_dict_block));
|
2018-01-09 17:05:23 +03:00
|
|
|
int i, j, k, r;
|
|
|
|
|
|
|
|
r = 0;
|
|
|
|
for (j = 0; j < imm_size; j++) {
|
|
|
|
for (i = 0; i < 9; i++) {
|
|
|
|
if (r < size && data[r] == j * 9 + i) r++;
|
|
|
|
imm_block_rank_set(sd->imm_part[j], i, r);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (k = 0; k < succ_size; k++) {
|
|
|
|
struct succ_dict_block *sd_block = &sd->succ_part[k];
|
|
|
|
int small_rank = 0;
|
|
|
|
sd_block->rank = r;
|
|
|
|
for (j = 0; j < 8; j++) {
|
|
|
|
uint64_t bits = 0;
|
|
|
|
if (j) small_block_rank_set(sd_block->small_block_ranks, j, small_rank);
|
|
|
|
for (i = 0; i < 64; i++) {
|
|
|
|
if (r < size && data[r] == k * 512 + j * 64 + i + IMMEDIATE_TABLE_SIZE) {
|
2018-01-10 08:57:15 +03:00
|
|
|
bits |= ((uint64_t)1) << i;
|
2018-01-09 17:05:23 +03:00
|
|
|
r++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
sd_block->bits[j] = bits;
|
|
|
|
small_rank += rb_popcount64(bits);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return sd;
|
|
|
|
}
|
|
|
|
|
|
|
|
static unsigned int *
|
|
|
|
succ_index_table_invert(int max_pos, struct succ_index_table *sd, int size)
|
|
|
|
{
|
|
|
|
const int imm_size = (max_pos < IMMEDIATE_TABLE_SIZE ? max_pos + 8 : IMMEDIATE_TABLE_SIZE) / 9;
|
|
|
|
const int succ_size = (max_pos < IMMEDIATE_TABLE_SIZE ? 0 : (max_pos - IMMEDIATE_TABLE_SIZE + 511)) / 512;
|
2019-10-07 10:56:08 +03:00
|
|
|
unsigned int *positions = ALLOC_N(unsigned int, size), *p;
|
2018-01-09 17:05:23 +03:00
|
|
|
int i, j, k, r = -1;
|
|
|
|
p = positions;
|
|
|
|
for (j = 0; j < imm_size; j++) {
|
|
|
|
for (i = 0; i < 9; i++) {
|
|
|
|
int nr = imm_block_rank_get(sd->imm_part[j], i);
|
|
|
|
if (r != nr) *p++ = j * 9 + i;
|
|
|
|
r = nr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
for (k = 0; k < succ_size; k++) {
|
|
|
|
for (j = 0; j < 8; j++) {
|
|
|
|
for (i = 0; i < 64; i++) {
|
2018-01-10 08:57:15 +03:00
|
|
|
if (sd->succ_part[k].bits[j] & (((uint64_t)1) << i)) {
|
2018-01-09 17:05:23 +03:00
|
|
|
*p++ = k * 512 + j * 64 + i + IMMEDIATE_TABLE_SIZE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return positions;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
succ_index_lookup(const struct succ_index_table *sd, int x)
|
|
|
|
{
|
|
|
|
if (x < IMMEDIATE_TABLE_SIZE) {
|
|
|
|
const int i = x / 9;
|
|
|
|
const int j = x % 9;
|
|
|
|
return imm_block_rank_get(sd->imm_part[i], j);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
const int block_index = (x - IMMEDIATE_TABLE_SIZE) / 512;
|
|
|
|
const struct succ_dict_block *block = &sd->succ_part[block_index];
|
|
|
|
const int block_bit_index = (x - IMMEDIATE_TABLE_SIZE) % 512;
|
|
|
|
const int small_block_index = block_bit_index / 64;
|
|
|
|
const int small_block_popcount = small_block_rank_get(block->small_block_ranks, small_block_index);
|
|
|
|
const int popcnt = rb_popcount64(block->bits[small_block_index] << (63 - block_bit_index % 64));
|
2022-07-21 19:23:58 +03:00
|
|
|
|
2018-01-09 17:05:23 +03:00
|
|
|
return block->rank + small_block_popcount + popcnt;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2021-09-30 10:58:46 +03:00
|
|
|
|
|
|
|
/*
|
|
|
|
* call-seq:
|
|
|
|
* iseq.script_lines -> array or nil
|
|
|
|
*
|
2023-02-27 20:56:06 +03:00
|
|
|
* It returns recorded script lines if it is available.
|
2021-09-30 10:58:46 +03:00
|
|
|
* The script lines are not limited to the iseq range, but
|
|
|
|
* are entire lines of the source file.
|
|
|
|
*
|
|
|
|
* Note that this is an API for ruby internal use, debugging,
|
|
|
|
* and research. Do not use this for any other purpose.
|
|
|
|
* The compatibility is not guaranteed.
|
|
|
|
*/
|
|
|
|
static VALUE
|
|
|
|
iseqw_script_lines(VALUE self)
|
|
|
|
{
|
|
|
|
const rb_iseq_t *iseq = iseqw_check(self);
|
2022-03-23 22:19:48 +03:00
|
|
|
return ISEQ_BODY(iseq)->variable.script_lines;
|
2021-09-30 10:58:46 +03:00
|
|
|
}
|
|
|
|
|
2012-07-25 01:29:24 +04:00
|
|
|
/*
|
|
|
|
* Document-class: RubyVM::InstructionSequence
|
|
|
|
*
|
|
|
|
* The InstructionSequence class represents a compiled sequence of
|
2019-08-19 08:51:00 +03:00
|
|
|
* instructions for the Virtual Machine used in MRI. Not all implementations of Ruby
|
2019-07-15 05:39:57 +03:00
|
|
|
* may implement this class, and for the implementations that implement it,
|
|
|
|
* the methods defined and behavior of the methods can change in any version.
|
2012-12-29 11:44:54 +04:00
|
|
|
*
|
|
|
|
* With it, you can get a handle to the instructions that make up a method or
|
|
|
|
* a proc, compile strings of Ruby code down to VM instructions, and
|
|
|
|
* disassemble instruction sequences to strings for easy inspection. It is
|
2019-08-19 08:51:00 +03:00
|
|
|
* mostly useful if you want to learn how YARV works, but it also lets
|
2012-12-29 11:44:54 +04:00
|
|
|
* you control various settings for the Ruby iseq compiler.
|
|
|
|
*
|
|
|
|
* You can find the source for the VM instructions in +insns.def+ in the Ruby
|
|
|
|
* source.
|
|
|
|
*
|
|
|
|
* The instruction sequence results will almost certainly change as Ruby
|
|
|
|
* changes, so example output in this documentation may be different from what
|
|
|
|
* you see.
|
2019-08-19 08:51:00 +03:00
|
|
|
*
|
|
|
|
* Of course, this class is MRI specific.
|
2012-07-25 01:29:24 +04:00
|
|
|
*/
|
|
|
|
|
2006-12-31 18:02:22 +03:00
|
|
|
void
|
|
|
|
Init_ISeq(void)
|
|
|
|
{
|
2011-12-30 09:55:37 +04:00
|
|
|
/* declare ::RubyVM::InstructionSequence */
|
2008-06-29 21:26:16 +04:00
|
|
|
rb_cISeq = rb_define_class_under(rb_cRubyVM, "InstructionSequence", rb_cObject);
|
2016-08-22 03:02:59 +03:00
|
|
|
rb_undef_alloc_func(rb_cISeq);
|
2015-07-22 01:52:59 +03:00
|
|
|
rb_define_method(rb_cISeq, "inspect", iseqw_inspect, 0);
|
|
|
|
rb_define_method(rb_cISeq, "disasm", iseqw_disasm, 0);
|
|
|
|
rb_define_method(rb_cISeq, "disassemble", iseqw_disasm, 0);
|
|
|
|
rb_define_method(rb_cISeq, "to_a", iseqw_to_a, 0);
|
|
|
|
rb_define_method(rb_cISeq, "eval", iseqw_eval, 0);
|
2007-01-17 11:48:52 +03:00
|
|
|
|
2015-12-09 18:10:57 +03:00
|
|
|
rb_define_method(rb_cISeq, "to_binary", iseqw_to_binary, -1);
|
|
|
|
rb_define_singleton_method(rb_cISeq, "load_from_binary", iseqw_s_load_from_binary, 1);
|
|
|
|
rb_define_singleton_method(rb_cISeq, "load_from_binary_extra_data", iseqw_s_load_from_binary_extra_data, 1);
|
2015-12-08 16:59:15 +03:00
|
|
|
|
2012-11-30 22:02:43 +04:00
|
|
|
/* location APIs */
|
2015-07-22 01:52:59 +03:00
|
|
|
rb_define_method(rb_cISeq, "path", iseqw_path, 0);
|
|
|
|
rb_define_method(rb_cISeq, "absolute_path", iseqw_absolute_path, 0);
|
|
|
|
rb_define_method(rb_cISeq, "label", iseqw_label, 0);
|
|
|
|
rb_define_method(rb_cISeq, "base_label", iseqw_base_label, 0);
|
|
|
|
rb_define_method(rb_cISeq, "first_lineno", iseqw_first_lineno, 0);
|
2017-12-23 17:46:59 +03:00
|
|
|
rb_define_method(rb_cISeq, "trace_points", iseqw_trace_points, 0);
|
2017-12-23 15:48:24 +03:00
|
|
|
rb_define_method(rb_cISeq, "each_child", iseqw_each_child, 0);
|
2012-11-30 22:02:43 +04:00
|
|
|
|
2010-10-31 04:42:54 +03:00
|
|
|
#if 0 /* TBD */
|
2015-07-22 01:52:59 +03:00
|
|
|
rb_define_private_method(rb_cISeq, "marshal_dump", iseqw_marshal_dump, 0);
|
|
|
|
rb_define_private_method(rb_cISeq, "marshal_load", iseqw_marshal_load, 1);
|
2007-12-24 12:09:21 +03:00
|
|
|
/* disable this feature because there is no verifier. */
|
2017-12-05 11:56:50 +03:00
|
|
|
rb_define_singleton_method(rb_cISeq, "load", iseq_s_load, -1);
|
2017-12-05 11:58:57 +03:00
|
|
|
#endif
|
2008-06-01 23:55:25 +04:00
|
|
|
(void)iseq_s_load;
|
2007-12-24 12:09:21 +03:00
|
|
|
|
2015-07-22 01:52:59 +03:00
|
|
|
rb_define_singleton_method(rb_cISeq, "compile", iseqw_s_compile, -1);
|
2023-09-27 19:39:53 +03:00
|
|
|
rb_define_singleton_method(rb_cISeq, "compile_prism", iseqw_s_compile_prism, -1);
|
2023-11-09 10:39:12 +03:00
|
|
|
rb_define_singleton_method(rb_cISeq, "compile_file_prism", iseqw_s_compile_file_prism, -1);
|
2015-07-22 01:52:59 +03:00
|
|
|
rb_define_singleton_method(rb_cISeq, "new", iseqw_s_compile, -1);
|
|
|
|
rb_define_singleton_method(rb_cISeq, "compile_file", iseqw_s_compile_file, -1);
|
|
|
|
rb_define_singleton_method(rb_cISeq, "compile_option", iseqw_s_compile_option_get, 0);
|
|
|
|
rb_define_singleton_method(rb_cISeq, "compile_option=", iseqw_s_compile_option_set, 1);
|
|
|
|
rb_define_singleton_method(rb_cISeq, "disasm", iseqw_s_disasm, 1);
|
|
|
|
rb_define_singleton_method(rb_cISeq, "disassemble", iseqw_s_disasm, 1);
|
|
|
|
rb_define_singleton_method(rb_cISeq, "of", iseqw_s_of, 1);
|
2016-07-13 08:26:00 +03:00
|
|
|
|
2021-09-30 10:58:46 +03:00
|
|
|
// script lines
|
|
|
|
rb_define_method(rb_cISeq, "script_lines", iseqw_script_lines, 0);
|
|
|
|
|
2016-07-13 08:26:00 +03:00
|
|
|
rb_undef_method(CLASS_OF(rb_cISeq), "translate");
|
|
|
|
rb_undef_method(CLASS_OF(rb_cISeq), "load_iseq");
|
2006-12-31 18:02:22 +03:00
|
|
|
}
|