1 |
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
2 |
* |
3 |
* ***** BEGIN LICENSE BLOCK ***** |
4 |
* Version: MPL 1.1/GPL 2.0/LGPL 2.1 |
5 |
* |
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/ |
10 |
* |
11 |
* Software distributed under the License is distributed on an "AS IS" basis, |
12 |
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License |
13 |
* for the specific language governing rights and limitations under the |
14 |
* License. |
15 |
* |
16 |
* The Original Code is Mozilla Communicator client code, released |
17 |
* March 31, 1998. |
18 |
* |
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. |
23 |
* |
24 |
* Contributor(s): |
25 |
* |
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. |
37 |
* |
38 |
* ***** END LICENSE BLOCK ***** */ |
39 |
|
40 |
/* |
41 |
* JS atom table. |
42 |
*/ |
43 |
#include "jsstddef.h" |
44 |
#include <stdlib.h> |
45 |
#include <string.h> |
46 |
#include "jstypes.h" |
47 |
#include "jsutil.h" /* Added by JSIFY */ |
48 |
#include "jshash.h" /* Added by JSIFY */ |
49 |
#include "jsprf.h" |
50 |
#include "jsapi.h" |
51 |
#include "jsatom.h" |
52 |
#include "jscntxt.h" |
53 |
#include "jsversion.h" |
54 |
#include "jsgc.h" |
55 |
#include "jslock.h" |
56 |
#include "jsnum.h" |
57 |
#include "jsscan.h" |
58 |
#include "jsstr.h" |
59 |
|
60 |
const char * |
61 |
js_AtomToPrintableString(JSContext *cx, JSAtom *atom) |
62 |
{ |
63 |
return js_ValueToPrintableString(cx, ATOM_KEY(atom)); |
64 |
} |
65 |
|
66 |
#define JS_PROTO(name,code,init) const char js_##name##_str[] = #name; |
67 |
#include "jsproto.tbl" |
68 |
#undef JS_PROTO |
69 |
|
70 |
/* |
71 |
* String constants for common atoms defined in JSAtomState starting from |
72 |
* JSAtomState.emptyAtom until JSAtomState.lazy. |
73 |
* |
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. |
79 |
*/ |
80 |
JS_STATIC_ASSERT(JSTYPE_LIMIT == 8); |
81 |
JS_STATIC_ASSERT(JSVAL_TO_BOOLEAN(JSVAL_VOID) == 2); |
82 |
JS_STATIC_ASSERT(JSTYPE_VOID == 0); |
83 |
|
84 |
const char *const js_common_atom_names[] = { |
85 |
"", /* emptyAtom */ |
86 |
js_false_str, /* booleanAtoms[0] */ |
87 |
js_true_str, /* booleanAtoms[1] */ |
88 |
js_undefined_str, /* typeAtoms[JSTYPE_VOID] */ |
89 |
js_object_str, /* typeAtoms[JSTYPE_OBJECT] */ |
90 |
js_function_str, /* typeAtoms[JSTYPE_FUNCTION] */ |
91 |
"string", /* typeAtoms[JSTYPE_STRING] */ |
92 |
"number", /* typeAtoms[JSTYPE_NUMBER] */ |
93 |
"boolean", /* typeAtoms[JSTYPE_BOOLEAN] */ |
94 |
js_null_str, /* typeAtoms[JSTYPE_NULL] */ |
95 |
"xml", /* typeAtoms[JSTYPE_XML] */ |
96 |
js_null_str, /* nullAtom */ |
97 |
|
98 |
#define JS_PROTO(name,code,init) js_##name##_str, |
99 |
#include "jsproto.tbl" |
100 |
#undef JS_PROTO |
101 |
|
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 */ |
137 |
|
138 |
#if JS_HAS_XML_SUPPORT |
139 |
js_etago_str, /* etagoAtom */ |
140 |
js_namespace_str, /* namespaceAtom */ |
141 |
js_ptagc_str, /* ptagcAtom */ |
142 |
js_qualifier_str, /* qualifierAtom */ |
143 |
js_space_str, /* spaceAtom */ |
144 |
js_stago_str, /* stagoAtom */ |
145 |
js_star_str, /* starAtom */ |
146 |
js_starQualifier_str, /* starQualifierAtom */ |
147 |
js_tagc_str, /* tagcAtom */ |
148 |
js_xml_str, /* xmlAtom */ |
149 |
#endif |
150 |
|
151 |
#ifdef NARCISSUS |
152 |
js___call___str, /* __call__Atom */ |
153 |
js___construct___str, /* __construct__Atom */ |
154 |
js___hasInstance___str, /* __hasInstance__Atom */ |
155 |
js_ExecutionContext_str, /* ExecutionContextAtom */ |
156 |
js_current_str, /* currentAtom */ |
157 |
#endif |
158 |
}; |
159 |
|
160 |
JS_STATIC_ASSERT(JS_ARRAY_LENGTH(js_common_atom_names) * sizeof(JSAtom *) == |
161 |
LAZY_ATOM_OFFSET_START - ATOM_OFFSET_START); |
162 |
|
163 |
/* |
164 |
* Interpreter macros called by the trace recorder assume common atom indexes |
165 |
* fit in one byte of immediate operand. |
166 |
*/ |
167 |
JS_STATIC_ASSERT(JS_ARRAY_LENGTH(js_common_atom_names) < 256); |
168 |
|
169 |
const size_t js_common_atom_count = JS_ARRAY_LENGTH(js_common_atom_names); |
170 |
|
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"; |
207 |
|
208 |
#if JS_HAS_XML_SUPPORT |
209 |
const char js_etago_str[] = "</"; |
210 |
const char js_namespace_str[] = "namespace"; |
211 |
const char js_ptagc_str[] = "/>"; |
212 |
const char js_qualifier_str[] = "::"; |
213 |
const char js_space_str[] = " "; |
214 |
const char js_stago_str[] = "<"; |
215 |
const char js_star_str[] = "*"; |
216 |
const char js_starQualifier_str[] = "*::"; |
217 |
const char js_tagc_str[] = ">"; |
218 |
const char js_xml_str[] = "xml"; |
219 |
#endif |
220 |
|
221 |
#if JS_HAS_GENERATORS |
222 |
const char js_close_str[] = "close"; |
223 |
const char js_send_str[] = "send"; |
224 |
#endif |
225 |
|
226 |
#ifdef NARCISSUS |
227 |
const char js___call___str[] = "__call__"; |
228 |
const char js___construct___str[] = "__construct__"; |
229 |
const char js___hasInstance___str[] = "__hasInstance__"; |
230 |
const char js_ExecutionContext_str[] = "ExecutionContext"; |
231 |
const char js_current_str[] = "current"; |
232 |
#endif |
233 |
|
234 |
/* |
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. |
238 |
*/ |
239 |
typedef struct JSAtomHashEntry { |
240 |
JSDHashEntryHdr hdr; |
241 |
jsuword keyAndFlags; |
242 |
} JSAtomHashEntry; |
243 |
|
244 |
#define ATOM_ENTRY_FLAG_MASK (ATOM_PINNED | ATOM_INTERNED) |
245 |
|
246 |
JS_STATIC_ASSERT(ATOM_ENTRY_FLAG_MASK < JSVAL_ALIGN); |
247 |
|
248 |
/* |
249 |
* Helper macros to access and modify JSAtomHashEntry. |
250 |
*/ |
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))) |
262 |
|
263 |
static JSDHashNumber |
264 |
HashDouble(JSDHashTable *table, const void *key); |
265 |
|
266 |
static JSBool |
267 |
MatchDouble(JSDHashTable *table, const JSDHashEntryHdr *hdr, const void *key); |
268 |
|
269 |
static JSDHashNumber |
270 |
HashString(JSDHashTable *table, const void *key); |
271 |
|
272 |
static JSBool |
273 |
MatchString(JSDHashTable *table, const JSDHashEntryHdr *hdr, const void *key); |
274 |
|
275 |
static const JSDHashTableOps DoubleHashOps = { |
276 |
JS_DHashAllocTable, |
277 |
JS_DHashFreeTable, |
278 |
HashDouble, |
279 |
MatchDouble, |
280 |
JS_DHashMoveEntryStub, |
281 |
JS_DHashClearEntryStub, |
282 |
JS_DHashFinalizeStub, |
283 |
NULL |
284 |
}; |
285 |
|
286 |
static const JSDHashTableOps StringHashOps = { |
287 |
JS_DHashAllocTable, |
288 |
JS_DHashFreeTable, |
289 |
HashString, |
290 |
MatchString, |
291 |
JS_DHashMoveEntryStub, |
292 |
JS_DHashClearEntryStub, |
293 |
JS_DHashFinalizeStub, |
294 |
NULL |
295 |
}; |
296 |
|
297 |
#define IS_DOUBLE_TABLE(table) ((table)->ops == &DoubleHashOps) |
298 |
#define IS_STRING_TABLE(table) ((table)->ops == &StringHashOps) |
299 |
|
300 |
#define IS_INITIALIZED_STATE(state) IS_DOUBLE_TABLE(&(state)->doubleAtoms) |
301 |
|
302 |
static JSDHashNumber |
303 |
HashDouble(JSDHashTable *table, const void *key) |
304 |
{ |
305 |
jsdouble d; |
306 |
|
307 |
JS_ASSERT(IS_DOUBLE_TABLE(table)); |
308 |
d = *(jsdouble *)key; |
309 |
return JSDOUBLE_HI32(d) ^ JSDOUBLE_LO32(d); |
310 |
} |
311 |
|
312 |
static JSDHashNumber |
313 |
HashString(JSDHashTable *table, const void *key) |
314 |
{ |
315 |
JS_ASSERT(IS_STRING_TABLE(table)); |
316 |
return js_HashString((JSString *)key); |
317 |
} |
318 |
|
319 |
static JSBool |
320 |
MatchDouble(JSDHashTable *table, const JSDHashEntryHdr *hdr, const void *key) |
321 |
{ |
322 |
JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr); |
323 |
jsdouble d1, d2; |
324 |
|
325 |
JS_ASSERT(IS_DOUBLE_TABLE(table)); |
326 |
if (entry->keyAndFlags == 0) { |
327 |
/* See comments in MatchString. */ |
328 |
return JS_FALSE; |
329 |
} |
330 |
|
331 |
d1 = *(jsdouble *)ATOM_ENTRY_KEY(entry); |
332 |
d2 = *(jsdouble *)key; |
333 |
if (JSDOUBLE_IS_NaN(d1)) |
334 |
return JSDOUBLE_IS_NaN(d2); |
335 |
#if defined(XP_WIN) |
336 |
/* XXX MSVC miscompiles such that (NaN == 0) */ |
337 |
if (JSDOUBLE_IS_NaN(d2)) |
338 |
return JS_FALSE; |
339 |
#endif |
340 |
return d1 == d2; |
341 |
} |
342 |
|
343 |
static JSBool |
344 |
MatchString(JSDHashTable *table, const JSDHashEntryHdr *hdr, const void *key) |
345 |
{ |
346 |
JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr); |
347 |
|
348 |
JS_ASSERT(IS_STRING_TABLE(table)); |
349 |
if (entry->keyAndFlags == 0) { |
350 |
/* |
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. |
354 |
* |
355 |
* We always return false for such entries so JS_DHashTableOperate |
356 |
* never finds them. We clean them during GC's sweep phase. |
357 |
* |
358 |
* It means that with a contested lock or when GC is triggered outside |
359 |
* the lock we may end up adding two entries, but this is a price for |
360 |
* simpler code. |
361 |
*/ |
362 |
return JS_FALSE; |
363 |
} |
364 |
return js_EqualStrings((JSString *)ATOM_ENTRY_KEY(entry), (JSString *)key); |
365 |
} |
366 |
|
367 |
/* |
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. |
372 |
*/ |
373 |
#define JS_STRING_HASH_COUNT 1024 |
374 |
#define JS_DOUBLE_HASH_COUNT 64 |
375 |
|
376 |
JSBool |
377 |
js_InitAtomState(JSRuntime *rt) |
378 |
{ |
379 |
JSAtomState *state = &rt->atomState; |
380 |
|
381 |
/* |
382 |
* The caller must zero the state before calling this function. |
383 |
*/ |
384 |
JS_ASSERT(!state->stringAtoms.ops); |
385 |
JS_ASSERT(!state->doubleAtoms.ops); |
386 |
|
387 |
if (!JS_DHashTableInit(&state->stringAtoms, &StringHashOps, |
388 |
NULL, sizeof(JSAtomHashEntry), |
389 |
JS_DHASH_DEFAULT_CAPACITY(JS_STRING_HASH_COUNT))) { |
390 |
state->stringAtoms.ops = NULL; |
391 |
return JS_FALSE; |
392 |
} |
393 |
JS_ASSERT(IS_STRING_TABLE(&state->stringAtoms)); |
394 |
|
395 |
if (!JS_DHashTableInit(&state->doubleAtoms, &DoubleHashOps, |
396 |
NULL, sizeof(JSAtomHashEntry), |
397 |
JS_DHASH_DEFAULT_CAPACITY(JS_DOUBLE_HASH_COUNT))) { |
398 |
state->doubleAtoms.ops = NULL; |
399 |
JS_DHashTableFinish(&state->stringAtoms); |
400 |
state->stringAtoms.ops = NULL; |
401 |
return JS_FALSE; |
402 |
} |
403 |
JS_ASSERT(IS_DOUBLE_TABLE(&state->doubleAtoms)); |
404 |
|
405 |
#ifdef JS_THREADSAFE |
406 |
js_InitLock(&state->lock); |
407 |
#endif |
408 |
JS_ASSERT(IS_INITIALIZED_STATE(state)); |
409 |
return JS_TRUE; |
410 |
} |
411 |
|
412 |
static JSDHashOperator |
413 |
js_string_uninterner(JSDHashTable *table, JSDHashEntryHdr *hdr, |
414 |
uint32 number, void *arg) |
415 |
{ |
416 |
JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr); |
417 |
JSRuntime *rt = (JSRuntime *)arg; |
418 |
JSString *str; |
419 |
|
420 |
/* |
421 |
* Any string entry that remains at this point must be initialized, as the |
422 |
* last GC should clean any uninitialized ones. |
423 |
*/ |
424 |
JS_ASSERT(IS_STRING_TABLE(table)); |
425 |
JS_ASSERT(entry->keyAndFlags != 0); |
426 |
str = (JSString *)ATOM_ENTRY_KEY(entry); |
427 |
|
428 |
/* Pass null as context. */ |
429 |
js_FinalizeStringRT(rt, str, js_GetExternalStringGCType(str), NULL); |
430 |
return JS_DHASH_NEXT; |
431 |
} |
432 |
|
433 |
void |
434 |
js_FinishAtomState(JSRuntime *rt) |
435 |
{ |
436 |
JSAtomState *state = &rt->atomState; |
437 |
|
438 |
if (!IS_INITIALIZED_STATE(state)) { |
439 |
/* |
440 |
* We are called with uninitialized state when JS_NewRuntime fails and |
441 |
* calls JS_DestroyRuntime on a partially initialized runtime. |
442 |
*/ |
443 |
return; |
444 |
} |
445 |
|
446 |
JS_DHashTableEnumerate(&state->stringAtoms, js_string_uninterner, rt); |
447 |
JS_DHashTableFinish(&state->stringAtoms); |
448 |
JS_DHashTableFinish(&state->doubleAtoms); |
449 |
|
450 |
#ifdef JS_THREADSAFE |
451 |
js_FinishLock(&state->lock); |
452 |
#endif |
453 |
#ifdef DEBUG |
454 |
memset(state, JS_FREE_PATTERN, sizeof *state); |
455 |
#endif |
456 |
} |
457 |
|
458 |
JSBool |
459 |
js_InitCommonAtoms(JSContext *cx) |
460 |
{ |
461 |
JSAtomState *state = &cx->runtime->atomState; |
462 |
uintN i; |
463 |
JSAtom **atoms; |
464 |
|
465 |
atoms = COMMON_ATOMS_START(state); |
466 |
for (i = 0; i < JS_ARRAY_LENGTH(js_common_atom_names); i++, atoms++) { |
467 |
*atoms = js_Atomize(cx, js_common_atom_names[i], |
468 |
strlen(js_common_atom_names[i]), ATOM_PINNED); |
469 |
if (!*atoms) |
470 |
return JS_FALSE; |
471 |
} |
472 |
JS_ASSERT((uint8 *)atoms - (uint8 *)state == LAZY_ATOM_OFFSET_START); |
473 |
memset(atoms, 0, ATOM_OFFSET_LIMIT - LAZY_ATOM_OFFSET_START); |
474 |
|
475 |
return JS_TRUE; |
476 |
} |
477 |
|
478 |
static JSDHashOperator |
479 |
js_atom_unpinner(JSDHashTable *table, JSDHashEntryHdr *hdr, |
480 |
uint32 number, void *arg) |
481 |
{ |
482 |
JS_ASSERT(IS_STRING_TABLE(table)); |
483 |
CLEAR_ATOM_ENTRY_FLAGS(TO_ATOM_ENTRY(hdr), ATOM_PINNED); |
484 |
return JS_DHASH_NEXT; |
485 |
} |
486 |
|
487 |
void |
488 |
js_FinishCommonAtoms(JSContext *cx) |
489 |
{ |
490 |
JSAtomState *state = &cx->runtime->atomState; |
491 |
|
492 |
JS_DHashTableEnumerate(&state->stringAtoms, js_atom_unpinner, NULL); |
493 |
#ifdef DEBUG |
494 |
memset(COMMON_ATOMS_START(state), JS_FREE_PATTERN, |
495 |
ATOM_OFFSET_LIMIT - ATOM_OFFSET_START); |
496 |
#endif |
497 |
} |
498 |
|
499 |
static JSDHashOperator |
500 |
js_locked_atom_tracer(JSDHashTable *table, JSDHashEntryHdr *hdr, |
501 |
uint32 number, void *arg) |
502 |
{ |
503 |
JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr); |
504 |
JSTracer *trc = (JSTracer *)arg; |
505 |
|
506 |
if (entry->keyAndFlags == 0) { |
507 |
/* Ignore uninitialized entries during tracing. */ |
508 |
return JS_DHASH_NEXT; |
509 |
} |
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; |
514 |
} |
515 |
|
516 |
static JSDHashOperator |
517 |
js_pinned_atom_tracer(JSDHashTable *table, JSDHashEntryHdr *hdr, |
518 |
uint32 number, void *arg) |
519 |
{ |
520 |
JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr); |
521 |
JSTracer *trc = (JSTracer *)arg; |
522 |
uintN flags = ATOM_ENTRY_FLAGS(entry); |
523 |
|
524 |
JS_ASSERT(IS_STRING_TABLE(table)); |
525 |
if (flags & (ATOM_PINNED | ATOM_INTERNED)) { |
526 |
JS_SET_TRACING_INDEX(trc, |
527 |
flags & ATOM_PINNED |
528 |
? "pinned_atom" |
529 |
: "interned_atom", |
530 |
(size_t)number); |
531 |
JS_CallTracer(trc, ATOM_ENTRY_KEY(entry), JSTRACE_STRING); |
532 |
} |
533 |
return JS_DHASH_NEXT; |
534 |
} |
535 |
|
536 |
void |
537 |
js_TraceAtomState(JSTracer *trc, JSBool allAtoms) |
538 |
{ |
539 |
JSAtomState *state; |
540 |
|
541 |
state = &trc->context->runtime->atomState; |
542 |
if (allAtoms) { |
543 |
JS_DHashTableEnumerate(&state->doubleAtoms, js_locked_atom_tracer, trc); |
544 |
JS_DHashTableEnumerate(&state->stringAtoms, js_locked_atom_tracer, trc); |
545 |
} else { |
546 |
JS_DHashTableEnumerate(&state->stringAtoms, js_pinned_atom_tracer, trc); |
547 |
} |
548 |
} |
549 |
|
550 |
static JSDHashOperator |
551 |
js_atom_sweeper(JSDHashTable *table, JSDHashEntryHdr *hdr, |
552 |
uint32 number, void *arg) |
553 |
{ |
554 |
JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr); |
555 |
JSContext *cx = (JSContext *)arg; |
556 |
|
557 |
/* Remove uninitialized entries. */ |
558 |
if (entry->keyAndFlags == 0) |
559 |
return JS_DHASH_REMOVE; |
560 |
|
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; |
567 |
} |
568 |
return JS_DHASH_NEXT; |
569 |
} |
570 |
|
571 |
void |
572 |
js_SweepAtomState(JSContext *cx) |
573 |
{ |
574 |
JSAtomState *state = &cx->runtime->atomState; |
575 |
|
576 |
JS_DHashTableEnumerate(&state->doubleAtoms, js_atom_sweeper, cx); |
577 |
JS_DHashTableEnumerate(&state->stringAtoms, js_atom_sweeper, cx); |
578 |
|
579 |
/* |
580 |
* Optimize for simplicity and mutate table generation numbers even if the |
581 |
* sweeper has not removed any entries. |
582 |
*/ |
583 |
state->doubleAtoms.generation++; |
584 |
state->stringAtoms.generation++; |
585 |
} |
586 |
|
587 |
JSAtom * |
588 |
js_AtomizeDouble(JSContext *cx, jsdouble d) |
589 |
{ |
590 |
JSAtomState *state; |
591 |
JSDHashTable *table; |
592 |
JSAtomHashEntry *entry; |
593 |
uint32 gen; |
594 |
jsdouble *key; |
595 |
jsval v; |
596 |
|
597 |
state = &cx->runtime->atomState; |
598 |
table = &state->doubleAtoms; |
599 |
|
600 |
JS_LOCK(cx, &state->lock); |
601 |
entry = TO_ATOM_ENTRY(JS_DHashTableOperate(table, &d, JS_DHASH_ADD)); |
602 |
if (!entry) |
603 |
goto failed_hash_add; |
604 |
if (entry->keyAndFlags == 0) { |
605 |
gen = ++table->generation; |
606 |
JS_UNLOCK(cx, &state->lock); |
607 |
|
608 |
key = js_NewWeaklyRootedDouble(cx, d); |
609 |
if (!key) |
610 |
return NULL; |
611 |
|
612 |
JS_LOCK(cx, &state->lock); |
613 |
if (table->generation == gen) { |
614 |
JS_ASSERT(entry->keyAndFlags == 0); |
615 |
} else { |
616 |
entry = TO_ATOM_ENTRY(JS_DHashTableOperate(table, key, |
617 |
JS_DHASH_ADD)); |
618 |
if (!entry) |
619 |
goto failed_hash_add; |
620 |
if (entry->keyAndFlags != 0) |
621 |
goto finish; |
622 |
++table->generation; |
623 |
} |
624 |
INIT_ATOM_ENTRY(entry, key); |
625 |
} |
626 |
|
627 |
finish: |
628 |
v = DOUBLE_TO_JSVAL((jsdouble *)ATOM_ENTRY_KEY(entry)); |
629 |
cx->weakRoots.lastAtom = v; |
630 |
JS_UNLOCK(cx, &state->lock); |
631 |
|
632 |
return (JSAtom *)v; |
633 |
|
634 |
failed_hash_add: |
635 |
JS_UNLOCK(cx, &state->lock); |
636 |
JS_ReportOutOfMemory(cx); |
637 |
return NULL; |
638 |
} |
639 |
|
640 |
JSAtom * |
641 |
js_AtomizeString(JSContext *cx, JSString *str, uintN flags) |
642 |
{ |
643 |
jsval v; |
644 |
JSAtomState *state; |
645 |
JSDHashTable *table; |
646 |
JSAtomHashEntry *entry; |
647 |
JSString *key; |
648 |
uint32 gen; |
649 |
|
650 |
JS_ASSERT(!(flags & ~(ATOM_PINNED|ATOM_INTERNED|ATOM_TMPSTR|ATOM_NOCOPY))); |
651 |
JS_ASSERT_IF(flags & ATOM_NOCOPY, flags & ATOM_TMPSTR); |
652 |
|
653 |
state = &cx->runtime->atomState; |
654 |
table = &state->stringAtoms; |
655 |
|
656 |
JS_LOCK(cx, &state->lock); |
657 |
entry = TO_ATOM_ENTRY(JS_DHashTableOperate(table, str, JS_DHASH_ADD)); |
658 |
if (!entry) |
659 |
goto failed_hash_add; |
660 |
if (entry->keyAndFlags != 0) { |
661 |
key = (JSString *)ATOM_ENTRY_KEY(entry); |
662 |
} else { |
663 |
/* |
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. |
668 |
*/ |
669 |
++table->generation; |
670 |
if (!(flags & ATOM_TMPSTR) && JSSTRING_IS_FLAT(str)) { |
671 |
JSFLATSTR_CLEAR_MUTABLE(str); |
672 |
key = str; |
673 |
} else { |
674 |
gen = table->generation; |
675 |
JS_UNLOCK(cx, &state->lock); |
676 |
|
677 |
if (flags & ATOM_TMPSTR) { |
678 |
if (flags & ATOM_NOCOPY) { |
679 |
key = js_NewString(cx, JSFLATSTR_CHARS(str), |
680 |
JSFLATSTR_LENGTH(str)); |
681 |
if (!key) |
682 |
return NULL; |
683 |
|
684 |
/* Finish handing off chars to the GC'ed key string. */ |
685 |
str->u.chars = NULL; |
686 |
} else { |
687 |
key = js_NewStringCopyN(cx, JSFLATSTR_CHARS(str), |
688 |
JSFLATSTR_LENGTH(str)); |
689 |
if (!key) |
690 |
return NULL; |
691 |
} |
692 |
} else { |
693 |
JS_ASSERT(JSSTRING_IS_DEPENDENT(str)); |
694 |
if (!js_UndependString(cx, str)) |
695 |
return NULL; |
696 |
key = str; |
697 |
} |
698 |
|
699 |
JS_LOCK(cx, &state->lock); |
700 |
if (table->generation == gen) { |
701 |
JS_ASSERT(entry->keyAndFlags == 0); |
702 |
} else { |
703 |
entry = TO_ATOM_ENTRY(JS_DHashTableOperate(table, key, |
704 |
JS_DHASH_ADD)); |
705 |
if (!entry) |
706 |
goto failed_hash_add; |
707 |
if (entry->keyAndFlags != 0) { |
708 |
key = (JSString *)ATOM_ENTRY_KEY(entry); |
709 |
goto finish; |
710 |
} |
711 |
++table->generation; |
712 |
} |
713 |
} |
714 |
INIT_ATOM_ENTRY(entry, key); |
715 |
JSFLATSTR_SET_ATOMIZED(key); |
716 |
} |
717 |
|
718 |
finish: |
719 |
ADD_ATOM_ENTRY_FLAGS(entry, flags & (ATOM_PINNED | ATOM_INTERNED)); |
720 |
JS_ASSERT(JSSTRING_IS_ATOMIZED(key)); |
721 |
v = STRING_TO_JSVAL(key); |
722 |
cx->weakRoots.lastAtom = v; |
723 |
JS_UNLOCK(cx, &state->lock); |
724 |
return (JSAtom *)v; |
725 |
|
726 |
failed_hash_add: |
727 |
JS_UNLOCK(cx, &state->lock); |
728 |
JS_ReportOutOfMemory(cx); |
729 |
return NULL; |
730 |
} |
731 |
|
732 |
JSAtom * |
733 |
js_Atomize(JSContext *cx, const char *bytes, size_t length, uintN flags) |
734 |
{ |
735 |
jschar *chars; |
736 |
JSString str; |
737 |
JSAtom *atom; |
738 |
|
739 |
/* |
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. |
745 |
*/ |
746 |
#define ATOMIZE_BUF_MAX 32 |
747 |
jschar inflated[ATOMIZE_BUF_MAX]; |
748 |
size_t inflatedLength = ATOMIZE_BUF_MAX - 1; |
749 |
|
750 |
if (length < ATOMIZE_BUF_MAX) { |
751 |
js_InflateStringToBuffer(cx, bytes, length, inflated, &inflatedLength); |
752 |
inflated[inflatedLength] = 0; |
753 |
chars = inflated; |
754 |
} else { |
755 |
inflatedLength = length; |
756 |
chars = js_InflateString(cx, bytes, &inflatedLength); |
757 |
if (!chars) |
758 |
return NULL; |
759 |
flags |= ATOM_NOCOPY; |
760 |
} |
761 |
|
762 |
JSFLATSTR_INIT(&str, (jschar *)chars, inflatedLength); |
763 |
atom = js_AtomizeString(cx, &str, ATOM_TMPSTR | flags); |
764 |
if (chars != inflated && str.u.chars) |
765 |
JS_free(cx, chars); |
766 |
return atom; |
767 |
} |
768 |
|
769 |
JSAtom * |
770 |
js_AtomizeChars(JSContext *cx, const jschar *chars, size_t length, uintN flags) |
771 |
{ |
772 |
JSString str; |
773 |
|
774 |
JSFLATSTR_INIT(&str, (jschar *)chars, length); |
775 |
return js_AtomizeString(cx, &str, ATOM_TMPSTR | flags); |
776 |
} |
777 |
|
778 |
JSAtom * |
779 |
js_GetExistingStringAtom(JSContext *cx, const jschar *chars, size_t length) |
780 |
{ |
781 |
JSString str, *str2; |
782 |
JSAtomState *state; |
783 |
JSDHashEntryHdr *hdr; |
784 |
|
785 |
JSFLATSTR_INIT(&str, (jschar *)chars, length); |
786 |
state = &cx->runtime->atomState; |
787 |
|
788 |
JS_LOCK(cx, &state->lock); |
789 |
hdr = JS_DHashTableOperate(&state->stringAtoms, &str, JS_DHASH_LOOKUP); |
790 |
str2 = JS_DHASH_ENTRY_IS_BUSY(hdr) |
791 |
? (JSString *)ATOM_ENTRY_KEY(TO_ATOM_ENTRY(hdr)) |
792 |
: NULL; |
793 |
JS_UNLOCK(cx, &state->lock); |
794 |
|
795 |
return str2 ? (JSAtom *)STRING_TO_JSVAL(str2) : NULL; |
796 |
} |
797 |
|
798 |
JSBool |
799 |
js_AtomizePrimitiveValue(JSContext *cx, jsval v, JSAtom **atomp) |
800 |
{ |
801 |
JSAtom *atom; |
802 |
|
803 |
if (JSVAL_IS_STRING(v)) { |
804 |
atom = js_AtomizeString(cx, JSVAL_TO_STRING(v), 0); |
805 |
if (!atom) |
806 |
return JS_FALSE; |
807 |
} else if (JSVAL_IS_DOUBLE(v)) { |
808 |
atom = js_AtomizeDouble(cx, *JSVAL_TO_DOUBLE(v)); |
809 |
if (!atom) |
810 |
return JS_FALSE; |
811 |
} else { |
812 |
JS_ASSERT(JSVAL_IS_INT(v) || JSVAL_IS_BOOLEAN(v) || |
813 |
JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)); |
814 |
atom = (JSAtom *)v; |
815 |
} |
816 |
*atomp = atom; |
817 |
return JS_TRUE; |
818 |
} |
819 |
|
820 |
JSBool |
821 |
js_ValueToStringId(JSContext *cx, jsval v, jsid *idp) |
822 |
{ |
823 |
JSString *str; |
824 |
JSAtom *atom; |
825 |
|
826 |
/* |
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. |
832 |
*/ |
833 |
if (JSVAL_IS_STRING(v)) { |
834 |
str = JSVAL_TO_STRING(v); |
835 |
if (JSSTRING_IS_ATOMIZED(str)) { |
836 |
cx->weakRoots.lastAtom = v; |
837 |
*idp = ATOM_TO_JSID((JSAtom *) v); |
838 |
return JS_TRUE; |
839 |
} |
840 |
} else { |
841 |
str = js_ValueToString(cx, v); |
842 |
if (!str) |
843 |
return JS_FALSE; |
844 |
} |
845 |
atom = js_AtomizeString(cx, str, 0); |
846 |
if (!atom) |
847 |
return JS_FALSE; |
848 |
*idp = ATOM_TO_JSID(atom); |
849 |
return JS_TRUE; |
850 |
} |
851 |
|
852 |
#ifdef DEBUG |
853 |
|
854 |
static JSDHashOperator |
855 |
atom_dumper(JSDHashTable *table, JSDHashEntryHdr *hdr, |
856 |
uint32 number, void *arg) |
857 |
{ |
858 |
JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr); |
859 |
FILE *fp = (FILE *)arg; |
860 |
void *key; |
861 |
uintN flags; |
862 |
|
863 |
fprintf(fp, "%3u %08x ", number, (uintN)entry->hdr.keyHash); |
864 |
if (entry->keyAndFlags == 0) { |
865 |
fputs("<uninitialized>", fp); |
866 |
} else { |
867 |
key = ATOM_ENTRY_KEY(entry); |
868 |
if (IS_DOUBLE_TABLE(table)) { |
869 |
fprintf(fp, "%.16g", *(jsdouble *)key); |
870 |
} else { |
871 |
JS_ASSERT(IS_STRING_TABLE(table)); |
872 |
js_FileEscapedString(fp, (JSString *)key, '"'); |
873 |
} |
874 |
flags = ATOM_ENTRY_FLAGS(entry); |
875 |
if (flags != 0) { |
876 |
fputs((flags & (ATOM_PINNED | ATOM_INTERNED)) |
877 |
? " pinned | interned" |
878 |
: (flags & ATOM_PINNED) ? " pinned" : " interned", |
879 |
fp); |
880 |
} |
881 |
} |
882 |
putc('\n', fp); |
883 |
return JS_DHASH_NEXT; |
884 |
} |
885 |
|
886 |
JS_FRIEND_API(void) |
887 |
js_DumpAtoms(JSContext *cx, FILE *fp) |
888 |
{ |
889 |
JSAtomState *state = &cx->runtime->atomState; |
890 |
|
891 |
fprintf(fp, "stringAtoms table contents:\n"); |
892 |
JS_DHashTableEnumerate(&state->stringAtoms, atom_dumper, fp); |
893 |
#ifdef JS_DHASHMETER |
894 |
JS_DHashTableDumpMeter(&state->stringAtoms, atom_dumper, fp); |
895 |
#endif |
896 |
putc('\n', fp); |
897 |
|
898 |
fprintf(fp, "doubleAtoms table contents:\n"); |
899 |
JS_DHashTableEnumerate(&state->doubleAtoms, atom_dumper, fp); |
900 |
#ifdef JS_DHASHMETER |
901 |
JS_DHashTableDumpMeter(&state->doubleAtoms, atom_dumper, fp); |
902 |
#endif |
903 |
putc('\n', fp); |
904 |
} |
905 |
|
906 |
#endif |
907 |
|
908 |
static JSHashNumber |
909 |
js_hash_atom_ptr(const void *key) |
910 |
{ |
911 |
const JSAtom *atom = (const JSAtom *) key; |
912 |
return ATOM_HASH(atom); |
913 |
} |
914 |
|
915 |
static void * |
916 |
js_alloc_temp_space(void *priv, size_t size) |
917 |
{ |
918 |
JSContext *cx = (JSContext *) priv; |
919 |
void *space; |
920 |
|
921 |
JS_ARENA_ALLOCATE(space, &cx->tempPool, size); |
922 |
if (!space) |
923 |
js_ReportOutOfScriptQuota(cx); |
924 |
return space; |
925 |
} |
926 |
|
927 |
static void |
928 |
js_free_temp_space(void *priv, void *item) |
929 |
{ |
930 |
} |
931 |
|
932 |
static JSHashEntry * |
933 |
js_alloc_temp_entry(void *priv, const void *key) |
934 |
{ |
935 |
JSContext *cx = (JSContext *) priv; |
936 |
JSAtomListElement *ale; |
937 |
|
938 |
JS_ARENA_ALLOCATE_TYPE(ale, JSAtomListElement, &cx->tempPool); |
939 |
if (!ale) { |
940 |
js_ReportOutOfScriptQuota(cx); |
941 |
return NULL; |
942 |
} |
943 |
return &ale->entry; |
944 |
} |
945 |
|
946 |
static void |
947 |
js_free_temp_entry(void *priv, JSHashEntry *he, uintN flag) |
948 |
{ |
949 |
} |
950 |
|
951 |
static JSHashAllocOps temp_alloc_ops = { |
952 |
js_alloc_temp_space, js_free_temp_space, |
953 |
js_alloc_temp_entry, js_free_temp_entry |
954 |
}; |
955 |
|
956 |
JSAtomListElement * |
957 |
js_IndexAtom(JSContext *cx, JSAtom *atom, JSAtomList *al) |
958 |
{ |
959 |
JSAtomListElement *ale, *ale2, *next; |
960 |
JSHashEntry **hep; |
961 |
|
962 |
ATOM_LIST_LOOKUP(ale, hep, al, atom); |
963 |
if (!ale) { |
964 |
if (al->count < 10) { |
965 |
/* Few enough for linear search, no hash table needed. */ |
966 |
JS_ASSERT(!al->table); |
967 |
ale = (JSAtomListElement *)js_alloc_temp_entry(cx, atom); |
968 |
if (!ale) |
969 |
return NULL; |
970 |
ALE_SET_ATOM(ale, atom); |
971 |
ale->entry.next = al->list; |
972 |
al->list = &ale->entry; |
973 |
} else { |
974 |
/* We want to hash. Have we already made a hash table? */ |
975 |
if (!al->table) { |
976 |
/* No hash table yet, so hep had better be null! */ |
977 |
JS_ASSERT(!hep); |
978 |
al->table = JS_NewHashTable(al->count + 1, js_hash_atom_ptr, |
979 |
JS_CompareValues, JS_CompareValues, |
980 |
&temp_alloc_ops, cx); |
981 |
if (!al->table) |
982 |
return NULL; |
983 |
|
984 |
/* |
985 |
* Set ht->nentries explicitly, because we are moving entries |
986 |
* from al to ht, not calling JS_HashTable(Raw|)Add. |
987 |
*/ |
988 |
al->table->nentries = al->count; |
989 |
|
990 |
/* Insert each ale on al->list into the new hash table. */ |
991 |
for (ale2 = (JSAtomListElement *)al->list; ale2; ale2 = next) { |
992 |
next = ALE_NEXT(ale2); |
993 |
ale2->entry.keyHash = ATOM_HASH(ALE_ATOM(ale2)); |
994 |
hep = JS_HashTableRawLookup(al->table, ale2->entry.keyHash, |
995 |
ale2->entry.key); |
996 |
ale2->entry.next = *hep; |
997 |
*hep = &ale2->entry; |
998 |
} |
999 |
al->list = NULL; |
1000 |
|
1001 |
/* Set hep for insertion of atom's ale, immediately below. */ |
1002 |
hep = JS_HashTableRawLookup(al->table, ATOM_HASH(atom), atom); |
1003 |
} |
1004 |
|
1005 |
/* Finally, add an entry for atom into the hash bucket at hep. */ |
1006 |
ale = (JSAtomListElement *) |
1007 |
JS_HashTableRawAdd(al->table, hep, ATOM_HASH(atom), atom, |
1008 |
NULL); |
1009 |
if (!ale) |
1010 |
return NULL; |
1011 |
} |
1012 |
|
1013 |
ALE_SET_INDEX(ale, al->count++); |
1014 |
} |
1015 |
return ale; |
1016 |
} |
1017 |
|
1018 |
static intN |
1019 |
js_map_atom(JSHashEntry *he, intN i, void *arg) |
1020 |
{ |
1021 |
JSAtomListElement *ale = (JSAtomListElement *)he; |
1022 |
JSAtom **vector = (JSAtom **) arg; |
1023 |
|
1024 |
vector[ALE_INDEX(ale)] = ALE_ATOM(ale); |
1025 |
return HT_ENUMERATE_NEXT; |
1026 |
} |
1027 |
|
1028 |
#ifdef DEBUG |
1029 |
static jsrefcount js_atom_map_count; |
1030 |
static jsrefcount js_atom_map_hash_table_count; |
1031 |
#endif |
1032 |
|
1033 |
void |
1034 |
js_InitAtomMap(JSContext *cx, JSAtomMap *map, JSAtomList *al) |
1035 |
{ |
1036 |
JSAtom **vector; |
1037 |
JSAtomListElement *ale; |
1038 |
uint32 count; |
1039 |
|
1040 |
/* Map length must already be initialized. */ |
1041 |
JS_ASSERT(al->count == map->length); |
1042 |
#ifdef DEBUG |
1043 |
JS_ATOMIC_INCREMENT(&js_atom_map_count); |
1044 |
#endif |
1045 |
ale = (JSAtomListElement *)al->list; |
1046 |
if (!ale && !al->table) { |
1047 |
JS_ASSERT(!map->vector); |
1048 |
return; |
1049 |
} |
1050 |
|
1051 |
count = al->count; |
1052 |
vector = map->vector; |
1053 |
if (al->table) { |
1054 |
#ifdef DEBUG |
1055 |
JS_ATOMIC_INCREMENT(&js_atom_map_hash_table_count); |
1056 |
#endif |
1057 |
JS_HashTableEnumerateEntries(al->table, js_map_atom, vector); |
1058 |
} else { |
1059 |
do { |
1060 |
vector[ALE_INDEX(ale)] = ALE_ATOM(ale); |
1061 |
} while ((ale = ALE_NEXT(ale)) != NULL); |
1062 |
} |
1063 |
ATOM_LIST_INIT(al); |
1064 |
} |