2012/6/15 SASADA Koichi <ko1 / atdot.net>:
> 見逃していました.
> レビューはコミットされた後で,ってのはなかなか意外.
>
> (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 * と混ざっているよう
> ですが.

何で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 ではないんじゃないかと.

そのグループはruby_init_stackの直前で終わってます。

>
>  構成的には,
>
> - 必須な項目
> - 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 とかって,これは埋め込みのためなんだろうか.

Matzのcopyrightを表示したいというニーズはどこかにあるんじゃないですかね。
少なくともextのためのものではないので。

extのためのものでないAPIを一まとめにしてみました。

>
>> +/*! 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 にあったんですね.うーん,これらを使うシチュエーショ
> ンってあるでしょうか.

ですです。使う必要が無いなら隠しても良いですが、互換性への配慮は必要ですね。
とりあえずdeprecated attributeでも付けてみますか?

>
>> +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 が成功ってのは,ほかもありましたっけ.

この辺の関数は全部exit(3)に渡す前提なので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() でいいんじゃないかな.

I agree


>
> --
> // SASADA Koichi at atdot dot net
>



-- 
Yuki Sonoda (Yugui)
yugui / yugui.jp
http://yugui.jp