diff options
Diffstat (limited to 'include/ruby/internal/symbol.h')
| -rw-r--r-- | include/ruby/internal/symbol.h | 269 |
1 files changed, 249 insertions, 20 deletions
diff --git a/include/ruby/internal/symbol.h b/include/ruby/internal/symbol.h index 762f1e8f9b..8bfd686fbe 100644 --- a/include/ruby/internal/symbol.h +++ b/include/ruby/internal/symbol.h @@ -17,12 +17,12 @@ * recursively included from extension libraries written in C++. * Do not expect for instance `__VA_ARGS__` is always available. * We assume C99 for ruby itself but we don't assume languages of - * extension libraries. They could be written in C++98. + * extension libraries. They could be written in C++98. * @brief Defines #rb_intern */ #include "ruby/internal/config.h" -#ifdef HAVE_STDDEF_H +#ifdef STDC_HEADERS # include <stddef.h> #endif @@ -30,43 +30,257 @@ # include <string.h> #endif +#include "ruby/internal/attr/noalias.h" #include "ruby/internal/attr/nonnull.h" #include "ruby/internal/attr/pure.h" -#include "ruby/internal/attr/noalias.h" #include "ruby/internal/cast.h" #include "ruby/internal/constant_p.h" #include "ruby/internal/dllexport.h" #include "ruby/internal/has/builtin.h" #include "ruby/internal/value.h" -#define RB_ID2SYM rb_id2sym -#define RB_SYM2ID rb_sym2id -#define ID2SYM RB_ID2SYM -#define SYM2ID RB_SYM2ID -#define CONST_ID_CACHE RUBY_CONST_ID_CACHE -#define CONST_ID RUBY_CONST_ID +#define RB_ID2SYM rb_id2sym /**< @alias{rb_id2sym} */ +#define RB_SYM2ID rb_sym2id /**< @alias{rb_sym2id} */ +#define ID2SYM RB_ID2SYM /**< @old{RB_ID2SYM} */ +#define SYM2ID RB_SYM2ID /**< @old{RB_SYM2ID} */ +#define CONST_ID_CACHE RUBY_CONST_ID_CACHE /**< @old{RUBY_CONST_ID_CACHE} */ +#define CONST_ID RUBY_CONST_ID /**< @old{RUBY_CONST_ID} */ /** @cond INTERNAL_MACRO */ #define rb_intern_const rb_intern_const /** @endcond */ RBIMPL_SYMBOL_EXPORT_BEGIN() -ID rb_sym2id(VALUE); -VALUE rb_id2sym(ID); -ID rb_intern(const char*); -ID rb_intern2(const char*, long); + +/** + * Converts an instance of ::rb_cSymbol into an ::ID. + * + * @param[in] obj An instance of ::rb_cSymbol. + * @exception rb_eTypeError `obj` is not an instance of ::rb_cSymbol. + * @return An ::ID of the identical symbol. + */ +ID rb_sym2id(VALUE obj); + +/** + * Allocates an instance of ::rb_cSymbol that has the given id. + * + * @param[in] id An id. + * @retval RUBY_Qfalse No such id ever existed in the history. + * @retval Otherwise An allocated ::rb_cSymbol instance. + */ +VALUE rb_id2sym(ID id); + +RBIMPL_ATTR_NONNULL(()) +/** + * Finds or creates a symbol of the given name. + * + * @param[in] name The name of the id. + * @exception rb_eRuntimeError Too many symbols. + * @return A (possibly new) id whose value is the given name. + * @note These days Ruby internally has two kinds of symbols (static / + * dynamic). Symbols created using this function would become a + * static one; i.e. would never be garbage collected. It is up to + * you to avoid memory leaks. Think twice before using it. + */ +ID rb_intern(const char *name); + +/** + * Identical to rb_intern(), except it additionally takes the length of the + * string. This way you can have a symbol that contains NUL characters. + * + * @param[in] name The name of the id. + * @param[in] len Length of `name`. + * @exception rb_eRuntimeError Too many symbols. + * @return A (possibly new) id whose value is the given name. + * @note These days Ruby internally has two kinds of symbols + * (static/dynamic). Symbols created using this function would + * become static ones; i.e. would never be garbage collected. It + * is up to you to avoid memory leaks. Think twice before using + * it. + */ +ID rb_intern2(const char *name, long len); + +/** + * Identical to rb_intern(), except it takes a `T_STRING` object. + * + * @param[in] str The name of the id. + * @pre `rb_type(str)` must be `T_STRING`. + * @exception rb_eEncodingError `str` contains invalid character(s). + * @exception rb_eRuntimeError Too many symbols. + * @return A (possibly new) id whose value is the given str. + * @note These days Ruby internally has two kinds of symbols + * (static/dynamic). Symbols created using this function would + * become static ones; i.e. would never be garbage collected. It + * is up to you to avoid memory leaks. Think twice before using + * it. + */ ID rb_intern_str(VALUE str); -const char *rb_id2name(ID); -ID rb_check_id(volatile VALUE *); -ID rb_to_id(VALUE); -VALUE rb_id2str(ID); -VALUE rb_sym2str(VALUE); + +/** + * Retrieves the name mapped to the given id. + * + * @param[in] id An id to query. + * @retval NULL Unknown id. + * @retval otherwise A name that the id represents. + * @note The return value is managed by the interpreter. Don't pass it + * to free(). + * @note The underlying name can contain internal NUL bytes, so the return + * value might be a truncated representation due to the nature of C + * strings. + * @note This C string is backed by an underlying Ruby string. The Ruby + * string may move during GC compaction which would make this + * C string point to invalid memory. Do not use the return value + * of this function after a potential GC entry point. + */ +const char *rb_id2name(ID id); + +RBIMPL_ATTR_NONNULL(()) +/** + * Detects if the given name is already interned or not. It first tries to + * convert the argument to an instance of ::rb_cString if it is neither an + * instance of ::rb_cString nor ::rb_cSymbol. The conversion result is written + * back to the variable. Then queries if that name was already interned + * before. If found it returns such id, otherwise zero. + * + * We eventually introduced this API to avoid inadvertent symbol pin-down. + * Before, there was no way to know if an ID was already interned or not + * without actually creating one (== leaking memory). By using this API you + * can avoid such situations: + * + * ```CXX + * bool does_interning_this_leak_memory(VALUE obj) + * { + * auto tmp = obj; + * if (auto id = rb_check_id(&tmp); id) { + * return false; + * } + * else { + * return true; // Let GC sweep tmp if necessary. + * } + * } + * ``` + * + * @param[in,out] namep A pointer to a name to query. + * @pre The object referred by `*namep` must either be an instance + * of ::rb_cSymbol, or an instance of ::rb_cString, or responds + * to `#to_str` method. + * @exception rb_eTypeError Can't convert `*namep` into ::rb_cString. + * @exception rb_eEncodingError Given string contains invalid character(s). + * @retval 0 No such id ever existed in the history. + * @retval otherwise The id that represents the given name. + * @post The object that `*namep` points to is a converted result + * object, which is always an instance of either ::rb_cSymbol + * or ::rb_cString. + * @see rb_str_to_str + * @see https://bugs.ruby-lang.org/issues/5072 + */ +ID rb_check_id(volatile VALUE *namep); + +/** + * Identical to rb_intern_str(), except it tries to convert the parameter object + * to an instance of ::rb_cString or its subclasses. + * + * @param[in] str The name of the id. + * @pre `str` must either be an instance of ::rb_cSymbol, or an instance + * of ::rb_cString, or responds to `#to_str` method. + * @exception rb_eTypeError Can't convert `str` into ::rb_cString. + * @exception rb_eEncodingError Given string contains invalid character(s). + * @exception rb_eRuntimeError Too many symbols. + * @return A (possibly new) id whose value is the given str. + * @note These days Ruby internally has two kinds of symbols + * (static/dynamic). Symbols created using this function would + * become static ones; i.e. would never be garbage collected. It + * is up to you to avoid memory leaks. Think twice before using + * it. + * @see rb_str_to_str + */ +ID rb_to_id(VALUE str); + +/** + * Identical to rb_id2name(), except it returns a frozen Ruby String instead of + * a C String. + * + * @param[in] id An id to query. + * @retval RUBY_Qfalse No such id ever existed in the history. + * @retval otherwise An instance of ::rb_cString with the name of id. + * + * @internal + * + * In reality "rb_id2str() is identical to rb_id2name() except it returns Ruby + * string" is just describing things upside down; truth is `rb_id2name(foo)` is + * a shorthand of `RSTRING_PTR(rb_id2str(foo))`. + */ +VALUE rb_id2str(ID id); + +/** + * Obtain a frozen string representation of a symbol (not including the leading + * colon). Done without any object allocations. + * + * @param[in] symbol A ::rb_cSymbol instance to query. + * @return A frozen instance of ::rb_cString with the name of `symbol`. + * @note This does not create a permanent ::ID using the symbol. + */ +VALUE rb_sym2str(VALUE symbol); + +/** + * Identical to rb_intern_str(), except it generates a dynamic symbol if + * necessary. + * + * @param[in] name The name of the id. + * @pre `name` must either be an instance of ::rb_cSymbol, or an + * instance of ::rb_cString, or responds to `#to_str` method. + * @exception rb_eTypeError Can't convert `name` into ::rb_cString. + * @exception rb_eRuntimeError Too many symbols. + * @return A (possibly new) id whose value is the given name. + * @note These days Ruby internally has two kinds of symbols + * (static/dynamic). Symbols created using this function would + * become dynamic ones; i.e. would be garbage collected. It could + * be safer for you to use it than alternatives, when applicable. + */ VALUE rb_to_symbol(VALUE name); + +RBIMPL_ATTR_NONNULL(()) +/** + * Identical to rb_check_id(), except it returns an instance of ::rb_cSymbol + * instead. + * + * @param[in,out] namep A pointer to a name to query. + * @pre The object referred by `*namep` must either be an instance + * of ::rb_cSymbol, or an instance of ::rb_cString, or responds + * to `#to_str` method. + * @exception rb_eTypeError Can't convert `*namep` into ::rb_cString. + * @exception rb_eEncodingError Given string contains invalid character(s). + * @retval RUBY_Qnil No such id ever existed in the history. + * @retval otherwise The id that represents the given name. + * @post The object that `*namep` points to is a converted result + * object, which is always an instance of either ::rb_cSymbol + * or ::rb_cString. + * @see https://bugs.ruby-lang.org/issues/5072 + * @see rb_str_to_str + */ VALUE rb_check_symbol(volatile VALUE *namep); RBIMPL_SYMBOL_EXPORT_END() RBIMPL_ATTR_PURE() RBIMPL_ATTR_NONNULL(()) +/** + * This is a "tiny optimisation" over rb_intern(). If you pass a string + * _literal_, and if your C compiler can special-case strlen of such literal to + * strength-reduce into an integer constant expression, then this inline + * function can precalc a part of conversion. + * + * @note This function also works happily for non-constant strings. Why + * bother then? Just apply liberally to everything. + * @note But #rb_intern() could be faster on compilers with statement + * expressions, because they can cache the created ::ID. + * @param[in] str The name of the id. + * @exception rb_eRuntimeError Too many symbols. + * @return A (possibly new) id whose value is the given str. + * @note These days Ruby internally has two kinds of symbols (static / + * dynamic). Symbols created using this function would become a + * static one; i.e. would never be garbage collected. It is up to + * you to avoid memory leaks. Think twice before using it. + */ static inline ID rb_intern_const(const char *str) { @@ -76,6 +290,11 @@ rb_intern_const(const char *str) RBIMPL_ATTR_NOALIAS() RBIMPL_ATTR_NONNULL(()) +/** + * @private + * + * This is an implementation detail of #rb_intern(). Just don't use it. + */ static inline ID rbimpl_intern_const(ID *ptr, const char *str) { @@ -86,13 +305,22 @@ rbimpl_intern_const(ID *ptr, const char *str) return *ptr; } -/* Does anyone use it? Preserved for backward compat. */ +/** + * Old implementation detail of rb_intern(). + * @deprecated Does anyone use it? Preserved for backward compat. + */ #define RUBY_CONST_ID_CACHE(result, str) \ { \ static ID rb_intern_id_cache; \ rbimpl_intern_const(&rb_intern_id_cache, (str)); \ result rb_intern_id_cache; \ } + +/** + * Returns the cached ID for the given str in var, in compiler + * independent manner. Use this instead of GCC specific rb_intern() + * when you want to cache the ID on all platforms certainly. + */ #define RUBY_CONST_ID(var, str) \ do { \ static ID rbimpl_id; \ @@ -100,7 +328,8 @@ rbimpl_intern_const(ID *ptr, const char *str) } while (0) #if defined(HAVE_STMT_AND_DECL_IN_EXPR) -/* __builtin_constant_p and statement expression is available +/* GCC specific shorthand for RUBY_CONST_ID(). + * __builtin_constant_p and statement expression is available * since gcc-2.7.2.3 at least. */ #define rb_intern(str) \ (RBIMPL_CONSTANT_P(str) ? \ |
