* introduce new ISeq binary format serializer/de-serializer

and a pre-compilation/runtime loader sample.
  [Feature #11788]

* iseq.c: add new methods:
  * RubyVM::InstructionSequence#to_binary_format(extra_data = nil)
  * RubyVM::InstructionSequence.from_binary_format(binary)
  * RubyVM::InstructionSequence.from_binary_format_extra_data(binary)

* compile.c: implement body of this new feature.

* load.c (rb_load_internal0), iseq.c (rb_iseq_load_iseq):
  call RubyVM::InstructionSequence.load_iseq(fname) with
  loading script name if this method is defined.

  We can return any ISeq object as a result value.
  Otherwise loading will be continue as usual.

  This interface is not matured and is not extensible.
  So that we don't guarantee the future compatibility of this method.
  Basically, you should'nt use this method.

* iseq.h: move ISEQ_MAJOR/MINOR_VERSION (and some definitions)
  from iseq.c.

* encoding.c (rb_data_is_encoding), internal.h: added.

* vm_core.h: add several supports for lazy load.
  * add USE_LAZY_LOAD macro to specify enable or disable of
    this feature.
  * add several fields to rb_iseq_t.
  * introduce new macro rb_iseq_check().

* insns.def: some check for lazy loading feature.

* vm_insnhelper.c: ditto.

* proc.c: ditto.

* vm.c: ditto.

* test/lib/iseq_loader_checker.rb: enabled iff suitable
  environment variables are provided.

* test/runner.rb: enable lib/iseq_loader_checker.rb.

* sample/iseq_loader.rb: add sample compiler and loader.

    $ ruby sample/iseq_loader.rb [dir]

  will compile all ruby scripts in [dir].
  With default setting, this compile creates *.rb.yarb files
  in same directory of target .rb scripts.

    $ ruby -r sample/iseq_loader.rb [app]

  will run with enable to load compiled binary data.




git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@52949 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
ko1 2015-12-08 13:58:50 +00:00
Родитель 8f620b9b17
Коммит 3dbb390180
16 изменённых файлов: 1979 добавлений и 89 удалений

Просмотреть файл

@ -1,3 +1,63 @@
Tue Dec 8 22:31:58 2015 Koichi Sasada <ko1@atdot.net>
* introduce new ISeq binary format serializer/de-serializer
and a pre-compilation/runtime loader sample.
[Feature #11788]
* iseq.c: add new methods:
* RubyVM::InstructionSequence#to_binary_format(extra_data = nil)
* RubyVM::InstructionSequence.from_binary_format(binary)
* RubyVM::InstructionSequence.from_binary_format_extra_data(binary)
* compile.c: implement body of this new feature.
* load.c (rb_load_internal0), iseq.c (rb_iseq_load_iseq):
call RubyVM::InstructionSequence.load_iseq(fname) with
loading script name if this method is defined.
We can return any ISeq object as a result value.
Otherwise loading will be continue as usual.
This interface is not matured and is not extensible.
So that we don't guarantee the future compatibility of this method.
Basically, you should'nt use this method.
* iseq.h: move ISEQ_MAJOR/MINOR_VERSION (and some definitions)
from iseq.c.
* encoding.c (rb_data_is_encoding), internal.h: added.
* vm_core.h: add several supports for lazy load.
* add USE_LAZY_LOAD macro to specify enable or disable of
this feature.
* add several fields to rb_iseq_t.
* introduce new macro rb_iseq_check().
* insns.def: some check for lazy loading feature.
* vm_insnhelper.c: ditto.
* proc.c: ditto.
* vm.c: ditto.
* test/lib/iseq_loader_checker.rb: enabled iff suitable
environment variables are provided.
* test/runner.rb: enable lib/iseq_loader_checker.rb.
* sample/iseq_loader.rb: add sample compiler and loader.
$ ruby sample/iseq_loader.rb [dir]
will compile all ruby scripts in [dir].
With default setting, this compile creates *.rb.yarb files
in same directory of target .rb scripts.
$ ruby -r sample/iseq_loader.rb [app]
will run with enable to load compiled binary data.
Tue Dec 8 21:21:16 2015 Kazuhiro NISHIYAMA <zn@mbf.nifty.com>
* NEWS: mention about Enumerator::Lazy#grep_v.

8
NEWS
Просмотреть файл

@ -116,6 +116,14 @@ with all sufficient information, see the ChangeLog file.
* Regexp/String: Updated Unicode version from 7.0.0 to 8.0.0
* RubyVM::InstructionSequence
* add the following methods as a primitive tool of iseq loader.
See sample/iseq_loader.rb for usage.
[Feature #11788]
* RubyVM::InstructionSequence#to_binary_format(extra_data = nil)
* RubyVM::InstructionSequence.from_binary_format(binary)
* RubyVM::InstructionSequence.from_binary_format_extra_data(binary)
* String
* String#+@ and String#- are added to get mutable/frozen strings.

1431
compile.c

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -86,6 +86,12 @@ static const rb_data_type_t encoding_data_type = {
#define is_data_encoding(obj) (RTYPEDDATA_P(obj) && RTYPEDDATA_TYPE(obj) == &encoding_data_type)
#define is_obj_encoding(obj) (RB_TYPE_P((obj), T_DATA) && is_data_encoding(obj))
int
rb_data_is_encoding(VALUE obj)
{
return is_data_encoding(obj);
}
static VALUE
enc_new(rb_encoding *encoding)
{

Просмотреть файл

@ -928,13 +928,15 @@ defineclass
rb_bug("unknown defineclass type: %d", (int)type);
}
rb_iseq_check(class_iseq);
/* enter scope */
vm_push_frame(th, class_iseq, VM_FRAME_MAGIC_CLASS, klass,
VM_ENVVAL_BLOCK_PTR(GET_BLOCK_PTR()),
(VALUE)vm_cref_push(th, klass, NULL, FALSE),
class_iseq->body->iseq_encoded, GET_SP(),
class_iseq->body->local_size, class_iseq->body->stack_max);
class_iseq->body->local_size,
class_iseq->body->stack_max);
RESTORE_REGS();
NEXT_INSN();
}

Просмотреть файл

@ -725,6 +725,7 @@ void rb_encdb_declare(const char *name);
void rb_enc_set_base(const char *name, const char *orig);
int rb_enc_set_dummy(int index);
void rb_encdb_set_unicode(int index);
int rb_data_is_encoding(VALUE obj);
/* enum.c */
VALUE rb_f_send(int argc, VALUE *argv, VALUE recv);

149
iseq.c
Просмотреть файл

@ -25,9 +25,6 @@
#include "insns.inc"
#include "insns_info.inc"
#define ISEQ_MAJOR_VERSION 2
#define ISEQ_MINOR_VERSION 3
VALUE rb_cISeq;
static VALUE iseqw_new(const rb_iseq_t *iseq);
static const rb_iseq_t *iseqw_check(VALUE iseqw);
@ -71,30 +68,32 @@ rb_iseq_free(const rb_iseq_t *iseq)
RUBY_FREE_ENTER("iseq");
if (iseq) {
ruby_xfree((void *)iseq->body->iseq_encoded);
ruby_xfree((void *)iseq->body->line_info_table);
ruby_xfree((void *)iseq->body->local_table);
ruby_xfree((void *)iseq->body->is_entries);
if (iseq->body) {
ruby_xfree((void *)iseq->body->iseq_encoded);
ruby_xfree((void *)iseq->body->line_info_table);
ruby_xfree((void *)iseq->body->local_table);
ruby_xfree((void *)iseq->body->is_entries);
if (iseq->body->ci_entries) {
unsigned int i;
struct rb_call_info_with_kwarg *ci_kw_entries = (struct rb_call_info_with_kwarg *)&iseq->body->ci_entries[iseq->body->ci_size];
for (i=0; i<iseq->body->ci_kw_size; i++) {
const struct rb_call_info_kw_arg *kw_arg = ci_kw_entries[i].kw_arg;
ruby_xfree((void *)kw_arg);
if (iseq->body->ci_entries) {
unsigned int i;
struct rb_call_info_with_kwarg *ci_kw_entries = (struct rb_call_info_with_kwarg *)&iseq->body->ci_entries[iseq->body->ci_size];
for (i=0; i<iseq->body->ci_kw_size; i++) {
const struct rb_call_info_kw_arg *kw_arg = ci_kw_entries[i].kw_arg;
ruby_xfree((void *)kw_arg);
}
ruby_xfree(iseq->body->ci_entries);
ruby_xfree(iseq->body->cc_entries);
}
ruby_xfree(iseq->body->ci_entries);
ruby_xfree(iseq->body->cc_entries);
}
ruby_xfree((void *)iseq->body->catch_table);
ruby_xfree((void *)iseq->body->param.opt_table);
ruby_xfree((void *)iseq->body->catch_table);
ruby_xfree((void *)iseq->body->param.opt_table);
if (iseq->body->param.keyword != NULL) {
ruby_xfree((void *)iseq->body->param.keyword->default_values);
ruby_xfree((void *)iseq->body->param.keyword);
if (iseq->body->param.keyword != NULL) {
ruby_xfree((void *)iseq->body->param.keyword->default_values);
ruby_xfree((void *)iseq->body->param.keyword);
}
compile_data_free(ISEQ_COMPILE_DATA(iseq));
ruby_xfree(iseq->body);
}
compile_data_free(ISEQ_COMPILE_DATA(iseq));
ruby_xfree(iseq->body);
}
RUBY_FREE_LEAVE("iseq");
}
@ -116,9 +115,11 @@ rb_iseq_mark(const rb_iseq_t *iseq)
RUBY_MARK_UNLESS_NULL(body->location.absolute_path);
}
if (ISEQ_COMPILE_DATA(iseq) != 0) {
if (FL_TEST(iseq, ISEQ_NOT_LOADED_YET)) {
rb_gc_mark(iseq->aux.loader.obj);
}
else if (ISEQ_COMPILE_DATA(iseq) != 0) {
const struct iseq_compile_data *const compile_data = ISEQ_COMPILE_DATA(iseq);
RUBY_MARK_UNLESS_NULL(compile_data->mark_ary);
RUBY_MARK_UNLESS_NULL(compile_data->err_info);
RUBY_MARK_UNLESS_NULL(compile_data->catch_table_ary);
@ -205,7 +206,7 @@ iseq_memsize(const rb_iseq_t *iseq)
static rb_iseq_t *
iseq_alloc(void)
{
rb_iseq_t *iseq = (rb_iseq_t *)rb_imemo_new(imemo_iseq, 0, 0, 0, 0);
rb_iseq_t *iseq = iseq_imemo_alloc();
iseq->body = ZALLOC(struct rb_iseq_constant_body);
return iseq;
}
@ -259,16 +260,6 @@ rb_iseq_add_mark_object(const rb_iseq_t *iseq, VALUE obj)
rb_ary_push(ISEQ_MARK_ARY(iseq), obj);
}
static VALUE
iseq_mark_ary_create(int flip_cnt)
{
VALUE ary = rb_ary_tmp_new(3);
rb_ary_push(ary, Qnil); /* ISEQ_MARK_ARY_COVERAGE */
rb_ary_push(ary, INT2FIX(flip_cnt)); /* ISEQ_MARK_ARY_FLIP_CNT */
rb_ary_push(ary, Qnil); /* ISEQ_MARK_ARY_ORIGINAL_ISEQ */
return ary;
}
static VALUE
prepare_iseq_build(rb_iseq_t *iseq,
VALUE name, VALUE path, VALUE absolute_path, VALUE first_lineno,
@ -485,6 +476,19 @@ rb_iseq_new_with_opt(NODE *node, VALUE name, VALUE path, VALUE absolute_path,
return iseq_translate(iseq);
}
const rb_iseq_t *
rb_iseq_load_iseq(VALUE fname)
{
if (rb_respond_to(rb_cISeq, rb_intern("load_iseq"))) {
VALUE iseqv = rb_funcall(rb_cISeq, rb_intern("load_iseq"), 1, fname);
if (CLASS_OF(iseqv) == rb_cISeq) {
return iseqw_check(iseqv);
}
}
return NULL;
}
#define CHECK_ARRAY(v) rb_convert_type((v), T_ARRAY, "Array", "to_ary")
#define CHECK_HASH(v) rb_convert_type((v), T_HASH, "Hash", "to_hash")
#define CHECK_STRING(v) rb_convert_type((v), T_STRING, "String", "to_str")
@ -583,8 +587,7 @@ static VALUE
iseq_s_load(int argc, VALUE *argv, VALUE self)
{
VALUE data, opt=Qnil;
rb_scan_args(argc, argv, "11", &data, &opt);
rb_scan_args(argc, argv, "01", &opt);
return iseq_load(data, NULL, opt);
}
@ -892,7 +895,11 @@ iseqw_s_compile_option_get(VALUE self)
static const rb_iseq_t *
iseqw_check(VALUE iseqw)
{
const rb_iseq_t *iseq = DATA_PTR(iseqw);
rb_iseq_t *iseq = DATA_PTR(iseqw);
if (!iseq->body) {
ibf_load_iseq_complete(iseq);
}
if (!iseq->body->location.label) {
rb_raise(rb_eTypeError, "uninitialized InstructionSequence");
@ -1235,7 +1242,7 @@ rb_insn_operand_intern(const rb_iseq_t *iseq,
{
const char *types = insn_op_types(insn);
char type = types[op_no];
VALUE ret;
VALUE ret = Qundef;
switch (type) {
case TS_OFFSET: /* LONG */
@ -1281,8 +1288,8 @@ rb_insn_operand_intern(const rb_iseq_t *iseq,
case TS_ISEQ: /* iseq */
{
rb_iseq_t *iseq = (rb_iseq_t *)op;
if (iseq) {
if (op) {
const rb_iseq_t *iseq = rb_iseq_check((rb_iseq_t *)op);
ret = iseq->body->location.label;
if (child) {
rb_ary_push(child, (VALUE)iseq);
@ -1492,7 +1499,7 @@ rb_iseq_disasm(const rb_iseq_t *iseq)
catch_type((int)entry->type), (int)entry->start,
(int)entry->end, (int)entry->sp, (int)entry->cont);
if (entry->iseq) {
rb_str_concat(str, rb_iseq_disasm(entry->iseq));
rb_str_concat(str, rb_iseq_disasm(rb_iseq_check(entry->iseq)));
}
}
}
@ -1561,7 +1568,7 @@ rb_iseq_disasm(const rb_iseq_t *iseq)
for (l = 0; l < RARRAY_LEN(child); l++) {
VALUE isv = rb_ary_entry(child, l);
rb_str_concat(str, rb_iseq_disasm((rb_iseq_t *)isv));
rb_str_concat(str, rb_iseq_disasm(rb_iseq_check((rb_iseq_t *)isv)));
}
return str;
@ -1907,7 +1914,7 @@ iseq_data_to_ary(const rb_iseq_t *iseq)
{
const rb_iseq_t *iseq = (rb_iseq_t *)*seq;
if (iseq) {
VALUE val = iseq_data_to_ary(iseq);
VALUE val = iseq_data_to_ary(rb_iseq_check(iseq));
rb_ary_push(ary, val);
}
else {
@ -2002,7 +2009,7 @@ iseq_data_to_ary(const rb_iseq_t *iseq)
const struct iseq_catch_table_entry *entry = &iseq->body->catch_table->entries[i];
rb_ary_push(ary, exception_type2symbol(entry->type));
if (entry->iseq) {
rb_ary_push(ary, iseq_data_to_ary(entry->iseq));
rb_ary_push(ary, iseq_data_to_ary(rb_iseq_check(entry->iseq)));
}
else {
rb_ary_push(ary, Qnil);
@ -2325,6 +2332,51 @@ rb_iseqw_local_variables(VALUE iseqval)
return rb_iseq_local_variables(iseqw_check(iseqval));
}
/*
* call-seq:
* iseq.to_binary_format(extra_data = nil) -> binary str
*
* Returns serialized iseq binary format data as a String object.
* A correspnding iseq object is created by
* RubyVM::InstructionSequence.from_binary_format() method.
*
* String extra_data will be saved with binary data.
* You can access this data with
* RubyVM::InstructionSequence.from_binary_format_extra_data(binary).
*/
static VALUE
iseqw_to_binary_format(int argc, VALUE *argv, VALUE self)
{
VALUE opt;
rb_scan_args(argc, argv, "01", &opt);
return iseq_ibf_dump(iseqw_check(self), opt);
}
/*
* call-seq:
* RubyVM::InstructionSequence.from_binary_format(binary) -> iseq
*
* Load an iseq object from binary format String object
* created by RubyVM::InstructionSequence.to_binary_format.
*/
static VALUE
iseqw_s_from_binary_format(VALUE self, VALUE str)
{
return iseqw_new(iseq_ibf_load(str));
}
/*
* call-seq:
* RubyVM::InstructionSequence.from_binary_format_extra_data(binary) -> str
*
* Load extra data embed into binary format String object.
*/
static VALUE
iseqw_s_from_binary_format_extra_data(VALUE self, VALUE str)
{
return iseq_ibf_load_extra_data(str);
}
/*
* Document-class: RubyVM::InstructionSequence
*
@ -2356,6 +2408,11 @@ Init_ISeq(void)
rb_define_method(rb_cISeq, "to_a", iseqw_to_a, 0);
rb_define_method(rb_cISeq, "eval", iseqw_eval, 0);
rb_define_method(rb_cISeq, "to_binary_format", iseqw_to_binary_format, -1);
rb_define_singleton_method(rb_cISeq, "from_binary_format", iseqw_s_from_binary_format, 1);
rb_define_singleton_method(rb_cISeq, "from_binary_format_extra_data", iseqw_s_from_binary_format_extra_data, 1);
/* location APIs */
rb_define_method(rb_cISeq, "path", iseqw_path, 0);
rb_define_method(rb_cISeq, "absolute_path", iseqw_absolute_path, 0);

33
iseq.h
Просмотреть файл

@ -12,6 +12,9 @@
#ifndef RUBY_ISEQ_H
#define RUBY_ISEQ_H 1
#define ISEQ_MAJOR_VERSION 2
#define ISEQ_MINOR_VERSION 3
#ifndef rb_iseq_t
typedef struct rb_iseq_struct rb_iseq_t;
#define rb_iseq_t rb_iseq_t
@ -29,16 +32,27 @@ enum iseq_mark_ary_index {
ISEQ_MARK_ARY_ORIGINAL_ISEQ = 2,
};
static inline VALUE
iseq_mark_ary_create(int flip_cnt)
{
VALUE ary = rb_ary_tmp_new(3);
rb_ary_push(ary, Qnil); /* ISEQ_MARK_ARY_COVERAGE */
rb_ary_push(ary, INT2FIX(flip_cnt)); /* ISEQ_MARK_ARY_FLIP_CNT */
rb_ary_push(ary, Qnil); /* ISEQ_MARK_ARY_ORIGINAL_ISEQ */
return ary;
}
#define ISEQ_MARK_ARY(iseq) (iseq)->body->mark_ary
#define ISEQ_COVERAGE(iseq) RARRAY_AREF(ISEQ_MARK_ARY(iseq), ISEQ_MARK_ARY_COVERAGE)
#define ISEQ_COVERAGE_SET(iseq, cov) RARRAY_ASET(ISEQ_MARK_ARY(iseq), ISEQ_MARK_ARY_COVERAGE, cov)
#define ISEQ_FLIP_CNT(iseq) FIX2INT(RARRAY_AREF(ISEQ_MARK_ARY(iseq), ISEQ_MARK_ARY_FLIP_CNT))
static inline int
ISEQ_FLIP_CNT_INCREMENT(const rb_iseq_t *iseq)
{
VALUE cntv = RARRAY_AREF(ISEQ_MARK_ARY(iseq), ISEQ_MARK_ARY_FLIP_CNT);
int cnt = FIX2INT(cntv);
int cnt = ISEQ_FLIP_CNT(iseq);
RARRAY_ASET(ISEQ_MARK_ARY(iseq), ISEQ_MARK_ARY_FLIP_CNT, INT2FIX(cnt+1));
return cnt;
}
@ -59,7 +73,20 @@ ISEQ_ORIGINAL_ISEQ_ALLOC(const rb_iseq_t *iseq, long size)
return (VALUE *)RSTRING_PTR(str);
}
#define ISEQ_COMPILE_DATA(iseq) (iseq)->compile_data_
#define ISEQ_COMPILE_DATA(iseq) (iseq)->aux.compile_data
static inline rb_iseq_t *
iseq_imemo_alloc(void)
{
return (rb_iseq_t *)rb_imemo_new(imemo_iseq, 0, 0, 0, 0);
}
#define ISEQ_NOT_LOADED_YET IMEMO_FL_USER1
VALUE iseq_ibf_dump(const rb_iseq_t *iseq, VALUE opt);
void ibf_load_iseq_complete(rb_iseq_t *iseq);
const rb_iseq_t *iseq_ibf_load(VALUE str);
VALUE iseq_ibf_load_extra_data(VALUE str);
RUBY_SYMBOL_EXPORT_BEGIN

16
load.c
Просмотреть файл

@ -575,6 +575,7 @@ rb_provide(const char *feature)
}
NORETURN(static void load_failed(VALUE));
const rb_iseq_t *rb_iseq_load_iseq(VALUE fname);
static int
rb_load_internal0(rb_thread_t *th, VALUE fname, int wrap)
@ -604,12 +605,17 @@ rb_load_internal0(rb_thread_t *th, VALUE fname, int wrap)
state = EXEC_TAG();
if (state == 0) {
NODE *node;
rb_iseq_t *iseq;
const rb_iseq_t *iseq;
th->mild_compile_error++;
node = (NODE *)rb_load_file_str(fname);
iseq = rb_iseq_new_top(node, rb_str_new2("<top (required)>"), fname, rb_realpath_internal(Qnil, fname, 1), NULL);
th->mild_compile_error--;
if ((iseq = rb_iseq_load_iseq(fname)) != NULL) {
/* OK */
}
else {
th->mild_compile_error++;
node = (NODE *)rb_load_file_str(fname);
iseq = rb_iseq_new_top(node, rb_str_new2("<top (required)>"), fname, rb_realpath_internal(Qnil, fname, 1), NULL);
th->mild_compile_error--;
}
rb_iseq_eval(iseq);
}
TH_POP_TAG();

13
proc.c
Просмотреть файл

@ -984,12 +984,15 @@ rb_proc_get_iseq(VALUE self, int *is_proc)
iseq = rb_method_iseq((VALUE)ifunc->data);
if (is_proc) *is_proc = 0;
}
return iseq;
}
else if (SYMBOL_P(iseq)) {
self = rb_sym_to_proc((VALUE)iseq);
goto again;
}
return iseq;
else {
return rb_iseq_check(iseq);
}
}
static VALUE
@ -998,6 +1001,7 @@ iseq_location(const rb_iseq_t *iseq)
VALUE loc[2];
if (!iseq) return Qnil;
rb_iseq_check(iseq);
loc[0] = iseq->body->location.path;
if (iseq->body->line_info_table) {
loc[1] = rb_iseq_first_lineno(iseq);
@ -1142,7 +1146,7 @@ proc_to_s(VALUE self)
iseq = proc->block.iseq;
is_lambda = proc->is_lambda ? " (lambda)" : "";
if (RUBY_VM_NORMAL_ISEQ_P(iseq)) {
if (RUBY_VM_NORMAL_ISEQ_P(iseq) && rb_iseq_check(iseq)) {
int first_lineno = 0;
if (iseq->body->line_info_table) {
@ -2152,7 +2156,7 @@ rb_method_entry_min_max_arity(const rb_method_entry_t *me, int *max)
case VM_METHOD_TYPE_BMETHOD:
return rb_proc_min_max_arity(def->body.proc, max);
case VM_METHOD_TYPE_ISEQ: {
const rb_iseq_t *iseq = def->body.iseq.iseqptr;
const rb_iseq_t *iseq = rb_iseq_check(def->body.iseq.iseqptr);
return rb_iseq_min_max_arity(iseq, max);
}
case VM_METHOD_TYPE_UNDEF:
@ -2289,7 +2293,7 @@ method_def_iseq(const rb_method_definition_t *def)
{
switch (def->type) {
case VM_METHOD_TYPE_ISEQ:
return def->body.iseq.iseqptr;
return rb_iseq_check(def->body.iseq.iseqptr);
case VM_METHOD_TYPE_BMETHOD:
return get_proc_iseq(def->body.proc, 0);
case VM_METHOD_TYPE_ALIAS:
@ -2654,6 +2658,7 @@ proc_binding(VALUE self)
bind->env = envval;
if (iseq) {
rb_iseq_check(iseq);
bind->path = iseq->body->location.path;
bind->first_lineno = FIX2INT(rb_iseq_first_lineno(iseq));
}

240
sample/iseq_loader.rb Normal file
Просмотреть файл

@ -0,0 +1,240 @@
#
# iseq_loader.rb - sample of compiler/loader for binary compiled file
#
# Usage as a compiler: ruby iseq_loader.rb [file or directory] ...
#
# It compiles and stores specified files.
# If directories are specified, then compiles and stores all *.rb files.
# (using Dir.glob)
#
# TODO: add remove option
# TODO: add verify option
#
# Usage as a loader: simply require this file with the following setting.
#
# Setting with environment variables.
#
# * RUBY_ISEQ_LOADER_STORAGE to select storage type
# * dbm: use dbm
# * fs: [default] use file system. locate a compiled binary files in same
# directory of scripts like Rubinius. foo.rb.yarb will be created for foo.rb.
# * fs2: use file system. locate compiled file in specified directory.
# * nothing: do nothing.
#
# * RUBY_ISEQ_LOADER_STORAGE_DIR to select directory
# * default: ~/.ruby_binaries/
#
# * RUBY_ISEQ_LOADER_STORAGE_COMPILE_IF_NOT_COMPILED
# * true: store compiled file if compiled data is not available.
# * false: [default] do nothing if there is no compiled iseq data.
class RubyVM::InstructionSequence
$ISEQ_LOADER_LOADED = 0
$ISEQ_LOADER_COMPILED = 0
$ISEQ_LOADER_IGNORED = 0
LAUNCHED_TIME = Time.now
COMPILE_FILE_ENABLE = false || true
COMPILE_VERBOSE = $VERBOSE || false # || true
COMPILE_DEBUG = ENV['RUBY_ISEQ_LOADER_DEBUG']
COMPILE_IF_NOT_COMPILED = ENV['RUBY_ISEQ_LOADER_STORAGE_COMPILE_IF_NOT_COMPILED'] == 'true'
at_exit{
STDERR.puts "[ISEQ_LOADER] #{Process.pid} time: #{Time.now - LAUNCHED_TIME}, " +
"loaded: #{$ISEQ_LOADER_LOADED}, " +
"compied: #{$ISEQ_LOADER_COMPILED}, " +
"ignored: #{$ISEQ_LOADER_IGNORED}"
} if COMPILE_VERBOSE
unless cf_dir = ENV['RUBY_ISEQ_LOADER_STORAGE_DIR']
cf_dir = File.expand_path("~/.ruby_binaries")
unless File.exist?(cf_dir)
Dir.mkdir(cf_dir)
end
end
CF_PREFIX = "#{cf_dir}/cb."
class NullStorage
def load_iseq fname; end
def compile_and_save_isq fname; end
def unlink_compiled_iseq; end
end
class BasicStorage
def initialize
require 'digest/sha1'
end
def load_iseq fname
iseq_key = iseq_key_name(fname)
if compiled_iseq_exist?(fname, iseq_key) && compiled_iseq_is_younger?(fname, iseq_key)
$ISEQ_LOADER_LOADED += 1
STDERR.puts "[ISEQ_LOADER] #{Process.pid} load #{fname} from #{iseq_key}" if COMPILE_DEBUG
binary = read_compiled_iseq(fname, iseq_key)
RubyVM::InstructionSequence.from_binary_format(binary)
elsif COMPILE_IF_NOT_COMPILED
compile_and_save_iseq(fname, iseq_key)
else
$ISEQ_LOADER_IGNORED += 1
# p fname
nil
end
end
def extra_data fname
"SHA-1:#{::Digest::SHA1.file(fname).digest}"
end
def compile_and_save_iseq fname, iseq_key = iseq_key_name(fname)
$ISEQ_LOADER_COMPILED += 1
STDERR.puts "[RUBY_COMPILED_FILE] compile #{fname}" if COMPILE_DEBUG
iseq = RubyVM::InstructionSequence.compile_file(fname)
binary = iseq.to_binary_format(extra_data(fname))
write_compiled_iseq(fname, iseq_key, binary)
iseq
end
# def unlink_compiled_iseq; nil; end # should implement at sub classes
private
def iseq_key_name fname
fname
end
# should implement at sub classes
# def compiled_iseq_younger? fname, iseq_key; end
# def compiled_iseq_exist? fname, iseq_key; end
# def read_compiled_file fname, iseq_key; end
# def write_compiled_file fname, iseq_key, binary; end
end
class FSStorage < BasicStorage
def initialize
super
require 'fileutils'
@dir = CF_PREFIX + "files"
unless File.directory?(@dir)
FileUtils.mkdir_p(@dir)
end
end
def unlink_compiled_iseq
File.unlink(compile_file_path)
end
private
def iseq_key_name fname
"#{fname}.yarb" # same directory
end
def compiled_iseq_exist? fname, iseq_key
File.exist?(iseq_key)
end
def compiled_iseq_is_younger? fname, iseq_key
File.mtime(iseq_key) >= File.mtime(fname)
end
def read_compiled_iseq fname, iseq_key
open(iseq_key, 'rb'){|f| f.read}
end
def write_compiled_iseq fname, iseq_key, binary
open(iseq_key, 'wb'){|f| f.write(binary)}
end
end
class FS2Storage < FSStorage
def iseq_key_name fname
@dir + fname.gsub(/[^A-Za-z0-9\._-]/){|c| '%02x' % c.ord} # special directory
end
end
class DBMStorage < BasicStorage
def initialize
require 'dbm'
@db = DBM.open(CF_PREFIX+'db')
end
def unlink_compiled_iseq
@db.delete fname
end
private
def date_key_name fname
"date.#{fname}"
end
def iseq_key_name fname
"body.#{fname}"
end
def compiled_iseq_exist? fname, iseq_key
@db.has_key? iseq_key
end
def compiled_iseq_is_younger? fname, iseq_key
date_key = date_key_name(fname)
if @db.has_key? date_key
@db[date_key].to_i >= File.mtime(fname).to_i
end
end
def read_compiled_iseq fname, iseq_key
@db[iseq_key]
end
def write_compiled_iseq fname, iseq_key, binary
date_key = date_key_name(fname)
@db[iseq_key] = binary
@db[date_key] = Time.now.to_i
end
end
STORAGE = case ENV['RUBY_ISEQ_LOADER_STORAGE']
when 'dbm'
DBMStorage.new
when 'fs'
FSStorage.new
when 'fs2'
FS2Storage.new
when 'null'
NullStorage.new
else
FSStorage.new
end
STDERR.puts "[ISEQ_LOADER] use #{STORAGE.class} " if COMPILE_VERBOSE
def self.load_iseq fname
STORAGE.load_iseq(fname)
end
def self.compile_and_save_iseq fname
STORAGE.compile_and_save_iseq fname
end
def self.unlink_compiled_iseq fname
STORAGE.unlink_compiled_iseq fname
end
end
if __FILE__ == $0
ARGV.each{|path|
if File.directory?(path)
pattern = File.join(path, '**/*.rb')
Dir.glob(pattern){|file|
begin
RubyVM::InstructionSequence.compile_and_save_iseq(file)
rescue SyntaxError => e
STDERR.puts e
end
}
else
RubyVM::InstructionSequence.compile_and_save_iseq(path)
end
}
end

Просмотреть файл

@ -1,5 +1,8 @@
require '-test-/iseq_load/iseq_load'
begin
require '-test-/iseq_load/iseq_load'
rescue LoadError
end
require 'tempfile'
class RubyVM::InstructionSequence
@ -21,9 +24,6 @@ class RubyVM::InstructionSequence
d2 = i2.disasm_if_possible
if d1 != d2
p i1
return
STDERR.puts "expected:"
STDERR.puts d1
STDERR.puts "actual:"
@ -37,19 +37,38 @@ class RubyVM::InstructionSequence
i2
end
CHECK_TO_A = ENV['RUBY_ISEQ_DUMP_DEBUG'] == 'to_a'
CHECK_TO_BINARY = ENV['RUBY_ISEQ_DUMP_DEBUG'] == 'to_binary'
def self.translate i1
# check to_a/load_iseq
i2 = compare_dump_and_load(i1,
proc{|iseq|
ary = iseq.to_a
ary[9] == :top ? ary : nil
},
proc{|ary|
RubyVM::InstructionSequence.iseq_load(ary)
})
i2_ary = compare_dump_and_load(i1,
proc{|iseq|
ary = iseq.to_a
ary[9] == :top ? ary : nil
},
proc{|ary|
RubyVM::InstructionSequence.iseq_load(ary)
}) if CHECK_TO_A && defined?(RubyVM::InstructionSequence.iseq_load)
# check to_binary_format
i2_bin = compare_dump_and_load(i1,
proc{|iseq|
begin
iseq.to_binary_format
rescue RuntimeError => e # not a toplevel
# STDERR.puts [:failed, e, iseq].inspect
nil
end
},
proc{|bin|
iseq = RubyVM::InstructionSequence.from_binary_format(bin)
# STDERR.puts iseq.inspect
iseq
}) if CHECK_TO_BINARY
# return value
i1
end
i2_bin if CHECK_TO_BINARY
end if CHECK_TO_A || CHECK_TO_BINARY
end
#require_relative 'x'; exit(1)

Просмотреть файл

@ -22,7 +22,7 @@ ENV["GEM_SKIP"] = ENV["GEM_HOME"] = ENV["GEM_PATH"] = "".freeze
require_relative 'lib/profile_test_all' if ENV.has_key?('RUBY_TEST_ALL_PROFILE')
require_relative 'lib/tracepointchecker'
require_relative 'lib/zombie_hunter'
# require_relative 'lib/iseq_loader_checker'
require_relative 'lib/iseq_loader_checker'
if ENV['COVERAGE']
%w[doclie simplecov-html simplecov].each do |f|

3
vm.c
Просмотреть файл

@ -945,7 +945,7 @@ invoke_block_from_c_0(rb_thread_t *th, const rb_block_t *block,
return Qnil;
}
else if (LIKELY(RUBY_VM_NORMAL_ISEQ_P(block->iseq))) {
const rb_iseq_t *iseq = block->iseq;
const rb_iseq_t *iseq = rb_iseq_check(block->iseq);
int i, opt_pc;
int type = block_proc_is_lambda(block->proc) ? VM_FRAME_MAGIC_LAMBDA : VM_FRAME_MAGIC_BLOCK;
VALUE *sp = th->cfp->sp;
@ -1816,6 +1816,7 @@ vm_exec(rb_thread_t *th)
if (catch_iseq != NULL) { /* found catch table */
/* enter catch scope */
rb_iseq_check(catch_iseq);
cfp->sp = vm_base_ptr(cfp) + cont_sp;
cfp->pc = cfp->iseq->body->iseq_encoded + cont_pc;

Просмотреть файл

@ -257,10 +257,10 @@ struct rb_call_cache {
#endif
typedef struct rb_iseq_location_struct {
const VALUE path;
const VALUE absolute_path;
const VALUE base_label;
const VALUE label;
VALUE path;
VALUE absolute_path;
VALUE base_label;
VALUE label;
VALUE first_lineno; /* TODO: may be unsigned short */
} rb_iseq_location_t;
@ -376,7 +376,7 @@ struct rb_iseq_constant_body {
*/
struct rb_call_cache *cc_entries; /* size is ci_size = ci_kw_size */
const VALUE mark_ary; /* Array: includes operands which should be GC marked */
VALUE mark_ary; /* Array: includes operands which should be GC marked */
unsigned int local_table_size;
unsigned int is_size;
@ -389,12 +389,40 @@ struct rb_iseq_constant_body {
/* typedef rb_iseq_t is in method.h */
struct rb_iseq_struct {
VALUE flags;
struct iseq_compile_data *compile_data_; /* used at compile time */
struct rb_iseq_constant_body *body;
VALUE reserved1;
VALUE reserved2;
struct rb_iseq_constant_body *body;
union { /* 4, 5 words */
struct iseq_compile_data *compile_data; /* used at compile time */
struct {
VALUE obj;
int index;
} loader;
} aux;
};
#define USE_LAZY_LOAD 0
#ifndef USE_LAZY_LOAD
#define USE_LAZY_LOAD
#endif
#if USE_LAZY_LOAD
const rb_iseq_t *rb_iseq_complete(const rb_iseq_t *iseq);
static inline const rb_iseq_t *
rb_iseq_check(const rb_iseq_t *iseq)
{
if (iseq->body == NULL) {
rb_iseq_complete((rb_iseq_t *)iseq);
}
return iseq;
}
#else
#define rb_iseq_check(iseq) iseq
#endif
enum ruby_special_exceptions {
ruby_error_reenter,
ruby_error_nomemory,
@ -962,7 +990,7 @@ rb_block_t *rb_vm_control_frame_block_ptr(const rb_control_frame_t *cfp);
(!RUBY_VM_VALID_CONTROL_FRAME_P((cfp), RUBY_VM_END_CONTROL_FRAME(th)))
#define RUBY_VM_IFUNC_P(ptr) (RB_TYPE_P((VALUE)(ptr), T_IMEMO) && imemo_type((VALUE)ptr) == imemo_ifunc)
#define RUBY_VM_NORMAL_ISEQ_P(ptr) (RB_TYPE_P((VALUE)(ptr), T_IMEMO) && imemo_type((VALUE)ptr) == imemo_iseq)
#define RUBY_VM_NORMAL_ISEQ_P(ptr) (RB_TYPE_P((VALUE)(ptr), T_IMEMO) && imemo_type((VALUE)ptr) == imemo_iseq && rb_iseq_check((rb_iseq_t *)ptr))
#define RUBY_VM_GET_BLOCK_PTR_IN_CFP(cfp) ((rb_block_t *)(&(cfp)->self))
#define RUBY_VM_GET_CFP_FROM_BLOCK_PTR(b) \

Просмотреть файл

@ -1400,7 +1400,7 @@ def_iseq_ptr(rb_method_definition_t *def)
#if VM_CHECK_MODE > 0
if (def->type != VM_METHOD_TYPE_ISEQ) rb_bug("def_iseq_ptr: not iseq (%d)", def->type);
#endif
return def->body.iseq.iseqptr;
return rb_iseq_check(def->body.iseq.iseqptr);
}
static VALUE
@ -2428,15 +2428,14 @@ static VALUE
vm_invoke_block(rb_thread_t *th, rb_control_frame_t *reg_cfp, struct rb_calling_info *calling, const struct rb_call_info *ci)
{
const rb_block_t *block = VM_CF_BLOCK_PTR(reg_cfp);
const rb_iseq_t *iseq;
VALUE type = GET_ISEQ()->body->local_iseq->body->type;
if ((type != ISEQ_TYPE_METHOD && type != ISEQ_TYPE_CLASS) || block == 0) {
rb_vm_localjump_error("no block given (yield)", Qnil, 0);
}
iseq = block->iseq;
if (RUBY_VM_NORMAL_ISEQ_P(iseq)) {
if (RUBY_VM_NORMAL_ISEQ_P(block->iseq)) {
const rb_iseq_t *iseq = block->iseq;
const int arg_size = iseq->body->param.size;
int is_lambda = block_proc_is_lambda(block->proc);
VALUE * const rsp = GET_SP() - calling->argc;