#pragma once #include #include #include "helpers.h" // Initialize internal data needed by some ruby helpers. Should be called during start, before any actual // usage of ruby helpers. void ruby_helpers_init(void); // Processes any pending interruptions, including exceptions to be raised. // If there's an exception to be raised, it raises it. In that case, this function does not return. static inline VALUE process_pending_interruptions(DDTRACE_UNUSED VALUE _) { rb_thread_check_ints(); return Qnil; } // RB_UNLIKELY is not supported on Ruby 2.3 #ifndef RB_UNLIKELY #define RB_UNLIKELY(x) x #endif // Calls process_pending_interruptions BUT "rescues" any exceptions to be raised, returning them instead as // a non-zero `pending_exception`. // // Thus, if there's a non-zero `pending_exception`, the caller MUST call `rb_jump_tag(pending_exception)` after any // needed clean-ups. // // Usage example: // // ```c // foo = ruby_xcalloc(...); // pending_exception = check_if_pending_exception(); // if (pending_exception) { // ruby_xfree(foo); // rb_jump_tag(pending_exception); // Re-raises exception // } // ``` __attribute__((warn_unused_result)) static inline int check_if_pending_exception(void) { int pending_exception; rb_protect(process_pending_interruptions, Qnil, &pending_exception); return pending_exception; } #define ADD_QUOTES_HELPER(x) #x #define ADD_QUOTES(x) ADD_QUOTES_HELPER(x) // Ruby has a Check_Type(value, type) that is roughly equivalent to this BUT Ruby's version is rather cryptic when it fails // e.g. "wrong argument type nil (expected String)". This is a replacement that prints more information to help debugging. #define ENFORCE_TYPE(value, type) \ { if (RB_UNLIKELY(!RB_TYPE_P(value, type))) raise_unexpected_type(value, ADD_QUOTES(value), ADD_QUOTES(type), __FILE__, __LINE__, __func__); } #define ENFORCE_BOOLEAN(value) \ { if (RB_UNLIKELY(value != Qtrue && value != Qfalse)) raise_unexpected_type(value, ADD_QUOTES(value), "true or false", __FILE__, __LINE__, __func__); } // Called by ENFORCE_TYPE; should not be used directly NORETURN(void raise_unexpected_type( VALUE value, const char *value_name, const char *type_name, const char *file, int line, const char *function_name )); #define VALUE_COUNT(array) (sizeof(array) / sizeof(VALUE)) NORETURN( void grab_gvl_and_raise(VALUE exception_class, const char *format_string, ...) __attribute__ ((format (printf, 2, 3))); ); NORETURN( void grab_gvl_and_raise_syserr(int syserr_errno, const char *format_string, ...) __attribute__ ((format (printf, 2, 3))); ); #define ENFORCE_SUCCESS_GVL(expression) ENFORCE_SUCCESS_HELPER(expression, true) #define ENFORCE_SUCCESS_NO_GVL(expression) ENFORCE_SUCCESS_HELPER(expression, false) #define ENFORCE_SUCCESS_HELPER(expression, have_gvl) \ { int result_syserr_errno = expression; if (RB_UNLIKELY(result_syserr_errno)) raise_syserr(result_syserr_errno, have_gvl, ADD_QUOTES(expression), __FILE__, __LINE__, __func__); } // Called by ENFORCE_SUCCESS_HELPER; should not be used directly NORETURN(void raise_syserr( int syserr_errno, bool have_gvl, const char *expression, const char *file, int line, const char *function_name )); // Alternative to ruby_strdup that takes a size argument. // Similar to C's strndup but slightly less smart as size is expected to // be smaller or equal to the real size of str (minus null termination if it // exists). // A new string will be returned with size+1 bytes and last byte set to '\0'. // The returned string must be freed explicitly. // // WARN: Cannot be used during GC or outside the GVL. char* ruby_strndup(const char *str, size_t size); // Native wrapper to get an object ref from an id. Returns true on success and // writes the ref to the value pointer parameter if !NULL. False if id doesn't // reference a valid object (in which case value is not changed). bool ruby_ref_from_id(size_t id, VALUE *value); // Native wrapper to get the approximate/estimated current size of the passed // object. size_t ruby_obj_memsize_of(VALUE obj); // Safely inspect any ruby object. If the object responds to 'inspect', // return a string with the result of that call. Elsif the object responds to // 'to_s', return a string with the result of that call. Otherwise, return Qnil. VALUE ruby_safe_inspect(VALUE obj);