summaryrefslogtreecommitdiff
path: root/jni/ruby/symbol.c
diff options
context:
space:
mode:
authorJari Vetoniemi <jari.vetoniemi@indooratlas.com>2020-03-16 18:49:26 +0900
committerJari Vetoniemi <jari.vetoniemi@indooratlas.com>2020-03-30 00:39:06 +0900
commitfcbf63e62c627deae76c1b8cb8c0876c536ed811 (patch)
tree64cb17de3f41a2b6fef2368028fbd00349946994 /jni/ruby/symbol.c
Fresh start
Diffstat (limited to 'jni/ruby/symbol.c')
-rw-r--r--jni/ruby/symbol.c1128
1 files changed, 1128 insertions, 0 deletions
diff --git a/jni/ruby/symbol.c b/jni/ruby/symbol.c
new file mode 100644
index 0000000..903fb5b
--- /dev/null
+++ b/jni/ruby/symbol.c
@@ -0,0 +1,1128 @@
+/**********************************************************************
+
+ symbol.h -
+
+ $Author$
+ created at: Tue Jul 8 15:49:54 JST 2014
+
+ Copyright (C) 2014 Yukihiro Matsumoto
+
+**********************************************************************/
+
+#include "internal.h"
+#include "ruby/st.h"
+#include "node.h"
+#include "symbol.h"
+#include "gc.h"
+#include "probes.h"
+
+#ifndef SYMBOL_DEBUG
+# define SYMBOL_DEBUG 0
+#endif
+
+#define SYMBOL_PINNED_P(sym) (RSYMBOL(sym)->id&~ID_SCOPE_MASK)
+
+#define STATIC_SYM2ID(sym) RSHIFT((unsigned long)(sym), RUBY_SPECIAL_SHIFT)
+
+static ID register_static_symid(ID, const char *, long, rb_encoding *);
+static ID register_static_symid_str(ID, VALUE);
+#define REGISTER_SYMID(id, name) register_static_symid((id), (name), strlen(name), enc)
+#include "id.c"
+
+#define is_identchar(p,e,enc) (rb_enc_isalnum((unsigned char)(*(p)),(enc)) || (*(p)) == '_' || !ISASCII(*(p)))
+
+#define tUPLUS RUBY_TOKEN(UPLUS)
+#define tUMINUS RUBY_TOKEN(UMINUS)
+#define tPOW RUBY_TOKEN(POW)
+#define tCMP RUBY_TOKEN(CMP)
+#define tEQ RUBY_TOKEN(EQ)
+#define tEQQ RUBY_TOKEN(EQQ)
+#define tNEQ RUBY_TOKEN(NEQ)
+#define tGEQ RUBY_TOKEN(GEQ)
+#define tLEQ RUBY_TOKEN(LEQ)
+#define tMATCH RUBY_TOKEN(MATCH)
+#define tNMATCH RUBY_TOKEN(NMATCH)
+#define tDOT2 RUBY_TOKEN(DOT2)
+#define tDOT3 RUBY_TOKEN(DOT3)
+#define tAREF RUBY_TOKEN(AREF)
+#define tASET RUBY_TOKEN(ASET)
+#define tLSHFT RUBY_TOKEN(LSHFT)
+#define tRSHFT RUBY_TOKEN(RSHFT)
+#define tCOLON2 RUBY_TOKEN(COLON2)
+#define tANDOP RUBY_TOKEN(ANDOP)
+#define tOROP RUBY_TOKEN(OROP)
+
+static const struct {
+ unsigned short token;
+ const char name[3], term;
+} op_tbl[] = {
+ {tDOT2, ".."},
+ {tDOT3, "..."},
+ {tPOW, "**"},
+ {tUPLUS, "+@"},
+ {tUMINUS, "-@"},
+ {tCMP, "<=>"},
+ {tGEQ, ">="},
+ {tLEQ, "<="},
+ {tEQ, "=="},
+ {tEQQ, "==="},
+ {tNEQ, "!="},
+ {tMATCH, "=~"},
+ {tNMATCH, "!~"},
+ {tAREF, "[]"},
+ {tASET, "[]="},
+ {tLSHFT, "<<"},
+ {tRSHFT, ">>"},
+ {tCOLON2, "::"},
+ {tANDOP, "&&"},
+ {tOROP, "||"},
+};
+
+#define op_tbl_count numberof(op_tbl)
+STATIC_ASSERT(op_tbl_name_size, sizeof(op_tbl[0].name) == 3);
+#define op_tbl_len(i) (!op_tbl[i].name[1] ? 1 : !op_tbl[i].name[2] ? 2 : 3)
+
+static void
+Init_op_tbl(void)
+{
+ int i;
+ rb_encoding *const enc = rb_usascii_encoding();
+
+ for (i = '!'; i <= '~'; ++i) {
+ if (!ISALNUM(i) && i != '_') {
+ char c = (char)i;
+ register_static_symid(i, &c, 1, enc);
+ }
+ }
+ for (i = 0; i < op_tbl_count; ++i) {
+ register_static_symid(op_tbl[i].token, op_tbl[i].name, op_tbl_len(i), enc);
+ }
+}
+
+enum {ID_ENTRY_UNIT = 512};
+
+enum id_entry_type {
+ ID_ENTRY_STR,
+ ID_ENTRY_SYM,
+ ID_ENTRY_SIZE
+};
+
+static struct symbols {
+ ID last_id;
+ st_table *str_sym;
+ VALUE ids;
+ VALUE dsymbol_fstr_hash;
+} global_symbols = {tNEXT_ID-1};
+
+static const struct st_hash_type symhash = {
+ rb_str_hash_cmp,
+ rb_str_hash,
+};
+
+void
+Init_sym(void)
+{
+ VALUE dsym_fstrs = rb_ident_hash_new();
+ global_symbols.dsymbol_fstr_hash = dsym_fstrs;
+ rb_gc_register_mark_object(dsym_fstrs);
+ rb_obj_hide(dsym_fstrs);
+
+ global_symbols.str_sym = st_init_table_with_size(&symhash, 1000);
+ global_symbols.ids = rb_ary_tmp_new(0);
+ rb_gc_register_mark_object(global_symbols.ids);
+
+ Init_op_tbl();
+ Init_id();
+}
+
+WARN_UNUSED_RESULT(static VALUE dsymbol_alloc(const VALUE klass, const VALUE str, rb_encoding *const enc, const ID type));
+WARN_UNUSED_RESULT(static VALUE dsymbol_check(const VALUE sym));
+WARN_UNUSED_RESULT(static ID lookup_str_id(VALUE str));
+WARN_UNUSED_RESULT(static VALUE lookup_str_sym(const VALUE str));
+WARN_UNUSED_RESULT(static VALUE lookup_id_str(ID id));
+WARN_UNUSED_RESULT(static ID attrsetname_to_attr(VALUE name));
+WARN_UNUSED_RESULT(static ID attrsetname_to_attr_id(VALUE name));
+WARN_UNUSED_RESULT(static ID intern_str(VALUE str, int mutable));
+
+#define id_to_serial(id) (is_notop_id(id) ? id >> ID_SCOPE_SHIFT : id)
+
+ID
+rb_id_attrset(ID id)
+{
+ VALUE str, sym;
+ int scope;
+
+ if (!is_notop_id(id)) {
+ switch (id) {
+ case tAREF: case tASET:
+ return tASET; /* only exception */
+ }
+ rb_name_error(id, "cannot make operator ID :%"PRIsVALUE" attrset",
+ rb_id2str(id));
+ }
+ else {
+ scope = id_type(id);
+ switch (scope) {
+ case ID_LOCAL: case ID_INSTANCE: case ID_GLOBAL:
+ case ID_CONST: case ID_CLASS: case ID_JUNK:
+ break;
+ case ID_ATTRSET:
+ return id;
+ default:
+ {
+ if ((str = lookup_id_str(id)) != 0) {
+ rb_name_error(id, "cannot make unknown type ID %d:%"PRIsVALUE" attrset",
+ scope, str);
+ }
+ else {
+ rb_name_error_str(Qnil, "cannot make unknown type anonymous ID %d:%"PRIxVALUE" attrset",
+ scope, (VALUE)id);
+ }
+ }
+ }
+ }
+
+ /* make new symbol and ID */
+ if (!(str = lookup_id_str(id))) {
+ static const char id_types[][8] = {
+ "local",
+ "instance",
+ "invalid",
+ "global",
+ "attrset",
+ "const",
+ "class",
+ "junk",
+ };
+ rb_name_error(id, "cannot make anonymous %.*s ID %"PRIxVALUE" attrset",
+ (int)sizeof(id_types[0]), id_types[scope], (VALUE)id);
+ }
+ str = rb_str_dup(str);
+ rb_str_cat(str, "=", 1);
+ sym = lookup_str_sym(str);
+ id = sym ? rb_sym2id(sym) : intern_str(str, 1);
+ return id;
+}
+
+ID
+rb_id_attrget(ID id)
+{
+ return attrsetname_to_attr(rb_id2str(id));
+}
+
+static int
+is_special_global_name(const char *m, const char *e, rb_encoding *enc)
+{
+ int mb = 0;
+
+ if (m >= e) return 0;
+ if (is_global_name_punct(*m)) {
+ ++m;
+ }
+ else if (*m == '-') {
+ if (++m >= e) return 0;
+ if (is_identchar(m, e, enc)) {
+ if (!ISASCII(*m)) mb = 1;
+ m += rb_enc_mbclen(m, e, enc);
+ }
+ }
+ else {
+ if (!rb_enc_isdigit(*m, enc)) return 0;
+ do {
+ if (!ISASCII(*m)) mb = 1;
+ ++m;
+ } while (m < e && rb_enc_isdigit(*m, enc));
+ }
+ return m == e ? mb + 1 : 0;
+}
+
+int
+rb_symname_p(const char *name)
+{
+ return rb_enc_symname_p(name, rb_ascii8bit_encoding());
+}
+
+int
+rb_enc_symname_p(const char *name, rb_encoding *enc)
+{
+ return rb_enc_symname2_p(name, strlen(name), enc);
+}
+
+#define IDSET_ATTRSET_FOR_SYNTAX ((1U<<ID_LOCAL)|(1U<<ID_CONST))
+#define IDSET_ATTRSET_FOR_INTERN (~(~0U<<(1<<ID_SCOPE_SHIFT)) & ~(1U<<ID_ATTRSET))
+
+static int
+rb_enc_symname_type(const char *name, long len, rb_encoding *enc, unsigned int allowed_attrset)
+{
+ const char *m = name;
+ const char *e = m + len;
+ int type = ID_JUNK;
+
+ if (!rb_enc_asciicompat(enc)) return -1;
+ if (!m || len <= 0) return -1;
+ switch (*m) {
+ case '\0':
+ return -1;
+
+ case '$':
+ type = ID_GLOBAL;
+ if (is_special_global_name(++m, e, enc)) return type;
+ goto id;
+
+ case '@':
+ type = ID_INSTANCE;
+ if (*++m == '@') {
+ ++m;
+ type = ID_CLASS;
+ }
+ goto id;
+
+ case '<':
+ switch (*++m) {
+ case '<': ++m; break;
+ case '=': if (*++m == '>') ++m; break;
+ default: break;
+ }
+ break;
+
+ case '>':
+ switch (*++m) {
+ case '>': case '=': ++m; break;
+ }
+ break;
+
+ case '=':
+ switch (*++m) {
+ case '~': ++m; break;
+ case '=': if (*++m == '=') ++m; break;
+ default: return -1;
+ }
+ break;
+
+ case '*':
+ if (*++m == '*') ++m;
+ break;
+
+ case '+': case '-':
+ if (*++m == '@') ++m;
+ break;
+
+ case '|': case '^': case '&': case '/': case '%': case '~': case '`':
+ ++m;
+ break;
+
+ case '[':
+ if (m[1] != ']') goto id;
+ ++m;
+ if (*++m == '=') ++m;
+ break;
+
+ case '!':
+ if (len == 1) return ID_JUNK;
+ switch (*++m) {
+ case '=': case '~': ++m; break;
+ default:
+ if (allowed_attrset & (1U << ID_JUNK)) goto id;
+ return -1;
+ }
+ break;
+
+ default:
+ type = rb_enc_isupper(*m, enc) ? ID_CONST : ID_LOCAL;
+ id:
+ if (m >= e || (*m != '_' && !rb_enc_isalpha(*m, enc) && ISASCII(*m))) {
+ if (len > 1 && *(e-1) == '=') {
+ type = rb_enc_symname_type(name, len-1, enc, allowed_attrset);
+ if (type != ID_ATTRSET) return ID_ATTRSET;
+ }
+ return -1;
+ }
+ while (m < e && is_identchar(m, e, enc)) m += rb_enc_mbclen(m, e, enc);
+ if (m >= e) break;
+ switch (*m) {
+ case '!': case '?':
+ if (type == ID_GLOBAL || type == ID_CLASS || type == ID_INSTANCE) return -1;
+ type = ID_JUNK;
+ ++m;
+ if (m + 1 < e || *m != '=') break;
+ /* fall through */
+ case '=':
+ if (!(allowed_attrset & (1U << type))) return -1;
+ type = ID_ATTRSET;
+ ++m;
+ break;
+ }
+ break;
+ }
+ return m == e ? type : -1;
+}
+
+int
+rb_enc_symname2_p(const char *name, long len, rb_encoding *enc)
+{
+ return rb_enc_symname_type(name, len, enc, IDSET_ATTRSET_FOR_SYNTAX) != -1;
+}
+
+static int
+rb_str_symname_type(VALUE name, unsigned int allowed_attrset)
+{
+ const char *ptr = StringValuePtr(name);
+ long len = RSTRING_LEN(name);
+ int type = rb_enc_symname_type(ptr, len, rb_enc_get(name), allowed_attrset);
+ RB_GC_GUARD(name);
+ return type;
+}
+
+static void
+set_id_entry(ID num, VALUE str, VALUE sym)
+{
+ size_t idx = num / ID_ENTRY_UNIT;
+ VALUE ary, ids = global_symbols.ids;
+ if (idx >= (size_t)RARRAY_LEN(ids) || NIL_P(ary = rb_ary_entry(ids, (long)idx))) {
+ ary = rb_ary_tmp_new(ID_ENTRY_UNIT * ID_ENTRY_SIZE);
+ rb_ary_store(ids, (long)idx, ary);
+ }
+ idx = (num % ID_ENTRY_UNIT) * ID_ENTRY_SIZE;
+ rb_ary_store(ary, (long)idx + ID_ENTRY_STR, str);
+ rb_ary_store(ary, (long)idx + ID_ENTRY_SYM, sym);
+}
+
+static VALUE
+get_id_entry(ID num, const enum id_entry_type t)
+{
+ if (num && num <= global_symbols.last_id) {
+ size_t idx = num / ID_ENTRY_UNIT;
+ VALUE ids = global_symbols.ids;
+ VALUE ary;
+ if (idx < (size_t)RARRAY_LEN(ids) && !NIL_P(ary = rb_ary_entry(ids, (long)idx))) {
+ VALUE result = rb_ary_entry(ary, (long)(num % ID_ENTRY_UNIT) * ID_ENTRY_SIZE + t);
+ if (!NIL_P(result)) return result;
+ }
+ }
+ return 0;
+}
+
+#if SYMBOL_DEBUG
+static int
+register_sym_update_callback(st_data_t *key, st_data_t *value, st_data_t arg, int existing)
+{
+ if (existing) {
+ rb_fatal("symbol :% "PRIsVALUE" is already registered with %"PRIxVALUE,
+ (VALUE)*key, (VALUE)*value);
+ }
+ *value = arg;
+ return ST_CONTINUE;
+}
+#endif
+
+static void
+register_sym(VALUE str, VALUE sym)
+{
+#if SYMBOL_DEBUG
+ st_update(global_symbols.str_sym, (st_data_t)str,
+ register_sym_update_callback, (st_data_t)sym);
+#else
+ st_add_direct(global_symbols.str_sym, (st_data_t)str, (st_data_t)sym);
+#endif
+}
+
+static void
+unregister_sym(VALUE str, VALUE sym)
+{
+ st_data_t str_data = (st_data_t)str;
+ if (!st_delete(global_symbols.str_sym, &str_data, NULL)) {
+ rb_bug("%p can't remove str from str_id (%s)", (void *)sym, RSTRING_PTR(str));
+ }
+}
+
+static ID
+register_static_symid(ID id, const char *name, long len, rb_encoding *enc)
+{
+ VALUE str = rb_enc_str_new(name, len, enc);
+ return register_static_symid_str(id, str);
+}
+
+static ID
+register_static_symid_str(ID id, VALUE str)
+{
+ ID num = id_to_serial(id);
+ VALUE sym = STATIC_ID2SYM(id);
+
+ OBJ_FREEZE(str);
+ str = rb_fstring(str);
+
+ if (RUBY_DTRACE_SYMBOL_CREATE_ENABLED()) {
+ RUBY_DTRACE_SYMBOL_CREATE(RSTRING_PTR(str), rb_sourcefile(), rb_sourceline());
+ }
+
+ register_sym(str, sym);
+ set_id_entry(num, str, sym);
+
+ return id;
+}
+
+static int
+sym_check_asciionly(VALUE str)
+{
+ if (!rb_enc_asciicompat(rb_enc_get(str))) return FALSE;
+ switch (rb_enc_str_coderange(str)) {
+ case ENC_CODERANGE_BROKEN:
+ rb_raise(rb_eEncodingError, "invalid encoding symbol");
+ case ENC_CODERANGE_7BIT:
+ return TRUE;
+ }
+ return FALSE;
+}
+
+#if 0
+/*
+ * _str_ itself will be registered at the global symbol table. _str_
+ * can be modified before the registration, since the encoding will be
+ * set to ASCII-8BIT if it is a special global name.
+ */
+
+static inline void
+must_be_dynamic_symbol(VALUE x)
+{
+ if (UNLIKELY(!DYNAMIC_SYM_P(x))) {
+ if (STATIC_SYM_P(x)) {
+ VALUE str = lookup_id_str(RSHIFT((unsigned long)(x),RUBY_SPECIAL_SHIFT));
+
+ if (str) {
+ rb_bug("wrong argument: %s (inappropriate Symbol)", RSTRING_PTR(str));
+ }
+ else {
+ rb_bug("wrong argument: inappropriate Symbol (%p)", (void *)x);
+ }
+ }
+ else {
+ rb_bug("wrong argument type %s (expected Symbol)", rb_builtin_class_name(x));
+ }
+ }
+}
+#endif
+
+static VALUE
+dsymbol_alloc(const VALUE klass, const VALUE str, rb_encoding * const enc, const ID type)
+{
+ const VALUE dsym = rb_newobj_of(klass, T_SYMBOL | FL_WB_PROTECTED);
+ st_index_t hashval;
+
+ rb_enc_associate(dsym, enc);
+ OBJ_FREEZE(dsym);
+ RB_OBJ_WRITE(dsym, &RSYMBOL(dsym)->fstr, str);
+ RSYMBOL(dsym)->id = type;
+
+ /* we want hashval to be in Fixnum range [ruby-core:15713] r15672 */
+ hashval = rb_str_hash(str);
+ hashval <<= 1;
+ RSYMBOL(dsym)->hashval = (st_index_t)RSHIFT(hashval, 1);
+
+ register_sym(str, dsym);
+ rb_hash_aset(global_symbols.dsymbol_fstr_hash, str, Qtrue);
+
+ if (RUBY_DTRACE_SYMBOL_CREATE_ENABLED()) {
+ RUBY_DTRACE_SYMBOL_CREATE(RSTRING_PTR(RSYMBOL(dsym)->fstr), rb_sourcefile(), rb_sourceline());
+ }
+
+ return dsym;
+}
+
+static inline VALUE
+dsymbol_check(const VALUE sym)
+{
+ if (UNLIKELY(rb_objspace_garbage_object_p(sym))) {
+ const VALUE fstr = RSYMBOL(sym)->fstr;
+ const ID type = RSYMBOL(sym)->id & ID_SCOPE_MASK;
+ RSYMBOL(sym)->fstr = 0;
+
+ unregister_sym(fstr, sym);
+ return dsymbol_alloc(rb_cSymbol, fstr, rb_enc_get(fstr), type);
+ }
+ else {
+ return sym;
+ }
+}
+
+static ID
+lookup_str_id(VALUE str)
+{
+ st_data_t sym_data;
+ if (st_lookup(global_symbols.str_sym, (st_data_t)str, &sym_data)) {
+ const VALUE sym = (VALUE)sym_data;
+
+ if (STATIC_SYM_P(sym)) {
+ return STATIC_SYM2ID(sym);
+ }
+ else if (DYNAMIC_SYM_P(sym)) {
+ ID id = RSYMBOL(sym)->id;
+ if (id & ~ID_SCOPE_MASK) return id;
+ }
+ else {
+ rb_bug("non-symbol object %s:%"PRIxVALUE" for %"PRIsVALUE" in symbol table",
+ rb_builtin_class_name(sym), sym, str);
+ }
+ }
+ return (ID)0;
+}
+
+static VALUE
+lookup_str_sym(const VALUE str)
+{
+ st_data_t sym_data;
+ if (st_lookup(global_symbols.str_sym, (st_data_t)str, &sym_data)) {
+ VALUE sym = (VALUE)sym_data;
+
+ if (DYNAMIC_SYM_P(sym)) {
+ sym = dsymbol_check(sym);
+ }
+ return sym;
+ }
+ else {
+ return (VALUE)0;
+ }
+}
+
+static VALUE
+lookup_id_str(ID id)
+{
+ return get_id_entry(id_to_serial(id), ID_ENTRY_STR);
+}
+
+ID
+rb_intern3(const char *name, long len, rb_encoding *enc)
+{
+ VALUE sym;
+ struct RString fake_str;
+ VALUE str = rb_setup_fake_str(&fake_str, name, len, enc);
+ OBJ_FREEZE(str);
+
+ sym = lookup_str_sym(str);
+ if (sym) return rb_sym2id(sym);
+ str = rb_enc_str_new(name, len, enc); /* make true string */
+ return intern_str(str, 1);
+}
+
+static ID
+next_id_base(void)
+{
+ if (global_symbols.last_id >= ~(ID)0 >> (ID_SCOPE_SHIFT+RUBY_SPECIAL_SHIFT)) {
+ return (ID)-1;
+ }
+ else {
+ const size_t num = ++global_symbols.last_id;
+ return num << ID_SCOPE_SHIFT;
+ }
+}
+
+static ID
+intern_str(VALUE str, int mutable)
+{
+ ID id;
+ ID nid;
+
+ id = rb_str_symname_type(str, IDSET_ATTRSET_FOR_INTERN);
+ if (id == (ID)-1) id = ID_JUNK;
+ if (sym_check_asciionly(str)) {
+ if (!mutable) str = rb_str_dup(str);
+ rb_enc_associate(str, rb_usascii_encoding());
+ }
+ if ((nid = next_id_base()) == (ID)-1) {
+ str = rb_str_ellipsize(str, 20);
+ rb_raise(rb_eRuntimeError, "symbol table overflow (symbol %"PRIsVALUE")",
+ str);
+ }
+ id |= nid;
+ id |= ID_STATIC_SYM;
+ return register_static_symid_str(id, str);
+}
+
+ID
+rb_intern2(const char *name, long len)
+{
+ return rb_intern3(name, len, rb_usascii_encoding());
+}
+
+#undef rb_intern
+ID
+rb_intern(const char *name)
+{
+ return rb_intern2(name, strlen(name));
+}
+
+ID
+rb_intern_str(VALUE str)
+{
+ VALUE sym = lookup_str_sym(str);
+
+ if (sym) {
+ return SYM2ID(sym);
+ }
+
+ return intern_str(str, 0);
+}
+
+void
+rb_gc_free_dsymbol(VALUE sym)
+{
+ VALUE str = RSYMBOL(sym)->fstr;
+
+ if (str) {
+ RSYMBOL(sym)->fstr = 0;
+ unregister_sym(str, sym);
+ rb_hash_delete_entry(global_symbols.dsymbol_fstr_hash, str);
+ }
+}
+
+/*
+ * call-seq:
+ * str.intern -> symbol
+ * str.to_sym -> symbol
+ *
+ * Returns the <code>Symbol</code> corresponding to <i>str</i>, creating the
+ * symbol if it did not previously exist. See <code>Symbol#id2name</code>.
+ *
+ * "Koala".intern #=> :Koala
+ * s = 'cat'.to_sym #=> :cat
+ * s == :cat #=> true
+ * s = '@cat'.to_sym #=> :@cat
+ * s == :@cat #=> true
+ *
+ * This can also be used to create symbols that cannot be represented using the
+ * <code>:xxx</code> notation.
+ *
+ * 'cat and dog'.to_sym #=> :"cat and dog"
+ */
+
+VALUE
+rb_str_intern(VALUE str)
+{
+#if USE_SYMBOL_GC
+ rb_encoding *enc, *ascii;
+ int type;
+#else
+ ID id;
+#endif
+ VALUE sym = lookup_str_sym(str);
+
+ if (sym) {
+ return sym;
+ }
+
+#if USE_SYMBOL_GC
+ enc = rb_enc_get(str);
+ ascii = rb_usascii_encoding();
+ if (enc != ascii) {
+ if (sym_check_asciionly(str)) {
+ str = rb_str_dup(str);
+ rb_enc_associate(str, ascii);
+ OBJ_FREEZE(str);
+ enc = ascii;
+ }
+ }
+ str = rb_fstring(str);
+ type = rb_str_symname_type(str, IDSET_ATTRSET_FOR_INTERN);
+ if (type < 0) type = ID_JUNK;
+ return dsymbol_alloc(rb_cSymbol, str, enc, type);
+#else
+ id = intern_str(str, 0);
+ return ID2SYM(id);
+#endif
+}
+
+ID
+rb_sym2id(VALUE sym)
+{
+ ID id;
+ if (STATIC_SYM_P(sym)) {
+ id = STATIC_SYM2ID(sym);
+ }
+ else if (DYNAMIC_SYM_P(sym)) {
+ sym = dsymbol_check(sym);
+ id = RSYMBOL(sym)->id;
+ if (UNLIKELY(!(id & ~ID_SCOPE_MASK))) {
+ VALUE fstr = RSYMBOL(sym)->fstr;
+ ID num = next_id_base();
+
+ RSYMBOL(sym)->id = id |= num;
+ /* make it permanent object */
+ set_id_entry(num >>= ID_SCOPE_SHIFT, fstr, sym);
+ rb_hash_delete_entry(global_symbols.dsymbol_fstr_hash, fstr);
+ }
+ }
+ else {
+ rb_raise(rb_eTypeError, "wrong argument type %s (expected Symbol)",
+ rb_builtin_class_name(sym));
+ }
+ return id;
+}
+
+#undef rb_id2sym
+VALUE
+rb_id2sym(ID x)
+{
+ if (!DYNAMIC_ID_P(x)) return STATIC_ID2SYM(x);
+ return get_id_entry(id_to_serial(x), ID_ENTRY_SYM);
+}
+
+
+VALUE
+rb_sym2str(VALUE sym)
+{
+ if (DYNAMIC_SYM_P(sym)) {
+ return RSYMBOL(sym)->fstr;
+ }
+ else {
+ return rb_id2str(STATIC_SYM2ID(sym));
+ }
+}
+
+VALUE
+rb_id2str(ID id)
+{
+ VALUE str;
+
+ if ((str = lookup_id_str(id)) != 0) {
+ if (RBASIC(str)->klass == 0)
+ RBASIC_SET_CLASS_RAW(str, rb_cString);
+ return str;
+ }
+
+ return 0;
+}
+
+const char *
+rb_id2name(ID id)
+{
+ VALUE str = rb_id2str(id);
+
+ if (!str) return 0;
+ return RSTRING_PTR(str);
+}
+
+ID
+rb_make_internal_id(void)
+{
+ return next_id_base() | ID_INTERNAL | ID_STATIC_SYM;
+}
+
+static int
+symbols_i(st_data_t key, st_data_t value, st_data_t arg)
+{
+ VALUE ary = (VALUE)arg;
+ VALUE sym = (VALUE)value;
+
+ if (STATIC_SYM_P(sym)) {
+ rb_ary_push(ary, sym);
+ return ST_CONTINUE;
+ }
+ else if (!DYNAMIC_SYM_P(sym)) {
+ rb_bug("invalid symbol: %s", RSTRING_PTR((VALUE)key));
+ }
+ else if (!SYMBOL_PINNED_P(sym) && rb_objspace_garbage_object_p(sym)) {
+ RSYMBOL(sym)->fstr = 0;
+ return ST_DELETE;
+ }
+ else {
+ rb_ary_push(ary, sym);
+ return ST_CONTINUE;
+ }
+
+}
+
+/*
+ * call-seq:
+ * Symbol.all_symbols => array
+ *
+ * Returns an array of all the symbols currently in Ruby's symbol
+ * table.
+ *
+ * Symbol.all_symbols.size #=> 903
+ * Symbol.all_symbols[1,20] #=> [:floor, :ARGV, :Binding, :symlink,
+ * :chown, :EOFError, :$;, :String,
+ * :LOCK_SH, :"setuid?", :$<,
+ * :default_proc, :compact, :extend,
+ * :Tms, :getwd, :$=, :ThreadGroup,
+ * :wait2, :$>]
+ */
+
+VALUE
+rb_sym_all_symbols(void)
+{
+ VALUE ary = rb_ary_new2(global_symbols.str_sym->num_entries);
+ st_foreach(global_symbols.str_sym, symbols_i, ary);
+ return ary;
+}
+
+int
+rb_is_const_id(ID id)
+{
+ return is_const_id(id);
+}
+
+int
+rb_is_class_id(ID id)
+{
+ return is_class_id(id);
+}
+
+int
+rb_is_global_id(ID id)
+{
+ return is_global_id(id);
+}
+
+int
+rb_is_instance_id(ID id)
+{
+ return is_instance_id(id);
+}
+
+int
+rb_is_attrset_id(ID id)
+{
+ return is_attrset_id(id);
+}
+
+int
+rb_is_local_id(ID id)
+{
+ return is_local_id(id);
+}
+
+int
+rb_is_junk_id(ID id)
+{
+ return is_junk_id(id);
+}
+
+int
+rb_is_const_sym(VALUE sym)
+{
+ return is_const_sym(sym);
+}
+
+int
+rb_is_class_sym(VALUE sym)
+{
+ return is_class_sym(sym);
+}
+
+int
+rb_is_global_sym(VALUE sym)
+{
+ return is_global_sym(sym);
+}
+
+int
+rb_is_instance_sym(VALUE sym)
+{
+ return is_instance_sym(sym);
+}
+
+int
+rb_is_attrset_sym(VALUE sym)
+{
+ return is_attrset_sym(sym);
+}
+
+int
+rb_is_local_sym(VALUE sym)
+{
+ return is_local_sym(sym);
+}
+
+int
+rb_is_junk_sym(VALUE sym)
+{
+ return is_junk_sym(sym);
+}
+
+/**
+ * Returns ID for the given name if it is interned already, or 0.
+ *
+ * \param namep the pointer to the name object
+ * \return the ID for *namep
+ * \pre the object referred by \p namep must be a Symbol or
+ * a String, or possible to convert with to_str method.
+ * \post the object referred by \p namep is a Symbol or a
+ * String if non-zero value is returned, or is a String
+ * if 0 is returned.
+ */
+ID
+rb_check_id(volatile VALUE *namep)
+{
+ VALUE tmp;
+ VALUE name = *namep;
+
+ if (STATIC_SYM_P(name)) {
+ return STATIC_SYM2ID(name);
+ }
+ else if (DYNAMIC_SYM_P(name)) {
+ if (SYMBOL_PINNED_P(name)) {
+ return RSYMBOL(name)->id;
+ }
+ else {
+ *namep = RSYMBOL(name)->fstr;
+ return 0;
+ }
+ }
+ else if (!RB_TYPE_P(name, T_STRING)) {
+ tmp = rb_check_string_type(name);
+ if (NIL_P(tmp)) {
+ tmp = rb_inspect(name);
+ rb_raise(rb_eTypeError, "%s is not a symbol nor a string",
+ RSTRING_PTR(tmp));
+ }
+ name = tmp;
+ *namep = name;
+ }
+
+ sym_check_asciionly(name);
+
+ return lookup_str_id(name);
+}
+
+VALUE
+rb_check_symbol(volatile VALUE *namep)
+{
+ VALUE sym;
+ VALUE tmp;
+ VALUE name = *namep;
+
+ if (STATIC_SYM_P(name)) {
+ return name;
+ }
+ else if (DYNAMIC_SYM_P(name)) {
+ if (!SYMBOL_PINNED_P(name)) {
+ name = dsymbol_check(name);
+ *namep = name;
+ }
+ return name;
+ }
+ else if (!RB_TYPE_P(name, T_STRING)) {
+ tmp = rb_check_string_type(name);
+ if (NIL_P(tmp)) {
+ tmp = rb_inspect(name);
+ rb_raise(rb_eTypeError, "%s is not a symbol nor a string",
+ RSTRING_PTR(tmp));
+ }
+ name = tmp;
+ *namep = name;
+ }
+
+ sym_check_asciionly(name);
+
+ if ((sym = lookup_str_sym(name)) != 0) {
+ return sym;
+ }
+
+ return Qnil;
+}
+
+ID
+rb_check_id_cstr(const char *ptr, long len, rb_encoding *enc)
+{
+ struct RString fake_str;
+ const VALUE name = rb_setup_fake_str(&fake_str, ptr, len, enc);
+
+ sym_check_asciionly(name);
+
+ return lookup_str_id(name);
+}
+
+VALUE
+rb_check_symbol_cstr(const char *ptr, long len, rb_encoding *enc)
+{
+ VALUE sym;
+ struct RString fake_str;
+ const VALUE name = rb_setup_fake_str(&fake_str, ptr, len, enc);
+
+ sym_check_asciionly(name);
+
+ if ((sym = lookup_str_sym(name)) != 0) {
+ return sym;
+ }
+
+ return Qnil;
+}
+
+static ID
+attrsetname_to_attr_id(VALUE name)
+{
+ ID id;
+ struct RString fake_str;
+ /* make local name by chopping '=' */
+ const VALUE localname = rb_setup_fake_str(&fake_str,
+ RSTRING_PTR(name), RSTRING_LEN(name) - 1,
+ rb_enc_get(name));
+ OBJ_FREEZE(localname);
+
+ if ((id = lookup_str_id(localname)) != 0) {
+ return id;
+ }
+ RB_GC_GUARD(name);
+ return (ID)0;
+}
+
+static ID
+attrsetname_to_attr(VALUE name)
+{
+ if (rb_is_attrset_name(name)) {
+ return attrsetname_to_attr_id(name);
+ }
+
+ return (ID)0;
+}
+
+int
+rb_is_const_name(VALUE name)
+{
+ return rb_str_symname_type(name, 0) == ID_CONST;
+}
+
+int
+rb_is_class_name(VALUE name)
+{
+ return rb_str_symname_type(name, 0) == ID_CLASS;
+}
+
+int
+rb_is_global_name(VALUE name)
+{
+ return rb_str_symname_type(name, 0) == ID_GLOBAL;
+}
+
+int
+rb_is_instance_name(VALUE name)
+{
+ return rb_str_symname_type(name, 0) == ID_INSTANCE;
+}
+
+int
+rb_is_attrset_name(VALUE name)
+{
+ return rb_str_symname_type(name, IDSET_ATTRSET_FOR_INTERN) == ID_ATTRSET;
+}
+
+int
+rb_is_local_name(VALUE name)
+{
+ return rb_str_symname_type(name, 0) == ID_LOCAL;
+}
+
+int
+rb_is_method_name(VALUE name)
+{
+ switch (rb_str_symname_type(name, 0)) {
+ case ID_LOCAL: case ID_ATTRSET: case ID_JUNK:
+ return TRUE;
+ }
+ return FALSE;
+}
+
+int
+rb_is_junk_name(VALUE name)
+{
+ return rb_str_symname_type(name, IDSET_ATTRSET_FOR_SYNTAX) == -1;
+}