1 |
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
2 |
* |
* |
3 |
* ***** BEGIN LICENSE BLOCK ***** |
* ***** BEGIN LICENSE BLOCK ***** |
4 |
* Version: MPL 1.1/GPL 2.0/LGPL 2.1 |
* Version: MPL 1.1/GPL 2.0/LGPL 2.1 |
40 |
/* |
/* |
41 |
* JS atom table. |
* JS atom table. |
42 |
*/ |
*/ |
|
#include "jsstddef.h" |
|
43 |
#include <stdlib.h> |
#include <stdlib.h> |
44 |
#include <string.h> |
#include <string.h> |
45 |
#include "jstypes.h" |
#include "jstypes.h" |
46 |
|
#include "jsstdint.h" |
47 |
#include "jsutil.h" /* Added by JSIFY */ |
#include "jsutil.h" /* Added by JSIFY */ |
48 |
#include "jshash.h" /* Added by JSIFY */ |
#include "jshash.h" /* Added by JSIFY */ |
49 |
#include "jsprf.h" |
#include "jsprf.h" |
58 |
#include "jsscan.h" |
#include "jsscan.h" |
59 |
#include "jsstr.h" |
#include "jsstr.h" |
60 |
#include "jsversion.h" |
#include "jsversion.h" |
61 |
|
#include "jsstrinlines.h" |
62 |
|
|
63 |
/* |
/* |
64 |
* ATOM_HASH assumes that JSHashNumber is 32-bit even on 64-bit systems. |
* ATOM_HASH assumes that JSHashNumber is 32-bit even on 64-bit systems. |
99 |
* The elements of the array after the first empty string define strings |
* The elements of the array after the first empty string define strings |
100 |
* corresponding to the two boolean literals, false and true, followed by the |
* corresponding to the two boolean literals, false and true, followed by the |
101 |
* JSType enumerators from jspubtd.h starting with "undefined" for JSTYPE_VOID |
* JSType enumerators from jspubtd.h starting with "undefined" for JSTYPE_VOID |
102 |
* (which is pseudo-boolean 2) and continuing as initialized below. The static |
* (which is special-value 2) and continuing as initialized below. The static |
103 |
* asserts check these relations. |
* asserts check these relations. |
104 |
*/ |
*/ |
105 |
JS_STATIC_ASSERT(JSTYPE_LIMIT == 8); |
JS_STATIC_ASSERT(JSTYPE_LIMIT == 8); |
326 |
static JSDHashNumber |
static JSDHashNumber |
327 |
HashDouble(JSDHashTable *table, const void *key) |
HashDouble(JSDHashTable *table, const void *key) |
328 |
{ |
{ |
|
jsdouble d; |
|
|
|
|
329 |
JS_ASSERT(IS_DOUBLE_TABLE(table)); |
JS_ASSERT(IS_DOUBLE_TABLE(table)); |
330 |
d = *(jsdouble *)key; |
return JS_HASH_DOUBLE(*(jsdouble *)key); |
|
return JSDOUBLE_HI32(d) ^ JSDOUBLE_LO32(d); |
|
331 |
} |
} |
332 |
|
|
333 |
static JSDHashNumber |
static JSDHashNumber |
557 |
void |
void |
558 |
js_TraceAtomState(JSTracer *trc, JSBool allAtoms) |
js_TraceAtomState(JSTracer *trc, JSBool allAtoms) |
559 |
{ |
{ |
560 |
JSAtomState *state; |
JSRuntime *rt = trc->context->runtime; |
561 |
|
JSAtomState *state = &rt->atomState; |
562 |
|
|
|
state = &trc->context->runtime->atomState; |
|
563 |
if (allAtoms) { |
if (allAtoms) { |
564 |
JS_DHashTableEnumerate(&state->doubleAtoms, js_locked_atom_tracer, trc); |
JS_DHashTableEnumerate(&state->doubleAtoms, js_locked_atom_tracer, trc); |
565 |
JS_DHashTableEnumerate(&state->stringAtoms, js_locked_atom_tracer, trc); |
JS_DHashTableEnumerate(&state->stringAtoms, js_locked_atom_tracer, trc); |
671 |
JS_ASSERT(!(flags & ~(ATOM_PINNED|ATOM_INTERNED|ATOM_TMPSTR|ATOM_NOCOPY))); |
JS_ASSERT(!(flags & ~(ATOM_PINNED|ATOM_INTERNED|ATOM_TMPSTR|ATOM_NOCOPY))); |
672 |
JS_ASSERT_IF(flags & ATOM_NOCOPY, flags & ATOM_TMPSTR); |
JS_ASSERT_IF(flags & ATOM_NOCOPY, flags & ATOM_TMPSTR); |
673 |
|
|
674 |
|
if (str->isAtomized()) |
675 |
|
return (JSAtom *) STRING_TO_JSVAL(str); |
676 |
|
|
677 |
|
size_t length = str->length(); |
678 |
|
if (length == 1) { |
679 |
|
jschar c = str->chars()[0]; |
680 |
|
if (c < UNIT_STRING_LIMIT) |
681 |
|
return (JSAtom *) STRING_TO_JSVAL(JSString::unitString(c)); |
682 |
|
} |
683 |
|
|
684 |
|
/* |
685 |
|
* Here we know that JSString::intStringTable covers only 256 (or at least |
686 |
|
* not 1000 or more) chars. We rely on order here to resolve the unit vs. |
687 |
|
* int string atom identity issue by giving priority to unit strings for |
688 |
|
* '0' through '9' (see JSString::intString in jsstrinlines.h). |
689 |
|
*/ |
690 |
|
JS_STATIC_ASSERT(INT_STRING_LIMIT <= 999); |
691 |
|
if (2 <= length && length <= 3) { |
692 |
|
const jschar *chars = str->chars(); |
693 |
|
|
694 |
|
if ('1' <= chars[0] && chars[0] <= '9' && |
695 |
|
'0' <= chars[1] && chars[1] <= '9' && |
696 |
|
(length == 2 || ('0' <= chars[2] && chars[2] <= '9'))) { |
697 |
|
jsint i = (chars[0] - '0') * 10 + chars[1] - '0'; |
698 |
|
|
699 |
|
if (length == 3) |
700 |
|
i = i * 10 + chars[2] - '0'; |
701 |
|
if (jsuint(i) < INT_STRING_LIMIT) |
702 |
|
return (JSAtom *) STRING_TO_JSVAL(JSString::intString(i)); |
703 |
|
} |
704 |
|
} |
705 |
|
|
706 |
state = &cx->runtime->atomState; |
state = &cx->runtime->atomState; |
707 |
table = &state->stringAtoms; |
table = &state->stringAtoms; |
708 |
|
|
720 |
* trigger GC which may rehash the table and make the entry invalid. |
* trigger GC which may rehash the table and make the entry invalid. |
721 |
*/ |
*/ |
722 |
++table->generation; |
++table->generation; |
723 |
if (!(flags & ATOM_TMPSTR) && JSSTRING_IS_FLAT(str)) { |
if (!(flags & ATOM_TMPSTR) && str->isFlat()) { |
724 |
JSFLATSTR_CLEAR_MUTABLE(str); |
str->flatClearMutable(); |
725 |
key = str; |
key = str; |
726 |
} else { |
} else { |
727 |
gen = table->generation; |
gen = table->generation; |
729 |
|
|
730 |
if (flags & ATOM_TMPSTR) { |
if (flags & ATOM_TMPSTR) { |
731 |
if (flags & ATOM_NOCOPY) { |
if (flags & ATOM_NOCOPY) { |
732 |
key = js_NewString(cx, JSFLATSTR_CHARS(str), |
key = js_NewString(cx, str->flatChars(), str->flatLength()); |
|
JSFLATSTR_LENGTH(str)); |
|
733 |
if (!key) |
if (!key) |
734 |
return NULL; |
return NULL; |
735 |
|
|
736 |
/* Finish handing off chars to the GC'ed key string. */ |
/* Finish handing off chars to the GC'ed key string. */ |
737 |
str->u.chars = NULL; |
str->mChars = NULL; |
738 |
} else { |
} else { |
739 |
key = js_NewStringCopyN(cx, JSFLATSTR_CHARS(str), |
key = js_NewStringCopyN(cx, str->flatChars(), str->flatLength()); |
|
JSFLATSTR_LENGTH(str)); |
|
740 |
if (!key) |
if (!key) |
741 |
return NULL; |
return NULL; |
742 |
} |
} |
743 |
} else { |
} else { |
744 |
JS_ASSERT(JSSTRING_IS_DEPENDENT(str)); |
JS_ASSERT(str->isDependent()); |
745 |
if (!js_UndependString(cx, str)) |
if (!js_UndependString(cx, str)) |
746 |
return NULL; |
return NULL; |
747 |
key = str; |
key = str; |
763 |
} |
} |
764 |
} |
} |
765 |
INIT_ATOM_ENTRY(entry, key); |
INIT_ATOM_ENTRY(entry, key); |
766 |
JSFLATSTR_SET_ATOMIZED(key); |
key->flatSetAtomized(); |
767 |
} |
} |
768 |
|
|
769 |
finish: |
finish: |
770 |
ADD_ATOM_ENTRY_FLAGS(entry, flags & (ATOM_PINNED | ATOM_INTERNED)); |
ADD_ATOM_ENTRY_FLAGS(entry, flags & (ATOM_PINNED | ATOM_INTERNED)); |
771 |
JS_ASSERT(JSSTRING_IS_ATOMIZED(key)); |
JS_ASSERT(key->isAtomized()); |
772 |
v = STRING_TO_JSVAL(key); |
v = STRING_TO_JSVAL(key); |
773 |
cx->weakRoots.lastAtom = v; |
cx->weakRoots.lastAtom = v; |
774 |
JS_UNLOCK(cx, &state->lock); |
JS_UNLOCK(cx, &state->lock); |
810 |
flags |= ATOM_NOCOPY; |
flags |= ATOM_NOCOPY; |
811 |
} |
} |
812 |
|
|
813 |
JSFLATSTR_INIT(&str, (jschar *)chars, inflatedLength); |
str.initFlat(chars, inflatedLength); |
814 |
atom = js_AtomizeString(cx, &str, ATOM_TMPSTR | flags); |
atom = js_AtomizeString(cx, &str, ATOM_TMPSTR | flags); |
815 |
if (chars != inflated && str.u.chars) |
if (chars != inflated && str.flatChars()) |
816 |
JS_free(cx, chars); |
cx->free(chars); |
817 |
return atom; |
return atom; |
818 |
} |
} |
819 |
|
|
822 |
{ |
{ |
823 |
JSString str; |
JSString str; |
824 |
|
|
825 |
JSFLATSTR_INIT(&str, (jschar *)chars, length); |
str.initFlat((jschar *)chars, length); |
826 |
return js_AtomizeString(cx, &str, ATOM_TMPSTR | flags); |
return js_AtomizeString(cx, &str, ATOM_TMPSTR | flags); |
827 |
} |
} |
828 |
|
|
833 |
JSAtomState *state; |
JSAtomState *state; |
834 |
JSDHashEntryHdr *hdr; |
JSDHashEntryHdr *hdr; |
835 |
|
|
836 |
JSFLATSTR_INIT(&str, (jschar *)chars, length); |
if (length == 1) { |
837 |
|
jschar c = *chars; |
838 |
|
if (c < UNIT_STRING_LIMIT) |
839 |
|
return (JSAtom *) STRING_TO_JSVAL(JSString::unitString(c)); |
840 |
|
} |
841 |
|
|
842 |
|
str.initFlat((jschar *)chars, length); |
843 |
state = &cx->runtime->atomState; |
state = &cx->runtime->atomState; |
844 |
|
|
845 |
JS_LOCK(cx, &state->lock); |
JS_LOCK(cx, &state->lock); |
874 |
return JS_TRUE; |
return JS_TRUE; |
875 |
} |
} |
876 |
|
|
|
JSBool |
|
|
js_ValueToStringId(JSContext *cx, jsval v, jsid *idp) |
|
|
{ |
|
|
JSString *str; |
|
|
JSAtom *atom; |
|
|
|
|
|
/* |
|
|
* Optimize for the common case where v is an already-atomized string. The |
|
|
* comment in jsstr.h before the JSSTRING_SET_ATOMIZED macro's definition |
|
|
* explains why this is thread-safe. The extra rooting via lastAtom (which |
|
|
* would otherwise be done in js_js_AtomizeString) ensures the caller that |
|
|
* the resulting id at is least weakly rooted. |
|
|
*/ |
|
|
if (JSVAL_IS_STRING(v)) { |
|
|
str = JSVAL_TO_STRING(v); |
|
|
if (JSSTRING_IS_ATOMIZED(str)) { |
|
|
cx->weakRoots.lastAtom = v; |
|
|
*idp = ATOM_TO_JSID((JSAtom *) v); |
|
|
return JS_TRUE; |
|
|
} |
|
|
} else { |
|
|
str = js_ValueToString(cx, v); |
|
|
if (!str) |
|
|
return JS_FALSE; |
|
|
} |
|
|
atom = js_AtomizeString(cx, str, 0); |
|
|
if (!atom) |
|
|
return JS_FALSE; |
|
|
*idp = ATOM_TO_JSID(atom); |
|
|
return JS_TRUE; |
|
|
} |
|
|
|
|
877 |
#ifdef DEBUG |
#ifdef DEBUG |
878 |
|
|
879 |
static JSDHashOperator |
static JSDHashOperator |
1130 |
* with the given key. |
* with the given key. |
1131 |
*/ |
*/ |
1132 |
if (how == HOIST && ale->entry.next) { |
if (how == HOIST && ale->entry.next) { |
1133 |
|
JS_ASSERT(*hep == &ale->entry); |
1134 |
*hep = ale->entry.next; |
*hep = ale->entry.next; |
1135 |
ale->entry.next = NULL; |
ale->entry.next = NULL; |
1136 |
do { |
do { |