Bug 463806 - [PATCH][@font-face] Downloaded font activation on Mac may fail due to...
[wine-gecko.git] / js / src / jsatom.cpp
blob7bcfeca9b2b944ef86420b2994a6bce8e18b9275
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is Mozilla Communicator client code, released
17 * March 31, 1998.
19 * The Initial Developer of the Original Code is
20 * Netscape Communications Corporation.
21 * Portions created by the Initial Developer are Copyright (C) 1998
22 * the Initial Developer. All Rights Reserved.
24 * Contributor(s):
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
41 * JS atom table.
43 #include "jsstddef.h"
44 #include <stdlib.h>
45 #include <string.h>
46 #include "jstypes.h"
47 #include "jsutil.h" /* Added by JSIFY */
48 #include "jshash.h" /* Added by JSIFY */
49 #include "jsprf.h"
50 #include "jsapi.h"
51 #include "jsatom.h"
52 #include "jscntxt.h"
53 #include "jsversion.h"
54 #include "jsgc.h"
55 #include "jslock.h"
56 #include "jsnum.h"
57 #include "jsscan.h"
58 #include "jsstr.h"
60 const char *
61 js_AtomToPrintableString(JSContext *cx, JSAtom *atom)
63 return js_ValueToPrintableString(cx, ATOM_KEY(atom));
66 #define JS_PROTO(name,code,init) const char js_##name##_str[] = #name;
67 #include "jsproto.tbl"
68 #undef JS_PROTO
71 * String constants for common atoms defined in JSAtomState starting from
72 * JSAtomState.emptyAtom until JSAtomState.lazy.
74 * The elements of the array after the first empty string define strings
75 * corresponding to the two boolean literals, false and true, followed by the
76 * JSType enumerators from jspubtd.h starting with "undefined" for JSTYPE_VOID
77 * (which is pseudo-boolean 2) and continuing as initialized below. The static
78 * asserts check these relations.
80 JS_STATIC_ASSERT(JSTYPE_LIMIT == 8);
81 JS_STATIC_ASSERT(JSVAL_TO_BOOLEAN(JSVAL_VOID) == 2);
82 JS_STATIC_ASSERT(JSTYPE_VOID == 0);
84 const char *const js_common_atom_names[] = {
85 "", /* emptyAtom */
86 js_false_str, /* booleanAtoms[0] */
87 js_true_str, /* booleanAtoms[1] */
88 js_undefined_str, /* typeAtoms[JSTYPE_VOID] */
89 js_object_str, /* typeAtoms[JSTYPE_OBJECT] */
90 js_function_str, /* typeAtoms[JSTYPE_FUNCTION] */
91 "string", /* typeAtoms[JSTYPE_STRING] */
92 "number", /* typeAtoms[JSTYPE_NUMBER] */
93 "boolean", /* typeAtoms[JSTYPE_BOOLEAN] */
94 js_null_str, /* typeAtoms[JSTYPE_NULL] */
95 "xml", /* typeAtoms[JSTYPE_XML] */
96 js_null_str, /* nullAtom */
98 #define JS_PROTO(name,code,init) js_##name##_str,
99 #include "jsproto.tbl"
100 #undef JS_PROTO
102 js_anonymous_str, /* anonymousAtom */
103 js_apply_str, /* applyAtom */
104 js_arguments_str, /* argumentsAtom */
105 js_arity_str, /* arityAtom */
106 js_call_str, /* callAtom */
107 js_callee_str, /* calleeAtom */
108 js_caller_str, /* callerAtom */
109 js_class_prototype_str, /* classPrototypeAtom */
110 js_constructor_str, /* constructorAtom */
111 js_count_str, /* countAtom */
112 js_each_str, /* eachAtom */
113 js_eval_str, /* evalAtom */
114 js_fileName_str, /* fileNameAtom */
115 js_get_str, /* getAtom */
116 js_getter_str, /* getterAtom */
117 js_index_str, /* indexAtom */
118 js_input_str, /* inputAtom */
119 js_iterator_str, /* iteratorAtom */
120 js_length_str, /* lengthAtom */
121 js_lineNumber_str, /* lineNumberAtom */
122 js_message_str, /* messageAtom */
123 js_name_str, /* nameAtom */
124 js_next_str, /* nextAtom */
125 js_noSuchMethod_str, /* noSuchMethodAtom */
126 js_parent_str, /* parentAtom */
127 js_proto_str, /* protoAtom */
128 js_set_str, /* setAtom */
129 js_setter_str, /* setterAtom */
130 js_stack_str, /* stackAtom */
131 js_toLocaleString_str, /* toLocaleStringAtom */
132 js_toSource_str, /* toSourceAtom */
133 js_toString_str, /* toStringAtom */
134 js_valueOf_str, /* valueOfAtom */
135 js_toJSON_str, /* toJSONAtom */
136 "(void 0)", /* void0Atom */
138 #if JS_HAS_XML_SUPPORT
139 js_etago_str, /* etagoAtom */
140 js_namespace_str, /* namespaceAtom */
141 js_ptagc_str, /* ptagcAtom */
142 js_qualifier_str, /* qualifierAtom */
143 js_space_str, /* spaceAtom */
144 js_stago_str, /* stagoAtom */
145 js_star_str, /* starAtom */
146 js_starQualifier_str, /* starQualifierAtom */
147 js_tagc_str, /* tagcAtom */
148 js_xml_str, /* xmlAtom */
149 #endif
151 #ifdef NARCISSUS
152 js___call___str, /* __call__Atom */
153 js___construct___str, /* __construct__Atom */
154 js___hasInstance___str, /* __hasInstance__Atom */
155 js_ExecutionContext_str, /* ExecutionContextAtom */
156 js_current_str, /* currentAtom */
157 #endif
160 JS_STATIC_ASSERT(JS_ARRAY_LENGTH(js_common_atom_names) * sizeof(JSAtom *) ==
161 LAZY_ATOM_OFFSET_START - ATOM_OFFSET_START);
164 * Interpreter macros called by the trace recorder assume common atom indexes
165 * fit in one byte of immediate operand.
167 JS_STATIC_ASSERT(JS_ARRAY_LENGTH(js_common_atom_names) < 256);
169 const size_t js_common_atom_count = JS_ARRAY_LENGTH(js_common_atom_names);
171 const char js_anonymous_str[] = "anonymous";
172 const char js_apply_str[] = "apply";
173 const char js_arguments_str[] = "arguments";
174 const char js_arity_str[] = "arity";
175 const char js_call_str[] = "call";
176 const char js_callee_str[] = "callee";
177 const char js_caller_str[] = "caller";
178 const char js_class_prototype_str[] = "prototype";
179 const char js_constructor_str[] = "constructor";
180 const char js_count_str[] = "__count__";
181 const char js_each_str[] = "each";
182 const char js_eval_str[] = "eval";
183 const char js_fileName_str[] = "fileName";
184 const char js_get_str[] = "get";
185 const char js_getter_str[] = "getter";
186 const char js_index_str[] = "index";
187 const char js_input_str[] = "input";
188 const char js_iterator_str[] = "__iterator__";
189 const char js_length_str[] = "length";
190 const char js_lineNumber_str[] = "lineNumber";
191 const char js_message_str[] = "message";
192 const char js_name_str[] = "name";
193 const char js_next_str[] = "next";
194 const char js_noSuchMethod_str[] = "__noSuchMethod__";
195 const char js_object_str[] = "object";
196 const char js_parent_str[] = "__parent__";
197 const char js_proto_str[] = "__proto__";
198 const char js_setter_str[] = "setter";
199 const char js_set_str[] = "set";
200 const char js_stack_str[] = "stack";
201 const char js_toSource_str[] = "toSource";
202 const char js_toString_str[] = "toString";
203 const char js_toLocaleString_str[] = "toLocaleString";
204 const char js_undefined_str[] = "undefined";
205 const char js_valueOf_str[] = "valueOf";
206 const char js_toJSON_str[] = "toJSON";
208 #if JS_HAS_XML_SUPPORT
209 const char js_etago_str[] = "</";
210 const char js_namespace_str[] = "namespace";
211 const char js_ptagc_str[] = "/>";
212 const char js_qualifier_str[] = "::";
213 const char js_space_str[] = " ";
214 const char js_stago_str[] = "<";
215 const char js_star_str[] = "*";
216 const char js_starQualifier_str[] = "*::";
217 const char js_tagc_str[] = ">";
218 const char js_xml_str[] = "xml";
219 #endif
221 #if JS_HAS_GENERATORS
222 const char js_close_str[] = "close";
223 const char js_send_str[] = "send";
224 #endif
226 #ifdef NARCISSUS
227 const char js___call___str[] = "__call__";
228 const char js___construct___str[] = "__construct__";
229 const char js___hasInstance___str[] = "__hasInstance__";
230 const char js_ExecutionContext_str[] = "ExecutionContext";
231 const char js_current_str[] = "current";
232 #endif
235 * JSAtomState.doubleAtoms and JSAtomState.stringAtoms hashtable entry. To
236 * support pinned and interned string atoms, we use the lowest bits of the
237 * keyAndFlags field to store ATOM_PINNED and ATOM_INTERNED flags.
239 typedef struct JSAtomHashEntry {
240 JSDHashEntryHdr hdr;
241 jsuword keyAndFlags;
242 } JSAtomHashEntry;
244 #define ATOM_ENTRY_FLAG_MASK (ATOM_PINNED | ATOM_INTERNED)
246 JS_STATIC_ASSERT(ATOM_ENTRY_FLAG_MASK < JSVAL_ALIGN);
249 * Helper macros to access and modify JSAtomHashEntry.
251 #define TO_ATOM_ENTRY(hdr) ((JSAtomHashEntry *) hdr)
252 #define ATOM_ENTRY_KEY(entry) \
253 ((void *)((entry)->keyAndFlags & ~ATOM_ENTRY_FLAG_MASK))
254 #define ATOM_ENTRY_FLAGS(entry) \
255 ((uintN)((entry)->keyAndFlags & ATOM_ENTRY_FLAG_MASK))
256 #define INIT_ATOM_ENTRY(entry, key) \
257 ((void)((entry)->keyAndFlags = (jsuword)(key)))
258 #define ADD_ATOM_ENTRY_FLAGS(entry, flags) \
259 ((void)((entry)->keyAndFlags |= (jsuword)(flags)))
260 #define CLEAR_ATOM_ENTRY_FLAGS(entry, flags) \
261 ((void)((entry)->keyAndFlags &= ~(jsuword)(flags)))
263 static JSDHashNumber
264 HashDouble(JSDHashTable *table, const void *key);
266 static JSBool
267 MatchDouble(JSDHashTable *table, const JSDHashEntryHdr *hdr, const void *key);
269 static JSDHashNumber
270 HashString(JSDHashTable *table, const void *key);
272 static JSBool
273 MatchString(JSDHashTable *table, const JSDHashEntryHdr *hdr, const void *key);
275 static const JSDHashTableOps DoubleHashOps = {
276 JS_DHashAllocTable,
277 JS_DHashFreeTable,
278 HashDouble,
279 MatchDouble,
280 JS_DHashMoveEntryStub,
281 JS_DHashClearEntryStub,
282 JS_DHashFinalizeStub,
283 NULL
286 static const JSDHashTableOps StringHashOps = {
287 JS_DHashAllocTable,
288 JS_DHashFreeTable,
289 HashString,
290 MatchString,
291 JS_DHashMoveEntryStub,
292 JS_DHashClearEntryStub,
293 JS_DHashFinalizeStub,
294 NULL
297 #define IS_DOUBLE_TABLE(table) ((table)->ops == &DoubleHashOps)
298 #define IS_STRING_TABLE(table) ((table)->ops == &StringHashOps)
300 #define IS_INITIALIZED_STATE(state) IS_DOUBLE_TABLE(&(state)->doubleAtoms)
302 static JSDHashNumber
303 HashDouble(JSDHashTable *table, const void *key)
305 jsdouble d;
307 JS_ASSERT(IS_DOUBLE_TABLE(table));
308 d = *(jsdouble *)key;
309 return JSDOUBLE_HI32(d) ^ JSDOUBLE_LO32(d);
312 static JSDHashNumber
313 HashString(JSDHashTable *table, const void *key)
315 JS_ASSERT(IS_STRING_TABLE(table));
316 return js_HashString((JSString *)key);
319 static JSBool
320 MatchDouble(JSDHashTable *table, const JSDHashEntryHdr *hdr, const void *key)
322 JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr);
323 jsdouble d1, d2;
325 JS_ASSERT(IS_DOUBLE_TABLE(table));
326 if (entry->keyAndFlags == 0) {
327 /* See comments in MatchString. */
328 return JS_FALSE;
331 d1 = *(jsdouble *)ATOM_ENTRY_KEY(entry);
332 d2 = *(jsdouble *)key;
333 if (JSDOUBLE_IS_NaN(d1))
334 return JSDOUBLE_IS_NaN(d2);
335 #if defined(XP_WIN)
336 /* XXX MSVC miscompiles such that (NaN == 0) */
337 if (JSDOUBLE_IS_NaN(d2))
338 return JS_FALSE;
339 #endif
340 return d1 == d2;
343 static JSBool
344 MatchString(JSDHashTable *table, const JSDHashEntryHdr *hdr, const void *key)
346 JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr);
348 JS_ASSERT(IS_STRING_TABLE(table));
349 if (entry->keyAndFlags == 0) {
351 * This happens when js_AtomizeString adds a new hash entry and
352 * releases the lock but before it takes the lock the second time to
353 * initialize keyAndFlags for the entry.
355 * We always return false for such entries so JS_DHashTableOperate
356 * never finds them. We clean them during GC's sweep phase.
358 * It means that with a contested lock or when GC is triggered outside
359 * the lock we may end up adding two entries, but this is a price for
360 * simpler code.
362 return JS_FALSE;
364 return js_EqualStrings((JSString *)ATOM_ENTRY_KEY(entry), (JSString *)key);
368 * For a browser build from 2007-08-09 after the browser starts up there are
369 * just 55 double atoms, but over 15000 string atoms. Not to penalize more
370 * economical embeddings allocating too much memory initially we initialize
371 * atomized strings with just 1K entries.
373 #define JS_STRING_HASH_COUNT 1024
374 #define JS_DOUBLE_HASH_COUNT 64
376 JSBool
377 js_InitAtomState(JSRuntime *rt)
379 JSAtomState *state = &rt->atomState;
382 * The caller must zero the state before calling this function.
384 JS_ASSERT(!state->stringAtoms.ops);
385 JS_ASSERT(!state->doubleAtoms.ops);
387 if (!JS_DHashTableInit(&state->stringAtoms, &StringHashOps,
388 NULL, sizeof(JSAtomHashEntry),
389 JS_DHASH_DEFAULT_CAPACITY(JS_STRING_HASH_COUNT))) {
390 state->stringAtoms.ops = NULL;
391 return JS_FALSE;
393 JS_ASSERT(IS_STRING_TABLE(&state->stringAtoms));
395 if (!JS_DHashTableInit(&state->doubleAtoms, &DoubleHashOps,
396 NULL, sizeof(JSAtomHashEntry),
397 JS_DHASH_DEFAULT_CAPACITY(JS_DOUBLE_HASH_COUNT))) {
398 state->doubleAtoms.ops = NULL;
399 JS_DHashTableFinish(&state->stringAtoms);
400 state->stringAtoms.ops = NULL;
401 return JS_FALSE;
403 JS_ASSERT(IS_DOUBLE_TABLE(&state->doubleAtoms));
405 #ifdef JS_THREADSAFE
406 js_InitLock(&state->lock);
407 #endif
408 JS_ASSERT(IS_INITIALIZED_STATE(state));
409 return JS_TRUE;
412 static JSDHashOperator
413 js_string_uninterner(JSDHashTable *table, JSDHashEntryHdr *hdr,
414 uint32 number, void *arg)
416 JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr);
417 JSRuntime *rt = (JSRuntime *)arg;
418 JSString *str;
421 * Any string entry that remains at this point must be initialized, as the
422 * last GC should clean any uninitialized ones.
424 JS_ASSERT(IS_STRING_TABLE(table));
425 JS_ASSERT(entry->keyAndFlags != 0);
426 str = (JSString *)ATOM_ENTRY_KEY(entry);
428 /* Pass null as context. */
429 js_FinalizeStringRT(rt, str, js_GetExternalStringGCType(str), NULL);
430 return JS_DHASH_NEXT;
433 void
434 js_FinishAtomState(JSRuntime *rt)
436 JSAtomState *state = &rt->atomState;
438 if (!IS_INITIALIZED_STATE(state)) {
440 * We are called with uninitialized state when JS_NewRuntime fails and
441 * calls JS_DestroyRuntime on a partially initialized runtime.
443 return;
446 JS_DHashTableEnumerate(&state->stringAtoms, js_string_uninterner, rt);
447 JS_DHashTableFinish(&state->stringAtoms);
448 JS_DHashTableFinish(&state->doubleAtoms);
450 #ifdef JS_THREADSAFE
451 js_FinishLock(&state->lock);
452 #endif
453 #ifdef DEBUG
454 memset(state, JS_FREE_PATTERN, sizeof *state);
455 #endif
458 JSBool
459 js_InitCommonAtoms(JSContext *cx)
461 JSAtomState *state = &cx->runtime->atomState;
462 uintN i;
463 JSAtom **atoms;
465 atoms = COMMON_ATOMS_START(state);
466 for (i = 0; i < JS_ARRAY_LENGTH(js_common_atom_names); i++, atoms++) {
467 *atoms = js_Atomize(cx, js_common_atom_names[i],
468 strlen(js_common_atom_names[i]), ATOM_PINNED);
469 if (!*atoms)
470 return JS_FALSE;
472 JS_ASSERT((uint8 *)atoms - (uint8 *)state == LAZY_ATOM_OFFSET_START);
473 memset(atoms, 0, ATOM_OFFSET_LIMIT - LAZY_ATOM_OFFSET_START);
475 return JS_TRUE;
478 static JSDHashOperator
479 js_atom_unpinner(JSDHashTable *table, JSDHashEntryHdr *hdr,
480 uint32 number, void *arg)
482 JS_ASSERT(IS_STRING_TABLE(table));
483 CLEAR_ATOM_ENTRY_FLAGS(TO_ATOM_ENTRY(hdr), ATOM_PINNED);
484 return JS_DHASH_NEXT;
487 void
488 js_FinishCommonAtoms(JSContext *cx)
490 JSAtomState *state = &cx->runtime->atomState;
492 JS_DHashTableEnumerate(&state->stringAtoms, js_atom_unpinner, NULL);
493 #ifdef DEBUG
494 memset(COMMON_ATOMS_START(state), JS_FREE_PATTERN,
495 ATOM_OFFSET_LIMIT - ATOM_OFFSET_START);
496 #endif
499 static JSDHashOperator
500 js_locked_atom_tracer(JSDHashTable *table, JSDHashEntryHdr *hdr,
501 uint32 number, void *arg)
503 JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr);
504 JSTracer *trc = (JSTracer *)arg;
506 if (entry->keyAndFlags == 0) {
507 /* Ignore uninitialized entries during tracing. */
508 return JS_DHASH_NEXT;
510 JS_SET_TRACING_INDEX(trc, "locked_atom", (size_t)number);
511 JS_CallTracer(trc, ATOM_ENTRY_KEY(entry),
512 IS_STRING_TABLE(table) ? JSTRACE_STRING : JSTRACE_DOUBLE);
513 return JS_DHASH_NEXT;
516 static JSDHashOperator
517 js_pinned_atom_tracer(JSDHashTable *table, JSDHashEntryHdr *hdr,
518 uint32 number, void *arg)
520 JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr);
521 JSTracer *trc = (JSTracer *)arg;
522 uintN flags = ATOM_ENTRY_FLAGS(entry);
524 JS_ASSERT(IS_STRING_TABLE(table));
525 if (flags & (ATOM_PINNED | ATOM_INTERNED)) {
526 JS_SET_TRACING_INDEX(trc,
527 flags & ATOM_PINNED
528 ? "pinned_atom"
529 : "interned_atom",
530 (size_t)number);
531 JS_CallTracer(trc, ATOM_ENTRY_KEY(entry), JSTRACE_STRING);
533 return JS_DHASH_NEXT;
536 void
537 js_TraceAtomState(JSTracer *trc, JSBool allAtoms)
539 JSAtomState *state;
541 state = &trc->context->runtime->atomState;
542 if (allAtoms) {
543 JS_DHashTableEnumerate(&state->doubleAtoms, js_locked_atom_tracer, trc);
544 JS_DHashTableEnumerate(&state->stringAtoms, js_locked_atom_tracer, trc);
545 } else {
546 JS_DHashTableEnumerate(&state->stringAtoms, js_pinned_atom_tracer, trc);
550 static JSDHashOperator
551 js_atom_sweeper(JSDHashTable *table, JSDHashEntryHdr *hdr,
552 uint32 number, void *arg)
554 JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr);
555 JSContext *cx = (JSContext *)arg;
557 /* Remove uninitialized entries. */
558 if (entry->keyAndFlags == 0)
559 return JS_DHASH_REMOVE;
561 if (ATOM_ENTRY_FLAGS(entry) & (ATOM_PINNED | ATOM_INTERNED)) {
562 /* Pinned or interned key cannot be finalized. */
563 JS_ASSERT(!js_IsAboutToBeFinalized(cx, ATOM_ENTRY_KEY(entry)));
564 } else if (js_IsAboutToBeFinalized(cx, ATOM_ENTRY_KEY(entry))) {
565 /* Remove entries with things about to be GC'ed. */
566 return JS_DHASH_REMOVE;
568 return JS_DHASH_NEXT;
571 void
572 js_SweepAtomState(JSContext *cx)
574 JSAtomState *state = &cx->runtime->atomState;
576 JS_DHashTableEnumerate(&state->doubleAtoms, js_atom_sweeper, cx);
577 JS_DHashTableEnumerate(&state->stringAtoms, js_atom_sweeper, cx);
580 * Optimize for simplicity and mutate table generation numbers even if the
581 * sweeper has not removed any entries.
583 state->doubleAtoms.generation++;
584 state->stringAtoms.generation++;
587 JSAtom *
588 js_AtomizeDouble(JSContext *cx, jsdouble d)
590 JSAtomState *state;
591 JSDHashTable *table;
592 JSAtomHashEntry *entry;
593 uint32 gen;
594 jsdouble *key;
595 jsval v;
597 state = &cx->runtime->atomState;
598 table = &state->doubleAtoms;
600 JS_LOCK(cx, &state->lock);
601 entry = TO_ATOM_ENTRY(JS_DHashTableOperate(table, &d, JS_DHASH_ADD));
602 if (!entry)
603 goto failed_hash_add;
604 if (entry->keyAndFlags == 0) {
605 gen = ++table->generation;
606 JS_UNLOCK(cx, &state->lock);
608 key = js_NewWeaklyRootedDouble(cx, d);
609 if (!key)
610 return NULL;
612 JS_LOCK(cx, &state->lock);
613 if (table->generation == gen) {
614 JS_ASSERT(entry->keyAndFlags == 0);
615 } else {
616 entry = TO_ATOM_ENTRY(JS_DHashTableOperate(table, key,
617 JS_DHASH_ADD));
618 if (!entry)
619 goto failed_hash_add;
620 if (entry->keyAndFlags != 0)
621 goto finish;
622 ++table->generation;
624 INIT_ATOM_ENTRY(entry, key);
627 finish:
628 v = DOUBLE_TO_JSVAL((jsdouble *)ATOM_ENTRY_KEY(entry));
629 cx->weakRoots.lastAtom = v;
630 JS_UNLOCK(cx, &state->lock);
632 return (JSAtom *)v;
634 failed_hash_add:
635 JS_UNLOCK(cx, &state->lock);
636 JS_ReportOutOfMemory(cx);
637 return NULL;
640 JSAtom *
641 js_AtomizeString(JSContext *cx, JSString *str, uintN flags)
643 jsval v;
644 JSAtomState *state;
645 JSDHashTable *table;
646 JSAtomHashEntry *entry;
647 JSString *key;
648 uint32 gen;
650 JS_ASSERT(!(flags & ~(ATOM_PINNED|ATOM_INTERNED|ATOM_TMPSTR|ATOM_NOCOPY)));
651 JS_ASSERT_IF(flags & ATOM_NOCOPY, flags & ATOM_TMPSTR);
653 state = &cx->runtime->atomState;
654 table = &state->stringAtoms;
656 JS_LOCK(cx, &state->lock);
657 entry = TO_ATOM_ENTRY(JS_DHashTableOperate(table, str, JS_DHASH_ADD));
658 if (!entry)
659 goto failed_hash_add;
660 if (entry->keyAndFlags != 0) {
661 key = (JSString *)ATOM_ENTRY_KEY(entry);
662 } else {
664 * We created a new hashtable entry. Unless str is already allocated
665 * from the GC heap and flat, we have to release state->lock as
666 * string construction is a complex operation. For example, it can
667 * trigger GC which may rehash the table and make the entry invalid.
669 ++table->generation;
670 if (!(flags & ATOM_TMPSTR) && JSSTRING_IS_FLAT(str)) {
671 JSFLATSTR_CLEAR_MUTABLE(str);
672 key = str;
673 } else {
674 gen = table->generation;
675 JS_UNLOCK(cx, &state->lock);
677 if (flags & ATOM_TMPSTR) {
678 if (flags & ATOM_NOCOPY) {
679 key = js_NewString(cx, JSFLATSTR_CHARS(str),
680 JSFLATSTR_LENGTH(str));
681 if (!key)
682 return NULL;
684 /* Finish handing off chars to the GC'ed key string. */
685 str->u.chars = NULL;
686 } else {
687 key = js_NewStringCopyN(cx, JSFLATSTR_CHARS(str),
688 JSFLATSTR_LENGTH(str));
689 if (!key)
690 return NULL;
692 } else {
693 JS_ASSERT(JSSTRING_IS_DEPENDENT(str));
694 if (!js_UndependString(cx, str))
695 return NULL;
696 key = str;
699 JS_LOCK(cx, &state->lock);
700 if (table->generation == gen) {
701 JS_ASSERT(entry->keyAndFlags == 0);
702 } else {
703 entry = TO_ATOM_ENTRY(JS_DHashTableOperate(table, key,
704 JS_DHASH_ADD));
705 if (!entry)
706 goto failed_hash_add;
707 if (entry->keyAndFlags != 0) {
708 key = (JSString *)ATOM_ENTRY_KEY(entry);
709 goto finish;
711 ++table->generation;
714 INIT_ATOM_ENTRY(entry, key);
715 JSFLATSTR_SET_ATOMIZED(key);
718 finish:
719 ADD_ATOM_ENTRY_FLAGS(entry, flags & (ATOM_PINNED | ATOM_INTERNED));
720 JS_ASSERT(JSSTRING_IS_ATOMIZED(key));
721 v = STRING_TO_JSVAL(key);
722 cx->weakRoots.lastAtom = v;
723 JS_UNLOCK(cx, &state->lock);
724 return (JSAtom *)v;
726 failed_hash_add:
727 JS_UNLOCK(cx, &state->lock);
728 JS_ReportOutOfMemory(cx);
729 return NULL;
732 JSAtom *
733 js_Atomize(JSContext *cx, const char *bytes, size_t length, uintN flags)
735 jschar *chars;
736 JSString str;
737 JSAtom *atom;
740 * Avoiding the malloc in js_InflateString on shorter strings saves us
741 * over 20,000 malloc calls on mozilla browser startup. This compares to
742 * only 131 calls where the string is longer than a 31 char (net) buffer.
743 * The vast majority of atomized strings are already in the hashtable. So
744 * js_AtomizeString rarely has to copy the temp string we make.
746 #define ATOMIZE_BUF_MAX 32
747 jschar inflated[ATOMIZE_BUF_MAX];
748 size_t inflatedLength = ATOMIZE_BUF_MAX - 1;
750 if (length < ATOMIZE_BUF_MAX) {
751 js_InflateStringToBuffer(cx, bytes, length, inflated, &inflatedLength);
752 inflated[inflatedLength] = 0;
753 chars = inflated;
754 } else {
755 inflatedLength = length;
756 chars = js_InflateString(cx, bytes, &inflatedLength);
757 if (!chars)
758 return NULL;
759 flags |= ATOM_NOCOPY;
762 JSFLATSTR_INIT(&str, (jschar *)chars, inflatedLength);
763 atom = js_AtomizeString(cx, &str, ATOM_TMPSTR | flags);
764 if (chars != inflated && str.u.chars)
765 JS_free(cx, chars);
766 return atom;
769 JSAtom *
770 js_AtomizeChars(JSContext *cx, const jschar *chars, size_t length, uintN flags)
772 JSString str;
774 JSFLATSTR_INIT(&str, (jschar *)chars, length);
775 return js_AtomizeString(cx, &str, ATOM_TMPSTR | flags);
778 JSAtom *
779 js_GetExistingStringAtom(JSContext *cx, const jschar *chars, size_t length)
781 JSString str, *str2;
782 JSAtomState *state;
783 JSDHashEntryHdr *hdr;
785 JSFLATSTR_INIT(&str, (jschar *)chars, length);
786 state = &cx->runtime->atomState;
788 JS_LOCK(cx, &state->lock);
789 hdr = JS_DHashTableOperate(&state->stringAtoms, &str, JS_DHASH_LOOKUP);
790 str2 = JS_DHASH_ENTRY_IS_BUSY(hdr)
791 ? (JSString *)ATOM_ENTRY_KEY(TO_ATOM_ENTRY(hdr))
792 : NULL;
793 JS_UNLOCK(cx, &state->lock);
795 return str2 ? (JSAtom *)STRING_TO_JSVAL(str2) : NULL;
798 JSBool
799 js_AtomizePrimitiveValue(JSContext *cx, jsval v, JSAtom **atomp)
801 JSAtom *atom;
803 if (JSVAL_IS_STRING(v)) {
804 atom = js_AtomizeString(cx, JSVAL_TO_STRING(v), 0);
805 if (!atom)
806 return JS_FALSE;
807 } else if (JSVAL_IS_DOUBLE(v)) {
808 atom = js_AtomizeDouble(cx, *JSVAL_TO_DOUBLE(v));
809 if (!atom)
810 return JS_FALSE;
811 } else {
812 JS_ASSERT(JSVAL_IS_INT(v) || JSVAL_IS_BOOLEAN(v) ||
813 JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v));
814 atom = (JSAtom *)v;
816 *atomp = atom;
817 return JS_TRUE;
820 JSBool
821 js_ValueToStringId(JSContext *cx, jsval v, jsid *idp)
823 JSString *str;
824 JSAtom *atom;
827 * Optimize for the common case where v is an already-atomized string. The
828 * comment in jsstr.h before the JSSTRING_SET_ATOMIZED macro's definition
829 * explains why this is thread-safe. The extra rooting via lastAtom (which
830 * would otherwise be done in js_js_AtomizeString) ensures the caller that
831 * the resulting id at is least weakly rooted.
833 if (JSVAL_IS_STRING(v)) {
834 str = JSVAL_TO_STRING(v);
835 if (JSSTRING_IS_ATOMIZED(str)) {
836 cx->weakRoots.lastAtom = v;
837 *idp = ATOM_TO_JSID((JSAtom *) v);
838 return JS_TRUE;
840 } else {
841 str = js_ValueToString(cx, v);
842 if (!str)
843 return JS_FALSE;
845 atom = js_AtomizeString(cx, str, 0);
846 if (!atom)
847 return JS_FALSE;
848 *idp = ATOM_TO_JSID(atom);
849 return JS_TRUE;
852 #ifdef DEBUG
854 static JSDHashOperator
855 atom_dumper(JSDHashTable *table, JSDHashEntryHdr *hdr,
856 uint32 number, void *arg)
858 JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr);
859 FILE *fp = (FILE *)arg;
860 void *key;
861 uintN flags;
863 fprintf(fp, "%3u %08x ", number, (uintN)entry->hdr.keyHash);
864 if (entry->keyAndFlags == 0) {
865 fputs("<uninitialized>", fp);
866 } else {
867 key = ATOM_ENTRY_KEY(entry);
868 if (IS_DOUBLE_TABLE(table)) {
869 fprintf(fp, "%.16g", *(jsdouble *)key);
870 } else {
871 JS_ASSERT(IS_STRING_TABLE(table));
872 js_FileEscapedString(fp, (JSString *)key, '"');
874 flags = ATOM_ENTRY_FLAGS(entry);
875 if (flags != 0) {
876 fputs((flags & (ATOM_PINNED | ATOM_INTERNED))
877 ? " pinned | interned"
878 : (flags & ATOM_PINNED) ? " pinned" : " interned",
879 fp);
882 putc('\n', fp);
883 return JS_DHASH_NEXT;
886 JS_FRIEND_API(void)
887 js_DumpAtoms(JSContext *cx, FILE *fp)
889 JSAtomState *state = &cx->runtime->atomState;
891 fprintf(fp, "stringAtoms table contents:\n");
892 JS_DHashTableEnumerate(&state->stringAtoms, atom_dumper, fp);
893 #ifdef JS_DHASHMETER
894 JS_DHashTableDumpMeter(&state->stringAtoms, atom_dumper, fp);
895 #endif
896 putc('\n', fp);
898 fprintf(fp, "doubleAtoms table contents:\n");
899 JS_DHashTableEnumerate(&state->doubleAtoms, atom_dumper, fp);
900 #ifdef JS_DHASHMETER
901 JS_DHashTableDumpMeter(&state->doubleAtoms, atom_dumper, fp);
902 #endif
903 putc('\n', fp);
906 #endif
908 static JSHashNumber
909 js_hash_atom_ptr(const void *key)
911 const JSAtom *atom = (const JSAtom *) key;
912 return ATOM_HASH(atom);
915 static void *
916 js_alloc_temp_space(void *priv, size_t size)
918 JSContext *cx = (JSContext *) priv;
919 void *space;
921 JS_ARENA_ALLOCATE(space, &cx->tempPool, size);
922 if (!space)
923 js_ReportOutOfScriptQuota(cx);
924 return space;
927 static void
928 js_free_temp_space(void *priv, void *item)
932 static JSHashEntry *
933 js_alloc_temp_entry(void *priv, const void *key)
935 JSContext *cx = (JSContext *) priv;
936 JSAtomListElement *ale;
938 JS_ARENA_ALLOCATE_TYPE(ale, JSAtomListElement, &cx->tempPool);
939 if (!ale) {
940 js_ReportOutOfScriptQuota(cx);
941 return NULL;
943 return &ale->entry;
946 static void
947 js_free_temp_entry(void *priv, JSHashEntry *he, uintN flag)
951 static JSHashAllocOps temp_alloc_ops = {
952 js_alloc_temp_space, js_free_temp_space,
953 js_alloc_temp_entry, js_free_temp_entry
956 JSAtomListElement *
957 js_IndexAtom(JSContext *cx, JSAtom *atom, JSAtomList *al)
959 JSAtomListElement *ale, *ale2, *next;
960 JSHashEntry **hep;
962 ATOM_LIST_LOOKUP(ale, hep, al, atom);
963 if (!ale) {
964 if (al->count < 10) {
965 /* Few enough for linear search, no hash table needed. */
966 JS_ASSERT(!al->table);
967 ale = (JSAtomListElement *)js_alloc_temp_entry(cx, atom);
968 if (!ale)
969 return NULL;
970 ALE_SET_ATOM(ale, atom);
971 ale->entry.next = al->list;
972 al->list = &ale->entry;
973 } else {
974 /* We want to hash. Have we already made a hash table? */
975 if (!al->table) {
976 /* No hash table yet, so hep had better be null! */
977 JS_ASSERT(!hep);
978 al->table = JS_NewHashTable(al->count + 1, js_hash_atom_ptr,
979 JS_CompareValues, JS_CompareValues,
980 &temp_alloc_ops, cx);
981 if (!al->table)
982 return NULL;
985 * Set ht->nentries explicitly, because we are moving entries
986 * from al to ht, not calling JS_HashTable(Raw|)Add.
988 al->table->nentries = al->count;
990 /* Insert each ale on al->list into the new hash table. */
991 for (ale2 = (JSAtomListElement *)al->list; ale2; ale2 = next) {
992 next = ALE_NEXT(ale2);
993 ale2->entry.keyHash = ATOM_HASH(ALE_ATOM(ale2));
994 hep = JS_HashTableRawLookup(al->table, ale2->entry.keyHash,
995 ale2->entry.key);
996 ale2->entry.next = *hep;
997 *hep = &ale2->entry;
999 al->list = NULL;
1001 /* Set hep for insertion of atom's ale, immediately below. */
1002 hep = JS_HashTableRawLookup(al->table, ATOM_HASH(atom), atom);
1005 /* Finally, add an entry for atom into the hash bucket at hep. */
1006 ale = (JSAtomListElement *)
1007 JS_HashTableRawAdd(al->table, hep, ATOM_HASH(atom), atom,
1008 NULL);
1009 if (!ale)
1010 return NULL;
1013 ALE_SET_INDEX(ale, al->count++);
1015 return ale;
1018 static intN
1019 js_map_atom(JSHashEntry *he, intN i, void *arg)
1021 JSAtomListElement *ale = (JSAtomListElement *)he;
1022 JSAtom **vector = (JSAtom **) arg;
1024 vector[ALE_INDEX(ale)] = ALE_ATOM(ale);
1025 return HT_ENUMERATE_NEXT;
1028 #ifdef DEBUG
1029 static jsrefcount js_atom_map_count;
1030 static jsrefcount js_atom_map_hash_table_count;
1031 #endif
1033 void
1034 js_InitAtomMap(JSContext *cx, JSAtomMap *map, JSAtomList *al)
1036 JSAtom **vector;
1037 JSAtomListElement *ale;
1038 uint32 count;
1040 /* Map length must already be initialized. */
1041 JS_ASSERT(al->count == map->length);
1042 #ifdef DEBUG
1043 JS_ATOMIC_INCREMENT(&js_atom_map_count);
1044 #endif
1045 ale = (JSAtomListElement *)al->list;
1046 if (!ale && !al->table) {
1047 JS_ASSERT(!map->vector);
1048 return;
1051 count = al->count;
1052 vector = map->vector;
1053 if (al->table) {
1054 #ifdef DEBUG
1055 JS_ATOMIC_INCREMENT(&js_atom_map_hash_table_count);
1056 #endif
1057 JS_HashTableEnumerateEntries(al->table, js_map_atom, vector);
1058 } else {
1059 do {
1060 vector[ALE_INDEX(ale)] = ALE_ATOM(ale);
1061 } while ((ale = ALE_NEXT(ale)) != NULL);
1063 ATOM_LIST_INIT(al);