класс RubyVM::InstructionSequence
Класс InstructionSequence представляет собой скомпилированную последовательность инструкций для виртуальной машины, используемой в MRI. Не все реализации Ruby могут реализовывать этот класс, и для тех реализаций, которые его реализуют, методы и поведение методов могут меняться в любой версии.
С его помощью можно получить доступ к инструкциям, составляющим метод или процедуру, скомпилировать строки кода Ruby до инструкций виртуальной машины и разобрать последовательность инструкций в строки для удобного просмотра. Он в основном полезен, если вы хотите узнать, как работает YARV, но он также позволяет управлять различными параметрами компилятора iseq Ruby.
Исходный код инструкций виртуальной машины можно найти в insns.def в исходном коде Ruby.
Результаты последовательности инструкций, практически наверняка, будут изменяться по мере изменения Ruby, поэтому примеры вывода в этой документации могут отличаться от того, что вы видите.
Конечно, этот класс специфичен для MRI.
Публичные методы класса
static VALUE
iseqw_s_compile(int argc, VALUE *argv, VALUE self)
{
VALUE src, file = Qnil, path = Qnil, line = INT2FIX(1), 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);
return iseqw_new(rb_iseq_compile_with_option(src, file, path, line, opt));
} Принимает source, строку кода Ruby, и компилирует её в InstructionSequence.
Необязательно принимает file, path, и line, которые описывают путь к файлу, фактический путь и номер первой строки кода Ruby в source, которые являются метаданными, присоединёнными к возвращаемому iseq.
file используется для `__FILE__` и отладки исключений. path используется для require_relative базы. Рекомендуется, чтобы эти пути были одинаковыми полными путями.
options, который может быть true, false или Hash, используется для изменения стандартного поведения компилятора iseq Ruby.
Для получения подробной информации об допустимых параметрах компиляции, см. ::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>
path = File.expand_path("test.rb")
RubyVM::InstructionSequence.compile(File.read(path), path, path)
#=> <RubyVM::InstructionSequence:<compiled>@/absolute/path/to/test.rb:1>
static VALUE
iseqw_s_compile_file(int argc, VALUE *argv, VALUE self)
{
VALUE file, line = INT2FIX(1), opt = Qnil;
VALUE parser, f, exc = Qnil, ret;
rb_ast_t *ast;
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 */
f = rb_file_open_str(file, "r");
parser = rb_parser_new();
rb_parser_set_context(parser, NULL, FALSE);
ast = rb_parser_compile_file_path(parser, file, f, NUM2INT(line));
if (!ast->body.root) exc = GET_EC()->errinfo;
rb_io_close(f);
if (!ast->body.root) {
rb_ast_dispose(ast);
rb_exc_raise(exc);
}
make_compile_option(&option, opt);
ret = iseqw_new(rb_iseq_new_with_opt(&ast->body, rb_fstring_lit("<main>"),
file,
rb_realpath_internal(Qnil, file, 1),
line, NULL, ISEQ_TYPE_TOP, &option));
rb_ast_dispose(ast);
return ret;
} Принимает file, строку с расположением файла исходного кода Ruby, читает, парсит и компилирует файл, и возвращает iseq, скомпилированную InstructionSequence с метаданными расположения источника.
Необязательно принимает options, который может быть true, false или Hash, чтобы изменить стандартное поведение компилятора iseq Ruby.
Для получения подробной информации об допустимых параметрах компиляции, см. ::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);
} Возвращает хеш с параметрами по умолчанию, используемыми компилятором iseq Ruby.
Для подробной информации, см. InstructionSequence.compile_option=.
static VALUE
iseqw_s_compile_option_set(VALUE self, VALUE opt)
{
rb_compile_option_t option;
make_compile_option(&option, opt);
COMPILE_OPTION_DEFAULT = option;
return opt;
} Устанавливает значения по умолчанию для различных оптимизаций в компиляторе iseq Ruby.
Возможные значения для options включают true, которое включает все параметры, false, которое отключает все параметры, и nil, которое оставляет все параметры без изменений.
Вы также можете передать хеш Hash с options, которые вы хотите изменить, любые параметры, отсутствующие в хеше, останутся без изменений.
Возможные имена параметров (которые являются ключами в options) которые могут быть установлены в true или false включают:
-
:inline_const_cache -
:instructions_unification -
:operands_unification -
:peephole_optimization -
:specialized_instruction -
:stack_caching -
:tailcall_optimization
Дополнительно, :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)
Для Процедуры:
# /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)
Для Процедуры:
# /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(rb_iseq_ibf_load(str));
} Загрузить объект iseq из двоичного формата String объекта, созданного методом RubyVM::InstructionSequence.to_binary.
Этот загрузчик не имеет верификатора, поэтому загрузка повреждённых/изменённых двоичных данных может вызвать критическую ошибку.
Вы не должны загружать двоичные данные, предоставленные другими. Вы должны использовать двоичные данные, преобразованные самостоятельно.
static VALUE
iseqw_s_load_from_binary_extra_data(VALUE self, VALUE str)
{
return rb_iseq_ibf_load_extra_data(str);
} Загрузить дополнительные данные, встроенные в двоичный формат String объекта.
static VALUE
iseqw_s_compile(int argc, VALUE *argv, VALUE self)
{
VALUE src, file = Qnil, path = Qnil, line = INT2FIX(1), 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);
return iseqw_new(rb_iseq_compile_with_option(src, file, path, line, opt));
} Принимает source, строку кода Ruby, и компилирует её в InstructionSequence.
Необязательно принимает file, path, и line, которые описывают путь к файлу, фактический путь и номер первой строки кода Ruby в source, которые являются метаданными, присоединёнными к возвращаемому iseq.
file используется для `__FILE__` и отладки исключений. path используется для require_relative базы. Рекомендуется, чтобы эти пути были одинаковыми полными путями.
options, который может быть true, false или Hash, используется для изменения стандартного поведения компилятора iseq Ruby.
Для получения подробной информации об допустимых параметрах компиляции, см. ::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>
path = File.expand_path("test.rb")
RubyVM::InstructionSequence.compile(File.read(path), path, path)
#=> <RubyVM::InstructionSequence:<compiled>@/absolute/path/to/test.rb:1>
static VALUE
iseqw_s_of(VALUE klass, VALUE body)
{
const rb_iseq_t *iseq = NULL;
if (rb_obj_is_proc(body)) {
iseq = vm_proc_iseq(body);
if (!rb_obj_is_iseq((VALUE)iseq)) {
iseq = NULL;
}
}
else if (rb_obj_is_method(body)) {
iseq = rb_method_iseq(body);
}
else if (rb_typeddata_is_instance_of(body, &iseqw_data_type)) {
return 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_realpath(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_each_child(VALUE self)
{
const rb_iseq_t *iseq = iseqw_check(self);
iseq_iterate_children(iseq, yield_each_children, NULL);
return self;
} Перебирает все непосредственные дочерние последовательности инструкций. Порядок итерации определён реализацией/версией, поэтому полагаться на порядок не рекомендуется.
static VALUE
iseqw_eval(VALUE self)
{
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);
const struct rb_iseq_constant_body *const body = iseq->body;
VALUE klass = rb_class_name(rb_obj_class(self));
if (!body->location.label) {
return rb_sprintf("#<%"PRIsVALUE": uninitialized>", klass);
}
else {
return rb_sprintf("<%"PRIsVALUE":%"PRIsVALUE"@%"PRIsVALUE":%d>",
klass,
body->location.label, rb_iseq_path(iseq),
FIX2INT(rb_iseq_first_lineno(iseq)));
}
} Возвращает удобочитаемое строковое представление этой последовательности инструкций, включая label и 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> 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);
return iseq_data_to_ary(iseq);
} Возвращает Array с 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, иplain. - 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_check_arity(argc, 0, 1) ? Qnil : argv[0];
return rb_iseq_ibf_dump(iseqw_check(self), opt);
} Возвращает данные сериализованного iseq в двоичном формате в виде объекта String. Соответствующий объект iseq создаётся методом RubyVM::InstructionSequence.load_from_binary().
Дополнительно String extra_data будет сохранён вместе с двоичными данными. Вы можете получить доступ к этим данным с помощью RubyVM::InstructionSequence.load_from_binary_extra_data(binary).
Обратите внимание, что переведённые двоичные данные не являются переносимыми. Вы не можете перемещать эти двоичные данные на другую машину. Вы не можете использовать двоичные данные, созданные другой версией/архитектурой Ruby.
static VALUE
iseqw_trace_points(VALUE self)
{
const rb_iseq_t *iseq = iseqw_check(self);
const struct rb_iseq_constant_body *const body = iseq->body;
unsigned int i;
VALUE ary = rb_ary_new();
for (i=0; i<body->insns_info.size; i++) {
const struct iseq_insn_info_entry *entry = &body->insns_info.body[i];
if (entry->events) {
push_event_info(iseq, entry->events, entry->line_no, ary);
}
}
return ary;
} Возвращает точки отслеживания в последовательности инструкций. Возвращает массив пар [строка, символ_события].
Ruby Core © 1993–2017 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.