[前][次][番号順一覧][スレッド一覧]

ruby-changes:24028

From: yugui <ko1@a...>
Date: Thu, 14 Jun 2012 11:22:21 +0900 (JST)
Subject: [ruby-changes:24028] yugui:r36079 (trunk): Embedding CRuby interpreter without internal headers has been difficult

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

Index: include/ruby/intern.h
===================================================================
--- include/ruby/intern.h	(revision 36078)
+++ include/ruby/intern.h	(revision 36079)
@@ -386,9 +386,6 @@
 void rb_set_end_proc(void (*)(VALUE), VALUE);
 void rb_mark_end_proc(void);
 void rb_exec_end_proc(void);
-void ruby_finalize(void);
-NORETURN(void ruby_stop(int));
-int ruby_cleanup(volatile int);
 DEPRECATED(void rb_gc_mark_threads(void));
 void rb_thread_schedule(void);
 void rb_thread_wait_fd(int);
@@ -432,10 +429,7 @@
 VALUE rb_str_encode_ospath(VALUE);
 int rb_is_absolute_path(const char *);
 /* gc.c */
-void ruby_set_stack_size(size_t);
 NORETURN(void rb_memerror(void));
-int ruby_stack_check(void);
-size_t ruby_stack_length(VALUE**);
 int rb_during_gc(void);
 void rb_gc_mark_locations(VALUE*, VALUE*);
 void rb_mark_tbl(struct st_table*);
@@ -451,7 +445,6 @@
 VALUE rb_gc_enable(void);
 VALUE rb_gc_disable(void);
 VALUE rb_gc_start(void);
-#define Init_stack(addr) ruby_init_stack(addr)
 void rb_gc_set_params(void);
 /* hash.c */
 void st_foreach_safe(struct st_table *, int (*)(ANYARGS), st_data_t);
@@ -662,12 +655,6 @@
 RUBY_EXTERN VALUE rb_argv0;
 VALUE rb_get_argv(void);
 void *rb_load_file(const char*);
-void ruby_script(const char*);
-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*);
 /* signal.c */
 VALUE rb_f_kill(int, VALUE*);
 void rb_gc_mark_trap_list(void);
@@ -675,7 +662,6 @@
 #define posix_signal ruby_posix_signal
 RETSIGTYPE (*posix_signal(int, RETSIGTYPE (*)(int)))(int);
 #endif
-void ruby_sig_finalize(void);
 void rb_trap_exit(void);
 void rb_trap_exec(void);
 const char *ruby_signal_name(int);
@@ -929,9 +915,6 @@
 void rb_define_class_variable(VALUE, const char*, VALUE);
 VALUE rb_mod_class_variables(VALUE);
 VALUE rb_mod_remove_cvar(VALUE, VALUE);
-/* version.c */
-void ruby_show_version(void);
-void ruby_show_copyright(void);
 
 ID rb_frame_callee(void);
 VALUE rb_str_succ(VALUE);
Index: include/ruby/ruby.h
===================================================================
--- include/ruby/ruby.h	(revision 36078)
+++ include/ruby/ruby.h	(revision 36079)
@@ -1229,21 +1229,6 @@
 
 VALUE rb_require(const char*);
 
-#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 RUBY_INIT_STACK \
-    VALUE variable_in_this_stack_frame; \
-    ruby_init_stack(&variable_in_this_stack_frame);
-void ruby_init(void);
-void *ruby_options(int, char**);
-int ruby_run_node(void *);
-int ruby_exec_node(void *);
-int ruby_executable_node(void *n, int *status);
-
 RUBY_EXTERN VALUE rb_mKernel;
 RUBY_EXTERN VALUE rb_mComparable;
 RUBY_EXTERN VALUE rb_mEnumerable;
@@ -1400,14 +1385,6 @@
 static char *dln_libs_to_be_linked[] = { EXTLIB, 0 };
 #endif
 
-#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
-
-void ruby_sysinit(int *, char ***);
-
 #define RUBY_VM 1 /* YARV */
 #define HAVE_NATIVETHREAD
 int ruby_native_thread_p(void);
@@ -1495,6 +1472,89 @@
 #include "ruby/subst.h"
 #endif
 
