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
16 * The Original Code is Mozilla Communicator client code, released
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.
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 ***** */
47 #include "jsutil.h" /* Added by JSIFY */
48 #include "jshash.h" /* Added by JSIFY */
53 #include "jsversion.h"
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"
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
[] = {
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"
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 */
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 */
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";
221 #if JS_HAS_GENERATORS
222 const char js_close_str
[] = "close";
223 const char js_send_str
[] = "send";
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";
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
{
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)))
264 HashDouble(JSDHashTable
*table
, const void *key
);
267 MatchDouble(JSDHashTable
*table
, const JSDHashEntryHdr
*hdr
, const void *key
);
270 HashString(JSDHashTable
*table
, const void *key
);
273 MatchString(JSDHashTable
*table
, const JSDHashEntryHdr
*hdr
, const void *key
);
275 static const JSDHashTableOps DoubleHashOps
= {
280 JS_DHashMoveEntryStub
,
281 JS_DHashClearEntryStub
,
282 JS_DHashFinalizeStub
,
286 static const JSDHashTableOps StringHashOps
= {
291 JS_DHashMoveEntryStub
,
292 JS_DHashClearEntryStub
,
293 JS_DHashFinalizeStub
,
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)
303 HashDouble(JSDHashTable
*table
, const void *key
)
307 JS_ASSERT(IS_DOUBLE_TABLE(table
));
308 d
= *(jsdouble
*)key
;
309 return JSDOUBLE_HI32(d
) ^ JSDOUBLE_LO32(d
);
313 HashString(JSDHashTable
*table
, const void *key
)
315 JS_ASSERT(IS_STRING_TABLE(table
));
316 return js_HashString((JSString
*)key
);
320 MatchDouble(JSDHashTable
*table
, const JSDHashEntryHdr
*hdr
, const void *key
)
322 JSAtomHashEntry
*entry
= TO_ATOM_ENTRY(hdr
);
325 JS_ASSERT(IS_DOUBLE_TABLE(table
));
326 if (entry
->keyAndFlags
== 0) {
327 /* See comments in MatchString. */
331 d1
= *(jsdouble
*)ATOM_ENTRY_KEY(entry
);
332 d2
= *(jsdouble
*)key
;
333 if (JSDOUBLE_IS_NaN(d1
))
334 return JSDOUBLE_IS_NaN(d2
);
336 /* XXX MSVC miscompiles such that (NaN == 0) */
337 if (JSDOUBLE_IS_NaN(d2
))
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
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
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
;
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
;
403 JS_ASSERT(IS_DOUBLE_TABLE(&state
->doubleAtoms
));
406 js_InitLock(&state
->lock
);
408 JS_ASSERT(IS_INITIALIZED_STATE(state
));
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
;
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
;
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.
446 JS_DHashTableEnumerate(&state
->stringAtoms
, js_string_uninterner
, rt
);
447 JS_DHashTableFinish(&state
->stringAtoms
);
448 JS_DHashTableFinish(&state
->doubleAtoms
);
451 js_FinishLock(&state
->lock
);
454 memset(state
, JS_FREE_PATTERN
, sizeof *state
);
459 js_InitCommonAtoms(JSContext
*cx
)
461 JSAtomState
*state
= &cx
->runtime
->atomState
;
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
);
472 JS_ASSERT((uint8
*)atoms
- (uint8
*)state
== LAZY_ATOM_OFFSET_START
);
473 memset(atoms
, 0, ATOM_OFFSET_LIMIT
- LAZY_ATOM_OFFSET_START
);
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
;
488 js_FinishCommonAtoms(JSContext
*cx
)
490 JSAtomState
*state
= &cx
->runtime
->atomState
;
492 JS_DHashTableEnumerate(&state
->stringAtoms
, js_atom_unpinner
, NULL
);
494 memset(COMMON_ATOMS_START(state
), JS_FREE_PATTERN
,
495 ATOM_OFFSET_LIMIT
- ATOM_OFFSET_START
);
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
,
531 JS_CallTracer(trc
, ATOM_ENTRY_KEY(entry
), JSTRACE_STRING
);
533 return JS_DHASH_NEXT
;
537 js_TraceAtomState(JSTracer
*trc
, JSBool allAtoms
)
541 state
= &trc
->context
->runtime
->atomState
;
543 JS_DHashTableEnumerate(&state
->doubleAtoms
, js_locked_atom_tracer
, trc
);
544 JS_DHashTableEnumerate(&state
->stringAtoms
, js_locked_atom_tracer
, trc
);
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
;
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
++;
588 js_AtomizeDouble(JSContext
*cx
, jsdouble d
)
592 JSAtomHashEntry
*entry
;
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
));
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
);
612 JS_LOCK(cx
, &state
->lock
);
613 if (table
->generation
== gen
) {
614 JS_ASSERT(entry
->keyAndFlags
== 0);
616 entry
= TO_ATOM_ENTRY(JS_DHashTableOperate(table
, key
,
619 goto failed_hash_add
;
620 if (entry
->keyAndFlags
!= 0)
624 INIT_ATOM_ENTRY(entry
, key
);
628 v
= DOUBLE_TO_JSVAL((jsdouble
*)ATOM_ENTRY_KEY(entry
));
629 cx
->weakRoots
.lastAtom
= v
;
630 JS_UNLOCK(cx
, &state
->lock
);
635 JS_UNLOCK(cx
, &state
->lock
);
636 JS_ReportOutOfMemory(cx
);
641 js_AtomizeString(JSContext
*cx
, JSString
*str
, uintN flags
)
646 JSAtomHashEntry
*entry
;
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
));
659 goto failed_hash_add
;
660 if (entry
->keyAndFlags
!= 0) {
661 key
= (JSString
*)ATOM_ENTRY_KEY(entry
);
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.
670 if (!(flags
& ATOM_TMPSTR
) && JSSTRING_IS_FLAT(str
)) {
671 JSFLATSTR_CLEAR_MUTABLE(str
);
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
));
684 /* Finish handing off chars to the GC'ed key string. */
687 key
= js_NewStringCopyN(cx
, JSFLATSTR_CHARS(str
),
688 JSFLATSTR_LENGTH(str
));
693 JS_ASSERT(JSSTRING_IS_DEPENDENT(str
));
694 if (!js_UndependString(cx
, str
))
699 JS_LOCK(cx
, &state
->lock
);
700 if (table
->generation
== gen
) {
701 JS_ASSERT(entry
->keyAndFlags
== 0);
703 entry
= TO_ATOM_ENTRY(JS_DHashTableOperate(table
, key
,
706 goto failed_hash_add
;
707 if (entry
->keyAndFlags
!= 0) {
708 key
= (JSString
*)ATOM_ENTRY_KEY(entry
);
714 INIT_ATOM_ENTRY(entry
, key
);
715 JSFLATSTR_SET_ATOMIZED(key
);
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
);
727 JS_UNLOCK(cx
, &state
->lock
);
728 JS_ReportOutOfMemory(cx
);
733 js_Atomize(JSContext
*cx
, const char *bytes
, size_t length
, uintN flags
)
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;
755 inflatedLength
= length
;
756 chars
= js_InflateString(cx
, bytes
, &inflatedLength
);
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
)
770 js_AtomizeChars(JSContext
*cx
, const jschar
*chars
, size_t length
, uintN flags
)
774 JSFLATSTR_INIT(&str
, (jschar
*)chars
, length
);
775 return js_AtomizeString(cx
, &str
, ATOM_TMPSTR
| flags
);
779 js_GetExistingStringAtom(JSContext
*cx
, const jschar
*chars
, size_t length
)
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
))
793 JS_UNLOCK(cx
, &state
->lock
);
795 return str2
? (JSAtom
*)STRING_TO_JSVAL(str2
) : NULL
;
799 js_AtomizePrimitiveValue(JSContext
*cx
, jsval v
, JSAtom
**atomp
)
803 if (JSVAL_IS_STRING(v
)) {
804 atom
= js_AtomizeString(cx
, JSVAL_TO_STRING(v
), 0);
807 } else if (JSVAL_IS_DOUBLE(v
)) {
808 atom
= js_AtomizeDouble(cx
, *JSVAL_TO_DOUBLE(v
));
812 JS_ASSERT(JSVAL_IS_INT(v
) || JSVAL_IS_BOOLEAN(v
) ||
813 JSVAL_IS_NULL(v
) || JSVAL_IS_VOID(v
));
821 js_ValueToStringId(JSContext
*cx
, jsval v
, jsid
*idp
)
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
);
841 str
= js_ValueToString(cx
, v
);
845 atom
= js_AtomizeString(cx
, str
, 0);
848 *idp
= ATOM_TO_JSID(atom
);
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
;
863 fprintf(fp
, "%3u %08x ", number
, (uintN
)entry
->hdr
.keyHash
);
864 if (entry
->keyAndFlags
== 0) {
865 fputs("<uninitialized>", fp
);
867 key
= ATOM_ENTRY_KEY(entry
);
868 if (IS_DOUBLE_TABLE(table
)) {
869 fprintf(fp
, "%.16g", *(jsdouble
*)key
);
871 JS_ASSERT(IS_STRING_TABLE(table
));
872 js_FileEscapedString(fp
, (JSString
*)key
, '"');
874 flags
= ATOM_ENTRY_FLAGS(entry
);
876 fputs((flags
& (ATOM_PINNED
| ATOM_INTERNED
))
877 ? " pinned | interned"
878 : (flags
& ATOM_PINNED
) ? " pinned" : " interned",
883 return JS_DHASH_NEXT
;
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
);
894 JS_DHashTableDumpMeter(&state
->stringAtoms
, atom_dumper
, fp
);
898 fprintf(fp
, "doubleAtoms table contents:\n");
899 JS_DHashTableEnumerate(&state
->doubleAtoms
, atom_dumper
, fp
);
901 JS_DHashTableDumpMeter(&state
->doubleAtoms
, atom_dumper
, fp
);
909 js_hash_atom_ptr(const void *key
)
911 const JSAtom
*atom
= (const JSAtom
*) key
;
912 return ATOM_HASH(atom
);
916 js_alloc_temp_space(void *priv
, size_t size
)
918 JSContext
*cx
= (JSContext
*) priv
;
921 JS_ARENA_ALLOCATE(space
, &cx
->tempPool
, size
);
923 js_ReportOutOfScriptQuota(cx
);
928 js_free_temp_space(void *priv
, void *item
)
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
);
940 js_ReportOutOfScriptQuota(cx
);
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
957 js_IndexAtom(JSContext
*cx
, JSAtom
*atom
, JSAtomList
*al
)
959 JSAtomListElement
*ale
, *ale2
, *next
;
962 ATOM_LIST_LOOKUP(ale
, hep
, al
, atom
);
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
);
970 ALE_SET_ATOM(ale
, atom
);
971 ale
->entry
.next
= al
->list
;
972 al
->list
= &ale
->entry
;
974 /* We want to hash. Have we already made a hash table? */
976 /* No hash table yet, so hep had better be null! */
978 al
->table
= JS_NewHashTable(al
->count
+ 1, js_hash_atom_ptr
,
979 JS_CompareValues
, JS_CompareValues
,
980 &temp_alloc_ops
, cx
);
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
,
996 ale2
->entry
.next
= *hep
;
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
,
1013 ALE_SET_INDEX(ale
, al
->count
++);
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
;
1029 static jsrefcount js_atom_map_count
;
1030 static jsrefcount js_atom_map_hash_table_count
;
1034 js_InitAtomMap(JSContext
*cx
, JSAtomMap
*map
, JSAtomList
*al
)
1037 JSAtomListElement
*ale
;
1040 /* Map length must already be initialized. */
1041 JS_ASSERT(al
->count
== map
->length
);
1043 JS_ATOMIC_INCREMENT(&js_atom_map_count
);
1045 ale
= (JSAtomListElement
*)al
->list
;
1046 if (!ale
&& !al
->table
) {
1047 JS_ASSERT(!map
->vector
);
1052 vector
= map
->vector
;
1055 JS_ATOMIC_INCREMENT(&js_atom_map_hash_table_count
);
1057 JS_HashTableEnumerateEntries(al
->table
, js_map_atom
, vector
);
1060 vector
[ALE_INDEX(ale
)] = ALE_ATOM(ale
);
1061 } while ((ale
= ALE_NEXT(ale
)) != NULL
);