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 ***** */
60 #include "jsversion.h"
63 #include "jsstrinlines.h"
64 #include "jsatominlines.h"
65 #include "jsobjinlines.h"
68 using namespace js::gc
;
71 * ATOM_HASH assumes that JSHashNumber is 32-bit even on 64-bit systems.
73 JS_STATIC_ASSERT(sizeof(JSHashNumber
) == 4);
74 JS_STATIC_ASSERT(sizeof(JSAtom
*) == JS_BYTES_PER_WORD
);
77 * Start and limit offsets for atom pointers in JSAtomState must be aligned
78 * on the word boundary.
80 JS_STATIC_ASSERT(ATOM_OFFSET_START
% sizeof(JSAtom
*) == 0);
81 JS_STATIC_ASSERT(ATOM_OFFSET_LIMIT
% sizeof(JSAtom
*) == 0);
84 * JS_BOOLEAN_STR and JS_TYPE_STR assume that boolean names starts from the
85 * index 1 and type name starts from the index 1+2 atoms in JSAtomState.
87 JS_STATIC_ASSERT(1 * sizeof(JSAtom
*) ==
88 offsetof(JSAtomState
, booleanAtoms
) - ATOM_OFFSET_START
);
89 JS_STATIC_ASSERT((1 + 2) * sizeof(JSAtom
*) ==
90 offsetof(JSAtomState
, typeAtoms
) - ATOM_OFFSET_START
);
93 js_AtomToPrintableString(JSContext
*cx
, JSAtom
*atom
, JSAutoByteString
*bytes
)
95 return js_ValueToPrintable(cx
, StringValue(ATOM_TO_STRING(atom
)), bytes
);
98 #define JS_PROTO(name,code,init) const char js_##name##_str[] = #name;
99 #include "jsproto.tbl"
103 * String constants for common atoms defined in JSAtomState starting from
104 * JSAtomState.emptyAtom until JSAtomState.lazy.
106 * The elements of the array after the first empty string define strings
107 * corresponding to the two boolean literals, false and true, followed by the
108 * JSType enumerators from jspubtd.h starting with "undefined" for JSTYPE_VOID
109 * (which is special-value 2) and continuing as initialized below. The static
110 * asserts check these relations.
112 JS_STATIC_ASSERT(JSTYPE_LIMIT
== 8);
113 JS_STATIC_ASSERT(JSTYPE_VOID
== 0);
115 const char *const js_common_atom_names
[] = {
117 js_false_str
, /* booleanAtoms[0] */
118 js_true_str
, /* booleanAtoms[1] */
119 js_undefined_str
, /* typeAtoms[JSTYPE_VOID] */
120 js_object_str
, /* typeAtoms[JSTYPE_OBJECT] */
121 js_function_str
, /* typeAtoms[JSTYPE_FUNCTION] */
122 "string", /* typeAtoms[JSTYPE_STRING] */
123 "number", /* typeAtoms[JSTYPE_NUMBER] */
124 "boolean", /* typeAtoms[JSTYPE_BOOLEAN] */
125 js_null_str
, /* typeAtoms[JSTYPE_NULL] */
126 "xml", /* typeAtoms[JSTYPE_XML] */
127 js_null_str
, /* nullAtom */
129 #define JS_PROTO(name,code,init) js_##name##_str,
130 #include "jsproto.tbl"
133 js_anonymous_str
, /* anonymousAtom */
134 js_apply_str
, /* applyAtom */
135 js_arguments_str
, /* argumentsAtom */
136 js_arity_str
, /* arityAtom */
137 js_call_str
, /* callAtom */
138 js_callee_str
, /* calleeAtom */
139 js_caller_str
, /* callerAtom */
140 js_class_prototype_str
, /* classPrototypeAtom */
141 js_constructor_str
, /* constructorAtom */
142 js_each_str
, /* eachAtom */
143 js_eval_str
, /* evalAtom */
144 js_fileName_str
, /* fileNameAtom */
145 js_get_str
, /* getAtom */
146 js_global_str
, /* globalAtom */
147 js_ignoreCase_str
, /* ignoreCaseAtom */
148 js_index_str
, /* indexAtom */
149 js_input_str
, /* inputAtom */
150 "toISOString", /* toISOStringAtom */
151 js_iterator_str
, /* iteratorAtom */
152 js_join_str
, /* joinAtom */
153 js_lastIndex_str
, /* lastIndexAtom */
154 js_length_str
, /* lengthAtom */
155 js_lineNumber_str
, /* lineNumberAtom */
156 js_message_str
, /* messageAtom */
157 js_multiline_str
, /* multilineAtom */
158 js_name_str
, /* nameAtom */
159 js_next_str
, /* nextAtom */
160 js_noSuchMethod_str
, /* noSuchMethodAtom */
161 "[object Null]", /* objectNullAtom */
162 "[object Undefined]", /* objectUndefinedAtom */
163 js_proto_str
, /* protoAtom */
164 js_set_str
, /* setAtom */
165 js_source_str
, /* sourceAtom */
166 js_stack_str
, /* stackAtom */
167 js_sticky_str
, /* stickyAtom */
168 js_toGMTString_str
, /* toGMTStringAtom */
169 js_toLocaleString_str
, /* toLocaleStringAtom */
170 js_toSource_str
, /* toSourceAtom */
171 js_toString_str
, /* toStringAtom */
172 js_toUTCString_str
, /* toUTCStringAtom */
173 js_valueOf_str
, /* valueOfAtom */
174 js_toJSON_str
, /* toJSONAtom */
175 "(void 0)", /* void0Atom */
176 js_enumerable_str
, /* enumerableAtom */
177 js_configurable_str
, /* configurableAtom */
178 js_writable_str
, /* writableAtom */
179 js_value_str
, /* valueAtom */
180 js_test_str
, /* testAtom */
181 "use strict", /* useStrictAtom */
183 "line", /* lineAtom */
184 "Infinity", /* InfinityAtom */
186 "builder", /* builderAtom */
188 #if JS_HAS_XML_SUPPORT
189 js_etago_str
, /* etagoAtom */
190 js_namespace_str
, /* namespaceAtom */
191 js_ptagc_str
, /* ptagcAtom */
192 js_qualifier_str
, /* qualifierAtom */
193 js_space_str
, /* spaceAtom */
194 js_stago_str
, /* stagoAtom */
195 js_star_str
, /* starAtom */
196 js_starQualifier_str
, /* starQualifierAtom */
197 js_tagc_str
, /* tagcAtom */
198 js_xml_str
, /* xmlAtom */
199 "@mozilla.org/js/function", /* functionNamespaceURIAtom */
202 "Proxy", /* ProxyAtom */
204 "getOwnPropertyDescriptor", /* getOwnPropertyDescriptorAtom */
205 "getPropertyDescriptor", /* getPropertyDescriptorAtom */
206 "defineProperty", /* definePropertyAtom */
207 "delete", /* deleteAtom */
208 "getOwnPropertyNames", /* getOwnPropertyNames */
209 "enumerate", /* enumerateAtom */
213 "hasOwn", /* hasOwnAtom */
214 "keys", /* keysAtom */
215 "iterate" /* iterateAtom */
218 JS_STATIC_ASSERT(JS_ARRAY_LENGTH(js_common_atom_names
) * sizeof(JSAtom
*) ==
219 LAZY_ATOM_OFFSET_START
- ATOM_OFFSET_START
);
222 * Interpreter macros called by the trace recorder assume common atom indexes
223 * fit in one byte of immediate operand.
225 JS_STATIC_ASSERT(JS_ARRAY_LENGTH(js_common_atom_names
) < 256);
227 const size_t js_common_atom_count
= JS_ARRAY_LENGTH(js_common_atom_names
);
229 const char js_anonymous_str
[] = "anonymous";
230 const char js_apply_str
[] = "apply";
231 const char js_arguments_str
[] = "arguments";
232 const char js_arity_str
[] = "arity";
233 const char js_call_str
[] = "call";
234 const char js_callee_str
[] = "callee";
235 const char js_caller_str
[] = "caller";
236 const char js_class_prototype_str
[] = "prototype";
237 const char js_constructor_str
[] = "constructor";
238 const char js_each_str
[] = "each";
239 const char js_eval_str
[] = "eval";
240 const char js_fileName_str
[] = "fileName";
241 const char js_get_str
[] = "get";
242 const char js_getter_str
[] = "getter";
243 const char js_global_str
[] = "global";
244 const char js_ignoreCase_str
[] = "ignoreCase";
245 const char js_index_str
[] = "index";
246 const char js_input_str
[] = "input";
247 const char js_iterator_str
[] = "__iterator__";
248 const char js_join_str
[] = "join";
249 const char js_lastIndex_str
[] = "lastIndex";
250 const char js_length_str
[] = "length";
251 const char js_lineNumber_str
[] = "lineNumber";
252 const char js_message_str
[] = "message";
253 const char js_multiline_str
[] = "multiline";
254 const char js_name_str
[] = "name";
255 const char js_next_str
[] = "next";
256 const char js_noSuchMethod_str
[] = "__noSuchMethod__";
257 const char js_object_str
[] = "object";
258 const char js_proto_str
[] = "__proto__";
259 const char js_setter_str
[] = "setter";
260 const char js_set_str
[] = "set";
261 const char js_source_str
[] = "source";
262 const char js_stack_str
[] = "stack";
263 const char js_sticky_str
[] = "sticky";
264 const char js_toGMTString_str
[] = "toGMTString";
265 const char js_toLocaleString_str
[] = "toLocaleString";
266 const char js_toSource_str
[] = "toSource";
267 const char js_toString_str
[] = "toString";
268 const char js_toUTCString_str
[] = "toUTCString";
269 const char js_undefined_str
[] = "undefined";
270 const char js_valueOf_str
[] = "valueOf";
271 const char js_toJSON_str
[] = "toJSON";
272 const char js_enumerable_str
[] = "enumerable";
273 const char js_configurable_str
[] = "configurable";
274 const char js_writable_str
[] = "writable";
275 const char js_value_str
[] = "value";
276 const char js_test_str
[] = "test";
278 #if JS_HAS_XML_SUPPORT
279 const char js_etago_str
[] = "</";
280 const char js_namespace_str
[] = "namespace";
281 const char js_ptagc_str
[] = "/>";
282 const char js_qualifier_str
[] = "::";
283 const char js_space_str
[] = " ";
284 const char js_stago_str
[] = "<";
285 const char js_star_str
[] = "*";
286 const char js_starQualifier_str
[] = "*::";
287 const char js_tagc_str
[] = ">";
288 const char js_xml_str
[] = "xml";
291 #if JS_HAS_GENERATORS
292 const char js_close_str
[] = "close";
293 const char js_send_str
[] = "send";
297 * Helper macros to access and modify JSAtomHashEntry.
301 StringToInitialAtomEntry(JSString
*str
)
303 return (AtomEntryType
) str
;
307 AtomEntryFlags(AtomEntryType entry
)
309 return (uintN
) (entry
& ATOM_ENTRY_FLAG_MASK
);
313 * Conceptually, we have compressed a HashMap<JSAtom *, uint> into a
314 * HashMap<size_t>. Here, we promise that we are only changing the "value" of
315 * the HashMap entry, so the const_cast is safe.
319 AddAtomEntryFlags(const AtomEntryType
&entry
, uintN flags
)
321 const_cast<AtomEntryType
&>(entry
) |= AtomEntryType(flags
);
325 ClearAtomEntryFlags(const AtomEntryType
&entry
, uintN flags
)
327 const_cast<AtomEntryType
&>(entry
) &= ~AtomEntryType(flags
);
331 * For a browser build from 2007-08-09 after the browser starts up there are
332 * just 55 double atoms, but over 15000 string atoms. Not to penalize more
333 * economical embeddings allocating too much memory initially we initialize
334 * atomized strings with just 1K entries.
336 #define JS_STRING_HASH_COUNT 1024
339 js_InitAtomState(JSRuntime
*rt
)
341 JSAtomState
*state
= &rt
->atomState
;
343 JS_ASSERT(!state
->atoms
.initialized());
344 if (!state
->atoms
.init(JS_STRING_HASH_COUNT
))
348 js_InitLock(&state
->lock
);
350 JS_ASSERT(state
->atoms
.initialized());
355 js_FinishAtomState(JSRuntime
*rt
)
357 JSAtomState
*state
= &rt
->atomState
;
359 if (!state
->atoms
.initialized()) {
361 * We are called with uninitialized state when JS_NewRuntime fails and
362 * calls JS_DestroyRuntime on a partially initialized runtime.
367 for (AtomSet::Range r
= state
->atoms
.all(); !r
.empty(); r
.popFront()) {
368 JSString
*str
= AtomEntryToKey(r
.front());
369 js_FinalizeStringRT(rt
, str
);
373 js_FinishLock(&state
->lock
);
378 js_InitCommonAtoms(JSContext
*cx
)
380 JSAtomState
*state
= &cx
->runtime
->atomState
;
384 atoms
= COMMON_ATOMS_START(state
);
385 for (i
= 0; i
< JS_ARRAY_LENGTH(js_common_atom_names
); i
++, atoms
++) {
386 *atoms
= js_Atomize(cx
, js_common_atom_names
[i
],
387 strlen(js_common_atom_names
[i
]), ATOM_PINNED
);
391 JS_ASSERT((uint8
*)atoms
- (uint8
*)state
== LAZY_ATOM_OFFSET_START
);
392 memset(atoms
, 0, ATOM_OFFSET_LIMIT
- LAZY_ATOM_OFFSET_START
);
394 cx
->runtime
->emptyString
= ATOM_TO_STRING(state
->emptyAtom
);
399 js_FinishCommonAtoms(JSContext
*cx
)
401 cx
->runtime
->emptyString
= NULL
;
402 JSAtomState
*state
= &cx
->runtime
->atomState
;
404 for (AtomSet::Range r
= state
->atoms
.all(); !r
.empty(); r
.popFront())
405 ClearAtomEntryFlags(r
.front(), ATOM_PINNED
);
408 memset(COMMON_ATOMS_START(state
), JS_FREE_PATTERN
,
409 ATOM_OFFSET_LIMIT
- ATOM_OFFSET_START
);
414 js_TraceAtomState(JSTracer
*trc
)
416 JSRuntime
*rt
= trc
->context
->runtime
;
417 JSAtomState
*state
= &rt
->atomState
;
423 if (rt
->gcKeepAtoms
) {
424 for (AtomSet::Range r
= state
->atoms
.all(); !r
.empty(); r
.popFront()) {
425 JS_SET_TRACING_INDEX(trc
, "locked_atom", number
++);
426 MarkString(trc
, AtomEntryToKey(r
.front()));
429 for (AtomSet::Range r
= state
->atoms
.all(); !r
.empty(); r
.popFront()) {
430 AtomEntryType entry
= r
.front();
431 uintN flags
= AtomEntryFlags(entry
);
432 if (flags
& (ATOM_PINNED
| ATOM_INTERNED
)) {
433 JS_SET_TRACING_INDEX(trc
,
438 MarkString(trc
, AtomEntryToKey(entry
));
445 js_SweepAtomState(JSContext
*cx
)
447 JSAtomState
*state
= &cx
->runtime
->atomState
;
449 for (AtomSet::Enum
e(state
->atoms
); !e
.empty(); e
.popFront()) {
450 AtomEntryType entry
= e
.front();
451 if (AtomEntryFlags(entry
) & (ATOM_PINNED
| ATOM_INTERNED
)) {
452 /* Pinned or interned key cannot be finalized. */
453 JS_ASSERT(!IsAboutToBeFinalized(cx
, AtomEntryToKey(entry
)));
454 } else if (IsAboutToBeFinalized(cx
, AtomEntryToKey(entry
))) {
461 js_AtomizeString(JSContext
*cx
, JSString
*strArg
, uintN flags
)
463 JS_ASSERT(!(flags
& ~(ATOM_PINNED
|ATOM_INTERNED
|ATOM_TMPSTR
|ATOM_NOCOPY
)));
464 JS_ASSERT_IF(flags
& ATOM_NOCOPY
, flags
& ATOM_TMPSTR
);
466 if (strArg
->isAtomized())
467 return STRING_TO_ATOM(strArg
);
469 JSLinearString
*str
= strArg
->ensureLinear(cx
);
473 const jschar
*chars
= str
->chars();
474 size_t length
= str
->length();
476 JSString
*staticStr
= JSString::lookupStaticString(chars
, length
);
478 return STRING_TO_ATOM(staticStr
);
480 JSAtomState
*state
= &cx
->runtime
->atomState
;
481 AtomSet
&atoms
= state
->atoms
;
483 AutoLockAtomsCompartment
lock(cx
);
484 AtomSet::AddPtr p
= atoms
.lookupForAdd(str
);
486 /* Hashing the string should have flattened it if it was a rope. */
487 JS_ASSERT(str
->isFlat() || str
->isDependent());
491 key
= AtomEntryToKey(*p
);
494 * Ensure that any atomized string lives only in the default
497 bool needNewString
= !!(flags
& ATOM_TMPSTR
) ||
498 str
->asCell()->compartment() != cx
->runtime
->atomsCompartment
;
501 * Unless str is already comes from the default compartment and flat,
502 * we have to relookup the key as the last ditch GC invoked from the
503 * string allocation or OOM handling may unlock the default
506 if (!needNewString
&& str
->isFlat()) {
507 str
->flatClearExtensible();
509 atoms
.add(p
, StringToInitialAtomEntry(key
));
512 SwitchToCompartment
sc(cx
, cx
->runtime
->atomsCompartment
);
513 if (flags
& ATOM_NOCOPY
) {
514 key
= js_NewString(cx
, const_cast<jschar
*>(str
->flatChars()), length
);
518 /* Finish handing off chars to the GC'ed key string. */
519 JS_ASSERT(flags
& ATOM_TMPSTR
);
522 key
= js_NewStringCopyN(cx
, chars
, length
);
527 JS_ASSERT(str
->isDependent());
528 if (!str
->undepend(cx
))
533 if (!atoms
.relookupOrAdd(p
, key
, StringToInitialAtomEntry(key
))) {
534 JS_ReportOutOfMemory(cx
); /* SystemAllocPolicy does not report */
538 key
->flatSetAtomized();
541 AddAtomEntryFlags(*p
, flags
& (ATOM_PINNED
| ATOM_INTERNED
));
543 JSAtom
*atom
= STRING_TO_ATOM(key
);
548 js_Atomize(JSContext
*cx
, const char *bytes
, size_t length
, uintN flags
)
557 * Avoiding the malloc in js_InflateString on shorter strings saves us
558 * over 20,000 malloc calls on mozilla browser startup. This compares to
559 * only 131 calls where the string is longer than a 31 char (net) buffer.
560 * The vast majority of atomized strings are already in the hashtable. So
561 * js_AtomizeString rarely has to copy the temp string we make.
563 #define ATOMIZE_BUF_MAX 32
564 jschar inflated
[ATOMIZE_BUF_MAX
];
565 size_t inflatedLength
= ATOMIZE_BUF_MAX
- 1;
567 if (length
< ATOMIZE_BUF_MAX
) {
568 js_InflateStringToBuffer(cx
, bytes
, length
, inflated
, &inflatedLength
);
569 inflated
[inflatedLength
] = 0;
572 inflatedLength
= length
;
573 chars
= js_InflateString(cx
, bytes
, &inflatedLength
);
576 flags
|= ATOM_NOCOPY
;
579 str
.initFlat(chars
, inflatedLength
);
580 atom
= js_AtomizeString(cx
, &str
, ATOM_TMPSTR
| flags
);
581 if (chars
!= inflated
&& str
.flatChars())
587 js_AtomizeChars(JSContext
*cx
, const jschar
*chars
, size_t length
, uintN flags
)
593 if (!CheckStringLength(cx
, length
))
596 str
.initFlatNotTerminated((jschar
*)chars
, length
);
597 return js_AtomizeString(cx
, &str
, ATOM_TMPSTR
| flags
);
601 js_GetExistingStringAtom(JSContext
*cx
, const jschar
*chars
, size_t length
)
608 if (c
< UNIT_STRING_LIMIT
)
609 return STRING_TO_ATOM(JSString::unitString(c
));
612 str
.initFlatNotTerminated((jschar
*)chars
, length
);
613 state
= &cx
->runtime
->atomState
;
615 JS_LOCK(cx
, &state
->lock
);
616 AtomSet::Ptr p
= state
->atoms
.lookup(str
.assertIsFlat());
617 str2
= p
? AtomEntryToKey(*p
) : NULL
;
618 JS_UNLOCK(cx
, &state
->lock
);
620 return str2
? STRING_TO_ATOM(str2
) : NULL
;
625 js_DumpAtoms(JSContext
*cx
, FILE *fp
)
627 JSAtomState
*state
= &cx
->runtime
->atomState
;
629 fprintf(fp
, "atoms table contents:\n");
631 for (AtomSet::Range r
= state
->atoms
.all(); !r
.empty(); r
.popFront()) {
632 AtomEntryType entry
= r
.front();
633 fprintf(fp
, "%3u ", number
++);
635 fputs("<uninitialized>", fp
);
637 JSAtom
*key
= AtomEntryToKey(entry
);
638 FileEscapedString(fp
, key
, '"');
639 uintN flags
= AtomEntryFlags(entry
);
641 fputs((flags
& (ATOM_PINNED
| ATOM_INTERNED
))
642 ? " pinned | interned"
643 : (flags
& ATOM_PINNED
) ? " pinned" : " interned",
654 js_hash_atom_ptr(const void *key
)
656 const JSAtom
*atom
= (const JSAtom
*) key
;
657 return ATOM_HASH(atom
);
660 #if JS_BITS_PER_WORD == 32
661 # define TEMP_SIZE_START_LOG2 5
663 # define TEMP_SIZE_START_LOG2 6
665 #define TEMP_SIZE_LIMIT_LOG2 (TEMP_SIZE_START_LOG2 + NUM_TEMP_FREELISTS)
667 #define TEMP_SIZE_START JS_BIT(TEMP_SIZE_START_LOG2)
668 #define TEMP_SIZE_LIMIT JS_BIT(TEMP_SIZE_LIMIT_LOG2)
670 JS_STATIC_ASSERT(TEMP_SIZE_START
>= sizeof(JSHashTable
));
673 js_alloc_temp_space(void *priv
, size_t size
)
675 Parser
*parser
= (Parser
*) priv
;
678 if (size
< TEMP_SIZE_LIMIT
) {
679 int bin
= JS_CeilingLog2(size
) - TEMP_SIZE_START_LOG2
;
680 JS_ASSERT(unsigned(bin
) < NUM_TEMP_FREELISTS
);
682 space
= parser
->tempFreeList
[bin
];
684 parser
->tempFreeList
[bin
] = *(void **)space
;
689 JS_ARENA_ALLOCATE(space
, &parser
->context
->tempPool
, size
);
691 js_ReportOutOfScriptQuota(parser
->context
);
696 js_free_temp_space(void *priv
, void *item
, size_t size
)
698 if (size
>= TEMP_SIZE_LIMIT
)
701 Parser
*parser
= (Parser
*) priv
;
702 int bin
= JS_CeilingLog2(size
) - TEMP_SIZE_START_LOG2
;
703 JS_ASSERT(unsigned(bin
) < NUM_TEMP_FREELISTS
);
705 *(void **)item
= parser
->tempFreeList
[bin
];
706 parser
->tempFreeList
[bin
] = item
;
710 js_alloc_temp_entry(void *priv
, const void *key
)
712 Parser
*parser
= (Parser
*) priv
;
713 JSAtomListElement
*ale
;
715 ale
= parser
->aleFreeList
;
717 parser
->aleFreeList
= ALE_NEXT(ale
);
721 JS_ARENA_ALLOCATE_TYPE(ale
, JSAtomListElement
, &parser
->context
->tempPool
);
723 js_ReportOutOfScriptQuota(parser
->context
);
730 js_free_temp_entry(void *priv
, JSHashEntry
*he
, uintN flag
)
732 Parser
*parser
= (Parser
*) priv
;
733 JSAtomListElement
*ale
= (JSAtomListElement
*) he
;
735 ALE_SET_NEXT(ale
, parser
->aleFreeList
);
736 parser
->aleFreeList
= ale
;
739 static JSHashAllocOps temp_alloc_ops
= {
740 js_alloc_temp_space
, js_free_temp_space
,
741 js_alloc_temp_entry
, js_free_temp_entry
745 JSAtomList::rawLookup(JSAtom
*atom
, JSHashEntry
**&hep
)
748 hep
= JS_HashTableRawLookup(table
, ATOM_HASH(atom
), atom
);
749 return (JSAtomListElement
*) *hep
;
752 JSHashEntry
**alep
= &list
;
754 JSAtomListElement
*ale
;
755 while ((ale
= (JSAtomListElement
*)*alep
) != NULL
) {
756 if (ALE_ATOM(ale
) == atom
) {
757 /* Hit, move atom's element to the front of the list. */
758 *alep
= ale
->entry
.next
;
759 ale
->entry
.next
= list
;
763 alep
= &ale
->entry
.next
;
768 #define ATOM_LIST_HASH_THRESHOLD 12
771 JSAtomList::add(Parser
*parser
, JSAtom
*atom
, AddHow how
)
775 JSAtomListElement
*ale
, *ale2
, *next
;
778 ale
= rawLookup(atom
, hep
);
779 if (!ale
|| how
!= UNIQUE
) {
780 if (count
< ATOM_LIST_HASH_THRESHOLD
&& !table
) {
781 /* Few enough for linear search and no hash table yet needed. */
782 ale
= (JSAtomListElement
*)js_alloc_temp_entry(parser
, atom
);
785 ALE_SET_ATOM(ale
, atom
);
788 ale
->entry
.next
= NULL
;
789 hep
= (JSHashEntry
**) &list
;
794 ale
->entry
.next
= list
;
799 * We should hash, or else we already are hashing, but count was
800 * reduced by JSAtomList::rawRemove below ATOM_LIST_HASH_THRESHOLD.
801 * Check whether we should create the table.
804 /* No hash table yet, so hep had better be null! */
806 table
= JS_NewHashTable(count
+ 1, js_hash_atom_ptr
,
807 JS_CompareValues
, JS_CompareValues
,
808 &temp_alloc_ops
, parser
);
813 * Set ht->nentries explicitly, because we are moving entries
814 * from list to ht, not calling JS_HashTable(Raw|)Add.
816 table
->nentries
= count
;
819 * Insert each ale on list into the new hash table. Append to
820 * the hash chain rather than inserting at the bucket head, to
821 * preserve order among entries with the same key.
823 for (ale2
= (JSAtomListElement
*)list
; ale2
; ale2
= next
) {
824 next
= ALE_NEXT(ale2
);
825 ale2
->entry
.keyHash
= ATOM_HASH(ALE_ATOM(ale2
));
826 hep
= JS_HashTableRawLookup(table
, ale2
->entry
.keyHash
,
831 ale2
->entry
.next
= NULL
;
835 /* Set hep for insertion of atom's ale, immediately below. */
836 hep
= JS_HashTableRawLookup(table
, ATOM_HASH(atom
), atom
);
839 /* Finally, add an entry for atom into the hash bucket at hep. */
840 ale
= (JSAtomListElement
*)
841 JS_HashTableRawAdd(table
, hep
, ATOM_HASH(atom
), atom
, NULL
);
846 * If hoisting, move ale to the end of its chain after we called
847 * JS_HashTableRawAdd, since RawAdd may have grown the table and
848 * then recomputed hep to refer to the pointer to the first entry
849 * with the given key.
851 if (how
== HOIST
&& ale
->entry
.next
) {
852 JS_ASSERT(*hep
== &ale
->entry
);
853 *hep
= ale
->entry
.next
;
854 ale
->entry
.next
= NULL
;
862 ALE_SET_INDEX(ale
, count
++);
868 JSAtomList::rawRemove(Parser
*parser
, JSAtomListElement
*ale
, JSHashEntry
**hep
)
871 JS_ASSERT(count
!= 0);
875 JS_HashTableRawRemove(table
, hep
, &ale
->entry
);
879 while (*hep
!= &ale
->entry
) {
883 *hep
= ale
->entry
.next
;
884 js_free_temp_entry(parser
, &ale
->entry
, HT_FREE_ENTRY
);
890 JSAutoAtomList::~JSAutoAtomList()
893 JS_HashTableDestroy(table
);
895 JSHashEntry
*hep
= list
;
897 JSHashEntry
*next
= hep
->next
;
898 js_free_temp_entry(parser
, hep
, HT_FREE_ENTRY
);
905 JSAtomListIterator::operator ()()
907 JSAtomListElement
*ale
;
910 if (index
== uint32(-1))
919 if (index
== JS_BIT(JS_HASH_BITS
- ht
->shift
))
921 next
= (JSAtomListElement
*) ht
->buckets
[index
++];
926 next
= ALE_NEXT(ale
);
935 js_map_atom(JSHashEntry
*he
, intN i
, void *arg
)
937 JSAtomListElement
*ale
= (JSAtomListElement
*)he
;
938 JSAtom
**vector
= (JSAtom
**) arg
;
940 vector
[ALE_INDEX(ale
)] = ALE_ATOM(ale
);
941 return HT_ENUMERATE_NEXT
;
945 static jsrefcount js_atom_map_count
;
946 static jsrefcount js_atom_map_hash_table_count
;
950 js_InitAtomMap(JSContext
*cx
, JSAtomMap
*map
, JSAtomList
*al
)
953 JSAtomListElement
*ale
;
956 /* Map length must already be initialized. */
957 JS_ASSERT(al
->count
== map
->length
);
959 JS_ATOMIC_INCREMENT(&js_atom_map_count
);
961 ale
= (JSAtomListElement
*)al
->list
;
962 if (!ale
&& !al
->table
) {
963 JS_ASSERT(!map
->vector
);
968 vector
= map
->vector
;
971 JS_ATOMIC_INCREMENT(&js_atom_map_hash_table_count
);
973 JS_HashTableEnumerateEntries(al
->table
, js_map_atom
, vector
);
976 vector
[ALE_INDEX(ale
)] = ALE_ATOM(ale
);
977 } while ((ale
= ALE_NEXT(ale
)) != NULL
);
982 #if JS_HAS_XML_SUPPORT
984 js_InternNonIntElementIdSlow(JSContext
*cx
, JSObject
*obj
, const Value
&idval
,
987 JS_ASSERT(idval
.isObject());
989 *idp
= OBJECT_TO_JSID(&idval
.toObject());
993 if (!js_IsFunctionQName(cx
, &idval
.toObject(), idp
))
995 if (!JSID_IS_VOID(*idp
))
998 return js_ValueToStringId(cx
, idval
, idp
);
1002 js_InternNonIntElementIdSlow(JSContext
*cx
, JSObject
*obj
, const Value
&idval
,
1003 jsid
*idp
, Value
*vp
)
1005 JS_ASSERT(idval
.isObject());
1007 JSObject
&idobj
= idval
.toObject();
1008 *idp
= OBJECT_TO_JSID(&idobj
);
1009 vp
->setObject(idobj
);
1013 if (!js_IsFunctionQName(cx
, &idval
.toObject(), idp
))
1015 if (!JSID_IS_VOID(*idp
)) {
1016 *vp
= IdToValue(*idp
);
1020 if (js_ValueToStringId(cx
, idval
, idp
)) {
1021 vp
->setString(JSID_TO_STRING(*idp
));