+/**
+ * @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;
+
+/*! @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.
+ * @{
+ */
+
+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);
+
+
+/*! 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);
+NORETURN(void ruby_stop(int));
+
+void ruby_set_stack_size(size_t);
+int ruby_stack_check(void);
+size_t ruby_stack_length(VALUE**);
+
+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);
+int ruby_exec_node(ruby_opaque_t n);
+int ruby_eval_main(ruby_opaque_t n, VALUE *result);
+
+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);
+
+/*! @} */
+
 #if defined(__cplusplus)
 #if 0
 { /* satisfy cc-mode */
Index: ChangeLog
===================================================================
--- ChangeLog	(revision 36078)
+++ ChangeLog	(revision 36079)
@@ -1,3 +1,26 @@
+Thu Jun 14 10:44:41 2012  Yuki Yugui Sonoda  <yugui@g...>
+
+	* 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.
+
+
 Thu Jun 14 10:39:48 2012  Yuki Yugui Sonoda  <yugui@g...>
 
 	* eval.c: Add doxygen comments.
Index: eval.c
===================================================================
--- eval.c	(revision 36078)
+++ eval.c	(revision 36079)
@@ -31,16 +31,18 @@
 #include "eval_error.c"
 #include "eval_jump.c"
 
-/* initialize ruby */
-
-void
-ruby_init(void)
+/* Initializes the Ruby VM and builtin libraries.
+ * @retval 0 if succeeded.
+ * @retval non-zero an error occured.
+ */
+int
+ruby_setup(void)
 {
     static int initialized = 0;
     int state;
 
     if (initialized)
-	return;
+	return 0;
     initialized = 1;
 
     ruby_init_stack((void *)&state);
@@ -54,11 +56,22 @@
     }
     POP_TAG();
 
+    if (!state) GET_VM()->running = 1;
+    return state;
+}
+
+/* Calls ruby_setup() and check error.
+ *
+ * Prints errors and calls exit(3) if an error occured.
+ */
+void
+ruby_init(void)
+{
+    int state = ruby_setup();
     if (state) {
 	error_print();
 	exit(EXIT_FAILURE);
     }
-    GET_VM()->running = 1;
 }
 
 /*! Processes command line arguments and compiles the Ruby source to execute.
@@ -71,7 +84,7 @@
  * @return an opaque pointer to the compiled source or an internal special value.
  * @sa ruby_executable_node().
  */
-void *
+ruby_opaque_t
 ruby_options(int argc, char **argv)
 {
     int state;
@@ -217,26 +230,6 @@
     return ex;
 }
 
-static int
-ruby_exec_internal(void *n)
-{
-    volatile int state;
-    VALUE iseq = (VALUE)n;
-    rb_thread_t *th = GET_THREAD();
-
-    if (!n) return 0;
-
-    PUSH_TAG();
-    if ((state = EXEC_TAG()) == 0) {
-	SAVE_ROOT_JMPBUF(th, {
-	    th->base_block = 0;
-	    rb_iseq_eval_main(iseq);
-	});
-    }
-    POP_TAG();
-    return state;
-}
-
 /*! Calls ruby_cleanup() and exits the process */
 void
 ruby_stop(int ex)
@@ -257,7 +250,7 @@
  * @retval 0 if the given value is such a special value.
  */
 int
-ruby_executable_node(void *n, int *status)
+ruby_executable_node(ruby_opaque_t n, int *status)
 {
     VALUE v = (VALUE)n;
     int s;
@@ -273,12 +266,33 @@
     return FALSE;
 }
 
+static int
+ruby_eval_main_internal(VALUE iseqval, VALUE* result)
+{
+    volatile int state;
+    volatile VALUE retval;
+    rb_thread_t *th = GET_THREAD();
+
+    if (!iseqval) return 0;
+
+    PUSH_TAG();
+    if ((state = EXEC_TAG()) == 0) {
+	SAVE_ROOT_JMPBUF(th, {
+	    th->base_block = 0;
+	    retval = rb_iseq_eval_main(iseqval);
+	});
+    }
+    POP_TAG();
+    *result = state ? th->errinfo : retval; 
+    return state;
+}
+
 /*! Runs the given compiled source and exits this process.
  * @retval 0 if successfully run thhe source
  * @retval non-zero if an error occurred.
 */
 int
