diff options
Diffstat (limited to 'mrbgems/mruby-bin-mruby')
| -rw-r--r-- | mrbgems/mruby-bin-mruby/bintest/mruby.rb | 158 | ||||
| -rw-r--r-- | mrbgems/mruby-bin-mruby/mrbgem.rake | 6 | ||||
| -rw-r--r-- | mrbgems/mruby-bin-mruby/tools/mruby/mruby.c | 377 |
3 files changed, 403 insertions, 138 deletions
diff --git a/mrbgems/mruby-bin-mruby/bintest/mruby.rb b/mrbgems/mruby-bin-mruby/bintest/mruby.rb index de211c1ba..bc25f18c1 100644 --- a/mrbgems/mruby-bin-mruby/bintest/mruby.rb +++ b/mrbgems/mruby-bin-mruby/bintest/mruby.rb @@ -1,18 +1,26 @@ require 'tempfile' +require 'open3' + +def assert_mruby(exp_out, exp_err, exp_success, args) + out, err, stat = Open3.capture3(cmd("mruby"), *args) + assert "assert_mruby" do + assert_operator(exp_out, :===, out, "standard output") + assert_operator(exp_err, :===, err, "standard error") + assert_equal(exp_success, stat.success?, "exit success?") + end +end assert('regression for #1564') do - o = `bin/mruby -e '<<' 2>&1` - assert_equal o, "-e:1:2: syntax error, unexpected tLSHFT\n" - o = `bin/mruby -e '<<-' 2>&1` - assert_equal o, "-e:1:3: syntax error, unexpected tLSHFT\n" + assert_mruby("", /\A-e:1:2: syntax error, .*\n\z/, false, %w[-e <<]) + assert_mruby("", /\A-e:1:3: syntax error, .*\n\z/, false, %w[-e <<-]) end assert('regression for #1572') do script, bin = Tempfile.new('test.rb'), Tempfile.new('test.mrb') - system "echo 'p \"ok\"' > #{script.path}" - system "bin/mrbc -g -o #{bin.path} #{script.path}" - o = `bin/mruby -b #{bin.path}`.strip - assert_equal o, '"ok"' + File.write script.path, 'p "ok"' + system "#{cmd('mrbc')} -g -o #{bin.path} #{script.path}" + o = `#{cmd('mruby')} #{bin.path}`.strip + assert_equal '"ok"', o end assert '$0 value' do @@ -21,14 +29,19 @@ assert '$0 value' do # .rb script script.write "p $0\n" script.flush - assert_equal "\"#{script.path}\"", `./bin/mruby "#{script.path}"`.chomp + assert_equal "\"#{script.path}\"", `#{cmd('mruby')} "#{script.path}"`.chomp # .mrb file - `./bin/mrbc -o "#{bin.path}" "#{script.path}"` - assert_equal "\"#{bin.path}\"", `./bin/mruby -b "#{bin.path}"`.chomp + `#{cmd('mrbc')} -o "#{bin.path}" "#{script.path}"` + assert_equal "\"#{bin.path}\"", `#{cmd('mruby')} "#{bin.path}"`.chomp # one liner - assert_equal '"-e"', `./bin/mruby -e 'p $0'`.chomp + assert_equal '"-e"', `#{cmd('mruby')} -e #{shellquote('p $0')}`.chomp +end + +assert 'ARGV value' do + assert_mruby(%{["ab", "cde"]\n}, "", true, %w[-e p(ARGV) ab cde]) + assert_mruby("[]\n", "", true, %w[-e p(ARGV)]) end assert '__END__', '8.6' do @@ -42,5 +55,124 @@ __END__ p 'legend' EOS script.flush - assert_equal "\"test\"\n\"fin\"\n", `./bin/mruby #{script.path}` + assert_equal "\"test\"\n\"fin\"\n", `#{cmd('mruby')} #{script.path}` +end + +assert('garbage collecting built-in classes') do + script = Tempfile.new('test.rb') + + script.write <<RUBY +NilClass = nil +GC.start +Array.dup +print nil.class.to_s +RUBY + script.flush + assert_equal "NilClass", `#{cmd('mruby')} #{script.path}` + assert_equal 0, $?.exitstatus +end + +assert('mruby -c option') do + assert_mruby("Syntax OK\n", "", true, ["-c", "-e", "p 1"]) + assert_mruby("", /\A-e:1:7: syntax error, .*\n\z/, false, ["-c", "-e", "p 1; 1."]) +end + +assert('mruby -d option') do + assert_mruby("false\n", "", true, ["-e", "p $DEBUG"]) + assert_mruby("true\n", "", true, ["-dep $DEBUG"]) +end + +assert('mruby -e option (no code specified)') do + assert_mruby("", /\A.*: No code specified for -e\n\z/, false, %w[-e]) +end + +assert('mruby -h option') do + assert_mruby(/\AUsage: #{Regexp.escape cmd("mruby")} .*/m, "", true, %w[-h]) +end + +assert('mruby -r option') do + lib = Tempfile.new('lib.rb') + lib.write <<EOS +class Hoge + def hoge + :hoge + end +end +EOS + lib.flush + + script = Tempfile.new('test.rb') + script.write <<EOS +print Hoge.new.hoge +EOS + script.flush + assert_equal 'hoge', `#{cmd('mruby')} -r #{lib.path} #{script.path}` + assert_equal 0, $?.exitstatus + + assert_equal 'hogeClass', `#{cmd('mruby')} -r #{lib.path} -r #{script.path} -e #{shellquote('print Hoge.class')}` + assert_equal 0, $?.exitstatus +end + +assert('mruby -r option (no library specified)') do + assert_mruby("", /\A.*: No library specified for -r\n\z/, false, %w[-r]) +end + +assert('mruby -r option (file not found)') do + assert_mruby("", /\A.*: Cannot open library file: .*\n\z/, false, %w[-r _no_exists_]) +end + +assert('mruby -v option') do + ver_re = '\Amruby \d+\.\d+\.\d+.* \(\d+-\d+-\d+\)\n' + assert_mruby(/#{ver_re}\z/, "", true, %w[-v]) + assert_mruby(/#{ver_re}^[^\n]*NODE.*\n:end\n\z/m, "", true, %w[-v -e p(:end)]) +end + +assert('mruby --verbose option') do + assert_mruby(/\A[^\n]*NODE.*\n:end\n\z/m, "", true, %w[--verbose -e p(:end)]) +end + +assert('mruby --') do + assert_mruby(%{["-x", "1"]\n}, "", true, %w[-e p(ARGV) -- -x 1]) +end + +assert('mruby invalid short option') do + assert_mruby("", /\A.*: invalid option -1 .*\n\z/, false, %w[-1]) +end + +assert('mruby invalid long option') do + assert_mruby("", /\A.*: invalid option --longopt .*\n\z/, false, %w[--longopt]) +end + +assert('unhandled exception') do + assert_mruby("", /\bEXCEPTION\b.*\n\z/, false, %w[-e raise("EXCEPTION")]) +end + +assert('program file not found') do + assert_mruby("", /\A.*: Cannot open program file: .*\n\z/, false, %w[_no_exists_]) +end + +assert('codegen error') do + code = "def f(#{(1..100).map{|n| "a#{n}"} * ","}); end" + assert_mruby("", /\A.*\n\z/, false, ["-e", code]) +end + +assert('top level local variables are in file scope') do + arb, amrb = Tempfile.new('a.rb'), Tempfile.new('a.mrb') + brb, bmrb = Tempfile.new('b.rb'), Tempfile.new('b.mrb') + crb, cmrb = Tempfile.new('c.rb'), Tempfile.new('c.mrb') + drb, dmrb = Tempfile.new('d.rb'), Tempfile.new('d.mrb') + + File.write arb.path, 'a = 1' + system "#{cmd('mrbc')} -g -o #{amrb.path} #{arb.path}" + File.write brb.path, 'p a' + system "#{cmd('mrbc')} -g -o #{bmrb.path} #{brb.path}" + assert_mruby("", /:1: undefined method 'a' \(NoMethodError\)\n\z/, false, ["-r", arb.path, brb.path]) + assert_mruby("", /:1: undefined method 'a' \(NoMethodError\)\n\z/, false, ["-b", "-r", amrb.path, bmrb.path]) + + File.write crb.path, 'a, b, c = 1, 2, 3; A = -> { b = -2; [a, b, c] }' + system "#{cmd('mrbc')} -g -o #{cmrb.path} #{crb.path}" + File.write drb.path, 'a, b = 5, 6; p A.call; p a, b' + system "#{cmd('mrbc')} -g -o #{dmrb.path} #{drb.path}" + assert_mruby("[1, -2, 3]\n5\n6\n", "", true, ["-r", crb.path, drb.path]) + assert_mruby("[1, -2, 3]\n5\n6\n", "", true, ["-b", "-r", cmrb.path, dmrb.path]) end diff --git a/mrbgems/mruby-bin-mruby/mrbgem.rake b/mrbgems/mruby-bin-mruby/mrbgem.rake index 4e2f6a142..36bf2fa61 100644 --- a/mrbgems/mruby-bin-mruby/mrbgem.rake +++ b/mrbgems/mruby-bin-mruby/mrbgem.rake @@ -3,4 +3,10 @@ MRuby::Gem::Specification.new('mruby-bin-mruby') do |spec| spec.author = 'mruby developers' spec.summary = 'mruby command' spec.bins = %w(mruby) + spec.add_dependency('mruby-compiler', :core => 'mruby-compiler') + spec.add_test_dependency('mruby-print', :core => 'mruby-print') + + if build.cxx_exception_enabled? + build.compile_as_cxx("#{spec.dir}/tools/mruby/mruby.c") + end end diff --git a/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c b/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c index 73617b162..b189aca69 100644 --- a/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c +++ b/mrbgems/mruby-bin-mruby/tools/mruby/mruby.c @@ -1,36 +1,43 @@ -#include <stdio.h> +#include <mruby.h> + +#ifdef MRB_NO_STDIO +# error mruby-bin-mruby conflicts 'MRB_NO_STDIO' in your build configuration +#endif + #include <stdlib.h> #include <string.h> -#include "mruby.h" -#include "mruby/array.h" -#include "mruby/compile.h" -#include "mruby/dump.h" -#include "mruby/variable.h" +#include <mruby/array.h> +#include <mruby/compile.h> +#include <mruby/dump.h> +#include <mruby/variable.h> +#include <mruby/proc.h> -#ifndef ENABLE_STDIO -static void -p(mrb_state *mrb, mrb_value obj) -{ - obj = mrb_funcall(mrb, obj, "inspect", 0); - fwrite(RSTRING_PTR(obj), RSTRING_LEN(obj), 1, stdout); - putc('\n', stdout); -} -#else -#define p(mrb,obj) mrb_p(mrb,obj) +#if defined(_WIN32) || defined(_WIN64) +# include <io.h> /* for setmode */ +# include <fcntl.h> #endif -void mrb_show_version(mrb_state *); -void mrb_show_copyright(mrb_state *); - struct _args { FILE *rfp; - char* cmdline; + char *cmdline; mrb_bool fname : 1; mrb_bool mrbfile : 1; mrb_bool check_syntax : 1; mrb_bool verbose : 1; + mrb_bool version : 1; + mrb_bool debug : 1; int argc; - char** argv; + char **argv; + int libc; + char **libv; +}; + +struct options { + int argc; + char **argv; + char *program; + char *opt; + char short_opt[2]; }; static void @@ -40,7 +47,9 @@ usage(const char *name) "switches:", "-b load and execute RiteBinary (mrb) file", "-c check syntax only", + "-d set debugging flags (set $DEBUG to true)", "-e 'command' one line of script", + "-r library load the library before executing your script", "-v print version number, then run in verbose mode", "--verbose run in verbose mode", "--version print the version", @@ -49,53 +58,101 @@ usage(const char *name) }; const char *const *p = usage_msg; - printf("Usage: %s [switches] programfile\n", name); + printf("Usage: %s [switches] [programfile] [arguments]\n", name); while (*p) printf(" %s\n", *p++); } -static int -parse_args(mrb_state *mrb, int argc, char **argv, struct _args *args) +static void +options_init(struct options *opts, int argc, char **argv) { - char **origargv = argv; - static const struct _args args_zero = { 0 }; + opts->argc = argc; + opts->argv = argv; + opts->program = *argv; + *opts->short_opt = 0; +} - *args = args_zero; +static const char * +options_opt(struct options *opts) +{ + /* concatenated short options (e.g. `-cv`) */ + if (*opts->short_opt && *++opts->opt) { + short_opt: + opts->short_opt[0] = *opts->opt; + opts->short_opt[1] = 0; + return opts->short_opt; + } - for (argc--,argv++; argc > 0; argc--,argv++) { - char *item; - if (argv[0][0] != '-') break; + while (++opts->argv, --opts->argc) { + opts->opt = *opts->argv; - if (strlen(*argv) <= 1) { - argc--; argv++; - args->rfp = stdin; - break; + /* empty || not start with `-` || `-` */ + if (!opts->opt[0] || opts->opt[0] != '-' || !opts->opt[1]) return NULL; + + if (opts->opt[1] == '-') { + /* `--` */ + if (!opts->opt[2]) { + ++opts->argv, --opts->argc; + return NULL; + } + /* long option */ + opts->opt += 2; + *opts->short_opt = 0; + return opts->opt; } + else { + /* short option */ + ++opts->opt; + goto short_opt; + } + } + return NULL; +} + +static const char * +options_arg(struct options *opts) +{ + if (*opts->short_opt && opts->opt[1]) { + /* concatenated short option and option argument (e.g. `-rLIBRARY`) */ + *opts->short_opt = 0; + return opts->opt + 1; + } + --opts->argc, ++opts->argv; + return opts->argc ? *opts->argv : NULL; +} + +static char * +dup_arg_item(mrb_state *mrb, const char *item) +{ + size_t buflen = strlen(item) + 1; + char *buf = (char*)mrb_malloc(mrb, buflen); + memcpy(buf, item, buflen); + return buf; +} + +static int +parse_args(mrb_state *mrb, int argc, char **argv, struct _args *args) +{ + static const struct _args args_zero = { 0 }; + struct options opts[1]; + const char *opt, *item; - item = argv[0] + 1; - switch (*item++) { - case 'b': + *args = args_zero; + options_init(opts, argc, argv); + while ((opt = options_opt(opts))) { + if (strcmp(opt, "b") == 0) { args->mrbfile = TRUE; - break; - case 'c': + } + else if (strcmp(opt, "c") == 0) { args->check_syntax = TRUE; - break; - case 'e': - if (item[0]) { - goto append_cmdline; - } - else if (argc > 1) { - argc--; argv++; - item = argv[0]; -append_cmdline: + } + else if (strcmp(opt, "d") == 0) { + args->debug = TRUE; + } + else if (strcmp(opt, "e") == 0) { + if ((item = options_arg(opts))) { if (!args->cmdline) { - size_t buflen; - char *buf; - - buflen = strlen(item) + 1; - buf = (char *)mrb_malloc(mrb, buflen); - memcpy(buf, item, buflen); - args->cmdline = buf; + args->cmdline = dup_arg_item(mrb, item); } else { size_t cmdlinelen; @@ -110,38 +167,65 @@ append_cmdline: } } else { - printf("%s: No code specified for -e\n", *origargv); - return EXIT_SUCCESS; + fprintf(stderr, "%s: No code specified for -e\n", opts->program); + return EXIT_FAILURE; } - break; - case 'v': - if (!args->verbose) mrb_show_version(mrb); - args->verbose = TRUE; - break; - case '-': - if (strcmp((*argv) + 2, "version") == 0) { - mrb_show_version(mrb); - exit(EXIT_SUCCESS); + } + else if (strcmp(opt, "h") == 0) { + usage(opts->program); + exit(EXIT_SUCCESS); + } + else if (strcmp(opt, "r") == 0) { + if ((item = options_arg(opts))) { + if (args->libc == 0) { + args->libv = (char**)mrb_malloc(mrb, sizeof(char*)); + } + else { + args->libv = (char**)mrb_realloc(mrb, args->libv, sizeof(char*) * (args->libc + 1)); + } + args->libv[args->libc++] = dup_arg_item(mrb, item); } - else if (strcmp((*argv) + 2, "verbose") == 0) { - args->verbose = TRUE; - break; + else { + fprintf(stderr, "%s: No library specified for -r\n", opts->program); + return EXIT_FAILURE; } - else if (strcmp((*argv) + 2, "copyright") == 0) { - mrb_show_copyright(mrb); - exit(EXIT_SUCCESS); + } + else if (strcmp(opt, "v") == 0) { + if (!args->verbose) { + mrb_show_version(mrb); + args->version = TRUE; } - default: + args->verbose = TRUE; + } + else if (strcmp(opt, "version") == 0) { + mrb_show_version(mrb); + exit(EXIT_SUCCESS); + } + else if (strcmp(opt, "verbose") == 0) { + args->verbose = TRUE; + } + else if (strcmp(opt, "copyright") == 0) { + mrb_show_copyright(mrb); + exit(EXIT_SUCCESS); + } + else { + fprintf(stderr, "%s: invalid option %s%s (-h will show valid options)\n", + opts->program, opt[1] ? "--" : "-", opt); return EXIT_FAILURE; } } - if (args->rfp == NULL && args->cmdline == NULL) { - if (*argv == NULL) args->rfp = stdin; + argc = opts->argc; argv = opts->argv; + if (args->cmdline == NULL) { + if (*argv == NULL) { + if (args->version) exit(EXIT_SUCCESS); + args->rfp = stdin; + } else { - args->rfp = fopen(argv[0], args->mrbfile ? "rb" : "r"); + args->rfp = strcmp(argv[0], "-") == 0 ? + stdin : fopen(argv[0], "rb"); if (args->rfp == NULL) { - printf("%s: Cannot open program file. (%s)\n", *origargv, *argv); + fprintf(stderr, "%s: Cannot open program file: %s\n", opts->program, argv[0]); return EXIT_FAILURE; } args->fname = TRUE; @@ -149,6 +233,11 @@ append_cmdline: argc--; argv++; } } +#if defined(_WIN32) || defined(_WIN64) + if (args->rfp == stdin) { + _setmode(_fileno(stdin), O_BINARY); + } +#endif args->argv = (char **)mrb_realloc(mrb, args->argv, sizeof(char*) * (argc + 1)); memcpy(args->argv, argv, (argc+1) * sizeof(char*)); args->argc = argc; @@ -161,10 +250,15 @@ cleanup(mrb_state *mrb, struct _args *args) { if (args->rfp && args->rfp != stdin) fclose(args->rfp); - if (args->cmdline && !args->fname) + if (!args->fname) mrb_free(mrb, args->cmdline); - if (args->argv) - mrb_free(mrb, args->argv); + mrb_free(mrb, args->argv); + if (args->libc) { + while (args->libc--) { + mrb_free(mrb, args->libv[args->libc]); + } + mrb_free(mrb, args->libv); + } mrb_close(mrb); } @@ -178,67 +272,100 @@ main(int argc, char **argv) mrb_value ARGV; mrbc_context *c; mrb_value v; - mrb_sym zero_sym; if (mrb == NULL) { - fputs("Invalid mrb_state, exiting mruby\n", stderr); + fprintf(stderr, "%s: Invalid mrb_state, exiting mruby\n", *argv); return EXIT_FAILURE; } n = parse_args(mrb, argc, argv, &args); if (n == EXIT_FAILURE || (args.cmdline == NULL && args.rfp == NULL)) { cleanup(mrb, &args); - usage(argv[0]); return n; } + else { + int ai = mrb_gc_arena_save(mrb); + ARGV = mrb_ary_new_capa(mrb, args.argc); + for (i = 0; i < args.argc; i++) { + char* utf8 = mrb_utf8_from_locale(args.argv[i], -1); + if (utf8) { + mrb_ary_push(mrb, ARGV, mrb_str_new_cstr(mrb, utf8)); + mrb_utf8_free(utf8); + } + } + mrb_define_global_const(mrb, "ARGV", ARGV); + mrb_gv_set(mrb, mrb_intern_lit(mrb, "$DEBUG"), mrb_bool_value(args.debug)); - ARGV = mrb_ary_new_capa(mrb, args.argc); - for (i = 0; i < args.argc; i++) { - mrb_ary_push(mrb, ARGV, mrb_str_new_cstr(mrb, args.argv[i])); - } - mrb_define_global_const(mrb, "ARGV", ARGV); - - c = mrbc_context_new(mrb); - if (args.verbose) - c->dump_result = TRUE; - if (args.check_syntax) - c->no_exec = TRUE; - - /* Set $0 */ - zero_sym = mrb_intern_lit(mrb, "$0"); - if (args.rfp) { - char *cmdline; - cmdline = args.cmdline ? args.cmdline : "-"; + c = mrbc_context_new(mrb); + if (args.verbose) + c->dump_result = TRUE; + if (args.check_syntax) + c->no_exec = TRUE; + + /* Set $0 */ + const char *cmdline; + if (args.rfp) { + cmdline = args.cmdline ? args.cmdline : "-"; + } + else { + cmdline = "-e"; + } + mrb_gv_set(mrb, mrb_intern_lit(mrb, "$0"), mrb_str_new_cstr(mrb, cmdline)); + + /* Load libraries */ + for (i = 0; i < args.libc; i++) { + struct REnv *e; + FILE *lfp = fopen(args.libv[i], "rb"); + if (lfp == NULL) { + fprintf(stderr, "%s: Cannot open library file: %s\n", *argv, args.libv[i]); + mrbc_context_free(mrb, c); + cleanup(mrb, &args); + return EXIT_FAILURE; + } + mrbc_filename(mrb, c, args.libv[i]); + if (args.mrbfile) { + v = mrb_load_irep_file_cxt(mrb, lfp, c); + } + else { + v = mrb_load_detect_file_cxt(mrb, lfp, c); + } + fclose(lfp); + e = mrb_vm_ci_env(mrb->c->cibase); + mrb_vm_ci_env_set(mrb->c->cibase, NULL); + mrb_env_unshare(mrb, e); + mrbc_cleanup_local_variables(mrb, c); + } + + /* set program file name */ mrbc_filename(mrb, c, cmdline); - mrb_gv_set(mrb, zero_sym, mrb_str_new_cstr(mrb, cmdline)); - } - else { - mrbc_filename(mrb, c, "-e"); - mrb_gv_set(mrb, zero_sym, mrb_str_new_lit(mrb, "-e")); - } - /* Load program */ - if (args.mrbfile) { - v = mrb_load_irep_file_cxt(mrb, args.rfp, c); - } - else if (args.rfp) { - v = mrb_load_file_cxt(mrb, args.rfp, c); - } - else { - v = mrb_load_string_cxt(mrb, args.cmdline, c); - } + /* Load program */ + if (args.mrbfile) { + v = mrb_load_irep_file_cxt(mrb, args.rfp, c); + } + else if (args.rfp) { + v = mrb_load_detect_file_cxt(mrb, args.rfp, c); + } + else { + char* utf8 = mrb_utf8_from_locale(args.cmdline, -1); + if (!utf8) abort(); + v = mrb_load_string_cxt(mrb, utf8, c); + mrb_utf8_free(utf8); + } - mrbc_context_free(mrb, c); - if (mrb->exc) { - if (!mrb_undef_p(v)) { - mrb_print_error(mrb); + mrb_gc_arena_restore(mrb, ai); + mrbc_context_free(mrb, c); + if (mrb->exc) { + if (!mrb_undef_p(v)) { + mrb_print_error(mrb); + } + n = EXIT_FAILURE; + } + else if (args.check_syntax) { + puts("Syntax OK"); } - n = -1; - } - else if (args.check_syntax) { - printf("Syntax OK\n"); } cleanup(mrb, &args); - return n == 0 ? EXIT_SUCCESS : EXIT_FAILURE; + return n; } |
