Skip to content

Commit

Permalink
Using mrb_funcall_argv() instead of mrb_funcall_id()
Browse files Browse the repository at this point in the history
`mrb_funcall_id()` is an attractive and easy-to-use function, but it requires more stack space.
This is because the `mrb_funcall_id()` function takes variable-length arguments and also requires copying to a fixed-length array.

If the `mrb_funcall_argv()` function is called directly, stack consumption can be avoided.
However, it requires an array to be defined, making it less usable.

Therefore, the `MRB_FUNCALL()` macro function is introduced to improve usability.
This is for internal implementation at this time, because if the number of arguments is wrong, the error will be hard to understand at compile time.

Currently, only calls with up to 4 arguments are made. For reserve purposes, calls with up to 6 arguments are valid.
Calls with 7 to 13 arguments will output a "function not defined" error at compile time.
For 14 or more arguments, a seemingly inexplicable error is output.

Also, `mrb_static_assert_expand()` has been renamed to `MRB_VA_EXPAND()` and moved to `include/mruby/common.h`.
  • Loading branch information
dearblue committed Oct 30, 2022
1 parent 6ee4f84 commit 981e2e7
Show file tree
Hide file tree
Showing 18 changed files with 108 additions and 31 deletions.
3 changes: 1 addition & 2 deletions include/mruby.h
Expand Up @@ -96,7 +96,6 @@

#ifndef mrb_static_assert
# define mrb_static_assert1(exp) mrb_static_assert2(exp, #exp)
# define mrb_static_assert_expand(...) __VA_ARGS__ /* for MSVC behaviour - https://stackoverflow.com/q/5530505 */
# define mrb_static_assert_selector(a, b, name, ...) name
/**
* The `mrb_static_assert()` macro function takes one or two arguments.
Expand All @@ -106,7 +105,7 @@
* mrb_static_assert(expect_condition, error_message);
*/
# define mrb_static_assert(...) \
mrb_static_assert_expand(mrb_static_assert_selector(__VA_ARGS__, mrb_static_assert2, mrb_static_assert1, _)(__VA_ARGS__))
MRB_VA_EXPAND(mrb_static_assert_selector(__VA_ARGS__, mrb_static_assert2, mrb_static_assert1, _)(__VA_ARGS__))
#endif

#define mrb_static_assert_powerof2(num) mrb_static_assert((num) > 0 && (num) == ((num) & -(num)), "need power of 2 for " #num)
Expand Down
2 changes: 2 additions & 0 deletions include/mruby/common.h
Expand Up @@ -93,6 +93,8 @@ MRB_BEGIN_DECL
# endif
#endif

#define MRB_VA_EXPAND(...) __VA_ARGS__ /* for MSVC behaviour - https://stackoverflow.com/q/5530505 */

MRB_END_DECL

#endif /* MRUBY_COMMON_H */
73 changes: 73 additions & 0 deletions include/mruby/internal.h
Expand Up @@ -159,6 +159,79 @@ mrb_value mrb_exec_irep(mrb_state *mrb, mrb_value self, struct RProc *p);
mrb_value mrb_obj_instance_eval(mrb_state*, mrb_value);
mrb_value mrb_mod_module_eval(mrb_state*, mrb_value);

/*
* Wrapper macro for mrb_funcall_argv() that takes variable length arguments from 0 to 6.
* The number of arguments need not be given because the preprocessor counts them.
*
* Examples are shown below:
*
* // MRB_FUNCALL(mrb, self, mid [, arg]* )
* MRB_FUNCALL(mrb, self, mid);
* MRB_FUNCALL(mrb, self, mid, arg1, arg2, arg3, arg4, arg5, arg6);
*/
#define MRB_FUNCALL(mrb, self, ...) MRB_VA_EXPAND(MRB_FUNCALL_NAME_MAKE(MRB_VA_ARGS_COUNT(__VA_ARGS__))(mrb, self, __VA_ARGS__))

#define MRB_FUNCALL_NAME_MAKE(num) MRB_FUNCALL_NAME_MAKE_1(mrb_funcall_argv_static, num)
#define MRB_FUNCALL_NAME_MAKE_1(prefix, num) prefix ## num

