From: SASADA Koichi Date: 2012-06-15T08:26:35+09:00 Subject: [ruby-dev:45745] Re: [ruby-changes:24028] yugui:r36079 (trunk): Embedding CRuby interpreter without internal headers has been difficult 見逃していました. レビューはコミットされた後で,ってのはなかなか意外. (2012/06/14 11:22), yugui wrote: > yugui 2012-06-14 11:22:08 +0900 (Thu, 14 Jun 2012) > > New Revision: 36079 > > http://svn.ruby-lang.org/cgi-bin/viewvc.cgi?view=rev&revision=36079 > > Log: > Embedding CRuby interpreter without internal headers has been difficult > for few years because: > * NODE is no longer accessible. > * rb_iseq_eval_main crashes without preparing with rb_thread_t. > * some existing APIs calls exit(3) without giving the opportunity to > finalize or handle errors to the client. > * No general-purpose function to compile a source to an iseq are > published in the public headers. > > This commit solves the problems. > > * include/ruby/ruby.h: Grouped APIs for embedding CRuby interpreter. > (ruby_setup, ruby_compile_main_from_file, > ruby_compile_main_from_string, ruby_eval_main, > ruby_set_script_name): new APIs to embed CRuby. > (ruby_opaque_t) Opaque pointer to an internal data, to NODE or iseq > in particular. > > * eval.c (ruby_setup): Similar to ruby_init but returns an error code > instead of exit(3) on error. > (ruby_eval_main): Similar to ruby_exec_node but returns the > evaluation result. > (ruby_eval_main_internal): renamed from ruby_exec_internal. > > * ruby.c (toplevel_context): new helper function. > (PREPARE_EVAL_MAIN): moved. > (process_options): refactored with new functions. > (parse_and_compile_main) new helper funciton. > (ruby_compile_main_from_file, ruby_compile_main_from_string) new API > (ruby_set_script_name): new API. > > Modified files: > trunk/ChangeLog > trunk/eval.c > trunk/include/ruby/intern.h > trunk/include/ruby/ruby.h > trunk/ruby.c  ChangeLog よりもコミットログが長いのは,そういうもんでしょうか.同じ内 容を書いておいた方が良いんじゃないかと. > Index: include/ruby/ruby.h > =================================================================== > --- include/ruby/ruby.h (revision 36078) > +++ include/ruby/ruby.h (revision 36079) ... > +/** > + * @defgroup embed CRuby Embedding APIs > + * CRuby interpreter APIs. These are APIs to embed MRI interpreter into your > + * program. > + * These functions are not a part of Ruby extention library API. > + * Extension libraries of Ruby should not depend on these functions. > + * @{ > + */ > + > +/*! Opaque pointer to an inner data structure. > + * > + * You do not have to know what the actual data type this pointer points. > + * It often changes for internal improvements. > + */ > +typedef void *ruby_opaque_t; そのまま void * じゃ駄目なんでしたっけ? 今,void * と混ざっているよう ですが. > + > +/*! @deprecated You no longer need to use this macro. */ > +#if (defined(__APPLE__) || defined(__NeXT__)) && defined(__MACH__) > +#define RUBY_GLOBAL_SETUP /* use linker option to link startup code with ObjC support */ > +#else > +#define RUBY_GLOBAL_SETUP > +#endif > + > +/** @defgroup ruby1 ruby(1) implementation > + * A part of the implementation of ruby(1) command. > + * Other programs that embed Ruby interpreter do not always need to use these > + * functions. > + * @{ > + */  この defgroup の範囲がわからないのですが,RUBY_INIT_STACK か,ruby_init_stack() はスタックの範囲(マシンスタックの GC mark の範 囲)を決めるのに必須になりますので, do not always ではないんじゃないかと.  構成的には, - 必須な項目 - optional な項目 の順番に並んでいた方がいいかと思います.が,そういうのはヘッダじゃなくて なんか README.embedded みたいなのを書くのが筋だろうか. > +void ruby_sysinit(int *argc, char ***argv); > +void ruby_init(void); > +ruby_opaque_t ruby_options(int argc, char** argv); > +int ruby_executable_node(ruby_opaque_t n, int *status); > +int ruby_run_node(ruby_opaque_t n); > + > +/* version.c */ > +void ruby_show_version(void); > +void ruby_show_copyright(void);  version とかって,これは埋め込みのためなんだろうか. > +/*! A convenience macro to call ruby_init_stack(). Must be placed just after > + * variable declarations */ > +#define RUBY_INIT_STACK \ > + VALUE variable_in_this_stack_frame; \ > + ruby_init_stack(&variable_in_this_stack_frame); > +/*! @} */ > + > +#ifdef __ia64 > +void ruby_init_stack(volatile VALUE*, void*); > +#define ruby_init_stack(addr) ruby_init_stack((addr), rb_ia64_bsp()) > +#else > +void ruby_init_stack(volatile VALUE*); > +#endif > +#define Init_stack(addr) ruby_init_stack(addr) > + > +int ruby_setup(void); > +int ruby_cleanup(volatile int); > + > +void ruby_finalize(void);  ruby_finalize と ruby_cleanup って何が違うんだっけ.... > +NORETURN(void ruby_stop(int)); > + > +void ruby_set_stack_size(size_t);  これはなんでしょう.実装が見つからないようですが. > +int ruby_stack_check(void);  これ,公開する必要あるんだっけ. > +size_t ruby_stack_length(VALUE**);  同上.  そもそも intern.h にあったんですね.うーん,これらを使うシチュエーショ ンってあるでしょうか. > +ruby_opaque_t ruby_compile_main_from_file(VALUE fname, const char* path, VALUE* error); > +ruby_opaque_t ruby_compile_main_from_string(VALUE fname, VALUE string, VALUE* error);  コンパイルって要りますかね.eval だけじゃ駄目なんでしたっけ.つまり, ファイル名,もしくは文字列渡して実行するインターフェースだけだと不十分?  ファイル名を渡すなら,ruby_options でファイル名渡せば main で実行され ますんで,文字列で実行する版だけがあればよい? > +int ruby_exec_node(ruby_opaque_t n); > +int ruby_eval_main(ruby_opaque_t n, VALUE *result);  eval_main というのが適切か自信がありません.eval_in_main_context とか だと冗長でしょうか. > +void ruby_script(const char* name); > +void ruby_set_script_name(VALUE name); > + > +void ruby_prog_init(void); > +void ruby_set_argv(int, char**); > +void *ruby_process_options(int, char**); > +void ruby_init_loadpath(void); > +void ruby_incpush(const char*); > +void ruby_sig_finalize(void);  最後のほうはすみません,よくわかりません. > Index: eval.c > =================================================================== > --- eval.c (revision 36078) > +++ eval.c (revision 36079) ... > +/* Initializes the Ruby VM and builtin libraries. > + * @retval 0 if succeeded. > + * @retval non-zero an error occured. > + */ > +int > +ruby_setup(void) > {  0 が成功ってのは,ほかもありましたっけ. > Index: ruby.c > =================================================================== > --- ruby.c (revision 36078) > +++ ruby.c (revision 36079) > @@ -496,6 +496,26 @@ > th->base_block = prev_base_block; > } > > +static rb_env_t* > +toplevel_context(void) > +{ > + rb_env_t *env; > + VALUE toplevel_binding = rb_const_get(rb_cObject, rb_intern("TOPLEVEL_BINDING")); > + rb_binding_t *bind; > + > + GetBindingPtr(toplevel_binding, bind); > + GetEnvPtr(bind->env, env); > + return env; > +}  context と env は違いますので,この関数名はまずい.context はいろんな 意味で使われるので,指定しない context はわかりづらい,という感じで す.cont.c だと rb_context_t なんてのがあるけど.  toplevel_env() でいいんじゃないかな. -- // SASADA Koichi at atdot dot net