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"
66 * ATOM_HASH assumes that JSHashNumber is 32-bit even on 64-bit systems.
68 JS_STATIC_ASSERT(sizeof(JSHashNumber
) == 4);
69 JS_STATIC_ASSERT(sizeof(JSAtom
*) == JS_BYTES_PER_WORD
);
72 * Start and limit offsets for atom pointers in JSAtomState must be aligned
73 * on the word boundary.
75 JS_STATIC_ASSERT(ATOM_OFFSET_START
% sizeof(JSAtom
*) == 0);
76 JS_STATIC_ASSERT(ATOM_OFFSET_LIMIT
% sizeof(JSAtom
*) == 0);
79 * JS_BOOLEAN_STR and JS_TYPE_STR assume that boolean names starts from the
80 * index 1 and type name starts from the index 1+2 atoms in JSAtomState.
82 JS_STATIC_ASSERT(1 * sizeof(JSAtom
*) ==
83 offsetof(JSAtomState
, booleanAtoms
) - ATOM_OFFSET_START
);
84 JS_STATIC_ASSERT((1 + 2) * sizeof(JSAtom
*) ==
85 offsetof(JSAtomState
, typeAtoms
) - ATOM_OFFSET_START
);
88 js_AtomToPrintableString(JSContext
*cx
, JSAtom
*atom
)
90 return js_ValueToPrintableString(cx
, ATOM_KEY(atom
));
93 #define JS_PROTO(name,code,init) const char js_##name##_str[] = #name;
94 #include "jsproto.tbl"
98 * String constants for common atoms defined in JSAtomState starting from
99 * JSAtomState.emptyAtom until JSAtomState.lazy.
101 * The elements of the array after the first empty string define strings
102 * corresponding to the two boolean literals, false and true, followed by the
103 * JSType enumerators from jspubtd.h starting with "undefined" for JSTYPE_VOID
104 * (which is special-value 2) and continuing as initialized below. The static
105 * asserts check these relations.
107 JS_STATIC_ASSERT(JSTYPE_LIMIT
== 8);
108 JS_STATIC_ASSERT(JSTYPE_VOID
== 0);
110 const char *const js_common_atom_names
[] = {
112 js_false_str
, /* booleanAtoms[0] */
113 js_true_str
, /* booleanAtoms[1] */
114 js_undefined_str
, /* typeAtoms[JSTYPE_VOID] */
115 js_object_str
, /* typeAtoms[JSTYPE_OBJECT] */
116 js_function_str
, /* typeAtoms[JSTYPE_FUNCTION] */
117 "string", /* typeAtoms[JSTYPE_STRING] */
118 "number", /* typeAtoms[JSTYPE_NUMBER] */
119 "boolean", /* typeAtoms[JSTYPE_BOOLEAN] */
120 js_null_str
, /* typeAtoms[JSTYPE_NULL] */
121 "xml", /* typeAtoms[JSTYPE_XML] */
122 js_null_str
, /* nullAtom */
124 #define JS_PROTO(name,code,init) js_##name##_str,
125 #include "jsproto.tbl"
128 js_anonymous_str
, /* anonymousAtom */
129 js_apply_str
, /* applyAtom */
130 js_arguments_str
, /* argumentsAtom */
131 js_arity_str
, /* arityAtom */
132 js_call_str
, /* callAtom */
133 js_callee_str
, /* calleeAtom */
134 js_caller_str
, /* callerAtom */
135 js_class_prototype_str
, /* classPrototypeAtom */
136 js_constructor_str
, /* constructorAtom */
137 js_each_str
, /* eachAtom */
138 js_eval_str
, /* evalAtom */
139 js_fileName_str
, /* fileNameAtom */
140 js_get_str
, /* getAtom */
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_proto_str
, /* protoAtom */
151 js_set_str
, /* setAtom */
152 js_stack_str
, /* stackAtom */
153 js_toLocaleString_str
, /* toLocaleStringAtom */
154 js_toSource_str
, /* toSourceAtom */
155 js_toString_str
, /* toStringAtom */
156 js_valueOf_str
, /* valueOfAtom */
157 js_toJSON_str
, /* toJSONAtom */
158 "(void 0)", /* void0Atom */
159 js_enumerable_str
, /* enumerableAtom */
160 js_configurable_str
, /* configurableAtom */
161 js_writable_str
, /* writableAtom */
162 js_value_str
, /* valueAtom */
163 "use strict", /* useStrictAtom */
165 #if JS_HAS_XML_SUPPORT
166 js_etago_str
, /* etagoAtom */
167 js_namespace_str
, /* namespaceAtom */
168 js_ptagc_str
, /* ptagcAtom */
169 js_qualifier_str
, /* qualifierAtom */
170 js_space_str
, /* spaceAtom */
171 js_stago_str
, /* stagoAtom */
172 js_star_str
, /* starAtom */
173 js_starQualifier_str
, /* starQualifierAtom */
174 js_tagc_str
, /* tagcAtom */
175 js_xml_str
, /* xmlAtom */
179 js___call___str
, /* __call__Atom */
180 js___construct___str
, /* __construct__Atom */
181 js___hasInstance___str
, /* __hasInstance__Atom */
182 js_ExecutionContext_str
, /* ExecutionContextAtom */
183 js_current_str
, /* currentAtom */
187 JS_STATIC_ASSERT(JS_ARRAY_LENGTH(js_common_atom_names
) * sizeof(JSAtom
*) ==
188 LAZY_ATOM_OFFSET_START
- ATOM_OFFSET_START
);
191 * Interpreter macros called by the trace recorder assume common atom indexes
192 * fit in one byte of immediate operand.
194 JS_STATIC_ASSERT(JS_ARRAY_LENGTH(js_common_atom_names
) < 256);
196 const size_t js_common_atom_count
= JS_ARRAY_LENGTH(js_common_atom_names
);
198 const char js_anonymous_str
[] = "anonymous";
199 const char js_apply_str
[] = "apply";
200 const char js_arguments_str
[] = "arguments";
201 const char js_arity_str
[] = "arity";
202 const char js_call_str
[] = "call";
203 const char js_callee_str
[] = "callee";
204 const char js_caller_str
[] = "caller";
205 const char js_class_prototype_str
[] = "prototype";
206 const char js_constructor_str
[] = "constructor";
207 const char js_each_str
[] = "each";
208 const char js_eval_str
[] = "eval";
209 const char js_fileName_str
[] = "fileName";
210 const char js_get_str
[] = "get";
211 const char js_getter_str
[] = "getter";
212 const char js_index_str
[] = "index";
213 const char js_input_str
[] = "input";
214 const char js_iterator_str
[] = "__iterator__";
215 const char js_length_str
[] = "length";
216 const char js_lineNumber_str
[] = "lineNumber";
217 const char js_message_str
[] = "message";
218 const char js_name_str
[] = "name";
219 const char js_next_str
[] = "next";
220 const char js_noSuchMethod_str
[] = "__noSuchMethod__";
221 const char js_object_str
[] = "object";
222 const char js_proto_str
[] = "__proto__";
223 const char js_setter_str
[] = "setter";
224 const char js_set_str
[] = "set";
225 const char js_stack_str
[] = "stack";
226 const char js_toSource_str
[] = "toSource";
227 const char js_toString_str
[] = "toString";
228 const char js_toLocaleString_str
[] = "toLocaleString";
229 const char js_undefined_str
[] = "undefined";
230 const char js_valueOf_str
[] = "valueOf";
231 const char js_toJSON_str
[] = "toJSON";
232 const char js_enumerable_str
[] = "enumerable";
233 const char js_configurable_str
[] = "configurable";
234 const char js_writable_str
[] = "writable";
235 const char js_value_str
[] = "value";
237 #if JS_HAS_XML_SUPPORT
238 const char js_etago_str
[] = "</";
239 const char js_namespace_str
[] = "namespace";
240 const char js_ptagc_str
[] = "/>";
241 const char js_qualifier_str
[] = "::";
242 const char js_space_str
[] = " ";
243 const char js_stago_str
[] = "<";
244 const char js_star_str
[] = "*";
245 const char js_starQualifier_str
[] = "*::";
246 const char js_tagc_str
[] = ">";
247 const char js_xml_str
[] = "xml";
250 #if JS_HAS_GENERATORS
251 const char js_close_str
[] = "close";
252 const char js_send_str
[] = "send";
256 const char js___call___str
[] = "__call__";
257 const char js___construct___str
[] = "__construct__";
258 const char js___hasInstance___str
[] = "__hasInstance__";
259 const char js_ExecutionContext_str
[] = "ExecutionContext";
260 const char js_current_str
[] = "current";
264 * JSAtomState.doubleAtoms and JSAtomState.stringAtoms hashtable entry. To
265 * support pinned and interned string atoms, we use the lowest bits of the
266 * keyAndFlags field to store ATOM_PINNED and ATOM_INTERNED flags.
268 typedef struct JSAtomHashEntry
{
273 #define ATOM_ENTRY_FLAG_MASK (ATOM_PINNED | ATOM_INTERNED)
275 JS_STATIC_ASSERT(ATOM_ENTRY_FLAG_MASK
< JSVAL_ALIGN
);
278 * Helper macros to access and modify JSAtomHashEntry.
280 #define TO_ATOM_ENTRY(hdr) ((JSAtomHashEntry *) hdr)
281 #define ATOM_ENTRY_KEY(entry) \
282 ((void *)((entry)->keyAndFlags & ~ATOM_ENTRY_FLAG_MASK))
283 #define ATOM_ENTRY_FLAGS(entry) \
284 ((uintN)((entry)->keyAndFlags & ATOM_ENTRY_FLAG_MASK))
285 #define INIT_ATOM_ENTRY(entry, key) \
286 ((void)((entry)->keyAndFlags = (jsuword)(key)))
287 #define ADD_ATOM_ENTRY_FLAGS(entry, flags) \
288 ((void)((entry)->keyAndFlags |= (jsuword)(flags)))
289 #define CLEAR_ATOM_ENTRY_FLAGS(entry, flags) \
290 ((void)((entry)->keyAndFlags &= ~(jsuword)(flags)))
293 HashDouble(JSDHashTable
*table
, const void *key
);
296 MatchDouble(JSDHashTable
*table
, const JSDHashEntryHdr
*hdr
, const void *key
);
299 HashString(JSDHashTable
*table
, const void *key
);
302 MatchString(JSDHashTable
*table
, const JSDHashEntryHdr
*hdr
, const void *key
);
304 static const JSDHashTableOps DoubleHashOps
= {
309 JS_DHashMoveEntryStub
,
310 JS_DHashClearEntryStub
,
311 JS_DHashFinalizeStub
,
315 static const JSDHashTableOps StringHashOps
= {
320 JS_DHashMoveEntryStub
,
321 JS_DHashClearEntryStub
,
322 JS_DHashFinalizeStub
,
326 #define IS_DOUBLE_TABLE(table) ((table)->ops == &DoubleHashOps)
327 #define IS_STRING_TABLE(table) ((table)->ops == &StringHashOps)
329 #define IS_INITIALIZED_STATE(state) IS_DOUBLE_TABLE(&(state)->doubleAtoms)
332 HashDouble(JSDHashTable
*table
, const void *key
)
334 JS_ASSERT(IS_DOUBLE_TABLE(table
));
335 return JS_HASH_DOUBLE(*(jsdouble
*)key
);
339 HashString(JSDHashTable
*table
, const void *key
)
341 JS_ASSERT(IS_STRING_TABLE(table
));
342 return js_HashString((JSString
*)key
);
346 MatchDouble(JSDHashTable
*table
, const JSDHashEntryHdr
*hdr
, const void *key
)
348 JSAtomHashEntry
*entry
= TO_ATOM_ENTRY(hdr
);
351 JS_ASSERT(IS_DOUBLE_TABLE(table
));
352 if (entry
->keyAndFlags
== 0) {
353 /* See comments in MatchString. */
357 d1
= *(jsdouble
*)ATOM_ENTRY_KEY(entry
);
358 d2
= *(jsdouble
*)key
;
359 if (JSDOUBLE_IS_NaN(d1
))
360 return JSDOUBLE_IS_NaN(d2
);
362 /* XXX MSVC miscompiles such that (NaN == 0) */
363 if (JSDOUBLE_IS_NaN(d2
))
370 MatchString(JSDHashTable
*table
, const JSDHashEntryHdr
*hdr
, const void *key
)
372 JSAtomHashEntry
*entry
= TO_ATOM_ENTRY(hdr
);
374 JS_ASSERT(IS_STRING_TABLE(table
));
375 if (entry
->keyAndFlags
== 0) {
377 * This happens when js_AtomizeString adds a new hash entry and
378 * releases the lock but before it takes the lock the second time to
379 * initialize keyAndFlags for the entry.
381 * We always return false for such entries so JS_DHashTableOperate
382 * never finds them. We clean them during GC's sweep phase.
384 * It means that with a contested lock or when GC is triggered outside
385 * the lock we may end up adding two entries, but this is a price for
390 return js_EqualStrings((JSString
*)ATOM_ENTRY_KEY(entry
), (JSString
*)key
);
394 * For a browser build from 2007-08-09 after the browser starts up there are
395 * just 55 double atoms, but over 15000 string atoms. Not to penalize more
396 * economical embeddings allocating too much memory initially we initialize
397 * atomized strings with just 1K entries.
399 #define JS_STRING_HASH_COUNT 1024
400 #define JS_DOUBLE_HASH_COUNT 64
403 js_InitAtomState(JSRuntime
*rt
)
405 JSAtomState
*state
= &rt
->atomState
;
408 * The caller must zero the state before calling this function.
410 JS_ASSERT(!state
->stringAtoms
.ops
);
411 JS_ASSERT(!state
->doubleAtoms
.ops
);
413 if (!JS_DHashTableInit(&state
->stringAtoms
, &StringHashOps
,
414 NULL
, sizeof(JSAtomHashEntry
),
415 JS_DHASH_DEFAULT_CAPACITY(JS_STRING_HASH_COUNT
))) {
416 state
->stringAtoms
.ops
= NULL
;
419 JS_ASSERT(IS_STRING_TABLE(&state
->stringAtoms
));
421 if (!JS_DHashTableInit(&state
->doubleAtoms
, &DoubleHashOps
,
422 NULL
, sizeof(JSAtomHashEntry
),
423 JS_DHASH_DEFAULT_CAPACITY(JS_DOUBLE_HASH_COUNT
))) {
424 state
->doubleAtoms
.ops
= NULL
;
425 JS_DHashTableFinish(&state
->stringAtoms
);
426 state
->stringAtoms
.ops
= NULL
;
429 JS_ASSERT(IS_DOUBLE_TABLE(&state
->doubleAtoms
));
432 js_InitLock(&state
->lock
);
434 JS_ASSERT(IS_INITIALIZED_STATE(state
));
438 static JSDHashOperator
439 js_string_uninterner(JSDHashTable
*table
, JSDHashEntryHdr
*hdr
,
440 uint32 number
, void *arg
)
442 JSAtomHashEntry
*entry
= TO_ATOM_ENTRY(hdr
);
443 JSRuntime
*rt
= (JSRuntime
*)arg
;
447 * Any string entry that remains at this point must be initialized, as the
448 * last GC should clean any uninitialized ones.
450 JS_ASSERT(IS_STRING_TABLE(table
));
451 JS_ASSERT(entry
->keyAndFlags
!= 0);
452 str
= (JSString
*)ATOM_ENTRY_KEY(entry
);
454 js_FinalizeStringRT(rt
, str
);
455 return JS_DHASH_NEXT
;
459 js_FinishAtomState(JSRuntime
*rt
)
461 JSAtomState
*state
= &rt
->atomState
;
463 if (!IS_INITIALIZED_STATE(state
)) {
465 * We are called with uninitialized state when JS_NewRuntime fails and
466 * calls JS_DestroyRuntime on a partially initialized runtime.
471 JS_DHashTableEnumerate(&state
->stringAtoms
, js_string_uninterner
, rt
);
472 JS_DHashTableFinish(&state
->stringAtoms
);
473 JS_DHashTableFinish(&state
->doubleAtoms
);
476 js_FinishLock(&state
->lock
);
479 memset(state
, JS_FREE_PATTERN
, sizeof *state
);
484 js_InitCommonAtoms(JSContext
*cx
)
486 JSAtomState
*state
= &cx
->runtime
->atomState
;
490 atoms
= COMMON_ATOMS_START(state
);
491 for (i
= 0; i
< JS_ARRAY_LENGTH(js_common_atom_names
); i
++, atoms
++) {
492 *atoms
= js_Atomize(cx
, js_common_atom_names
[i
],
493 strlen(js_common_atom_names
[i
]), ATOM_PINNED
);
497 JS_ASSERT((uint8
*)atoms
- (uint8
*)state
== LAZY_ATOM_OFFSET_START
);
498 memset(atoms
, 0, ATOM_OFFSET_LIMIT
- LAZY_ATOM_OFFSET_START
);
500 cx
->runtime
->emptyString
= ATOM_TO_STRING(state
->emptyAtom
);
504 static JSDHashOperator
505 js_atom_unpinner(JSDHashTable
*table
, JSDHashEntryHdr
*hdr
,
506 uint32 number
, void *arg
)
508 JS_ASSERT(IS_STRING_TABLE(table
));
509 CLEAR_ATOM_ENTRY_FLAGS(TO_ATOM_ENTRY(hdr
), ATOM_PINNED
);
510 return JS_DHASH_NEXT
;
514 js_FinishCommonAtoms(JSContext
*cx
)
516 cx
->runtime
->emptyString
= NULL
;
517 JSAtomState
*state
= &cx
->runtime
->atomState
;
518 JS_DHashTableEnumerate(&state
->stringAtoms
, js_atom_unpinner
, NULL
);
520 memset(COMMON_ATOMS_START(state
), JS_FREE_PATTERN
,
521 ATOM_OFFSET_LIMIT
- ATOM_OFFSET_START
);
525 static JSDHashOperator
526 js_locked_atom_tracer(JSDHashTable
*table
, JSDHashEntryHdr
*hdr
,
527 uint32 number
, void *arg
)
529 JSAtomHashEntry
*entry
= TO_ATOM_ENTRY(hdr
);
530 JSTracer
*trc
= (JSTracer
*)arg
;
532 if (entry
->keyAndFlags
== 0) {
533 /* Ignore uninitialized entries during tracing. */
534 return JS_DHASH_NEXT
;
536 JS_SET_TRACING_INDEX(trc
, "locked_atom", (size_t)number
);
537 js_CallGCMarker(trc
, ATOM_ENTRY_KEY(entry
),
538 IS_STRING_TABLE(table
) ? JSTRACE_STRING
: JSTRACE_DOUBLE
);
539 return JS_DHASH_NEXT
;
542 static JSDHashOperator
543 js_pinned_atom_tracer(JSDHashTable
*table
, JSDHashEntryHdr
*hdr
,
544 uint32 number
, void *arg
)
546 JSAtomHashEntry
*entry
= TO_ATOM_ENTRY(hdr
);
547 JSTracer
*trc
= (JSTracer
*)arg
;
548 uintN flags
= ATOM_ENTRY_FLAGS(entry
);
550 JS_ASSERT(IS_STRING_TABLE(table
));
551 if (flags
& (ATOM_PINNED
| ATOM_INTERNED
)) {
552 JS_SET_TRACING_INDEX(trc
,
557 js_CallGCMarker(trc
, ATOM_ENTRY_KEY(entry
), JSTRACE_STRING
);
559 return JS_DHASH_NEXT
;
563 js_TraceAtomState(JSTracer
*trc
)
565 JSRuntime
*rt
= trc
->context
->runtime
;
566 JSAtomState
*state
= &rt
->atomState
;
568 if (rt
->gcKeepAtoms
) {
569 JS_DHashTableEnumerate(&state
->doubleAtoms
, js_locked_atom_tracer
, trc
);
570 JS_DHashTableEnumerate(&state
->stringAtoms
, js_locked_atom_tracer
, trc
);
572 JS_DHashTableEnumerate(&state
->stringAtoms
, js_pinned_atom_tracer
, trc
);
576 static JSDHashOperator
577 js_atom_sweeper(JSDHashTable
*table
, JSDHashEntryHdr
*hdr
,
578 uint32 number
, void *arg
)
580 JSAtomHashEntry
*entry
= TO_ATOM_ENTRY(hdr
);
582 /* Remove uninitialized entries. */
583 if (entry
->keyAndFlags
== 0)
584 return JS_DHASH_REMOVE
;
586 if (ATOM_ENTRY_FLAGS(entry
) & (ATOM_PINNED
| ATOM_INTERNED
)) {
587 /* Pinned or interned key cannot be finalized. */
588 JS_ASSERT(!js_IsAboutToBeFinalized(ATOM_ENTRY_KEY(entry
)));
589 } else if (js_IsAboutToBeFinalized(ATOM_ENTRY_KEY(entry
))) {
590 /* Remove entries with things about to be GC'ed. */
591 return JS_DHASH_REMOVE
;
593 return JS_DHASH_NEXT
;
597 js_SweepAtomState(JSContext
*cx
)
599 JSAtomState
*state
= &cx
->runtime
->atomState
;
601 JS_DHashTableEnumerate(&state
->doubleAtoms
, js_atom_sweeper
, NULL
);
602 JS_DHashTableEnumerate(&state
->stringAtoms
, js_atom_sweeper
, NULL
);
605 * Optimize for simplicity and mutate table generation numbers even if the
606 * sweeper has not removed any entries.
608 state
->doubleAtoms
.generation
++;
609 state
->stringAtoms
.generation
++;
613 js_AtomizeDouble(JSContext
*cx
, jsdouble d
)
617 JSAtomHashEntry
*entry
;
622 state
= &cx
->runtime
->atomState
;
623 table
= &state
->doubleAtoms
;
625 JS_LOCK(cx
, &state
->lock
);
626 entry
= TO_ATOM_ENTRY(JS_DHashTableOperate(table
, &d
, JS_DHASH_ADD
));
628 goto failed_hash_add
;
629 if (entry
->keyAndFlags
== 0) {
630 gen
= ++table
->generation
;
631 JS_UNLOCK(cx
, &state
->lock
);
633 key
= js_NewWeaklyRootedDouble(cx
, d
);
637 JS_LOCK(cx
, &state
->lock
);
638 if (table
->generation
== gen
) {
639 JS_ASSERT(entry
->keyAndFlags
== 0);
641 entry
= TO_ATOM_ENTRY(JS_DHashTableOperate(table
, key
,
644 goto failed_hash_add
;
645 if (entry
->keyAndFlags
!= 0)
649 INIT_ATOM_ENTRY(entry
, key
);
653 v
= DOUBLE_TO_JSVAL((jsdouble
*)ATOM_ENTRY_KEY(entry
));
654 cx
->weakRoots
.lastAtom
= v
;
655 JS_UNLOCK(cx
, &state
->lock
);
660 JS_UNLOCK(cx
, &state
->lock
);
661 JS_ReportOutOfMemory(cx
);
666 js_AtomizeString(JSContext
*cx
, JSString
*str
, uintN flags
)
671 JSAtomHashEntry
*entry
;
675 JS_ASSERT(!(flags
& ~(ATOM_PINNED
|ATOM_INTERNED
|ATOM_TMPSTR
|ATOM_NOCOPY
)));
676 JS_ASSERT_IF(flags
& ATOM_NOCOPY
, flags
& ATOM_TMPSTR
);
678 if (str
->isAtomized())
679 return (JSAtom
*) STRING_TO_JSVAL(str
);
681 size_t length
= str
->length();
683 jschar c
= str
->chars()[0];
684 if (c
< UNIT_STRING_LIMIT
)
685 return (JSAtom
*) STRING_TO_JSVAL(JSString::unitString(c
));
689 * Here we know that JSString::intStringTable covers only 256 (or at least
690 * not 1000 or more) chars. We rely on order here to resolve the unit vs.
691 * int string atom identity issue by giving priority to unit strings for
692 * '0' through '9' (see JSString::intString in jsstrinlines.h).
694 JS_STATIC_ASSERT(INT_STRING_LIMIT
<= 999);
695 if (2 <= length
&& length
<= 3) {
696 const jschar
*chars
= str
->chars();
698 if ('1' <= chars
[0] && chars
[0] <= '9' &&
699 '0' <= chars
[1] && chars
[1] <= '9' &&
700 (length
== 2 || ('0' <= chars
[2] && chars
[2] <= '9'))) {
701 jsint i
= (chars
[0] - '0') * 10 + chars
[1] - '0';
704 i
= i
* 10 + chars
[2] - '0';
705 if (jsuint(i
) < INT_STRING_LIMIT
)
706 return (JSAtom
*) STRING_TO_JSVAL(JSString::intString(i
));
710 state
= &cx
->runtime
->atomState
;
711 table
= &state
->stringAtoms
;
713 JS_LOCK(cx
, &state
->lock
);
714 entry
= TO_ATOM_ENTRY(JS_DHashTableOperate(table
, str
, JS_DHASH_ADD
));
716 goto failed_hash_add
;
717 if (entry
->keyAndFlags
!= 0) {
718 key
= (JSString
*)ATOM_ENTRY_KEY(entry
);
721 * We created a new hashtable entry. Unless str is already allocated
722 * from the GC heap and flat, we have to release state->lock as
723 * string construction is a complex operation. For example, it can
724 * trigger GC which may rehash the table and make the entry invalid.
727 if (!(flags
& ATOM_TMPSTR
) && str
->isFlat()) {
728 str
->flatClearMutable();
731 gen
= table
->generation
;
732 JS_UNLOCK(cx
, &state
->lock
);
734 if (flags
& ATOM_TMPSTR
) {
735 if (flags
& ATOM_NOCOPY
) {
736 key
= js_NewString(cx
, str
->flatChars(), str
->flatLength());
740 /* Finish handing off chars to the GC'ed key string. */
743 key
= js_NewStringCopyN(cx
, str
->flatChars(), str
->flatLength());
748 JS_ASSERT(str
->isDependent());
749 if (!js_UndependString(cx
, str
))
754 JS_LOCK(cx
, &state
->lock
);
755 if (table
->generation
== gen
) {
756 JS_ASSERT(entry
->keyAndFlags
== 0);
758 entry
= TO_ATOM_ENTRY(JS_DHashTableOperate(table
, key
,
761 goto failed_hash_add
;
762 if (entry
->keyAndFlags
!= 0) {
763 key
= (JSString
*)ATOM_ENTRY_KEY(entry
);
769 INIT_ATOM_ENTRY(entry
, key
);
770 key
->flatSetAtomized();
774 ADD_ATOM_ENTRY_FLAGS(entry
, flags
& (ATOM_PINNED
| ATOM_INTERNED
));
775 JS_ASSERT(key
->isAtomized());
776 v
= STRING_TO_JSVAL(key
);
777 cx
->weakRoots
.lastAtom
= v
;
778 JS_UNLOCK(cx
, &state
->lock
);
782 JS_UNLOCK(cx
, &state
->lock
);
783 JS_ReportOutOfMemory(cx
);
788 js_Atomize(JSContext
*cx
, const char *bytes
, size_t length
, uintN flags
)
795 * Avoiding the malloc in js_InflateString on shorter strings saves us
796 * over 20,000 malloc calls on mozilla browser startup. This compares to
797 * only 131 calls where the string is longer than a 31 char (net) buffer.
798 * The vast majority of atomized strings are already in the hashtable. So
799 * js_AtomizeString rarely has to copy the temp string we make.
801 #define ATOMIZE_BUF_MAX 32
802 jschar inflated
[ATOMIZE_BUF_MAX
];
803 size_t inflatedLength
= ATOMIZE_BUF_MAX
- 1;
805 if (length
< ATOMIZE_BUF_MAX
) {
806 js_InflateStringToBuffer(cx
, bytes
, length
, inflated
, &inflatedLength
);
807 inflated
[inflatedLength
] = 0;
810 inflatedLength
= length
;
811 chars
= js_InflateString(cx
, bytes
, &inflatedLength
);
814 flags
|= ATOM_NOCOPY
;
817 str
.initFlat(chars
, inflatedLength
);
818 atom
= js_AtomizeString(cx
, &str
, ATOM_TMPSTR
| flags
);
819 if (chars
!= inflated
&& str
.flatChars())
825 js_AtomizeChars(JSContext
*cx
, const jschar
*chars
, size_t length
, uintN flags
)
829 str
.initFlat((jschar
*)chars
, length
);
830 return js_AtomizeString(cx
, &str
, ATOM_TMPSTR
| flags
);
834 js_GetExistingStringAtom(JSContext
*cx
, const jschar
*chars
, size_t length
)
838 JSDHashEntryHdr
*hdr
;
842 if (c
< UNIT_STRING_LIMIT
)
843 return (JSAtom
*) STRING_TO_JSVAL(JSString::unitString(c
));
846 str
.initFlat((jschar
*)chars
, length
);
847 state
= &cx
->runtime
->atomState
;
849 JS_LOCK(cx
, &state
->lock
);
850 hdr
= JS_DHashTableOperate(&state
->stringAtoms
, &str
, JS_DHASH_LOOKUP
);
851 str2
= JS_DHASH_ENTRY_IS_BUSY(hdr
)
852 ? (JSString
*)ATOM_ENTRY_KEY(TO_ATOM_ENTRY(hdr
))
854 JS_UNLOCK(cx
, &state
->lock
);
856 return str2
? (JSAtom
*)STRING_TO_JSVAL(str2
) : NULL
;
860 js_AtomizePrimitiveValue(JSContext
*cx
, jsval v
, JSAtom
**atomp
)
864 if (JSVAL_IS_STRING(v
)) {
865 atom
= js_AtomizeString(cx
, JSVAL_TO_STRING(v
), 0);
868 } else if (JSVAL_IS_DOUBLE(v
)) {
869 atom
= js_AtomizeDouble(cx
, *JSVAL_TO_DOUBLE(v
));
873 JS_ASSERT(JSVAL_IS_INT(v
) || JSVAL_IS_BOOLEAN(v
) ||
874 JSVAL_IS_NULL(v
) || JSVAL_IS_VOID(v
));
883 static JSDHashOperator
884 atom_dumper(JSDHashTable
*table
, JSDHashEntryHdr
*hdr
,
885 uint32 number
, void *arg
)
887 JSAtomHashEntry
*entry
= TO_ATOM_ENTRY(hdr
);
888 FILE *fp
= (FILE *)arg
;
892 fprintf(fp
, "%3u %08x ", number
, (uintN
)entry
->hdr
.keyHash
);
893 if (entry
->keyAndFlags
== 0) {
894 fputs("<uninitialized>", fp
);
896 key
= ATOM_ENTRY_KEY(entry
);
897 if (IS_DOUBLE_TABLE(table
)) {
898 fprintf(fp
, "%.16g", *(jsdouble
*)key
);
900 JS_ASSERT(IS_STRING_TABLE(table
));
901 js_FileEscapedString(fp
, (JSString
*)key
, '"');
903 flags
= ATOM_ENTRY_FLAGS(entry
);
905 fputs((flags
& (ATOM_PINNED
| ATOM_INTERNED
))
906 ? " pinned | interned"
907 : (flags
& ATOM_PINNED
) ? " pinned" : " interned",
912 return JS_DHASH_NEXT
;
916 js_DumpAtoms(JSContext
*cx
, FILE *fp
)
918 JSAtomState
*state
= &cx
->runtime
->atomState
;
920 fprintf(fp
, "stringAtoms table contents:\n");
921 JS_DHashTableEnumerate(&state
->stringAtoms
, atom_dumper
, fp
);
923 JS_DHashTableDumpMeter(&state
->stringAtoms
, atom_dumper
, fp
);
927 fprintf(fp
, "doubleAtoms table contents:\n");
928 JS_DHashTableEnumerate(&state
->doubleAtoms
, atom_dumper
, fp
);
930 JS_DHashTableDumpMeter(&state
->doubleAtoms
, atom_dumper
, fp
);
938 js_hash_atom_ptr(const void *key
)
940 const JSAtom
*atom
= (const JSAtom
*) key
;
941 return ATOM_HASH(atom
);
944 #if JS_BITS_PER_WORD == 32
945 # define TEMP_SIZE_START_LOG2 5
947 # define TEMP_SIZE_START_LOG2 6
949 #define TEMP_SIZE_LIMIT_LOG2 (TEMP_SIZE_START_LOG2 + NUM_TEMP_FREELISTS)
951 #define TEMP_SIZE_START JS_BIT(TEMP_SIZE_START_LOG2)
952 #define TEMP_SIZE_LIMIT JS_BIT(TEMP_SIZE_LIMIT_LOG2)
954 JS_STATIC_ASSERT(TEMP_SIZE_START
>= sizeof(JSHashTable
));
957 js_alloc_temp_space(void *priv
, size_t size
)
959 Parser
*parser
= (Parser
*) priv
;
962 if (size
< TEMP_SIZE_LIMIT
) {
963 int bin
= JS_CeilingLog2(size
) - TEMP_SIZE_START_LOG2
;
964 JS_ASSERT(unsigned(bin
) < NUM_TEMP_FREELISTS
);
966 space
= parser
->tempFreeList
[bin
];
968 parser
->tempFreeList
[bin
] = *(void **)space
;
973 JS_ARENA_ALLOCATE(space
, &parser
->context
->tempPool
, size
);
975 js_ReportOutOfScriptQuota(parser
->context
);
980 js_free_temp_space(void *priv
, void *item
, size_t size
)
982 if (size
>= TEMP_SIZE_LIMIT
)
985 Parser
*parser
= (Parser
*) priv
;
986 int bin
= JS_CeilingLog2(size
) - TEMP_SIZE_START_LOG2
;
987 JS_ASSERT(unsigned(bin
) < NUM_TEMP_FREELISTS
);
989 *(void **)item
= parser
->tempFreeList
[bin
];
990 parser
->tempFreeList
[bin
] = item
;
994 js_alloc_temp_entry(void *priv
, const void *key
)
996 Parser
*parser
= (Parser
*) priv
;
997 JSAtomListElement
*ale
;
999 ale
= parser
->aleFreeList
;
1001 parser
->aleFreeList
= ALE_NEXT(ale
);
1005 JS_ARENA_ALLOCATE_TYPE(ale
, JSAtomListElement
, &parser
->context
->tempPool
);
1007 js_ReportOutOfScriptQuota(parser
->context
);
1014 js_free_temp_entry(void *priv
, JSHashEntry
*he
, uintN flag
)
1016 Parser
*parser
= (Parser
*) priv
;
1017 JSAtomListElement
*ale
= (JSAtomListElement
*) he
;
1019 ALE_SET_NEXT(ale
, parser
->aleFreeList
);
1020 parser
->aleFreeList
= ale
;
1023 static JSHashAllocOps temp_alloc_ops
= {
1024 js_alloc_temp_space
, js_free_temp_space
,
1025 js_alloc_temp_entry
, js_free_temp_entry
1029 JSAtomList::rawLookup(JSAtom
*atom
, JSHashEntry
**&hep
)
1031 JSAtomListElement
*ale
;
1034 hep
= JS_HashTableRawLookup(table
, ATOM_HASH(atom
), atom
);
1035 ale
= *hep
? (JSAtomListElement
*) *hep
: NULL
;
1037 JSHashEntry
**alep
= &list
;
1039 while ((ale
= (JSAtomListElement
*)*alep
) != NULL
) {
1040 if (ALE_ATOM(ale
) == atom
) {
1041 /* Hit, move atom's element to the front of the list. */
1042 *alep
= ale
->entry
.next
;
1043 ale
->entry
.next
= list
;
1047 alep
= &ale
->entry
.next
;
1053 #define ATOM_LIST_HASH_THRESHOLD 12
1056 JSAtomList::add(Parser
*parser
, JSAtom
*atom
, AddHow how
)
1060 JSAtomListElement
*ale
, *ale2
, *next
;
1063 ale
= rawLookup(atom
, hep
);
1064 if (!ale
|| how
!= UNIQUE
) {
1065 if (count
< ATOM_LIST_HASH_THRESHOLD
&& !table
) {
1066 /* Few enough for linear search and no hash table yet needed. */
1067 ale
= (JSAtomListElement
*)js_alloc_temp_entry(parser
, atom
);
1070 ALE_SET_ATOM(ale
, atom
);
1073 ale
->entry
.next
= NULL
;
1074 hep
= (JSHashEntry
**) &list
;
1076 hep
= &(*hep
)->next
;
1079 ale
->entry
.next
= list
;
1084 * We should hash, or else we already are hashing, but count was
1085 * reduced by JSAtomList::rawRemove below ATOM_LIST_HASH_THRESHOLD.
1086 * Check whether we should create the table.
1089 /* No hash table yet, so hep had better be null! */
1091 table
= JS_NewHashTable(count
+ 1, js_hash_atom_ptr
,
1092 JS_CompareValues
, JS_CompareValues
,
1093 &temp_alloc_ops
, parser
);
1098 * Set ht->nentries explicitly, because we are moving entries
1099 * from list to ht, not calling JS_HashTable(Raw|)Add.
1101 table
->nentries
= count
;
1104 * Insert each ale on list into the new hash table. Append to
1105 * the hash chain rather than inserting at the bucket head, to
1106 * preserve order among entries with the same key.
1108 for (ale2
= (JSAtomListElement
*)list
; ale2
; ale2
= next
) {
1109 next
= ALE_NEXT(ale2
);
1110 ale2
->entry
.keyHash
= ATOM_HASH(ALE_ATOM(ale2
));
1111 hep
= JS_HashTableRawLookup(table
, ale2
->entry
.keyHash
,
1114 hep
= &(*hep
)->next
;
1115 *hep
= &ale2
->entry
;
1116 ale2
->entry
.next
= NULL
;
1120 /* Set hep for insertion of atom's ale, immediately below. */
1121 hep
= JS_HashTableRawLookup(table
, ATOM_HASH(atom
), atom
);
1124 /* Finally, add an entry for atom into the hash bucket at hep. */
1125 ale
= (JSAtomListElement
*)
1126 JS_HashTableRawAdd(table
, hep
, ATOM_HASH(atom
), atom
, NULL
);
1131 * If hoisting, move ale to the end of its chain after we called
1132 * JS_HashTableRawAdd, since RawAdd may have grown the table and
1133 * then recomputed hep to refer to the pointer to the first entry
1134 * with the given key.
1136 if (how
== HOIST
&& ale
->entry
.next
) {
1137 JS_ASSERT(*hep
== &ale
->entry
);
1138 *hep
= ale
->entry
.next
;
1139 ale
->entry
.next
= NULL
;
1141 hep
= &(*hep
)->next
;
1147 ALE_SET_INDEX(ale
, count
++);
1153 JSAtomList::rawRemove(Parser
*parser
, JSAtomListElement
*ale
, JSHashEntry
**hep
)
1156 JS_ASSERT(count
!= 0);
1160 JS_HashTableRawRemove(table
, hep
, &ale
->entry
);
1164 while (*hep
!= &ale
->entry
) {
1166 hep
= &(*hep
)->next
;
1168 *hep
= ale
->entry
.next
;
1169 js_free_temp_entry(parser
, &ale
->entry
, HT_FREE_ENTRY
);
1175 JSAutoAtomList::~JSAutoAtomList()
1178 JS_HashTableDestroy(table
);
1180 JSHashEntry
*hep
= list
;
1182 JSHashEntry
*next
= hep
->next
;
1183 js_free_temp_entry(parser
, hep
, HT_FREE_ENTRY
);
1190 JSAtomListIterator::operator ()()
1192 JSAtomListElement
*ale
;
1195 if (index
== uint32(-1))
1204 if (index
== JS_BIT(JS_HASH_BITS
- ht
->shift
))
1206 next
= (JSAtomListElement
*) ht
->buckets
[index
++];
1211 next
= ALE_NEXT(ale
);
1220 js_map_atom(JSHashEntry
*he
, intN i
, void *arg
)
1222 JSAtomListElement
*ale
= (JSAtomListElement
*)he
;
1223 JSAtom
**vector
= (JSAtom
**) arg
;
1225 vector
[ALE_INDEX(ale
)] = ALE_ATOM(ale
);
1226 return HT_ENUMERATE_NEXT
;
1230 static jsrefcount js_atom_map_count
;
1231 static jsrefcount js_atom_map_hash_table_count
;
1235 js_InitAtomMap(JSContext
*cx
, JSAtomMap
*map
, JSAtomList
*al
)
1238 JSAtomListElement
*ale
;
1241 /* Map length must already be initialized. */
1242 JS_ASSERT(al
->count
== map
->length
);
1244 JS_ATOMIC_INCREMENT(&js_atom_map_count
);
1246 ale
= (JSAtomListElement
*)al
->list
;
1247 if (!ale
&& !al
->table
) {
1248 JS_ASSERT(!map
->vector
);
1253 vector
= map
->vector
;
1256 JS_ATOMIC_INCREMENT(&js_atom_map_hash_table_count
);
1258 JS_HashTableEnumerateEntries(al
->table
, js_map_atom
, vector
);
1261 vector
[ALE_INDEX(ale
)] = ALE_ATOM(ale
);
1262 } while ((ale
= ALE_NEXT(ale
)) != NULL
);