From c9a66d4c41c7d645f0fc19cd8f5660584a3a948e Mon Sep 17 00:00:00 2001 From: Rob Fors Date: Tue, 20 Nov 2018 21:27:03 -0500 Subject: Update documentation to mrb_load_irep --- include/mruby/irep.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/mruby/irep.h b/include/mruby/irep.h index 78cbc2b74..027a294d5 100644 --- a/include/mruby/irep.h +++ b/include/mruby/irep.h @@ -48,8 +48,13 @@ typedef struct mrb_irep { #define MRB_ISEQ_NO_FREE 1 MRB_API mrb_irep *mrb_add_irep(mrb_state *mrb); + +/* @param [const uint8_t*] irep code, expected as a literal */ MRB_API mrb_value mrb_load_irep(mrb_state*, const uint8_t*); + +/* @param [const uint8_t*] irep code, expected as a literal */ MRB_API mrb_value mrb_load_irep_cxt(mrb_state*, const uint8_t*, mrbc_context*); + void mrb_irep_free(mrb_state*, struct mrb_irep*); void mrb_irep_incref(mrb_state*, struct mrb_irep*); void mrb_irep_decref(mrb_state*, struct mrb_irep*); -- cgit v1.2.3 From 91bf55bbe5c837531f5785309eb0983e7ce78633 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Wed, 21 Nov 2018 12:07:17 +0900 Subject: Remove `do { ... } while(0)` hacks from `MRB_TRY` macros. Because it can swallow `break` etc. if they are used in loops. --- include/mruby/throw.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/mruby/throw.h b/include/mruby/throw.h index 5d3d214e7..4a1fd8d60 100644 --- a/include/mruby/throw.h +++ b/include/mruby/throw.h @@ -15,9 +15,9 @@ #if defined(MRB_ENABLE_CXX_EXCEPTION) && defined(__cplusplus) -#define MRB_TRY(buf) do { try { +#define MRB_TRY(buf) try { #define MRB_CATCH(buf) } catch(mrb_jmpbuf_impl e) { if (e != (buf)->impl) { throw e; } -#define MRB_END_EXC(buf) } } while(0) +#define MRB_END_EXC(buf) } #define MRB_THROW(buf) throw((buf)->impl) typedef mrb_int mrb_jmpbuf_impl; @@ -34,9 +34,9 @@ typedef mrb_int mrb_jmpbuf_impl; #define MRB_LONGJMP longjmp #endif -#define MRB_TRY(buf) do { if (MRB_SETJMP((buf)->impl) == 0) { +#define MRB_TRY(buf) if (MRB_SETJMP((buf)->impl) == 0) { #define MRB_CATCH(buf) } else { -#define MRB_END_EXC(buf) } } while(0) +#define MRB_END_EXC(buf) } #define MRB_THROW(buf) MRB_LONGJMP((buf)->impl, 1); #define mrb_jmpbuf_impl jmp_buf -- cgit v1.2.3 From e2e6554b569648bca8bf26ffcb951737e07c6888 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Wed, 21 Nov 2018 12:08:51 +0900 Subject: Protect from exceptions raised outside of `mrb_vm_run()`. It can happen if signals are used (e.g. from `mruby-alarm` gem). --- mrbgems/mruby-bin-mirb/tools/mirb/mirb.c | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c b/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c index 19f533acd..8d7c719d8 100644 --- a/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c +++ b/mrbgems/mruby-bin-mirb/tools/mirb/mirb.c @@ -56,6 +56,7 @@ #include #include #include +#include #ifdef ENABLE_READLINE @@ -491,7 +492,10 @@ main(int argc, char **argv) while (TRUE) { char *utf8; + struct mrb_jmpbuf c_jmp; + MRB_TRY(&c_jmp); + mrb->jmp = &c_jmp; if (args.rfp) { if (fgets(last_code_line, sizeof(last_code_line)-1, args.rfp) != NULL) goto done; @@ -555,8 +559,7 @@ main(int argc, char **argv) MIRB_LINE_FREE(line); #endif -done: - + done: if (code_block_open) { if (strlen(ruby_code)+strlen(last_code_line) > sizeof(ruby_code)-1) { fputs("concatenated input string too long\n", stderr); @@ -648,6 +651,11 @@ done: } mrb_parser_free(parser); cxt->lineno++; + MRB_CATCH(&c_jmp) { + p(mrb, mrb_obj_value(mrb->exc), 0); + mrb->exc = 0; + } + MRB_END_EXC(&c_jmp); } #ifdef ENABLE_READLINE -- cgit v1.2.3 From 7ed9bb3bd361030c3fbeeaf72186e61c8e6563f0 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Sun, 25 Nov 2018 06:03:30 +0900 Subject: Fix wrong number of arguments in `Array#fetch`; fix #4170 --- mrbgems/mruby-array-ext/mrblib/array.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mrbgems/mruby-array-ext/mrblib/array.rb b/mrbgems/mruby-array-ext/mrblib/array.rb index bb95d70c5..6096696cb 100644 --- a/mrbgems/mruby-array-ext/mrblib/array.rb +++ b/mrbgems/mruby-array-ext/mrblib/array.rb @@ -291,7 +291,7 @@ class Array # #=> "100 is out of bounds" # - def fetch(n=nil, ifnone=NONE, &block) + def fetch(n, ifnone=NONE, &block) warn "block supersedes default value argument" if !n.nil? && ifnone != NONE && block idx = n -- cgit v1.2.3 From e6b72b2121b981c504f902723b3c1531be001c02 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Sun, 25 Nov 2018 07:30:32 +0900 Subject: Remove redundant rules from `parse.y`. --- mrbgems/mruby-compiler/core/parse.y | 36 ++++++++---------------------------- 1 file changed, 8 insertions(+), 28 deletions(-) diff --git a/mrbgems/mruby-compiler/core/parse.y b/mrbgems/mruby-compiler/core/parse.y index 70b9d0fc4..1485fb086 100644 --- a/mrbgems/mruby-compiler/core/parse.y +++ b/mrbgems/mruby-compiler/core/parse.y @@ -1171,7 +1171,7 @@ heredoc_end(parser_state *p) %type command_args aref_args opt_block_arg block_arg var_ref var_lhs %type command_asgn command_rhs mrhs superclass block_call block_command %type f_block_optarg f_block_opt -%type f_arglist f_args f_arg f_arg_item f_optarg f_marg f_marg_list f_margs +%type f_arglist f_args f_arg f_arg_item f_optarg f_margs %type assoc_list assocs assoc undef_list backref for_var %type block_param opt_block_param block_param_def f_opt %type bv_decls opt_bv_decl bvar f_larglist lambda_body @@ -2447,44 +2447,24 @@ for_var : lhs | mlhs ; -f_marg : f_norm_arg - { - $$ = new_arg(p, $1); - } - | tLPAREN f_margs rparen - { - $$ = new_masgn(p, $2, 0); - } - ; - -f_marg_list : f_marg - { - $$ = list1($1); - } - | f_marg_list ',' f_marg - { - $$ = push($1, $3); - } - ; - -f_margs : f_marg_list +f_margs : f_arg { $$ = list3($1,0,0); } - | f_marg_list ',' tSTAR f_norm_arg + | f_arg ',' tSTAR f_norm_arg { $$ = list3($1, new_arg(p, $4), 0); } - | f_marg_list ',' tSTAR f_norm_arg ',' f_marg_list + | f_arg ',' tSTAR f_norm_arg ',' f_arg { $$ = list3($1, new_arg(p, $4), $6); } - | f_marg_list ',' tSTAR + | f_arg ',' tSTAR { local_add_f(p, 0); $$ = list3($1, (node*)-1, 0); } - | f_marg_list ',' tSTAR ',' f_marg_list + | f_arg ',' tSTAR ',' f_arg { $$ = list3($1, (node*)-1, $5); } @@ -2492,7 +2472,7 @@ f_margs : f_marg_list { $$ = list3(0, new_arg(p, $2), 0); } - | tSTAR f_norm_arg ',' f_marg_list + | tSTAR f_norm_arg ',' f_arg { $$ = list3(0, new_arg(p, $2), $4); } @@ -2505,7 +2485,7 @@ f_margs : f_marg_list { local_add_f(p, 0); } - f_marg_list + f_arg { $$ = list3(0, (node*)-1, $4); } -- cgit v1.2.3 From 762f682b80460929d9c69b1957bcb2aad108ec93 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Sun, 25 Nov 2018 09:01:14 +0900 Subject: Allow destructuring in formal arguments. e.g. ``` def m(a,(b,c),d); p [a,b,c,d]; end m(1,[2,3],4) # => [1,2,3,4] ``` mruby limitation: Destructured arguments (`b` and `c` in above example) cannot be accessed from the default expression of optional arguments and keyword arguments, since actual assignment is done after the evaluation of those default expressions. Thus: ``` def f(a,(b,c),d=b) p [a,b,c,d] end f(1,[2,3]) ``` raises `NoMethodError` for `b` in mruby. --- mrbgems/mruby-compiler/core/codegen.c | 40 +++++++++++++++++++++++++++++------ mrbgems/mruby-compiler/core/parse.y | 31 +++++++++++++++++++++++++-- 2 files changed, 63 insertions(+), 8 deletions(-) diff --git a/mrbgems/mruby-compiler/core/codegen.c b/mrbgems/mruby-compiler/core/codegen.c index ba7d8cf63..b3659863b 100644 --- a/mrbgems/mruby-compiler/core/codegen.c +++ b/mrbgems/mruby-compiler/core/codegen.c @@ -734,15 +734,13 @@ lambda_body(codegen_scope *s, node *tree, int blk) mrb_aspec a; int ma, oa, ra, pa, ka, kd, ba; int pos, i; - node *n, *opt; + node *opt; + node *margs, *pargs; node *tail; /* mandatory arguments */ ma = node_len(tree->car->car); - n = tree->car->car; - while (n) { - n = n->cdr; - } + margs = tree->car->car; tail = tree->car->cdr->cdr->cdr->cdr; /* optional arguments */ @@ -751,6 +749,7 @@ lambda_body(codegen_scope *s, node *tree, int blk) ra = tree->car->cdr->cdr->car ? 1 : 0; /* mandatory arugments after rest argument */ pa = node_len(tree->car->cdr->cdr->cdr->car); + pargs = tree->car->cdr->cdr->cdr->car; /* keyword arguments */ ka = tail? node_len(tail->cdr->car) : 0; /* keyword dictionary? */ @@ -798,6 +797,7 @@ lambda_body(codegen_scope *s, node *tree, int blk) dispatch(s, pos+i*3+1); } + /* keyword arguments */ if (tail) { node *kwds = tail->cdr->car; int kwrest = 0; @@ -836,7 +836,34 @@ lambda_body(codegen_scope *s, node *tree, int blk) genop_0(s, OP_KEYEND); } } + + /* argument destructuring */ + if (margs) { + node *n = margs; + + pos = 1; + while (n) { + if (nint(n->car->car) == NODE_MASGN) { + gen_vmassignment(s, n->car->cdr->car, pos, NOVAL); + } + pos++; + n = n->cdr; + } + } + if (pargs) { + node *n = margs; + + pos = ma+oa+ra+1; + while (n) { + if (nint(n->car->car) == NODE_MASGN) { + gen_vmassignment(s, n->car->cdr->car, pos, NOVAL); + } + pos++; + n = n->cdr; + } + } } + codegen(s, tree->cdr->car, VAL); pop(); if (s->pc > 0) { @@ -1066,6 +1093,7 @@ gen_assignment(codegen_scope *s, node *tree, int sp, int val) idx = new_sym(s, nsym(tree)); genop_2(s, OP_SETGV, sp, idx); break; + case NODE_ARG: case NODE_LVAR: idx = lv_idx(s, nsym(tree)); if (idx > 0) { @@ -1173,7 +1201,7 @@ gen_vmassignment(codegen_scope *s, node *tree, int rhs, int val) pop_n(post+1); genop_3(s, OP_APOST, cursp(), n, post); n = 1; - if (t->car) { /* rest */ + if (t->car && t->car != (node*)-1) { /* rest */ gen_assignment(s, t->car, cursp(), NOVAL); } if (t->cdr && t->cdr->car) { diff --git a/mrbgems/mruby-compiler/core/parse.y b/mrbgems/mruby-compiler/core/parse.y index 1485fb086..d143344c3 100644 --- a/mrbgems/mruby-compiler/core/parse.y +++ b/mrbgems/mruby-compiler/core/parse.y @@ -682,6 +682,25 @@ new_arg(parser_state *p, mrb_sym sym) return cons((node*)NODE_ARG, nsym(sym)); } +static void +local_add_margs(parser_state *p, node *n) +{ + while (n) { + if (n->car->car == (node*)NODE_MASGN) { + node *t = n->car->cdr->cdr; + + n->car->cdr->cdr = NULL; + while (t) { + local_add_f(p, sym(t->car)); + t = t->cdr; + } + local_add_margs(p, n->car->cdr->car->car); + local_add_margs(p, n->car->cdr->car->cdr->cdr->car); + } + n = n->cdr; + } +} + /* (m o r m2 tail) */ /* m: (a b c) */ /* o: ((a . e1) (b . e2)) */ @@ -693,6 +712,8 @@ new_args(parser_state *p, node *m, node *opt, mrb_sym rest, node *m2, node *tail { node *n; + local_add_margs(p, m); + local_add_margs(p, m2); n = cons(m2, tail); n = cons(nsym(rest), n); n = cons(opt, n); @@ -3275,9 +3296,15 @@ f_arg_item : f_norm_arg { $$ = new_arg(p, $1); } - | tLPAREN f_margs rparen + | tLPAREN + { + $$ = local_switch(p); + } + f_margs rparen { - $$ = new_masgn(p, $2, 0); + $$ = new_masgn(p, $3, p->locals->car); + local_resume(p, $2); + local_add_f(p, 0); } ; -- cgit v1.2.3 From 26475d0a7897c25f8632b776014a19c3a6f6ecc2 Mon Sep 17 00:00:00 2001 From: "Yukihiro \"Matz\" Matsumoto" Date: Sun, 25 Nov 2018 18:03:27 +0900 Subject: Update `doc/limitations.md` for argument destructuring. --- doc/limitations.md | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/doc/limitations.md b/doc/limitations.md index 92858cb1f..825435f01 100644 --- a/doc/limitations.md +++ b/doc/limitations.md @@ -219,7 +219,7 @@ $ ruby -e 'def m(*r,**k) p [r,k] end; m("a"=>1,:b=>2)' [[{"a"=>1}], {:b=>2}] ``` -#### mruby [] +#### mruby [mruby 2.0.0] ``` $ ./bin/mruby -e 'def m(*r,**k) p [r,k] end; m("a"=>1,:b=>2)' @@ -227,3 +227,23 @@ trace (most recent call last): [0] -e:1 -e:1: keyword argument hash with non symbol keys (ArgumentError) ``` + +## Argument Destructuring + +```ruby +def m(a,(b,c),d); p [a,b,c,d]; end +m(1,[2,3],4) # => [1,2,3,4] +``` +Destructured arguments (`b` and `c` in above example) cannot be accessed +from the default expression of optional arguments and keyword arguments, +since actual assignment is done after the evaluation of those default +expressions. Thus: + +```ruby +def f(a,(b,c),d=b) + p [a,b,c,d] +end +f(1,[2,3]) +``` + +CRuby gives `[1,2,3,nil]`. mruby raises `NoMethodError` for `b`. -- cgit v1.2.3