summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog23
-rw-r--r--eval.c93
-rw-r--r--include/ruby/intern.h17
-rw-r--r--include/ruby/ruby.h106
-rw-r--r--ruby.c164
5 files changed, 306 insertions, 97 deletions
diff --git a/ChangeLog b/ChangeLog
index 30c7c4ddd6..6c00c92070 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,26 @@
+Thu Jun 14 10:44:41 2012 Yuki Yugui Sonoda <yugui@google.com>
+
+ * 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@google.com>
* eval.c: Add doxygen comments.
diff --git a/eval.c b/eval.c
index a706c23dab..499edfbbbf 100644
--- a/eval.c
+++ b/eval.c
@@ -31,16 +31,18 @@ VALUE rb_eSysStackError;
#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 @@ ruby_init(void)
}
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 @@ ruby_init(void)
* @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 @@ ruby_cleanup(volatile int ex)
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 @@ ruby_stop(int ex)
* @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 @@ ruby_executable_node(void *n, int *status)
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,10 +304,27 @@ ruby_run_node(void *n)
/*! 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);
}
/*
diff --git a/include/ruby/intern.h b/include/ruby/intern.h
index 54b16961c5..308a1a94db 100644
--- a/include/ruby/intern.h
+++ b/include/ruby/intern.h
@@ -386,9 +386,6 @@ VALUE rb_protect(VALUE (*)(VALUE), VALUE, int*);
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_file_directory_p(VALUE,VALUE);
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 @@ void rb_gc_call_finalizer_at_exit(void);
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 @@ int rb_reg_options(VALUE);
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 @@ void rb_gc_mark_trap_list(void);
#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 @@ VALUE rb_cv_get(VALUE, const char*);
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);
diff --git a/include/ruby/ruby.h b/include/ruby/ruby.h
index 06c2a7fdd4..39996483d9 100644
--- a/include/ruby/ruby.h
+++ b/include/ruby/ruby.h
@@ -1229,21 +1229,6 @@ NORETURN(void rb_throw_obj(VALUE,VALUE));
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 @@ rb_special_const_p(VALUE obj)
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 @@ int ruby_vsnprintf(char *str, size_t n, char const *fmt, va_list ap);
#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 */
diff --git a/ruby.c b/ruby.c
index beaf198612..72fddc4e22 100644
--- a/ruby.c
+++ b/ruby.c
@@ -496,6 +496,26 @@ require_libraries(VALUE *req_list)
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,22 +1385,7 @@ process_options(int argc, char **argv, struct cmdline_options *opt)
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;
-
- 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)
+ env = toplevel_context();
if (opt->e_script) {
VALUE progname = rb_progname;
@@ -1392,11 +1397,11 @@ process_options(int argc, char **argv, struct cmdline_options *opt)
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 @@ process_options(int argc, char **argv, struct cmdline_options *opt)
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 @@ process_options(int argc, char **argv, struct cmdline_options *opt)
}
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 @@ process_options(int argc, char **argv, struct cmdline_options *opt)
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 @@ load_file_internal(VALUE arg)
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 @@ rb_load_file(const char *fname)
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 @@ ruby_script(const char *name)
}
}
+/*! 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)
{