-ruby_run_node(void *n)
+ruby_run_node(ruby_opaque_t n)
 {
     int status;
     if (!ruby_executable_node(n, &status)) {
@@ -290,12 +304,29 @@
 
 /*! Runs the given compiled source */
 int
-ruby_exec_node(void *n)
+ruby_exec_node(ruby_opaque_t n)
 {
+    VALUE dummy;
     ruby_init_stack((void *)&n);
-    return ruby_exec_internal(n);
+    return ruby_eval_main_internal((VALUE)n, &dummy);
 }
 
+
+/*!
+ * Evaluates the given iseq in the main (toplevel) context.
+ *
+ * @param iseqval a VALUE that wraps an iseq.
+ * @param result Stores the evaluated value if succeeded,
+ *        or an exception if failed.
+ * @retval 0 if succeeded
+ * @retval non-zero if failed.
+ */
+int
+ruby_eval_main(ruby_opaque_t n, VALUE *result)
+{
+    return !!ruby_eval_main_internal((VALUE)n, result);
+}
+
 /*
  *  call-seq:
  *     Module.nesting    -> array
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;
+}
+
+#define PREPARE_PARSE_MAIN(th, env, expr) do { \
+    (th)->parse_in_eval--; \
+    (th)->base_block = &(env)->block; \
+    expr; \
+    (th)->parse_in_eval++; \
+    (th)->base_block = 0; \
+} while (0)
+
 static void
 process_sflag(int *sflag)
 {
@@ -1365,23 +1385,8 @@
     ruby_set_argv(argc, argv);
     process_sflag(&opt->sflag);
 
-    {
-	/* set eval context */
-	VALUE toplevel_binding = rb_const_get(rb_cObject, rb_intern("TOPLEVEL_BINDING"));
-	rb_binding_t *bind;
+    env = toplevel_context();
 
-	GetBindingPtr(toplevel_binding, bind);
-	GetEnvPtr(bind->env, env);
-    }
-
-#define PREPARE_PARSE_MAIN(expr) do { \
-    th->parse_in_eval--; \
-    th->base_block = &env->block; \
-    expr; \
-    th->parse_in_eval++; \
-    th->base_block = 0; \
-} while (0)
-
     if (opt->e_script) {
 	VALUE progname = rb_progname;
 	rb_encoding *eenc;
@@ -1392,11 +1397,11 @@
 	    eenc = lenc;
 	}
 	rb_enc_associate(opt->e_script, eenc);
-	rb_vm_set_progname(rb_progname = opt->script_name);
+        ruby_set_script_name(opt->script_name);
 	require_libraries(&opt->req_list);
-	rb_vm_set_progname(rb_progname = progname);
+        ruby_set_script_name(progname);
 
-	PREPARE_PARSE_MAIN({
+	PREPARE_PARSE_MAIN(th, env, {
 	    tree = rb_parser_compile_string(parser, opt->script, opt->e_script, 1);
 	});
     }
@@ -1405,12 +1410,11 @@
 	    forbid_setid("program input from stdin");
 	}
 
