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 */
60 #include "jsversion.h"
61 #include "jsstrinlines.h"
64 * ATOM_HASH assumes that JSHashNumber is 32-bit even on 64-bit systems.
66 JS_STATIC_ASSERT(sizeof(JSHashNumber
) == 4);
67 JS_STATIC_ASSERT(sizeof(JSAtom
*) == JS_BYTES_PER_WORD
);
70 * Start and limit offsets for atom pointers in JSAtomState must be aligned
71 * on the word boundary.
73 JS_STATIC_ASSERT(ATOM_OFFSET_START
% sizeof(JSAtom
*) == 0);
74 JS_STATIC_ASSERT(ATOM_OFFSET_LIMIT
% sizeof(JSAtom
*) == 0);
77 * JS_BOOLEAN_STR and JS_TYPE_STR assume that boolean names starts from the
78 * index 1 and type name starts from the index 1+2 atoms in JSAtomState.
80 JS_STATIC_ASSERT(1 * sizeof(JSAtom
*) ==
81 offsetof(JSAtomState
, booleanAtoms
) - ATOM_OFFSET_START
);
82 JS_STATIC_ASSERT((1 + 2) * sizeof(JSAtom
*) ==
83 offsetof(JSAtomState
, typeAtoms
) - ATOM_OFFSET_START
);
86 js_AtomToPrintableString(JSContext
*cx
, JSAtom
*atom
)
88 return js_ValueToPrintableString(cx
, ATOM_KEY(atom
));
91 #define JS_PROTO(name,code,init) const char js_##name##_str[] = #name;
92 #include "jsproto.tbl"
96 * String constants for common atoms defined in JSAtomState starting from
97 * JSAtomState.emptyAtom until JSAtomState.lazy.
99 * 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
101 * JSType enumerators from jspubtd.h starting with "undefined" for JSTYPE_VOID
102 * (which is special-value 2) and continuing as initialized below. The static
103 * asserts check these relations.
105 JS_STATIC_ASSERT(JSTYPE_LIMIT
== 8);
106 JS_STATIC_ASSERT(JSTYPE_VOID
== 0);
108 const char *const js_common_atom_names
[] = {
110 js_false_str
, /* booleanAtoms[0] */
111 js_true_str
, /* booleanAtoms[1] */
112 js_undefined_str
, /* typeAtoms[JSTYPE_VOID] */
113 js_object_str
, /* typeAtoms[JSTYPE_OBJECT] */
114 js_function_str
, /* typeAtoms[JSTYPE_FUNCTION] */
115 "string", /* typeAtoms[JSTYPE_STRING] */
116 "number", /* typeAtoms[JSTYPE_NUMBER] */
117 "boolean", /* typeAtoms[JSTYPE_BOOLEAN] */
118 js_null_str
, /* typeAtoms[JSTYPE_NULL] */
119 "xml", /* typeAtoms[JSTYPE_XML] */
120 js_null_str
, /* nullAtom */
122 #define JS_PROTO(name,code,init) js_##name##_str,
123 #include "jsproto.tbl"
126 js_anonymous_str
, /* anonymousAtom */
127 js_apply_str
, /* applyAtom */
128 js_arguments_str
, /* argumentsAtom */
129 js_arity_str
, /* arityAtom */
130 js_call_str
, /* callAtom */
131 js_callee_str
, /* calleeAtom */
132 js_caller_str
, /* callerAtom */
133 js_class_prototype_str
, /* classPrototypeAtom */
134 js_constructor_str
, /* constructorAtom */
135 js_count_str
, /* countAtom */
136 js_each_str
, /* eachAtom */
137 js_eval_str
, /* evalAtom */
138 js_fileName_str
, /* fileNameAtom */
139 js_get_str
, /* getAtom */
140 js_getter_str
, /* getterAtom */
141 js_index_str
, /* indexAtom */
142 js_input_str
, /* inputAtom */
143 js_iterator_str
, /* iteratorAtom */
144 js_length_str
, /* lengthAtom */
145 js_lineNumber_str
, /* lineNumberAtom */
146 js_message_str
, /* messageAtom */
147 js_name_str
, /* nameAtom */
148 js_next_str
, /* nextAtom */
149 js_noSuchMethod_str
, /* noSuchMethodAtom */
150 js_parent_str
, /* parentAtom */
151 js_proto_str
, /* protoAtom */
152 js_set_str
, /* setAtom */
153 js_setter_str
, /* setterAtom */
154 js_stack_str
, /* stackAtom */
155 js_toLocaleString_str
, /* toLocaleStringAtom */
156 js_toSource_str
, /* toSourceAtom */
157 js_toString_str
, /* toStringAtom */
158 js_valueOf_str
, /* valueOfAtom */
159 js_toJSON_str
, /* toJSONAtom */
160 "(void 0)", /* void0Atom */
161 js_enumerable_str
, /* enumerableAtom */
162 js_configurable_str
, /* configurableAtom */
163 js_writable_str
, /* writableAtom */
164 js_value_str
, /* valueAtom */
166 #if JS_HAS_XML_SUPPORT
167 js_etago_str
, /* etagoAtom */
168 js_namespace_str
, /* namespaceAtom */
169 js_ptagc_str
, /* ptagcAtom */
170 js_qualifier_str
, /* qualifierAtom */
171 js_space_str
, /* spaceAtom */
172 js_stago_str
, /* stagoAtom */
173 js_star_str
, /* starAtom */
174 js_starQualifier_str
, /* starQualifierAtom */
175 js_tagc_str
, /* tagcAtom */
176 js_xml_str
, /* xmlAtom */
180 js___call___str
, /* __call__Atom */
181 js___construct___str
, /* __construct__Atom */
182 js___hasInstance___str
, /* __hasInstance__Atom */
183 js_ExecutionContext_str
, /* ExecutionContextAtom */
184 js_current_str
, /* currentAtom */
188 JS_STATIC_ASSERT(JS_ARRAY_LENGTH(js_common_atom_names
) * sizeof(JSAtom
*) ==
189 LAZY_ATOM_OFFSET_START
- ATOM_OFFSET_START
);
192 * Interpreter macros called by the trace recorder assume common atom indexes
193 * fit in one byte of immediate operand.
195 JS_STATIC_ASSERT(JS_ARRAY_LENGTH(js_common_atom_names
) < 256);
197 const size_t js_common_atom_count
= JS_ARRAY_LENGTH(js_common_atom_names
);
199 const char js_anonymous_str
[] = "anonymous";
200 const char js_apply_str
[] = "apply";
201 const char js_arguments_str
[] = "arguments";
202 const char js_arity_str
[] = "arity";
203 const char js_call_str
[] = "call";
204 const char js_callee_str
[] = "callee";
205 const char js_caller_str
[] = "caller";
206 const char js_class_prototype_str
[] = "prototype";
207 const char js_constructor_str
[] = "constructor";
208 const char js_count_str
[] = "__count__";
209 const char js_each_str
[] = "each";
210 const char js_eval_str
[] = "eval";
211 const char js_fileName_str
[] = "fileName";
212 const char js_get_str
[] = "get";
213 const char js_getter_str
[] = "getter";
214 const char js_index_str
[] = "index";
215 const char js_input_str
[] = "input";
216 const char js_iterator_str
[] = "__iterator__";
217 const char js_length_str
[] = "length";
218 const char js_lineNumber_str
[] = "lineNumber";
219 const char js_message_str
[] = "message";
220 const char js_name_str
[] = "name";
221 const char js_next_str
[] = "next";
222 const char js_noSuchMethod_str
[] = "__noSuchMethod__";
223 const char js_object_str
[] = "object";
224 const char js_parent_str
[] = "__parent__";
225 const char js_proto_str
[] = "__proto__";
226 const char js_setter_str
[] = "setter";
227 const char js_set_str
[] = "set";
228 const char js_stack_str
[] = "stack";
229 const char js_toSource_str
[] = "toSource";
230 const char js_toString_str
[] = "toString";
231 const char js_toLocaleString_str
[] = "toLocaleString";
232 const char js_undefined_str
[] = "undefined";
233 const char js_valueOf_str
[] = "valueOf";
234 const char js_toJSON_str
[] = "toJSON";
235 const char js_enumerable_str
[] = "enumerable";
236 const char js_configurable_str
[] = "configurable";
237 const char js_writable_str
[] = "writable";
238 const char js_value_str
[] = "value";
240 #if JS_HAS_XML_SUPPORT
241 const char js_etago_str
[] = "</";
242 const char js_namespace_str
[] = "namespace";
243 const char js_ptagc_str
[] = "/>";
244 const char js_qualifier_str
[] = "::";
245 const char js_space_str
[] = " ";
246 const char js_stago_str
[] = "<";
247 const char js_star_str
[] = "*";
248 const char js_starQualifier_str
[] = "*::";
249 const char js_tagc_str
[] = ">";
250 const char js_xml_str
[] = "xml";
253 #if JS_HAS_GENERATORS
254 const char js_close_str
[] = "close";
255 const char js_send_str
[] = "send";
259 const char js___call___str
[] = "__call__";
260 const char js___construct___str
[] = "__construct__";
261 const char js___hasInstance___str
[] = "__hasInstance__";
262 const char js_ExecutionContext_str
[] = "ExecutionContext";
263 const char js_current_str
[] = "current";
267 * JSAtomState.doubleAtoms and JSAtomState.stringAtoms hashtable entry. To
268 * support pinned and interned string atoms, we use the lowest bits of the
269 * keyAndFlags field to store ATOM_PINNED and ATOM_INTERNED flags.
271 typedef struct JSAtomHashEntry
{
276 #define ATOM_ENTRY_FLAG_MASK (ATOM_PINNED | ATOM_INTERNED)
278 JS_STATIC_ASSERT(ATOM_ENTRY_FLAG_MASK
< JSVAL_ALIGN
);
281 * Helper macros to access and modify JSAtomHashEntry.
283 #define TO_ATOM_ENTRY(hdr) ((JSAtomHashEntry *) hdr)
284 #define ATOM_ENTRY_KEY(entry) \
285 ((void *)((entry)->keyAndFlags & ~ATOM_ENTRY_FLAG_MASK))
286 #define ATOM_ENTRY_FLAGS(entry) \
287 ((uintN)((entry)->keyAndFlags & ATOM_ENTRY_FLAG_MASK))
288 #define INIT_ATOM_ENTRY(entry, key) \
289 ((void)((entry)->keyAndFlags = (jsuword)(key)))
290 #define ADD_ATOM_ENTRY_FLAGS(entry, flags) \
291 ((void)((entry)->keyAndFlags |= (jsuword)(flags)))
292 #define CLEAR_ATOM_ENTRY_FLAGS(entry, flags) \
293 ((void)((entry)->keyAndFlags &= ~(jsuword)(flags)))
296 HashDouble(JSDHashTable
*table
, const void *key
);
299 MatchDouble(JSDHashTable
*table
, const JSDHashEntryHdr
*hdr
, const void *key
);
302 HashString(JSDHashTable
*table
, const void *key
);
305 MatchString(JSDHashTable
*table
, const JSDHashEntryHdr
*hdr
, const void *key
);
307 static const JSDHashTableOps DoubleHashOps
= {
312 JS_DHashMoveEntryStub
,
313 JS_DHashClearEntryStub
,
314 JS_DHashFinalizeStub
,
318 static const JSDHashTableOps StringHashOps
= {
323 JS_DHashMoveEntryStub
,
324 JS_DHashClearEntryStub
,
325 JS_DHashFinalizeStub
,
329 #define IS_DOUBLE_TABLE(table) ((table)->ops == &DoubleHashOps)
330 #define IS_STRING_TABLE(table) ((table)->ops == &StringHashOps)
332 #define IS_INITIALIZED_STATE(state) IS_DOUBLE_TABLE(&(state)->doubleAtoms)
335 HashDouble(JSDHashTable
*table
, const void *key
)
337 JS_ASSERT(IS_DOUBLE_TABLE(table
));
338 return JS_HASH_DOUBLE(*(jsdouble
*)key
);
342 HashString(JSDHashTable
*table
, const void *key
)
344 JS_ASSERT(IS_STRING_TABLE(table
));
345 return js_HashString((JSString
*)key
);
349 MatchDouble(JSDHashTable
*table
, const JSDHashEntryHdr
*hdr
, const void *key
)
351 JSAtomHashEntry
*entry
= TO_ATOM_ENTRY(hdr
);
354 JS_ASSERT(IS_DOUBLE_TABLE(table
));
355 if (entry
->keyAndFlags
== 0) {
356 /* See comments in MatchString. */
360 d1
= *(jsdouble
*)ATOM_ENTRY_KEY(entry
);
361 d2
= *(jsdouble
*)key
;
362 if (JSDOUBLE_IS_NaN(d1
))
363 return JSDOUBLE_IS_NaN(d2
);
365 /* XXX MSVC miscompiles such that (NaN == 0) */
366 if (JSDOUBLE_IS_NaN(d2
))
373 MatchString(JSDHashTable
*table
, const JSDHashEntryHdr
*hdr
, const void *key
)
375 JSAtomHashEntry
*entry
= TO_ATOM_ENTRY(hdr
);
377 JS_ASSERT(IS_STRING_TABLE(table
));
378 if (entry
->keyAndFlags
== 0) {
380 * This happens when js_AtomizeString adds a new hash entry and
381 * releases the lock but before it takes the lock the second time to
382 * initialize keyAndFlags for the entry.
384 * We always return false for such entries so JS_DHashTableOperate
385 * never finds them. We clean them during GC's sweep phase.
387 * It means that with a contested lock or when GC is triggered outside
388 * the lock we may end up adding two entries, but this is a price for
393 return js_EqualStrings((JSString
*)ATOM_ENTRY_KEY(entry
), (JSString
*)key
);
397 * For a browser build from 2007-08-09 after the browser starts up there are
398 * just 55 double atoms, but over 15000 string atoms. Not to penalize more
399 * economical embeddings allocating too much memory initially we initialize
400 * atomized strings with just 1K entries.
402 #define JS_STRING_HASH_COUNT 1024
403 #define JS_DOUBLE_HASH_COUNT 64
406 js_InitAtomState(JSRuntime
*rt
)
408 JSAtomState
*state
= &rt
->atomState
;
411 * The caller must zero the state before calling this function.
413 JS_ASSERT(!state
->stringAtoms
.ops
);
414 JS_ASSERT(!state
->doubleAtoms
.ops
);
416 if (!JS_DHashTableInit(&state
->stringAtoms
, &StringHashOps
,
417 NULL
, sizeof(JSAtomHashEntry
),
418 JS_DHASH_DEFAULT_CAPACITY(JS_STRING_HASH_COUNT
))) {
419 state
->stringAtoms
.ops
= NULL
;
422 JS_ASSERT(IS_STRING_TABLE(&state
->stringAtoms
));
424 if (!JS_DHashTableInit(&state
->doubleAtoms
, &DoubleHashOps
,
425 NULL
, sizeof(JSAtomHashEntry
),
426 JS_DHASH_DEFAULT_CAPACITY(JS_DOUBLE_HASH_COUNT
))) {
427 state
->doubleAtoms
.ops
= NULL
;
428 JS_DHashTableFinish(&state
->stringAtoms
);
429 state
->stringAtoms
.ops
= NULL
;
432 JS_ASSERT(IS_DOUBLE_TABLE(&state
->doubleAtoms
));
435 js_InitLock(&state
->lock
);
437 JS_ASSERT(IS_INITIALIZED_STATE(state
));
441 static JSDHashOperator
442 js_string_uninterner(JSDHashTable
*table
, JSDHashEntryHdr
*hdr
,
443 uint32 number
, void *arg
)
445 JSAtomHashEntry
*entry
= TO_ATOM_ENTRY(hdr
);
446 JSRuntime
*rt
= (JSRuntime
*)arg
;
450 * Any string entry that remains at this point must be initialized, as the
451 * last GC should clean any uninitialized ones.
453 JS_ASSERT(IS_STRING_TABLE(table
));
454 JS_ASSERT(entry
->keyAndFlags
!= 0);
455 str
= (JSString
*)ATOM_ENTRY_KEY(entry
);
457 /* Pass null as context. */
458 js_FinalizeStringRT(rt
, str
, js_GetExternalStringGCType(str
), NULL
);
459 return JS_DHASH_NEXT
;
463 js_FinishAtomState(JSRuntime
*rt
)
465 JSAtomState
*state
= &rt
->atomState
;
467 if (!IS_INITIALIZED_STATE(state
)) {
469 * We are called with uninitialized state when JS_NewRuntime fails and
470 * calls JS_DestroyRuntime on a partially initialized runtime.
475 JS_DHashTableEnumerate(&state
->stringAtoms
, js_string_uninterner
, rt
);
476 JS_DHashTableFinish(&state
->stringAtoms
);
477 JS_DHashTableFinish(&state
->doubleAtoms
);
480 js_FinishLock(&state
->lock
);
483 memset(state
, JS_FREE_PATTERN
, sizeof *state
);
488 js_InitCommonAtoms(JSContext
*cx
)
490 JSAtomState
*state
= &cx
->runtime
->atomState
;
494 atoms
= COMMON_ATOMS_START(state
);
495 for (i
= 0; i
< JS_ARRAY_LENGTH(js_common_atom_names
); i
++, atoms
++) {
496 *atoms
= js_Atomize(cx
, js_common_atom_names
[i
],
497 strlen(js_common_atom_names
[i
]), ATOM_PINNED
);
501 JS_ASSERT((uint8
*)atoms
- (uint8
*)state
== LAZY_ATOM_OFFSET_START
);
502 memset(atoms
, 0, ATOM_OFFSET_LIMIT
- LAZY_ATOM_OFFSET_START
);
507 static JSDHashOperator
508 js_atom_unpinner(JSDHashTable
*table
, JSDHashEntryHdr
*hdr
,
509 uint32 number
, void *arg
)
511 JS_ASSERT(IS_STRING_TABLE(table
));
512 CLEAR_ATOM_ENTRY_FLAGS(TO_ATOM_ENTRY(hdr
), ATOM_PINNED
);
513 return JS_DHASH_NEXT
;
517 js_FinishCommonAtoms(JSContext
*cx
)
519 JSAtomState
*state
= &cx
->runtime
->atomState
;
521 JS_DHashTableEnumerate(&state
->stringAtoms
, js_atom_unpinner
, NULL
);
523 memset(COMMON_ATOMS_START(state
), JS_FREE_PATTERN
,
524 ATOM_OFFSET_LIMIT
- ATOM_OFFSET_START
);
528 static JSDHashOperator
529 js_locked_atom_tracer(JSDHashTable
*table
, JSDHashEntryHdr
*hdr
,
530 uint32 number
, void *arg
)
532 JSAtomHashEntry
*entry
= TO_ATOM_ENTRY(hdr
);
533 JSTracer
*trc
= (JSTracer
*)arg
;
535 if (entry
->keyAndFlags
== 0) {
536 /* Ignore uninitialized entries during tracing. */
537 return JS_DHASH_NEXT
;
539 JS_SET_TRACING_INDEX(trc
, "locked_atom", (size_t)number
);
540 JS_CallTracer(trc
, ATOM_ENTRY_KEY(entry
),
541 IS_STRING_TABLE(table
) ? JSTRACE_STRING
: JSTRACE_DOUBLE
);
542 return JS_DHASH_NEXT
;
545 static JSDHashOperator
546 js_pinned_atom_tracer(JSDHashTable
*table
, JSDHashEntryHdr
*hdr
,
547 uint32 number
, void *arg
)
549 JSAtomHashEntry
*entry
= TO_ATOM_ENTRY(hdr
);
550 JSTracer
*trc
= (JSTracer
*)arg
;
551 uintN flags
= ATOM_ENTRY_FLAGS(entry
);
553 JS_ASSERT(IS_STRING_TABLE(table
));
554 if (flags
& (ATOM_PINNED
| ATOM_INTERNED
)) {
555 JS_SET_TRACING_INDEX(trc
,
560 JS_CallTracer(trc
, ATOM_ENTRY_KEY(entry
), JSTRACE_STRING
);
562 return JS_DHASH_NEXT
;
566 js_TraceAtomState(JSTracer
*trc
, JSBool allAtoms
)
568 JSRuntime
*rt
= trc
->context
->runtime
;
569 JSAtomState
*state
= &rt
->atomState
;
572 JS_DHashTableEnumerate(&state
->doubleAtoms
, js_locked_atom_tracer
, trc
);
573 JS_DHashTableEnumerate(&state
->stringAtoms
, js_locked_atom_tracer
, trc
);
575 JS_DHashTableEnumerate(&state
->stringAtoms
, js_pinned_atom_tracer
, trc
);
579 static JSDHashOperator
580 js_atom_sweeper(JSDHashTable
*table
, JSDHashEntryHdr
*hdr
,
581 uint32 number
, void *arg
)
583 JSAtomHashEntry
*entry
= TO_ATOM_ENTRY(hdr
);
584 JSContext
*cx
= (JSContext
*)arg
;
586 /* Remove uninitialized entries. */
587 if (entry
->keyAndFlags
== 0)
588 return JS_DHASH_REMOVE
;
590 if (ATOM_ENTRY_FLAGS(entry
) & (ATOM_PINNED
| ATOM_INTERNED
)) {
591 /* Pinned or interned key cannot be finalized. */
592 JS_ASSERT(!js_IsAboutToBeFinalized(cx
, ATOM_ENTRY_KEY(entry
)));
593 } else if (js_IsAboutToBeFinalized(cx
, ATOM_ENTRY_KEY(entry
))) {
594 /* Remove entries with things about to be GC'ed. */
595 return JS_DHASH_REMOVE
;
597 return JS_DHASH_NEXT
;
601 js_SweepAtomState(JSContext
*cx
)
603 JSAtomState
*state
= &cx
->runtime
->atomState
;
605 JS_DHashTableEnumerate(&state
->doubleAtoms
, js_atom_sweeper
, cx
);
606 JS_DHashTableEnumerate(&state
->stringAtoms
, js_atom_sweeper
, cx
);
609 * Optimize for simplicity and mutate table generation numbers even if the
610 * sweeper has not removed any entries.
612 state
->doubleAtoms
.generation
++;
613 state
->stringAtoms
.generation
++;
617 js_AtomizeDouble(JSContext
*cx
, jsdouble d
)
621 JSAtomHashEntry
*entry
;
626 state
= &cx
->runtime
->atomState
;
627 table
= &state
->doubleAtoms
;
629 JS_LOCK(cx
, &state
->lock
);
630 entry
= TO_ATOM_ENTRY(JS_DHashTableOperate(table
, &d
, JS_DHASH_ADD
));
632 goto failed_hash_add
;
633 if (entry
->keyAndFlags
== 0) {
634 gen
= ++table
->generation
;
635 JS_UNLOCK(cx
, &state
->lock
);
637 key
= js_NewWeaklyRootedDouble(cx
, d
);
641 JS_LOCK(cx
, &state
->lock
);
642 if (table
->generation
== gen
) {
643 JS_ASSERT(entry
->keyAndFlags
== 0);
645 entry
= TO_ATOM_ENTRY(JS_DHashTableOperate(table
, key
,
648 goto failed_hash_add
;
649 if (entry
->keyAndFlags
!= 0)
653 INIT_ATOM_ENTRY(entry
, key
);
657 v
= DOUBLE_TO_JSVAL((jsdouble
*)ATOM_ENTRY_KEY(entry
));
658 cx
->weakRoots
.lastAtom
= v
;
659 JS_UNLOCK(cx
, &state
->lock
);
664 JS_UNLOCK(cx
, &state
->lock
);
665 JS_ReportOutOfMemory(cx
);
670 js_AtomizeString(JSContext
*cx
, JSString
*str
, uintN flags
)
675 JSAtomHashEntry
*entry
;
679 JS_ASSERT(!(flags
& ~(ATOM_PINNED
|ATOM_INTERNED
|ATOM_TMPSTR
|ATOM_NOCOPY
)));
680 JS_ASSERT_IF(flags
& ATOM_NOCOPY
, flags
& ATOM_TMPSTR
);
682 if (str
->isAtomized())
683 return (JSAtom
*) STRING_TO_JSVAL(str
);
685 size_t length
= str
->length();
687 jschar c
= str
->chars()[0];
688 if (c
< UNIT_STRING_LIMIT
)
689 return (JSAtom
*) STRING_TO_JSVAL(JSString::unitString(c
));
693 * Here we know that JSString::intStringTable covers only 256 (or at least
694 * not 1000 or more) chars. We rely on order here to resolve the unit vs.
695 * int string atom identity issue by giving priority to unit strings for
696 * '0' through '9' (see JSString::intString in jsstrinlines.h).
698 JS_STATIC_ASSERT(INT_STRING_LIMIT
<= 999);
699 if (2 <= length
&& length
<= 3) {
700 const jschar
*chars
= str
->chars();
702 if ('1' <= chars
[0] && chars
[0] <= '9' &&
703 '0' <= chars
[1] && chars
[1] <= '9' &&
704 (length
== 2 || ('0' <= chars
[2] && chars
[2] <= '9'))) {
705 jsint i
= (chars
[0] - '0') * 10 + chars
[1] - '0';
708 i
= i
* 10 + chars
[2] - '0';
709 if (jsuint(i
) < INT_STRING_LIMIT
)
710 return (JSAtom
*) STRING_TO_JSVAL(JSString::intString(i
));
714 state
= &cx
->runtime
->atomState
;
715 table
= &state
->stringAtoms
;
717 JS_LOCK(cx
, &state
->lock
);
718 entry
= TO_ATOM_ENTRY(JS_DHashTableOperate(table
, str
, JS_DHASH_ADD
));
720 goto failed_hash_add
;
721 if (entry
->keyAndFlags
!= 0) {
722 key
= (JSString
*)ATOM_ENTRY_KEY(entry
);
725 * We created a new hashtable entry. Unless str is already allocated
726 * from the GC heap and flat, we have to release state->lock as
727 * string construction is a complex operation. For example, it can
728 * trigger GC which may rehash the table and make the entry invalid.
731 if (!(flags
& ATOM_TMPSTR
) && str
->isFlat()) {
732 str
->flatClearMutable();
735 gen
= table
->generation
;
736 JS_UNLOCK(cx
, &state
->lock
);
738 if (flags
& ATOM_TMPSTR
) {
739 if (flags
& ATOM_NOCOPY
) {
740 key
= js_NewString(cx
, str
->flatChars(), str
->flatLength());
744 /* Finish handing off chars to the GC'ed key string. */
747 key
= js_NewStringCopyN(cx
, str
->flatChars(), str
->flatLength());
752 JS_ASSERT(str
->isDependent());
753 if (!js_UndependString(cx
, str
))
758 JS_LOCK(cx
, &state
->lock
);
759 if (table
->generation
== gen
) {
760 JS_ASSERT(entry
->keyAndFlags
== 0);
762 entry
= TO_ATOM_ENTRY(JS_DHashTableOperate(table
, key
,
765 goto failed_hash_add
;
766 if (entry
->keyAndFlags
!= 0) {
767 key
= (JSString
*)ATOM_ENTRY_KEY(entry
);
773 INIT_ATOM_ENTRY(entry
, key
);
774 key
->flatSetAtomized();
778 ADD_ATOM_ENTRY_FLAGS(entry
, flags
& (ATOM_PINNED
| ATOM_INTERNED
));
779 JS_ASSERT(key
->isAtomized());
780 v
= STRING_TO_JSVAL(key
);
781 cx
->weakRoots
.lastAtom
= v
;
782 JS_UNLOCK(cx
, &state
->lock
);
786 JS_UNLOCK(cx
, &state
->lock
);
787 JS_ReportOutOfMemory(cx
);
792 js_Atomize(JSContext
*cx
, const char *bytes
, size_t length
, uintN flags
)
799 * Avoiding the malloc in js_InflateString on shorter strings saves us
800 * over 20,000 malloc calls on mozilla browser startup. This compares to
801 * only 131 calls where the string is longer than a 31 char (net) buffer.
802 * The vast majority of atomized strings are already in the hashtable. So
803 * js_AtomizeString rarely has to copy the temp string we make.
805 #define ATOMIZE_BUF_MAX 32
806 jschar inflated
[ATOMIZE_BUF_MAX
];
807 size_t inflatedLength
= ATOMIZE_BUF_MAX
- 1;
809 if (length
< ATOMIZE_BUF_MAX
) {
810 js_InflateStringToBuffer(cx
, bytes
, length
, inflated
, &inflatedLength
);
811 inflated
[inflatedLength
] = 0;
814 inflatedLength
= length
;
815 chars
= js_InflateString(cx
, bytes
, &inflatedLength
);
818 flags
|= ATOM_NOCOPY
;
821 str
.initFlat(chars
, inflatedLength
);
822 atom
= js_AtomizeString(cx
, &str
, ATOM_TMPSTR
| flags
);
823 if (chars
!= inflated
&& str
.flatChars())
829 js_AtomizeChars(JSContext
*cx
, const jschar
*chars
, size_t length
, uintN flags
)
833 str
.initFlat((jschar
*)chars
, length
);
834 return js_AtomizeString(cx
, &str
, ATOM_TMPSTR
| flags
);
838 js_GetExistingStringAtom(JSContext
*cx
, const jschar
*chars
, size_t length
)
842 JSDHashEntryHdr
*hdr
;
846 if (c
< UNIT_STRING_LIMIT
)
847 return (JSAtom
*) STRING_TO_JSVAL(JSString::unitString(c
));
850 str
.initFlat((jschar
*)chars
, length
);
851 state
= &cx
->runtime
->atomState
;
853 JS_LOCK(cx
, &state
->lock
);
854 hdr
= JS_DHashTableOperate(&state
->stringAtoms
, &str
, JS_DHASH_LOOKUP
);
855 str2
= JS_DHASH_ENTRY_IS_BUSY(hdr
)
856 ? (JSString
*)ATOM_ENTRY_KEY(TO_ATOM_ENTRY(hdr
))
858 JS_UNLOCK(cx
, &state
->lock
);
860 return str2
? (JSAtom
*)STRING_TO_JSVAL(str2
) : NULL
;
864 js_AtomizePrimitiveValue(JSContext
*cx
, jsval v
, JSAtom
**atomp
)
868 if (JSVAL_IS_STRING(v
)) {
869 atom
= js_AtomizeString(cx
, JSVAL_TO_STRING(v
), 0);
872 } else if (JSVAL_IS_DOUBLE(v
)) {
873 atom
= js_AtomizeDouble(cx
, *JSVAL_TO_DOUBLE(v
));
877 JS_ASSERT(JSVAL_IS_INT(v
) || JSVAL_IS_BOOLEAN(v
) ||
878 JSVAL_IS_NULL(v
) || JSVAL_IS_VOID(v
));
887 static JSDHashOperator
888 atom_dumper(JSDHashTable
*table
, JSDHashEntryHdr
*hdr
,
889 uint32 number
, void *arg
)
891 JSAtomHashEntry
*entry
= TO_ATOM_ENTRY(hdr
);
892 FILE *fp
= (FILE *)arg
;
896 fprintf(fp
, "%3u %08x ", number
, (uintN
)entry
->hdr
.keyHash
);
897 if (entry
->keyAndFlags
== 0) {
898 fputs("<uninitialized>", fp
);
900 key
= ATOM_ENTRY_KEY(entry
);
901 if (IS_DOUBLE_TABLE(table
)) {
902 fprintf(fp
, "%.16g", *(jsdouble
*)key
);
904 JS_ASSERT(IS_STRING_TABLE(table
));
905 js_FileEscapedString(fp
, (JSString
*)key
, '"');
907 flags
= ATOM_ENTRY_FLAGS(entry
);
909 fputs((flags
& (ATOM_PINNED
| ATOM_INTERNED
))
910 ? " pinned | interned"
911 : (flags
& ATOM_PINNED
) ? " pinned" : " interned",
916 return JS_DHASH_NEXT
;
920 js_DumpAtoms(JSContext
*cx
, FILE *fp
)
922 JSAtomState
*state
= &cx
->runtime
->atomState
;
924 fprintf(fp
, "stringAtoms table contents:\n");
925 JS_DHashTableEnumerate(&state
->stringAtoms
, atom_dumper
, fp
);
927 JS_DHashTableDumpMeter(&state
->stringAtoms
, atom_dumper
, fp
);
931 fprintf(fp
, "doubleAtoms table contents:\n");
932 JS_DHashTableEnumerate(&state
->doubleAtoms
, atom_dumper
, fp
);
934 JS_DHashTableDumpMeter(&state
->doubleAtoms
, atom_dumper
, fp
);
942 js_hash_atom_ptr(const void *key
)
944 const JSAtom
*atom
= (const JSAtom
*) key
;
945 return ATOM_HASH(atom
);
948 #if JS_BITS_PER_WORD == 32
949 # define TEMP_SIZE_START_LOG2 5
951 # define TEMP_SIZE_START_LOG2 6
953 #define TEMP_SIZE_LIMIT_LOG2 (TEMP_SIZE_START_LOG2 + NUM_TEMP_FREELISTS)
955 #define TEMP_SIZE_START JS_BIT(TEMP_SIZE_START_LOG2)
956 #define TEMP_SIZE_LIMIT JS_BIT(TEMP_SIZE_LIMIT_LOG2)
958 JS_STATIC_ASSERT(TEMP_SIZE_START
>= sizeof(JSHashTable
));
961 js_alloc_temp_space(void *priv
, size_t size
)
963 JSCompiler
*jsc
= (JSCompiler
*) priv
;
966 if (size
< TEMP_SIZE_LIMIT
) {
967 int bin
= JS_CeilingLog2(size
) - TEMP_SIZE_START_LOG2
;
968 JS_ASSERT(unsigned(bin
) < NUM_TEMP_FREELISTS
);
970 space
= jsc
->tempFreeList
[bin
];
972 jsc
->tempFreeList
[bin
] = *(void **)space
;
977 JS_ARENA_ALLOCATE(space
, &jsc
->context
->tempPool
, size
);
979 js_ReportOutOfScriptQuota(jsc
->context
);
984 js_free_temp_space(void *priv
, void *item
, size_t size
)
986 if (size
>= TEMP_SIZE_LIMIT
)
989 JSCompiler
*jsc
= (JSCompiler
*) priv
;
990 int bin
= JS_CeilingLog2(size
) - TEMP_SIZE_START_LOG2
;
991 JS_ASSERT(unsigned(bin
) < NUM_TEMP_FREELISTS
);
993 *(void **)item
= jsc
->tempFreeList
[bin
];
994 jsc
->tempFreeList
[bin
] = item
;
998 js_alloc_temp_entry(void *priv
, const void *key
)
1000 JSCompiler
*jsc
= (JSCompiler
*) priv
;
1001 JSAtomListElement
*ale
;
1003 ale
= jsc
->aleFreeList
;
1005 jsc
->aleFreeList
= ALE_NEXT(ale
);
1009 JS_ARENA_ALLOCATE_TYPE(ale
, JSAtomListElement
, &jsc
->context
->tempPool
);
1011 js_ReportOutOfScriptQuota(jsc
->context
);
1018 js_free_temp_entry(void *priv
, JSHashEntry
*he
, uintN flag
)
1020 JSCompiler
*jsc
= (JSCompiler
*) priv
;
1021 JSAtomListElement
*ale
= (JSAtomListElement
*) he
;
1023 ALE_SET_NEXT(ale
, jsc
->aleFreeList
);
1024 jsc
->aleFreeList
= ale
;
1027 static JSHashAllocOps temp_alloc_ops
= {
1028 js_alloc_temp_space
, js_free_temp_space
,
1029 js_alloc_temp_entry
, js_free_temp_entry
1033 JSAtomList::rawLookup(JSAtom
*atom
, JSHashEntry
**&hep
)
1035 JSAtomListElement
*ale
;
1038 hep
= JS_HashTableRawLookup(table
, ATOM_HASH(atom
), atom
);
1039 ale
= *hep
? (JSAtomListElement
*) *hep
: NULL
;
1041 JSHashEntry
**alep
= &list
;
1043 while ((ale
= (JSAtomListElement
*)*alep
) != NULL
) {
1044 if (ALE_ATOM(ale
) == atom
) {
1045 /* Hit, move atom's element to the front of the list. */
1046 *alep
= ale
->entry
.next
;
1047 ale
->entry
.next
= list
;
1051 alep
= &ale
->entry
.next
;
1057 #define ATOM_LIST_HASH_THRESHOLD 12
1060 JSAtomList::add(JSCompiler
*jsc
, JSAtom
*atom
, AddHow how
)
1064 JSAtomListElement
*ale
, *ale2
, *next
;
1067 ale
= rawLookup(atom
, hep
);
1068 if (!ale
|| how
!= UNIQUE
) {
1069 if (count
< ATOM_LIST_HASH_THRESHOLD
&& !table
) {
1070 /* Few enough for linear search and no hash table yet needed. */
1071 ale
= (JSAtomListElement
*)js_alloc_temp_entry(jsc
, atom
);
1074 ALE_SET_ATOM(ale
, atom
);
1077 ale
->entry
.next
= NULL
;
1078 hep
= (JSHashEntry
**) &list
;
1080 hep
= &(*hep
)->next
;
1083 ale
->entry
.next
= list
;
1088 * We should hash, or else we already are hashing, but count was
1089 * reduced by JSAtomList::rawRemove below ATOM_LIST_HASH_THRESHOLD.
1090 * Check whether we should create the table.
1093 /* No hash table yet, so hep had better be null! */
1095 table
= JS_NewHashTable(count
+ 1, js_hash_atom_ptr
,
1096 JS_CompareValues
, JS_CompareValues
,
1097 &temp_alloc_ops
, jsc
);
1102 * Set ht->nentries explicitly, because we are moving entries
1103 * from list to ht, not calling JS_HashTable(Raw|)Add.
1105 table
->nentries
= count
;
1108 * Insert each ale on list into the new hash table. Append to
1109 * the hash chain rather than inserting at the bucket head, to
1110 * preserve order among entries with the same key.
1112 for (ale2
= (JSAtomListElement
*)list
; ale2
; ale2
= next
) {
1113 next
= ALE_NEXT(ale2
);
1114 ale2
->entry
.keyHash
= ATOM_HASH(ALE_ATOM(ale2
));
1115 hep
= JS_HashTableRawLookup(table
, ale2
->entry
.keyHash
,
1118 hep
= &(*hep
)->next
;
1119 *hep
= &ale2
->entry
;
1120 ale2
->entry
.next
= NULL
;
1124 /* Set hep for insertion of atom's ale, immediately below. */
1125 hep
= JS_HashTableRawLookup(table
, ATOM_HASH(atom
), atom
);
1128 /* Finally, add an entry for atom into the hash bucket at hep. */
1129 ale
= (JSAtomListElement
*)
1130 JS_HashTableRawAdd(table
, hep
, ATOM_HASH(atom
), atom
, NULL
);
1135 * If hoisting, move ale to the end of its chain after we called
1136 * JS_HashTableRawAdd, since RawAdd may have grown the table and
1137 * then recomputed hep to refer to the pointer to the first entry
1138 * with the given key.
1140 if (how
== HOIST
&& ale
->entry
.next
) {
1141 JS_ASSERT(*hep
== &ale
->entry
);
1142 *hep
= ale
->entry
.next
;
1143 ale
->entry
.next
= NULL
;
1145 hep
= &(*hep
)->next
;
1151 ALE_SET_INDEX(ale
, count
++);
1157 JSAtomList::rawRemove(JSCompiler
*jsc
, JSAtomListElement
*ale
, JSHashEntry
**hep
)
1160 JS_ASSERT(count
!= 0);
1164 JS_HashTableRawRemove(table
, hep
, &ale
->entry
);
1168 while (*hep
!= &ale
->entry
) {
1170 hep
= &(*hep
)->next
;
1172 *hep
= ale
->entry
.next
;
1173 js_free_temp_entry(jsc
, &ale
->entry
, HT_FREE_ENTRY
);
1180 JSAtomListIterator::operator ()()
1182 JSAtomListElement
*ale
;
1185 if (index
== uint32(-1))
1194 if (index
== JS_BIT(JS_HASH_BITS
- ht
->shift
))
1196 next
= (JSAtomListElement
*) ht
->buckets
[index
++];
1201 next
= ALE_NEXT(ale
);
1210 js_map_atom(JSHashEntry
*he
, intN i
, void *arg
)
1212 JSAtomListElement
*ale
= (JSAtomListElement
*)he
;
1213 JSAtom
**vector
= (JSAtom
**) arg
;
1215 vector
[ALE_INDEX(ale
)] = ALE_ATOM(ale
);
1216 return HT_ENUMERATE_NEXT
;
1220 static jsrefcount js_atom_map_count
;
1221 static jsrefcount js_atom_map_hash_table_count
;
1225 js_InitAtomMap(JSContext
*cx
, JSAtomMap
*map
, JSAtomList
*al
)
1228 JSAtomListElement
*ale
;
1231 /* Map length must already be initialized. */
1232 JS_ASSERT(al
->count
== map
->length
);
1234 JS_ATOMIC_INCREMENT(&js_atom_map_count
);
1236 ale
= (JSAtomListElement
*)al
->list
;
1237 if (!ale
&& !al
->table
) {
1238 JS_ASSERT(!map
->vector
);
1243 vector
= map
->vector
;
1246 JS_ATOMIC_INCREMENT(&js_atom_map_hash_table_count
);
1248 JS_HashTableEnumerateEntries(al
->table
, js_map_atom
, vector
);
1251 vector
[ALE_INDEX(ale
)] = ALE_ATOM(ale
);
1252 } while ((ale
= ALE_NEXT(ale
)) != NULL
);