summaryrefslogtreecommitdiff
path: root/include/ruby/internal/symbol.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/ruby/internal/symbol.h')
-rw-r--r--include/ruby/internal/symbol.h269
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) ? \