summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author卜部昌平 <shyouhei@ruby-lang.org>2019-08-28 17:11:23 +0900
committer卜部昌平 <shyouhei@ruby-lang.org>2019-08-29 18:34:09 +0900
commit9ef51b0b89a10c8c401cb9f2337e47a25be72cbe (patch)
tree511f7b67ffb9a22c9028aef0c9f4346835348578
parent48e346a088395924298163941d4b4cdd8243e377 (diff)
drop-in type check for rb_define_method
The rb_define_method function takes a pointer to ANYARGS-ed functions, which in fact varies 18 different prototypes. We still need to preserve ANYARGS for storages but why not check the consistencies if possible. Q&As: Q: Where did the magic number "18" came from in the description above? A: Count the case branch of vm_method.c:call_cfunc_invoker_func(). Note also that the 18 branches has lasted for at least 25 years. See also 200e0ee2fd3c1c006c528874a88f684447215524. Q: What is this __weakref__ thing? A: That is a kind of function overloading mechanism that GCC provides. In this case for instance rb_define_method0 is an alias of rb_define_method, with a strong type. Q: What is this __transparent_union__ thing? A: That is another kind of function overloading mechanism that GCC provides. In this case the attributed function pointer is either VALUE(*)(int,VALUE*,VALUE) or VALUE(*)(int,const VALUE*,VALUE). This is better than void* or ANYARGS because we can reject all other possibilities than the two. Q: What does this rb_define_method macro mean? A: It selects appropriate alias of the rb_define_method function, depending on the arity. Q: Why the prototype change of rb_f_notimplement? A: Function pointer to rb_f_notimplement is special cased in vm_method.c:rb_add_method_cfunc(). That should be handled by the __builtin_choose_expr chain inside of rb_define_method macro expansion. In order to do so, comparison like (func == rb_f_notimplement) is inappropriate for __builtin_choose_expr's expression (which must be a compile-time integer constant but the address of rb_f_notimplement is not fixed until the linker). So instead we are using __builtin_types_compatible_p, and in doing so we need to distinguish rb_f_notimplement from others, by type.
-rw-r--r--class.c3
-rw-r--r--include/ruby/intern.h4
-rw-r--r--include/ruby/ruby.h48
-rw-r--r--vm_method.c2
-rw-r--r--win32/win32.c2
5 files changed, 55 insertions, 4 deletions
diff --git a/class.c b/class.c
index c9f36c8b42..f35e9bb38b 100644
--- a/class.c
+++ b/class.c
@@ -1540,6 +1540,9 @@ rb_define_method_id(VALUE klass, ID mid, VALUE (*func)(ANYARGS), int argc)
rb_add_method_cfunc(klass, mid, func, argc, METHOD_VISI_PUBLIC);
}
+#ifdef rb_define_method
+#undef rb_define_method
+#endif
void
rb_define_method(VALUE klass, const char *name, VALUE (*func)(ANYARGS), int argc)
{
diff --git a/include/ruby/intern.h b/include/ruby/intern.h
index 3e418c3911..612808875f 100644
--- a/include/ruby/intern.h
+++ b/include/ruby/intern.h
@@ -421,9 +421,9 @@ int rb_method_basic_definition_p(VALUE, ID);
VALUE rb_eval_cmd(VALUE, VALUE, int);
int rb_obj_respond_to(VALUE, ID, int);
int rb_respond_to(VALUE, ID);
-NORETURN(VALUE rb_f_notimplement(int argc, const VALUE *argv, VALUE obj));
+NORETURN(VALUE rb_f_notimplement(int argc, const VALUE *argv, VALUE obj, VALUE marker));
#if !defined(RUBY_EXPORT) && defined(_WIN32)
-RUBY_EXTERN VALUE (*const rb_f_notimplement_)(int, const VALUE *, VALUE);
+RUBY_EXTERN VALUE (*const rb_f_notimplement_)(int, const VALUE *, VALUE, VALUE marker);
#define rb_f_notimplement (*rb_f_notimplement_)
#endif
NORETURN(void rb_interrupt(void));
diff --git a/include/ruby/ruby.h b/include/ruby/ruby.h
index e15e3e60ef..59793e6547 100644
--- a/include/ruby/ruby.h
+++ b/include/ruby/ruby.h
@@ -2688,4 +2688,52 @@ RUBY_SYMBOL_EXPORT_END
#endif
} /* extern "C" { */
#endif
+
+#if defined(__has_attribute) && defined(HAVE_BUILTIN___BUILTIN_CHOOSE_EXPR_CONSTANT_P)
+#if __has_attribute(transparent_union) && __has_attribute(unused) && __has_attribute(weakref) && __has_attribute(nonnull)
+__attribute__((__unused__,__weakref__("rb_define_method"),__nonnull__(2,3)))static void rb_define_methodm3(VALUE,const char*,VALUE(*)(ANYARGS),int);
+__attribute__((__unused__,__weakref__("rb_define_method"),__nonnull__(2,3)))static void rb_define_methodm2(VALUE,const char*,VALUE(*)(VALUE,VALUE),int);
+__attribute__((__unused__,__weakref__("rb_define_method"),__nonnull__(2,3)))static void rb_define_methodm1(VALUE,const char*,VALUE(*)(int,union __attribute__((__transparent_union__)){VALUE*x;const VALUE*y;},VALUE),int);
+__attribute__((__unused__,__weakref__("rb_define_method"),__nonnull__(2,3)))static void rb_define_method0 (VALUE,const char*,VALUE(*)(VALUE),int);
+__attribute__((__unused__,__weakref__("rb_define_method"),__nonnull__(2,3)))static void rb_define_method1 (VALUE,const char*,VALUE(*)(VALUE,VALUE),int);
+__attribute__((__unused__,__weakref__("rb_define_method"),__nonnull__(2,3)))static void rb_define_method2 (VALUE,const char*,VALUE(*)(VALUE,VALUE,VALUE),int);
+__attribute__((__unused__,__weakref__("rb_define_method"),__nonnull__(2,3)))static void rb_define_method3 (VALUE,const char*,VALUE(*)(VALUE,VALUE,VALUE,VALUE),int);
+__attribute__((__unused__,__weakref__("rb_define_method"),__nonnull__(2,3)))static void rb_define_method4 (VALUE,const char*,VALUE(*)(VALUE,VALUE,VALUE,VALUE,VALUE),int);
+__attribute__((__unused__,__weakref__("rb_define_method"),__nonnull__(2,3)))static void rb_define_method5 (VALUE,const char*,VALUE(*)(VALUE,VALUE,VALUE,VALUE,VALUE,VALUE),int);
+__attribute__((__unused__,__weakref__("rb_define_method"),__nonnull__(2,3)))static void rb_define_method6 (VALUE,const char*,VALUE(*)(VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE),int);
+__attribute__((__unused__,__weakref__("rb_define_method"),__nonnull__(2,3)))static void rb_define_method7 (VALUE,const char*,VALUE(*)(VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE),int);
+__attribute__((__unused__,__weakref__("rb_define_method"),__nonnull__(2,3)))static void rb_define_method8 (VALUE,const char*,VALUE(*)(VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE),int);
+__attribute__((__unused__,__weakref__("rb_define_method"),__nonnull__(2,3)))static void rb_define_method9 (VALUE,const char*,VALUE(*)(VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE),int);
+__attribute__((__unused__,__weakref__("rb_define_method"),__nonnull__(2,3)))static void rb_define_method10(VALUE,const char*,VALUE(*)(VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE),int);
+__attribute__((__unused__,__weakref__("rb_define_method"),__nonnull__(2,3)))static void rb_define_method11(VALUE,const char*,VALUE(*)(VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE),int);
+__attribute__((__unused__,__weakref__("rb_define_method"),__nonnull__(2,3)))static void rb_define_method12(VALUE,const char*,VALUE(*)(VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE),int);
+__attribute__((__unused__,__weakref__("rb_define_method"),__nonnull__(2,3)))static void rb_define_method13(VALUE,const char*,VALUE(*)(VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE),int);
+__attribute__((__unused__,__weakref__("rb_define_method"),__nonnull__(2,3)))static void rb_define_method14(VALUE,const char*,VALUE(*)(VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE),int);
+__attribute__((__unused__,__weakref__("rb_define_method"),__nonnull__(2,3)))static void rb_define_method15(VALUE,const char*,VALUE(*)(VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE,VALUE),int);
+
+#define rb_f_notimplement_p(f) __builtin_types_compatible_p(__typeof__(f),__typeof__(rb_f_notimplement))
+#define rb_define_method_if_constexpr(x, t, f) __builtin_choose_expr(__builtin_choose_expr(__builtin_constant_p(x),(x),0),(t),(f))
+#define rb_define_method_choose_prototype15(n) rb_define_method_if_constexpr((n)==15,rb_define_method15,rb_define_methodm3)
+#define rb_define_method_choose_prototype14(n) rb_define_method_if_constexpr((n)==14,rb_define_method14,rb_define_method_choose_prototype15(n))
+#define rb_define_method_choose_prototype13(n) rb_define_method_if_constexpr((n)==13,rb_define_method13,rb_define_method_choose_prototype14(n))
+#define rb_define_method_choose_prototype12(n) rb_define_method_if_constexpr((n)==12,rb_define_method12,rb_define_method_choose_prototype13(n))
+#define rb_define_method_choose_prototype11(n) rb_define_method_if_constexpr((n)==11,rb_define_method11,rb_define_method_choose_prototype12(n))
+#define rb_define_method_choose_prototype10(n) rb_define_method_if_constexpr((n)==10,rb_define_method10,rb_define_method_choose_prototype11(n))
+#define rb_define_method_choose_prototype9(n) rb_define_method_if_constexpr((n)== 9,rb_define_method9, rb_define_method_choose_prototype10(n))
+#define rb_define_method_choose_prototype8(n) rb_define_method_if_constexpr((n)== 8,rb_define_method8, rb_define_method_choose_prototype9(n))
+#define rb_define_method_choose_prototype7(n) rb_define_method_if_constexpr((n)== 7,rb_define_method7, rb_define_method_choose_prototype8(n))
+#define rb_define_method_choose_prototype6(n) rb_define_method_if_constexpr((n)== 6,rb_define_method6, rb_define_method_choose_prototype7(n))
+#define rb_define_method_choose_prototype5(n) rb_define_method_if_constexpr((n)== 5,rb_define_method5, rb_define_method_choose_prototype6(n))
+#define rb_define_method_choose_prototype4(n) rb_define_method_if_constexpr((n)== 4,rb_define_method4, rb_define_method_choose_prototype5(n))
+#define rb_define_method_choose_prototype3(n) rb_define_method_if_constexpr((n)== 3,rb_define_method3, rb_define_method_choose_prototype4(n))
+#define rb_define_method_choose_prototype2(n) rb_define_method_if_constexpr((n)== 2,rb_define_method2, rb_define_method_choose_prototype3(n))
+#define rb_define_method_choose_prototype1(n) rb_define_method_if_constexpr((n)== 1,rb_define_method1, rb_define_method_choose_prototype2(n))
+#define rb_define_method_choose_prototype0(n) rb_define_method_if_constexpr((n)== 0,rb_define_method0, rb_define_method_choose_prototype1(n))
+#define rb_define_method_choose_prototypem1(n) rb_define_method_if_constexpr((n)==-1,rb_define_methodm1,rb_define_method_choose_prototype0(n))
+#define rb_define_method_choose_prototypem2(n) rb_define_method_if_constexpr((n)==-2,rb_define_methodm2,rb_define_method_choose_prototypem1(n))
+#define rb_define_method_choose_prototypem3(n, f) rb_define_method_if_constexpr(rb_f_notimplement_p(f),rb_define_methodm3,rb_define_method_choose_prototypem2(n))
+#define rb_define_method(klass, mid, func, arity) rb_define_method_choose_prototypem3((arity),(func))((klass),(mid),(func),(arity));
+#endif
+#endif
+
#endif /* RUBY_RUBY_H */
diff --git a/vm_method.c b/vm_method.c
index d5ebaa4d4c..154cd8e601 100644
--- a/vm_method.c
+++ b/vm_method.c
@@ -114,7 +114,7 @@ rb_clear_method_cache_by_class(VALUE klass)
}
VALUE
-rb_f_notimplement(int argc, const VALUE *argv, VALUE obj)
+rb_f_notimplement(int argc, const VALUE *argv, VALUE obj, VALUE marker)
{
rb_notimplement();
diff --git a/win32/win32.c b/win32/win32.c
index 9301c0f5d8..af3c45ab62 100644
--- a/win32/win32.c
+++ b/win32/win32.c
@@ -8169,7 +8169,7 @@ rb_w32_set_thread_description_str(HANDLE th, VALUE name)
return result;
}
-VALUE (*const rb_f_notimplement_)(int, const VALUE *, VALUE) = rb_f_notimplement;
+VALUE (*const rb_f_notimplement_)(int, const VALUE *, VALUE, VALUE) = rb_f_notimplement;
#if RUBY_MSVCRT_VERSION < 120
#include "missing/nextafter.c"