-	PREPARE_PARSE_MAIN({
+	PREPARE_PARSE_MAIN(th, env, {
 	    tree = load_file(parser, opt->script_name, 1, opt);
 	});
     }
-    rb_progname = opt->script_name;
-    rb_vm_set_progname(rb_progname);
+    ruby_set_script_name(opt->script_name);
     if (opt->dump & DUMP_BIT(yydebug)) return Qtrue;
 
     if (opt->ext.enc.index >= 0) {
@@ -1446,12 +1450,12 @@
     }
 
     if (opt->do_print) {
-	PREPARE_PARSE_MAIN({
+	PREPARE_PARSE_MAIN(th, env, {
 	    tree = rb_parser_append_print(parser, tree);
 	});
     }
     if (opt->do_loop) {
-	PREPARE_PARSE_MAIN({
+	PREPARE_PARSE_MAIN(th, env, {
 	    tree = rb_parser_while_loop(parser, tree, opt->do_line, opt->do_split);
 	});
 	rb_define_global_function("sub", rb_f_sub, -1);
@@ -1466,7 +1470,7 @@
 	return Qtrue;
     }
 
-    PREPARE_PARSE_MAIN({
+    PREPARE_PARSE_MAIN(th, env, {
 	VALUE path = Qnil;
 	if (!opt->e_script && strcmp(opt->script, "-"))
 	    path = rb_realpath_internal(Qnil, opt->script_name, 1);
@@ -1622,7 +1626,7 @@
 	    if (f != rb_stdin) rb_io_close(f);
 	    f = Qnil;
 	}
-	rb_vm_set_progname(rb_progname = opt->script_name);
+        ruby_set_script_name(opt->script_name);
 	require_libraries(&opt->req_list);	/* Why here? unnatural */
     }
     if (opt->src.enc.index >= 0) {
@@ -1689,6 +1693,103 @@
     return load_file(rb_parser_new(), fname_v, 0, cmdline_options_init(&opt));
 }
 
+struct ruby_compile_main_arg {
+    int is_string;
+    union {
+        VALUE path;
+        VALUE string;
+    } source;
+};
+
+static ruby_opaque_t
+parse_and_compile_main(VALUE fname, const struct ruby_compile_main_arg* arg, VALUE* error)
+{
+    rb_env_t *const env = toplevel_context();
+    rb_thread_t *const th = GET_THREAD();
+    NODE* tree;
+    VALUE iseq;
+    VALUE path;
+    int state;
+
+    PUSH_TAG();
+    if ((state = EXEC_TAG()) == 0) {
+        PREPARE_PARSE_MAIN(th, env, {
+            VALUE parser = rb_parser_new();
+            th->mild_compile_error++;
+            if (arg->is_string) {
+                FilePathValue(fname);
+                path = fname;
+                tree = rb_parser_compile_string(parser, RSTRING_PTR(fname), arg->source.string, 1);
+            }
+            else {
+                struct cmdline_options opt;
+                path = arg->source.path;
+                tree = load_file(parser, path, 0, cmdline_options_init(&opt));
+            }
+            th->mild_compile_error--;
+        });
+        if (!tree) rb_exc_raise(th->errinfo);
+
+        ruby_set_script_name(fname);
+
+        PREPARE_PARSE_MAIN(th, env, {
+            iseq = rb_iseq_new_main(tree, fname, path);
+        });
+    }
+    POP_TAG();
+    if (state) {
+        *error = th->errinfo;
+        return NULL;
+    } else {
+        *error = Qnil;
+        return iseq;
+    }
+}
+
+/**
+ * Compiles a main Ruby script file into the internal a data structure.
+ *
+ * This function:
+ * @li loads the file specified by path.
+ * @li parses the source and compiles it
+ *
+ * @param fname <code>$0</code> is set to this value. 
+ *              If nil,
+ *              uses the given path instead.
+ * @param path path to the source
+ * @param error where to store the exception if an error occured.
+ * @return The compiled source code. Or NULL if an error occured.
+ */
+ruby_opaque_t
+ruby_compile_main_from_file(VALUE fname, const char* path, VALUE* error)
+{
+    struct ruby_compile_main_arg arg;
+    arg.is_string = FALSE;
+    arg.source.path = rb_str_new_cstr(path);
+
+    if (NIL_P(fname)) fname = arg.source.path;
+    return parse_and_compile_main(fname, &arg, error);
+}
+
+/**
+ * Compiles a main Ruby script in a string into the internal a data structure.
+ *
+ * This function parses the given source and compiles it
+ *
+ * @param fname <code>$0</code> is set to this value. 
+ * @param source Ruby source string
+ * @param error where to store the exception if an error occured.
+ * @return The compiled source code. Or NULL if an error occured.
+ */
+ruby_opaque_t
+ruby_compile_main_from_string(VALUE fname, VALUE source, VALUE* error)
+{
+    struct ruby_compile_main_arg arg;
+    arg.is_string = TRUE;
+    arg.source.string = source;
+    return parse_and_compile_main(fname, &arg, error);
+}
+
 static void
 set_arg0(VALUE val, ID id)
 {
@@ -1720,6 +1821,17 @@
     }
 }
 
+/*! Sets the current script name to this value.
+ *
+ * Same as ruby_script() but accepts a VALUE.
+ */
+void
+ruby_set_script_name(VALUE name)
+{
+    rb_progname = rb_str_dup(name);
+    rb_vm_set_progname(rb_progname);
+}
+
 static void
 init_ids(struct cmdline_options *opt)
 {

--
ML: ruby-changes@q...
Info: http://www.atdot.net/~ko1/quickml/

[前][次][番号順一覧][スレッド一覧]