класс RubyVM::InstructionSequence
Класс InstructionSequence представляет собой скомпилированную последовательность инструкций для виртуальной машины Ruby.
С помощью него можно получить доступ к инструкциям, составляющим метод или блок кода, скомпилировать строки Ruby-кода до инструкций виртуальной машины и разобрать последовательности инструкций в строки для удобного просмотра. Он в основном полезен, если вы хотите узнать, как работает виртуальная машина Ruby, но также позволяет управлять различными настройками компилятора Ruby iseq.
Исходный код инструкций VM можно найти в insns.def в исходном коде Ruby.
Результаты последовательности инструкций, практически наверняка, будут меняться по мере изменения Ruby, поэтому примеры вывода в этой документации могут отличаться от того, что вы видите.
Публичные методы класса
static VALUE
iseqw_s_compile(int argc, VALUE *argv, VALUE self)
{
VALUE src, file = Qnil, path = Qnil, line = INT2FIX(1), opt = Qnil;
int i;
rb_secure(1);
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_cstr("<compiled>");
if (NIL_P(path)) path = file;
if (NIL_P(line)) line = INT2FIX(1);
Check_Type(path, T_STRING);
Check_Type(file, T_STRING);
return iseqw_new(rb_iseq_compile_with_option(src, file, path, line, 0, opt));
} Принимает source, строку Ruby-кода, и компилирует её в InstructionSequence.
По желанию принимает file, path, и line, которые описывают имя файла, абсолютный путь и номер первой строки Ruby-кода в source, которые являются метаданными, прикреплёнными к возвращаемому iseq.
options, который может быть true, false или Hash, используется для изменения стандартного поведения компилятора Ruby iseq.
Для получения подробной информации о допустимых параметрах компиляции, обратитесь к ::compile_option=.
RubyVM::InstructionSequence.compile("a = 1 + 2")
#=> <RubyVM::InstructionSequence:<compiled>@<compiled>>
static VALUE
iseqw_s_compile_file(int argc, VALUE *argv, VALUE self)
{
VALUE file, line = INT2FIX(1), opt = Qnil;
VALUE parser, f, exc = Qnil;
NODE *node;
rb_compile_option_t option;
int i;
rb_secure(1);
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 */
f = rb_file_open_str(file, "r");
parser = rb_parser_new();
rb_parser_set_context(parser, NULL, FALSE);
node = rb_parser_compile_file_path(parser, file, f, NUM2INT(line));
if (!node) exc = GET_THREAD()->errinfo;
rb_io_close(f);
if (!node) rb_exc_raise(exc);
make_compile_option(&option, opt);
return iseqw_new(rb_iseq_new_with_opt(node, rb_fstring_cstr("<main>"),
file,
rb_realpath_internal(Qnil, file, 1),
line, NULL, ISEQ_TYPE_TOP, &option));
} Принимает file, строку с местоположением файла Ruby-исходника, считывает, парсит и компилирует файл и возвращает iseq, скомпилированную InstructionSequence с установленными метаданными о местоположении исходного кода.
По желанию принимает options, который может быть true, false или Hash, для изменения стандартного поведения компилятора Ruby iseq.
Для получения подробной информации о допустимых параметрах компиляции, обратитесь к ::compile_option=.
# /tmp/hello.rb
puts "Hello, world!"
# elsewhere
RubyVM::InstructionSequence.compile_file("/tmp/hello.rb")
#=> <RubyVM::InstructionSequence:<main>@/tmp/hello.rb>
static VALUE
iseqw_s_compile_option_get(VALUE self)
{
return make_compile_option_value(&COMPILE_OPTION_DEFAULT);
} Возвращает хеш со значениями по умолчанию, используемыми компилятором Ruby iseq.
Для получения подробной информации, обратитесь к ::compile_option=.
static VALUE
iseqw_s_compile_option_set(VALUE self, VALUE opt)
{
rb_compile_option_t option;
rb_secure(1);
make_compile_option(&option, opt);
COMPILE_OPTION_DEFAULT = option;
return opt;
} Устанавливает значения по умолчанию для различных оптимизаций в компиляторе Ruby iseq.
Возможные значения для options включают true, который включает все параметры, false, который отключает все параметры, и nil, который оставляет все параметры без изменений.
Также можно передать Hash options, которые нужно изменить, любые параметры, отсутствующие в хеше, будут оставлены без изменений.
Возможные имена параметров (которые являются ключами в options) которые могут быть установлены в true или false включают:
-
:inline_const_cache -
:instructions_unification -
:operands_unification -
:peephole_optimization -
:specialized_instruction -
:stack_caching -
:tailcall_optimization -
:trace_instruction
Кроме того, :debug_level может быть установлено в целое число.
Эти значения по умолчанию могут быть переопределены для одного запуска компилятора iseq, передав любое из вышеперечисленных значений в качестве параметра options к ::new, ::compile и ::compile_file.
static VALUE
iseqw_s_disasm(VALUE klass, VALUE body)
{
VALUE iseqw = iseqw_s_of(klass, body);
return NIL_P(iseqw) ? Qnil : rb_iseq_disasm(iseqw_check(iseqw));
} Принимает body, объект Method или Proc, и возвращает строку с читаемым человеком описанием инструкций для body.
Для объекта Method:
# /tmp/method.rb def hello puts "hello, world" end puts RubyVM::InstructionSequence.disasm(method(:hello))
Выводит:
== 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)
Для блока Proc:
# /tmp/proc.rb
p = proc { num = 1 + 2 }
puts RubyVM::InstructionSequence.disasm(p)
Выводит:
== 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 0009 setlocal num, 0 0012 leave
static VALUE
iseqw_s_disasm(VALUE klass, VALUE body)
{
VALUE iseqw = iseqw_s_of(klass, body);
return NIL_P(iseqw) ? Qnil : rb_iseq_disasm(iseqw_check(iseqw));
} Принимает body, объект Method или Proc, и возвращает строку с читаемым человеком описанием инструкций для body.
Для объекта Method:
# /tmp/method.rb def hello puts "hello, world" end puts RubyVM::InstructionSequence.disasm(method(:hello))
Выводит:
== 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)
Для блока Proc:
# /tmp/proc.rb
p = proc { num = 1 + 2 }
puts RubyVM::InstructionSequence.disasm(p)
Выводит:
== 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 0009 setlocal num, 0 0012 leave
static VALUE
iseqw_s_load_from_binary(VALUE self, VALUE str)
{
return iseqw_new(iseq_ibf_load(str));
} Загружает объект iseq из двоичного формата строки, созданной методом #to_binary.
Этот загрузчик не имеет верификатора, поэтому загрузка повреждённых/изменённых двоичных данных может вызвать критическую ошибку.
Не следует загружать двоичные данные, предоставленные другими. Нужно использовать двоичные данные, преобразованные самостоятельно.
static VALUE
iseqw_s_load_from_binary_extra_data(VALUE self, VALUE str)
{
return iseq_ibf_load_extra_data(str);
} Загрузка дополнительных данных, встроенных в двоичную строку.
static VALUE
iseqw_s_compile(int argc, VALUE *argv, VALUE self)
{
VALUE src, file = Qnil, path = Qnil, line = INT2FIX(1), opt = Qnil;
int i;
rb_secure(1);
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_cstr("<compiled>");
if (NIL_P(path)) path = file;
if (NIL_P(line)) line = INT2FIX(1);
Check_Type(path, T_STRING);
Check_Type(file, T_STRING);
return iseqw_new(rb_iseq_compile_with_option(src, file, path, line, 0, opt));
} Принимает source, строку Ruby-кода, и компилирует её в InstructionSequence.
По желанию принимает file, path, и line, которые описывают имя файла, абсолютный путь и номер первой строки Ruby-кода в source, которые являются метаданными, прикреплёнными к возвращаемому iseq.
options, который может быть true, false или Hash, используется для изменения стандартного поведения компилятора Ruby iseq.
Для получения подробной информации о допустимых параметрах компиляции, обратитесь к ::compile_option=.
RubyVM::InstructionSequence.compile("a = 1 + 2")
#=> <RubyVM::InstructionSequence:<compiled>@<compiled>>
static VALUE
iseqw_s_of(VALUE klass, VALUE body)
{
const rb_iseq_t *iseq = NULL;
rb_secure(1);
if (rb_obj_is_proc(body)) {
iseq = vm_proc_iseq(body);
if (!rb_obj_is_iseq((VALUE)iseq)) {
iseq = NULL;
}
}
else {
iseq = rb_method_iseq(body);
}
return iseq ? iseqw_new(iseq) : Qnil;
} Возвращает последовательность инструкций, содержащую заданный блок кода или метод.
Например, в 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)> Используя ::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> Публичные методы экземпляра
static VALUE
iseqw_absolute_path(VALUE self)
{
return rb_iseq_absolute_path(iseqw_check(self));
} Возвращает абсолютный путь этой последовательности инструкций.
nil если iseq была получена из строки.
Например, используя ::compile_file:
# /tmp/method.rb
def hello
puts "hello, world"
end
# in irb
> iseq = RubyVM::InstructionSequence.compile_file('/tmp/method.rb')
> iseq.absolute_path #=> /tmp/method.rb static VALUE
iseqw_base_label(VALUE self)
{
return rb_iseq_base_label(iseqw_check(self));
} Возвращает базовую метку этой последовательности инструкций.
Например, используя irb:
iseq = RubyVM::InstructionSequence.compile('num = 1 + 2')
#=> <RubyVM::InstructionSequence:<compiled>@<compiled>>
iseq.base_label
#=> "<compiled>"
Используя ::compile_file:
# /tmp/method.rb
def hello
puts "hello, world"
end
# in irb
> iseq = RubyVM::InstructionSequence.compile_file('/tmp/method.rb')
> iseq.base_label #=> <main> static VALUE
iseqw_disasm(VALUE self)
{
return rb_iseq_disasm(iseqw_check(self));
} Возвращает последовательность инструкций в виде String в удобочитаемой форме.
puts RubyVM::InstructionSequence.compile('1 + 2').disasm
Производит:
== 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));
} Возвращает последовательность инструкций в виде String в удобочитаемой форме.
puts RubyVM::InstructionSequence.compile('1 + 2').disasm
Производит:
== 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_eval(VALUE self)
{
rb_secure(1);
return rb_iseq_eval(iseqw_check(self));
} Вычисляет последовательность инструкций и возвращает результат.
RubyVM::InstructionSequence.compile("1 + 2").eval #=> 3
static VALUE
iseqw_first_lineno(VALUE self)
{
return rb_iseq_first_lineno(iseqw_check(self));
} Возвращает номер первой строки исходного кода, из которой загружена последовательность инструкций.
Например, используя irb:
iseq = RubyVM::InstructionSequence.compile('num = 1 + 2')
#=> <RubyVM::InstructionSequence:<compiled>@<compiled>>
iseq.first_lineno
#=> 1
static VALUE
iseqw_inspect(VALUE self)
{
const rb_iseq_t *iseq = iseqw_check(self);
if (!iseq->body->location.label) {
return rb_sprintf("#<%s: uninitialized>", rb_obj_classname(self));
}
else {
return rb_sprintf("<%s:%s@%s>",
rb_obj_classname(self),
RSTRING_PTR(iseq->body->location.label), RSTRING_PTR(iseq->body->location.path));
}
} Возвращает удобочитаемое строковое представление этой последовательности инструкций, включая метку и путь.
static VALUE
iseqw_label(VALUE self)
{
return rb_iseq_label(iseqw_check(self));
} Возвращает метку этой последовательности инструкций.
<main> если она на верхнем уровне, <compiled> если она была вычислена из строки.
Например, используя irb:
iseq = RubyVM::InstructionSequence.compile('num = 1 + 2')
#=> <RubyVM::InstructionSequence:<compiled>@<compiled>>
iseq.label
#=> "<compiled>"
Используя ::compile_file:
# /tmp/method.rb
def hello
puts "hello, world"
end
# in irb
> iseq = RubyVM::InstructionSequence.compile_file('/tmp/method.rb')
> iseq.label #=> <main> VALUE
rb_iseqw_line_trace_all(VALUE iseqw)
{
VALUE result = rb_ary_new();
rb_iseqw_line_trace_each(iseqw, collect_trace, (void *)result);
return result;
} Экспериментальная функция, специфичная для MRI, доступна только как API уровня C.
Возвращает все specified_line события.
VALUE
rb_iseqw_line_trace_specify(VALUE iseqval, VALUE pos, VALUE set)
{
struct set_specifc_data data;
data.prev = 0;
data.pos = NUM2INT(pos);
if (data.pos < 0) rb_raise(rb_eTypeError, "`pos' is negative");
switch (set) {
case Qtrue: data.set = 1; break;
case Qfalse: data.set = 0; break;
default:
rb_raise(rb_eTypeError, "`set' should be true/false");
}
rb_iseqw_line_trace_each(iseqval, line_trace_specify, (void *)&data);
if (data.prev == 0) {
rb_raise(rb_eTypeError, "`pos' is out of range.");
}
return data.prev == 1 ? Qtrue : Qfalse;
} Экспериментальная функция, специфичная для MRI, доступна только как API уровня C.
Установить событие specified_line в заданной позиции строки, если параметр set равен true.
Этот метод полезен для создания точки останова отладчика в определенной строке.
Если set не является булевым значением, вызывается TypeError.
Если pos является отрицательным целым числом, генерируется исключение TypeError.
static VALUE
iseqw_path(VALUE self)
{
return rb_iseq_path(iseqw_check(self));
} Возвращает путь этой последовательности инструкций.
<compiled> если iseq была получена из строки.
Например, используя irb:
iseq = RubyVM::InstructionSequence.compile('num = 1 + 2')
#=> <RubyVM::InstructionSequence:<compiled>@<compiled>>
iseq.path
#=> "<compiled>"
Используя ::compile_file:
# /tmp/method.rb
def hello
puts "hello, world"
end
# in irb
> iseq = RubyVM::InstructionSequence.compile_file('/tmp/method.rb')
> iseq.path #=> /tmp/method.rb static VALUE
iseqw_to_a(VALUE self)
{
const rb_iseq_t *iseq = iseqw_check(self);
rb_secure(1);
return iseq_data_to_ary(iseq);
} Возвращает массив из 14 элементов, представляющих последовательность инструкций с следующими данными:
- magic
-
Строка, идентифицирующая формат данных. Всегда
YARVInstructionSequence/SimpleDataFormat. - major_version
-
Основная версия последовательности инструкций.
- minor_version
-
Дополнительная версия последовательности инструкций.
- format_type
-
Число, идентифицирующее формат данных. Всегда 1.
- misc
-
Хэш, содержащий:
-
:arg_size -
общее количество аргументов, принимаемых методом или блоком (0, если iseq не представляет метод или блок)
-
:local_size -
количество локальных переменных + 1
-
:stack_max -
используется для расчета глубины стека, при которой возникает SystemStackError.
-
- label
-
Имя контекста (блок, метод, класс, модуль и т. д.), к которому принадлежит эта последовательность инструкций.
<main>если она на верхнем уровне,<compiled>если она была вычислена из строки. - path
-
Относительный путь к файлу Ruby, из которого загружена последовательность инструкций.
<compiled>если iseq была получена из строки. - absolute_path
-
Абсолютный путь к файлу Ruby, из которого загружена последовательность инструкций.
nilесли iseq была получена из строки. - first_lineno
-
Номер первой строки исходного кода, из которой загружена последовательность инструкций.
- type
-
Тип последовательности инструкций.
Допустимые значения —
:top,:method,:block,:class,:rescue,:ensure,:eval,:main, и:defined_guard. - locals
-
Массив, содержащий имена всех аргументов и локальных переменных как символы.
- params
-
Объект Hash, содержащий информацию о параметрах.
Дополнительную информацию об этих значениях можно найти в
vm_core.h. - catch_table
-
Список исключений и операторов управления потоком (rescue, next, redo, break и т. д.).
- bytecode
-
Массив массивов, содержащих имена инструкций и операнды, составляющие тело последовательности инструкций.
Обратите внимание, что этот формат специфичен для MRI и зависит от версии.
static VALUE
iseqw_to_binary(int argc, VALUE *argv, VALUE self)
{
VALUE opt;
rb_scan_args(argc, argv, "01", &opt);
return iseq_ibf_dump(iseqw_check(self), opt);
} Возвращает данные последовательности инструкций в сериализованном двоичном формате как строку. Соответствующий объект iseq создаётся методом ::load_from_binary.
Строка extra_data будет сохранена вместе с двоичными данными. Вы можете получить доступ к этим данным с помощью метода ::load_from_binary_extra_data.
Обратите внимание, что переведённые двоичные данные не являются переносимыми. Вы не можете переместить эти двоичные данные на другую машину. Вы не можете использовать двоичные данные, созданные другой версией/архитектурой Ruby.
Ruby Core © 1993–2017 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.