/* Requires at least one sentinel argument. Actually count the number of commas. */
#define MRB_VA_ARGS_COUNT(...) MRB_VA_EXPAND(MRB_VA_ARGS_COUNT_1(__VA_ARGS__, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0))
#define MRB_VA_ARGS_COUNT_1(always, a, b, c, d, e, f, g, h, i, j, k, l, m, n, ...) n

static inline mrb_value
MRB_FUNCALL_NAME_MAKE(0)(mrb_state *mrb, mrb_value self, mrb_sym mid)
{
return mrb_funcall_argv(mrb, self, mid, 0, NULL);
}

static inline mrb_value
MRB_FUNCALL_NAME_MAKE(1)(mrb_state *mrb, mrb_value self, mrb_sym mid,
mrb_value arg1)
{
const mrb_value argv[] = { arg1 };
return mrb_funcall_argv(mrb, self, mid, sizeof(argv) / sizeof(argv[0]), argv);
}

static inline mrb_value
MRB_FUNCALL_NAME_MAKE(2)(mrb_state *mrb, mrb_value self, mrb_sym mid,
mrb_value arg1, mrb_value arg2)
{
const mrb_value argv[] = { arg1, arg2 };
return mrb_funcall_argv(mrb, self, mid, sizeof(argv) / sizeof(argv[0]), argv);
}

static inline mrb_value
MRB_FUNCALL_NAME_MAKE(3)(mrb_state *mrb, mrb_value self, mrb_sym mid,
mrb_value arg1, mrb_value arg2, mrb_value arg3)
{
const mrb_value argv[] = { arg1, arg2, arg3 };
return mrb_funcall_argv(mrb, self, mid, sizeof(argv) / sizeof(argv[0]), argv);
}

static inline mrb_value
MRB_FUNCALL_NAME_MAKE(4)(mrb_state *mrb, mrb_value self, mrb_sym mid,
mrb_value arg1, mrb_value arg2, mrb_value arg3, mrb_value arg4)
{
const mrb_value argv[] = { arg1, arg2, arg3, arg4 };
return mrb_funcall_argv(mrb, self, mid, sizeof(argv) / sizeof(argv[0]), argv);
}

static inline mrb_value
MRB_FUNCALL_NAME_MAKE(5)(mrb_state *mrb, mrb_value self, mrb_sym mid,
mrb_value arg1, mrb_value arg2, mrb_value arg3, mrb_value arg4, mrb_value arg5)
{
const mrb_value argv[] = { arg1, arg2, arg3, arg4, arg5 };
return mrb_funcall_argv(mrb, self, mid, sizeof(argv) / sizeof(argv[0]), argv);
}

static inline mrb_value
MRB_FUNCALL_NAME_MAKE(6)(mrb_state *mrb, mrb_value self, mrb_sym mid,
mrb_value arg1, mrb_value arg2, mrb_value arg3, mrb_value arg4, mrb_value arg5, mrb_value arg6)
{
const mrb_value argv[] = { arg1, arg2, arg3, arg4, arg5, arg6 };
return mrb_funcall_argv(mrb, self, mid, sizeof(argv) / sizeof(argv[0]), argv);
}

#ifdef MRB_USE_BIGINT
mrb_value mrb_bint_new_int(mrb_state *mrb, mrb_int x);
mrb_value mrb_bint_new_str(mrb_state *mrb, const char *x, mrb_int len, mrb_int base);
Expand Down
5 changes: 3 additions & 2 deletions mrbgems/mruby-bin-debugger/tools/mrdb/apiprint.c
Expand Up @@ -11,6 +11,7 @@
#include <mruby/error.h>
#include <mruby/numeric.h>
#include <mruby/string.h>
#include <mruby/internal.h>
#include <mruby/presym.h>
#include "apiprint.h"

