mirror of
https://github.com/mruby/mruby
synced 2026-06-08 16:11:16 +00:00
Store compressed aspec on cfunc RProc for correct arity/parameters
Compress the 24-bit aspec into 13 free flag bits on RProc (bits 0-6 and 14-19) when wrapping cfunc methods. Field widths: req/opt 3 bits (max 7), post/key 2 bits (max 3), rest/kdict/block 1 bit each. Values exceeding the compressed range are clamped and rest is forced to 1. This enables Proc#arity and Proc#parameters to return correct results for cfunc-backed Procs (e.g. from Method#to_proc) with zero memory overhead -- no struct change needed. Closes #6764
This commit is contained in:
@@ -102,6 +102,59 @@ struct RProc {
|
||||
#define MRB_PROC_ALIAS 8192
|
||||
#define MRB_PROC_ALIAS_P(p) (((p)->flags & MRB_PROC_ALIAS) != 0)
|
||||
|
||||
/* Compressed aspec for cfunc procs (13 bits in RProc.flags).
|
||||
* Uses free bits 0-6 and 14-19 to store a compressed argument spec.
|
||||
* Layout: block(0) kdict(1) key(2-3) post(4-5) rest(6) opt(14-16) req(17-19)
|
||||
* Field widths are smaller than the full 24-bit aspec: req/opt max 7, post/key max 3.
|
||||
* Values exceeding the compressed range are clamped and rest is forced to 1. */
|
||||
#define MRB_PROC_CASPEC_MASK 0xfc07fu /* bits 0-6 and 14-19 */
|
||||
|
||||
static inline uint32_t
|
||||
mrb_proc_compress_aspec(mrb_aspec aspec)
|
||||
{
|
||||
uint32_t req = MRB_ASPEC_REQ(aspec);
|
||||
uint32_t opt = MRB_ASPEC_OPT(aspec);
|
||||
uint32_t rest = MRB_ASPEC_REST(aspec);
|
||||
uint32_t post = MRB_ASPEC_POST(aspec);
|
||||
uint32_t key = MRB_ASPEC_KEY(aspec);
|
||||
uint32_t kdict = MRB_ASPEC_KDICT(aspec);
|
||||
uint32_t block = MRB_ASPEC_BLOCK(aspec);
|
||||
|
||||
if (req > 7 || opt > 7 || post > 3 || key > 3) {
|
||||
if (req > 7) req = 7;
|
||||
if (opt > 7) opt = 7;
|
||||
if (post > 3) post = 3;
|
||||
if (key > 3) key = 3;
|
||||
rest = 1;
|
||||
}
|
||||
|
||||
return block | (kdict << 1) | (key << 2) | (post << 4) | (rest << 6)
|
||||
| (opt << 14) | (req << 17);
|
||||
}
|
||||
|
||||
static inline mrb_aspec
|
||||
mrb_proc_decompress_caspec(uint32_t flags)
|
||||
{
|
||||
return (((flags >> 17) & 0x7) << 18) /* req */
|
||||
| (((flags >> 14) & 0x7) << 13) /* opt */
|
||||
| (((flags >> 6) & 0x1) << 12) /* rest */
|
||||
| (((flags >> 4) & 0x3) << 7) /* post */
|
||||
| (((flags >> 2) & 0x3) << 2) /* key */
|
||||
| (((flags >> 1) & 0x1) << 1) /* kdict */
|
||||
| (flags & 0x1); /* block */
|
||||
}
|
||||
|
||||
static inline void
|
||||
mrb_proc_set_cfunc_aspec(struct RProc *p, mrb_aspec aspec)
|
||||
{
|
||||
if (aspec == 0) {
|
||||
p->flags |= MRB_PROC_NOARG;
|
||||
}
|
||||
else {
|
||||
p->flags |= mrb_proc_compress_aspec(aspec);
|
||||
}
|
||||
}
|
||||
|
||||
#define mrb_proc_ptr(v) ((struct RProc*)(mrb_ptr(v)))
|
||||
|
||||
struct RProc *mrb_proc_new(mrb_state*, const mrb_irep*);
|
||||
|
||||
@@ -85,6 +85,7 @@ method_missing_prepare(mrb_state *mrb, mrb_sym *mid, mrb_value recv, struct RCla
|
||||
const struct RProc *proc;
|
||||
if (MRB_METHOD_FUNC_P(m)) {
|
||||
struct RProc *p = mrb_proc_new_cfunc(mrb, MRB_METHOD_FUNC(m));
|
||||
mrb_proc_set_cfunc_aspec(p, MRB_MT_ASPEC(m.flags));
|
||||
MRB_PROC_SET_TARGET_CLASS(p, *tc);
|
||||
proc = p;
|
||||
}
|
||||
@@ -393,9 +394,7 @@ method_search_vm(mrb_state *mrb, struct RClass **cp, mrb_sym mid)
|
||||
return MRB_METHOD_PROC(m);
|
||||
|
||||
struct RProc *proc = mrb_proc_new_cfunc(mrb, MRB_METHOD_FUNC(m));
|
||||
if (MRB_MT_ASPEC(m.flags) == 0) {
|
||||
proc->flags |= MRB_PROC_NOARG;
|
||||
}
|
||||
mrb_proc_set_cfunc_aspec(proc, MRB_MT_ASPEC(m.flags));
|
||||
return proc;
|
||||
}
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ assert 'Method#arity' do
|
||||
assert_equal(-3, method(:mo7).arity)
|
||||
assert_equal(1, method(:ma1).arity)
|
||||
|
||||
assert_equal(-1, method(:__send__).arity)
|
||||
assert_equal(-2, method(:__send__).arity)
|
||||
assert_equal(-1, method(:nothing).arity)
|
||||
end
|
||||
|
||||
|
||||
@@ -182,13 +182,27 @@ mrb_proc_parameters(mrb_state *mrb, mrb_value self)
|
||||
};
|
||||
int i;
|
||||
const struct RProc *proc = mrb_proc_ptr(self);
|
||||
mrb_aspec aspec;
|
||||
mrb_bool has_lv = TRUE;
|
||||
if (MRB_PROC_CFUNC_P(proc)) {
|
||||
/* TODO: cfunc aspec is not implemented yet - C functions don't store argument spec info */
|
||||
return mrb_ary_new(mrb);
|
||||
uint32_t caspec_bits = proc->flags & MRB_PROC_CASPEC_MASK;
|
||||
if (caspec_bits != 0) {
|
||||
aspec = mrb_proc_decompress_caspec(caspec_bits);
|
||||
}
|
||||
else if (MRB_PROC_NOARG_P(proc)) {
|
||||
aspec = 0;
|
||||
}
|
||||
else {
|
||||
return mrb_ary_new(mrb);
|
||||
}
|
||||
has_lv = FALSE;
|
||||
}
|
||||
const struct mrb_irep *irep = proc->body.irep;
|
||||
if (!irep || !irep->lv || *irep->iseq != OP_ENTER) {
|
||||
return mrb_ary_new(mrb);
|
||||
else {
|
||||
const struct mrb_irep *irep = proc->body.irep;
|
||||
if (!irep || !irep->lv || *irep->iseq != OP_ENTER) {
|
||||
return mrb_ary_new(mrb);
|
||||
}
|
||||
aspec = PEEK_W(irep->iseq+1);
|
||||
}
|
||||
|
||||
if (!MRB_PROC_STRICT_P(proc)) {
|
||||
@@ -196,7 +210,6 @@ mrb_proc_parameters(mrb_state *mrb, mrb_value self)
|
||||
parameters_list[3].name = MRB_SYM(opt);
|
||||
}
|
||||
|
||||
mrb_aspec aspec = PEEK_W(irep->iseq+1);
|
||||
parameters_list[0].size = MRB_ASPEC_REQ(aspec);
|
||||
parameters_list[1].size = MRB_ASPEC_OPT(aspec);
|
||||
parameters_list[2].size = MRB_ASPEC_REST(aspec);
|
||||
@@ -214,18 +227,19 @@ mrb_proc_parameters(mrb_state *mrb, mrb_value self)
|
||||
mrb_value krest = mrb_nil_value();
|
||||
mrb_value block = mrb_nil_value();
|
||||
|
||||
const mrb_sym *lv = has_lv ? proc->body.irep->lv : NULL;
|
||||
for (i = 0, p = parameters_list; p->name; p++) {
|
||||
mrb_value sname = mrb_symbol_value(p->name);
|
||||
|
||||
for (int j = 0; j < p->size; i++, j++) {
|
||||
mrb_value a = mrb_ary_new(mrb);
|
||||
mrb_ary_push(mrb, a, sname);
|
||||
if (i < max && irep->lv[i]) {
|
||||
mrb_ary_push(mrb, a, mrb_symbol_value(irep->lv[i]));
|
||||
if (lv && i < max && lv[i]) {
|
||||
mrb_ary_push(mrb, a, mrb_symbol_value(lv[i]));
|
||||
}
|
||||
if (p->name == MRB_SYM(block)) {
|
||||
if (irep->lv[i+1]) {
|
||||
mrb_ary_push(mrb, a, mrb_symbol_value(irep->lv[i+1]));
|
||||
if (lv && lv[i+1]) {
|
||||
mrb_ary_push(mrb, a, mrb_symbol_value(lv[i+1]));
|
||||
}
|
||||
block = a; continue;
|
||||
}
|
||||
|
||||
+12
-1
@@ -412,7 +412,18 @@ mrb_proc_arity(const struct RProc *p)
|
||||
int ma, op, ra, pa, arity;
|
||||
|
||||
if (MRB_PROC_CFUNC_P(p)) {
|
||||
/* TODO cfunc aspec not implemented yet */
|
||||
uint32_t caspec_bits = p->flags & MRB_PROC_CASPEC_MASK;
|
||||
if (caspec_bits != 0) {
|
||||
aspec = mrb_proc_decompress_caspec(caspec_bits);
|
||||
ma = MRB_ASPEC_REQ(aspec);
|
||||
op = MRB_ASPEC_OPT(aspec);
|
||||
ra = MRB_ASPEC_REST(aspec);
|
||||
pa = MRB_ASPEC_POST(aspec);
|
||||
return ra || op ? -(ma + pa + 1) : ma + pa;
|
||||
}
|
||||
if (MRB_PROC_NOARG_P(p)) {
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -901,7 +901,11 @@ exec_irep(mrb_state *mrb, mrb_value self, const struct RProc *p)
|
||||
MRB_PROC_RESOLVE_ALIAS(ci, p);
|
||||
CI_PROC_SET(ci, p);
|
||||
if (MRB_PROC_CFUNC_P(p)) {
|
||||
if (MRB_PROC_NOARG_P(p) && (ci->n > 0 || ci->nk > 0)) {
|
||||
uint32_t caspec_bits = p->flags & MRB_PROC_CASPEC_MASK;
|
||||
if (caspec_bits != 0) {
|
||||
check_argument_count(mrb, ci, mrb_proc_decompress_caspec(caspec_bits));
|
||||
}
|
||||
else if (MRB_PROC_NOARG_P(p) && (ci->n > 0 || ci->nk > 0)) {
|
||||
check_argument_count(mrb, ci, 0);
|
||||
}
|
||||
return MRB_PROC_CFUNC(p)(mrb, self);
|
||||
@@ -1052,7 +1056,11 @@ send_method(mrb_state *mrb, mrb_value self, mrb_bool pub)
|
||||
MRB_PROC_RESOLVE_ALIAS(ci, p);
|
||||
CI_PROC_SET(ci, p);
|
||||
if (MRB_PROC_CFUNC_P(p)) {
|
||||
if (MRB_PROC_NOARG_P(p) && (ci->n > 0 || ci->nk > 0)) {
|
||||
uint32_t caspec_bits = p->flags & MRB_PROC_CASPEC_MASK;
|
||||
if (caspec_bits != 0) {
|
||||
check_argument_count(mrb, ci, mrb_proc_decompress_caspec(caspec_bits));
|
||||
}
|
||||
else if (MRB_PROC_NOARG_P(p) && (ci->n > 0 || ci->nk > 0)) {
|
||||
check_argument_count(mrb, ci, 0);
|
||||
}
|
||||
return MRB_PROC_CFUNC(p)(mrb, self);
|
||||
|
||||
Reference in New Issue
Block a user