Skip to content

Commit

Permalink
vm.c: use prepare_missing in mrb_funcall_with_block
Browse files Browse the repository at this point in the history
Remove code duplication.
  • Loading branch information
matz committed Dec 31, 2021
1 parent 5ca17c2 commit 3de9ddf
Showing 1 changed file with 52 additions and 61 deletions.
113 changes: 52 additions & 61 deletions src/vm.c
Expand Up @@ -430,6 +430,52 @@ mrb_ci_nregs(mrb_callinfo *ci)
return nregs;
}

mrb_value mrb_obj_missing(mrb_state *mrb, mrb_value mod);

static mrb_method_t
prepare_missing(mrb_state *mrb, mrb_value recv, mrb_sym mid, struct RClass **clsp, uint32_t a, uint16_t *c, mrb_value blk, int super)
{
mrb_sym missing = MRB_SYM(method_missing);
mrb_callinfo *ci = mrb->c->ci;
uint16_t b = *c;
mrb_int n = b & 0xf;
mrb_int nk = (b>>4) & 0xf;
mrb_value *argv = &ci->stack[a+1];
mrb_value args;
mrb_method_t m;

/* pack positional arguments */
if (n == 15) args = argv[0];
else args = mrb_ary_new_from_values(mrb, n, argv);

if (mrb_func_basic_p(mrb, recv, missing, mrb_obj_missing)) {
method_missing:
if (super) mrb_no_method_error(mrb, mid, args, "no superclass method '%n'", mid);
else mrb_method_missing(mrb, mid, recv, args);
/* not reached */
}
if (mid != missing) {
*clsp = mrb_class(mrb, recv);
}
m = mrb_method_search_vm(mrb, clsp, missing);
if (MRB_METHOD_UNDEF_P(m)) goto method_missing; /* just in case */
mrb_stack_extend(mrb, a+4);

argv = &ci->stack[a+1]; /* maybe reallocated */
argv[0] = args;
if (nk == 0) {
argv[1] = blk;
}
else {
mrb_assert(nk == 15);
argv[1] = argv[n];
argv[2] = blk;
}
*c = 15 | (nk<<4);
mrb_ary_unshift(mrb, args, mrb_symbol_value(mid));
return m;
}

MRB_API mrb_value
mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc, const mrb_value *argv, mrb_value blk)
{
Expand Down Expand Up @@ -478,24 +524,15 @@ mrb_funcall_with_block(mrb_state *mrb, mrb_value self, mrb_sym mid, mrb_int argc
c = mrb_class(mrb, self);
m = mrb_method_search_vm(mrb, &c, mid);
mrb_stack_extend(mrb, n + argc + 3);
if (MRB_METHOD_UNDEF_P(m) || argc >= 15) {
mrb_value args = mrb_ary_new_from_values(mrb, argc, argv);

ci->stack[n+1] = args;
if (argc >= 15) {
ci->stack[n+1] = mrb_ary_new_from_values(mrb, argc, argv);
argc = 15;
}
if (MRB_METHOD_UNDEF_P(m)) {
mrb_sym missing = MRB_SYM(method_missing);
mrb_value args = ci->stack[n+1];

m = mrb_method_search_vm(mrb, &c, missing);
if (MRB_METHOD_UNDEF_P(m)) {
mrb_method_missing(mrb, mid, self, args);
}
mrb_ary_unshift(mrb, args, mrb_symbol_value(mid));
mrb_stack_extend(mrb, n+2);
ci->stack[n+1] = args;
argc = 15;
uint16_t ac = (uint16_t)argc;
m = prepare_missing(mrb, self, mid, &c, n, &ac, mrb_nil_value(), 0);

This comment has been minimized.

Copy link
@dearblue

dearblue Apr 30, 2022

Contributor

prepare_missing() is limited to references from mrb->c->ci->stack as method arguments.
Therefore, if a method retrieval fails, the argv passed to mrb_funcall_with_block() is ignored.

This will cause unintended value retrieval.

// code.c

#include <mruby.h>
#include <mruby/compile.h>

#define EVAL_LIT(MRB, ...) mrb_load_string((MRB), #__VA_ARGS__)

int
main(int argc, char *argv[])
{
  mrb_state *mrb = mrb_open();

  mrb_value obj = EVAL_LIT(mrb,
    obj = Object.new \n
    class << obj \n
      def method_missing(*a, &b) \n
        p [a, b] \n
      end \n
    end \n
    obj \n
  );

  mrb_value b = mrb_nil_value();
  mrb_sym mid = mrb_intern_lit(mrb, "missing method!");
  mrb_value args[] = { mrb_fixnum_value(1), mrb_fixnum_value(2), mrb_fixnum_value(3) };
  mrb_funcall_with_block(mrb, obj, mid, sizeof(args) / sizeof(args[0]), args, b);

  if (mrb->exc) {
    mrb_print_error(mrb);
  }

  mrb_close(mrb);

  return 0;
}
% `bin/mruby-config --cc --cflags` code.c `bin/mruby-config --ldflags --libs`
% ./a.out
[[:"missing method!", #<Object:0x800e35f80>, :method_missing, nil], nil]
argc = (mrb_int)ac;
mid = MRB_SYM(method_missing);
}
ci = cipush(mrb, n, 0, c, NULL, mid, argc);
if (MRB_METHOD_PROC_P(m)) {
Expand Down Expand Up @@ -1123,52 +1160,6 @@ hash_new_from_values(mrb_state *mrb, mrb_int argc, mrb_value *regs)
return hash;
}

mrb_value mrb_obj_missing(mrb_state *mrb, mrb_value mod);

static mrb_method_t
prepare_missing(mrb_state *mrb, mrb_value recv, mrb_sym mid, struct RClass **clsp, uint32_t a, uint16_t *c, mrb_value blk, int super)
{
mrb_sym missing = MRB_SYM(method_missing);
mrb_callinfo *ci = mrb->c->ci;
uint16_t b = *c;
mrb_int n = b & 0xf;
mrb_int nk = (b>>4) & 0xf;
mrb_value *argv = &ci->stack[a+1];
mrb_value args;
mrb_method_t m;

/* pack positional arguments */
if (n == 15) args = argv[0];
else args = mrb_ary_new_from_values(mrb, n, argv);

if (mrb_func_basic_p(mrb, recv, missing, mrb_obj_missing)) {
method_missing:
if (super) mrb_no_method_error(mrb, mid, args, "no superclass method '%n'", mid);
else mrb_method_missing(mrb, mid, recv, args);
/* not reached */
}
if (mid != missing) {
*clsp = mrb_class(mrb, recv);
}
m = mrb_method_search_vm(mrb, clsp, missing);
if (MRB_METHOD_UNDEF_P(m)) goto method_missing; /* just in case */
mrb_stack_extend(mrb, a+4);

argv = &ci->stack[a+1]; /* maybe reallocated */
argv[0] = args;
if (nk == 0) {
argv[1] = blk;
}
else {
mrb_assert(nk == 15);
argv[1] = argv[n];
argv[2] = blk;
}
*c = 15 | (nk<<4);
mrb_ary_unshift(mrb, args, mrb_symbol_value(mid));
return m;
}

void mrb_method_added(mrb_state *mrb, struct RClass *c, mrb_sym mid);
mrb_value mrb_str_aref(mrb_state *mrb, mrb_value str, mrb_value idx, mrb_value len);

Expand Down

0 comments on commit 3de9ddf

Please sign in to comment.