Expand Down Expand Up @@ -52,7 +53,7 @@ mrb_debug_eval(mrb_state *mrb, mrb_debug_context *dbg, const char *expr, size_t
else if (direct_eval) {
recv = dbg->regs[0];

v = mrb_funcall(mrb, recv, expr, 0);
v = MRB_FUNCALL(mrb, recv, mrb_intern_cstr(mrb, expr));
}
else {
/*
Expand All @@ -68,7 +69,7 @@ mrb_debug_eval(mrb_state *mrb, mrb_debug_context *dbg, const char *expr, size_t

recv = dbg->regs[0];

v = mrb_funcall_id(mrb, recv, MRB_SYM(instance_eval), 1, ruby_code);
v = MRB_FUNCALL(mrb, recv, MRB_SYM(instance_eval), ruby_code);
}

if (exc) {
Expand Down
5 changes: 3 additions & 2 deletions mrbgems/mruby-bin-mirb/tools/mirb/mirb.c
Expand Up @@ -19,6 +19,7 @@
#include <mruby/string.h>
#include <mruby/variable.h>
#include <mruby/error.h>
#include <mruby/internal.h>
#include <mruby/presym.h>

#include <stdlib.h>
Expand Down Expand Up @@ -116,13 +117,13 @@ p(mrb_state *mrb, mrb_value obj, int prompt)
mrb_value val;
char* msg;

val = mrb_funcall_id(mrb, obj, MRB_SYM(inspect), 0);
val = MRB_FUNCALL(mrb, obj, MRB_SYM(inspect));
if (prompt) {
if (!mrb->exc) {
fputs(" => ", stdout);
}
else {
val = mrb_funcall_id(mrb, mrb_obj_value(mrb->exc), MRB_SYM(inspect), 0);
val = MRB_FUNCALL(mrb, mrb_obj_value(mrb->exc), MRB_SYM(inspect));
}
}
if (!mrb_string_p(val)) {
Expand Down
2 changes: 1 addition & 1 deletion mrbgems/mruby-io/src/io.c
Expand Up @@ -104,7 +104,7 @@ io_set_process_status(mrb_state *mrb, pid_t pid, int status)
}
}
if (c_status != NULL) {
v = mrb_funcall_id(mrb, mrb_obj_value(c_status), MRB_SYM(new), 2, mrb_fixnum_value(pid), mrb_fixnum_value(status));
v = MRB_FUNCALL(mrb, mrb_obj_value(c_status), MRB_SYM(new), mrb_fixnum_value(pid), mrb_fixnum_value(status));
} else {
v = mrb_fixnum_value(WEXITSTATUS(status));
}
Expand Down
2 changes: 1 addition & 1 deletion mrbgems/mruby-metaprog/src/metaprog.c
Expand Up @@ -629,7 +629,7 @@ mrb_mod_remove_method(mrb_state *mrb, mrb_value mod)
mrb_check_frozen(mrb, mrb_obj_ptr(mod));
while (argc--) {
mrb_remove_method(mrb, c, mrb_obj_to_sym(mrb, *argv));
mrb_funcall_id(mrb, mod, MRB_SYM(method_removed), 1, *argv);
MRB_FUNCALL(mrb, mod, MRB_SYM(method_removed), *argv);
argv++;
}
return mod;
Expand Down
2 changes: 1 addition & 1 deletion mrbgems/mruby-method/src/method.c
Expand Up @@ -449,7 +449,7 @@ mrb_search_method_owner(mrb_state *mrb, struct RClass *c, mrb_value obj, mrb_sym
if (!mrb_respond_to(mrb, obj, MRB_SYM_Q(respond_to_missing))) {
goto name_error;
}
ret = mrb_funcall_id(mrb, obj, MRB_SYM_Q(respond_to_missing), 2, mrb_symbol_value(name), mrb_true_value());
ret = MRB_FUNCALL(mrb, obj, MRB_SYM_Q(respond_to_missing), mrb_symbol_value(name), mrb_true_value());
if (!mrb_test(ret)) {
goto name_error;
}
Expand Down
6 changes: 3 additions & 3 deletions mrbgems/mruby-rational/src/rational.c
Expand Up @@ -487,7 +487,7 @@ rational_cmp(mrb_state *mrb, mrb_value x)
}
#endif
default:
x = mrb_funcall_id(mrb, y, MRB_OPSYM(cmp), 1, x);
x = MRB_FUNCALL(mrb, y, MRB_OPSYM(cmp), x);
if (mrb_integer_p(x)) {
mrb_int z = mrb_integer(x);
return mrb_fixnum_value(-z);
Expand Down Expand Up @@ -544,7 +544,7 @@ mrb_rational_add(mrb_state *mrb, mrb_value x, mrb_value y)
#endif

default:
return mrb_funcall_id(mrb, y, MRB_OPSYM(add), 1, x);
return MRB_FUNCALL(mrb, y, MRB_OPSYM(add), x);
}
}

Expand Down Expand Up @@ -642,7 +642,7 @@ mrb_rational_mul(mrb_state *mrb, mrb_value x, mrb_value y)
#endif

default:
return mrb_funcall_id(mrb, y, MRB_OPSYM(mul), 1, x);
return MRB_FUNCALL(mrb, y, MRB_OPSYM(mul), x);
}
}

Expand Down
10 changes: 5 additions & 5 deletions mrbgems/mruby-socket/src/socket.c
Expand Up @@ -168,7 +168,7 @@ mrb_addrinfo_getaddrinfo(mrb_state *mrb, mrb_value klass)

for (res = res0; res != NULL; res = res->ai_next) {
sa = mrb_str_new(mrb, (char*)res->ai_addr, res->ai_addrlen);
ai = mrb_funcall_id(mrb, klass, MRB_SYM(new), 4, sa, mrb_fixnum_value(res->ai_family), mrb_fixnum_value(res->ai_socktype), mrb_fixnum_value(res->ai_protocol));
ai = MRB_FUNCALL(mrb, klass, MRB_SYM(new), sa, mrb_fixnum_value(res->ai_family), mrb_fixnum_value(res->ai_socktype), mrb_fixnum_value(res->ai_protocol));
mrb_ary_push(mrb, ary, ai);
mrb_gc_arena_restore(mrb, arena_idx);
}
Expand Down Expand Up @@ -343,7 +343,7 @@ mrb_basicsocket_getsockopt(mrb_state *mrb, mrb_value self)
c = mrb_const_get(mrb, mrb_obj_value(mrb_class_get_id(mrb, MRB_SYM(Socket))), MRB_SYM(Option));
family = socket_family(s);
data = mrb_str_new(mrb, opt, optlen);
return mrb_funcall_id(mrb, c, MRB_SYM(new), 4, mrb_fixnum_value(family), mrb_fixnum_value(level), mrb_fixnum_value(optname), data);
return MRB_FUNCALL(mrb, c, MRB_SYM(new), mrb_fixnum_value(family), mrb_fixnum_value(level), mrb_fixnum_value(optname), data);
}

static mrb_value
Expand Down Expand Up @@ -465,9 +465,9 @@ mrb_basicsocket_setsockopt(mrb_state *mrb, mrb_value self)
} else if (argc == 1) {
if (strcmp(mrb_obj_classname(mrb, so), "Socket::Option") != 0)
mrb_raise(mrb, E_ARGUMENT_ERROR, "not an instance of Socket::Option");
level = mrb_as_int(mrb, mrb_funcall_id(mrb, so, MRB_SYM(level), 0));
optname = mrb_as_int(mrb, mrb_funcall_id(mrb, so, MRB_SYM(optname), 0));
optval = mrb_funcall_id(mrb, so, MRB_SYM(data), 0);
level = mrb_as_int(mrb, MRB_FUNCALL(mrb, so, MRB_SYM(level)));
optname = mrb_as_int(mrb, MRB_FUNCALL(mrb, so, MRB_SYM(optname)));
optval = MRB_FUNCALL(mrb, so, MRB_SYM(data));
mrb_ensure_string_type(mrb, optval);
} else {
mrb_argnum_error(mrb, argc, 3, 3);
Expand Down
3 changes: 2 additions & 1 deletion mrbgems/mruby-sprintf/src/sprintf.c
Expand Up @@ -8,6 +8,7 @@
#include <mruby/string.h>
#include <mruby/hash.h>
#include <mruby/numeric.h>
#include <mruby/internal.h>
#include <mruby/presym.h>
#include <string.h>
#include <ctype.h>
Expand Down Expand Up @@ -759,7 +760,7 @@ mrb_str_format(mrb_state *mrb, mrb_int argc, const mrb_value *argv, mrb_value fm
tmp = mrb_str_new(mrb, buf, 1);
}
else {
tmp = mrb_funcall_id(mrb, val, MRB_SYM(chr), 0);
tmp = MRB_FUNCALL(mrb, val, MRB_SYM(chr));
mrb_check_type(mrb, tmp, MRB_TT_STRING);
}
#endif
Expand Down
2 changes: 1 addition & 1 deletion src/array.c
Expand Up @@ -1173,7 +1173,7 @@ mrb_ary_splat(mrb_state *mrb, mrb_value v)
return mrb_ary_new_from_values(mrb, 1, &v);
}

ary = mrb_funcall_id(mrb, v, MRB_SYM(to_a), 0);
ary = MRB_FUNCALL(mrb, v, MRB_SYM(to_a));
if (mrb_nil_p(ary)) {
return mrb_ary_new_from_values(mrb, 1, &v);
}
Expand Down
4 changes: 2 additions & 2 deletions src/class.c
Expand Up @@ -2551,7 +2551,7 @@ mrb_method_added(mrb_state *mrb, struct RClass *c, mrb_sym mid)
else {
added = MRB_SYM(method_added);
}
mrb_funcall_id(mrb, recv, added, 1, mrb_symbol_value(mid));
MRB_FUNCALL(mrb, recv, added, mrb_symbol_value(mid));
}

mrb_value
Expand Down Expand Up @@ -2776,7 +2776,7 @@ init_copy(mrb_state *mrb, mrb_value dest, mrb_value obj)
break;
}
if (!mrb_func_basic_p(mrb, dest, MRB_SYM(initialize_copy), mrb_obj_init_copy)) {
mrb_funcall_id(mrb, dest, MRB_SYM(initialize_copy), 1, obj);
MRB_FUNCALL(mrb, dest, MRB_SYM(initialize_copy), obj);
}
}

Expand Down
4 changes: 2 additions & 2 deletions src/error.c
Expand Up @@ -518,10 +518,10 @@ mrb_sys_fail(mrb_state *mrb, const char *mesg)
if (mrb_class_defined_id(mrb, MRB_SYM(SystemCallError))) {
sce = mrb_class_get_id(mrb, MRB_SYM(SystemCallError));
if (mesg != NULL) {
mrb_funcall_id(mrb, mrb_obj_value(sce), MRB_SYM(_sys_fail), 2, mrb_fixnum_value(no), mrb_str_new_cstr(mrb, mesg));
MRB_FUNCALL(mrb, mrb_obj_value(sce), MRB_SYM(_sys_fail), mrb_fixnum_value(no), mrb_str_new_cstr(mrb, mesg));
}
else {
mrb_funcall_id(mrb, mrb_obj_value(sce), MRB_SYM(_sys_fail), 1, mrb_fixnum_value(no));
MRB_FUNCALL(mrb, mrb_obj_value(sce), MRB_SYM(_sys_fail), mrb_fixnum_value(no));
}
}

Expand Down
6 changes: 3 additions & 3 deletions src/hash.c
Expand Up @@ -343,7 +343,7 @@ obj_hash_code(mrb_state *mrb, mrb_value key, struct RHash *h)
break;
default:
h_check_modified(mrb, h, {
hash_code_obj = mrb_funcall_argv(mrb, key, MRB_SYM(hash), 0, NULL);
hash_code_obj = MRB_FUNCALL(mrb, key, MRB_SYM(hash));
});

hash_code = U32(tt) ^ U32(mrb_integer(hash_code_obj));
Expand Down Expand Up @@ -1161,7 +1161,7 @@ hash_default(mrb_state *mrb, mrb_value hash, mrb_value key)
{
if (MRB_RHASH_DEFAULT_P(hash)) {
if (MRB_RHASH_PROCDEFAULT_P(hash)) {
return mrb_funcall_id(mrb, RHASH_PROCDEFAULT(hash), MRB_SYM(call), 2, hash, key);
return MRB_FUNCALL(mrb, RHASH_PROCDEFAULT(hash), MRB_SYM(call), hash, key);
}
else {
return RHASH_IFNONE(hash);
Expand Down Expand Up @@ -1361,7 +1361,7 @@ mrb_hash_default(mrb_state *mrb, mrb_value hash)
if (MRB_RHASH_DEFAULT_P(hash)) {
if (MRB_RHASH_PROCDEFAULT_P(hash)) {
if (!given) return mrb_nil_value();
return mrb_funcall_id(mrb, RHASH_PROCDEFAULT(hash), MRB_SYM(call), 2, hash, key);
return MRB_FUNCALL(mrb, RHASH_PROCDEFAULT(hash), MRB_SYM(call), hash, key);
}
else {
return RHASH_IFNONE(hash);
Expand Down
2 changes: 1 addition & 1 deletion src/kernel.c
Expand Up @@ -505,7 +505,7 @@ mrb_obj_ceqq(mrb_state *mrb, mrb_value self)
return mrb_false_value();
}
else {
ary = mrb_funcall_id(mrb, self, MRB_SYM(to_a), 0);
ary = MRB_FUNCALL(mrb, self, MRB_SYM(to_a));
if (mrb_nil_p(ary)) {
return mrb_funcall_argv(mrb, self, eqq, 1, &v);
}
Expand Down
2 changes: 1 addition & 1 deletion src/numeric.c
Expand Up @@ -2140,7 +2140,7 @@ mrb_cmp(mrb_state *mrb, mrb_value obj1, mrb_value obj2)
return mrb_str_cmp(mrb, obj1, obj2);
default:
if (!mrb_respond_to(mrb, obj1, MRB_OPSYM(cmp))) return -2;
v = mrb_funcall_id(mrb, obj1, MRB_OPSYM(cmp), 1, obj2);
v = MRB_FUNCALL(mrb, obj1, MRB_OPSYM(cmp), obj2);
if (mrb_nil_p(v) || !mrb_integer_p(v))
return -2;
return mrb_integer(v);
Expand Down
6 changes: 3 additions & 3 deletions src/object.c
Expand Up @@ -76,7 +76,7 @@ mrb_equal(mrb_state *mrb, mrb_value obj1, mrb_value obj2)
return FALSE;
}
#endif
result = mrb_funcall_id(mrb, obj1, MRB_OPSYM(eq), 1, obj2);
result = MRB_FUNCALL(mrb, obj1, MRB_OPSYM(eq), obj2);
if (mrb_test(result)) return TRUE;
return FALSE;
}
Expand Down Expand Up @@ -637,7 +637,7 @@ mrb_check_hash_type(mrb_state *mrb, mrb_value hash)
MRB_API mrb_value
mrb_inspect(mrb_state *mrb, mrb_value obj)
{
mrb_value v = mrb_funcall_id(mrb, obj, MRB_SYM(inspect), 0);
mrb_value v = MRB_FUNCALL(mrb, obj, MRB_SYM(inspect));
if (!mrb_string_p(v)) {
v = mrb_obj_as_string(mrb, obj);
}
Expand All @@ -648,5 +648,5 @@ MRB_API mrb_bool
mrb_eql(mrb_state *mrb, mrb_value obj1, mrb_value obj2)
{
if (mrb_obj_eq(mrb, obj1, obj2)) return TRUE;
return mrb_test(mrb_funcall_id(mrb, obj1, MRB_SYM_Q(eql), 1, obj2));
return mrb_test(MRB_FUNCALL(mrb, obj1, MRB_SYM_Q(eql), obj2));
}

0 comments on commit 981e2e7

Please sign in to comment.