/[jscoverage]/trunk/js/jsatom.cpp
ViewVC logotype

Contents of /trunk/js/jsatom.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 332 - (show annotations)
Thu Oct 23 19:03:33 2008 UTC (11 years ago) by siliconforks
File size: 34709 byte(s)
Add SpiderMonkey from Firefox 3.1b1.

The following directories and files were removed:
correct/, correct.js
liveconnect/
nanojit/
t/
v8/
vprof/
xpconnect/
all JavaScript files (Y.js, call.js, if.js, math-partial-sums.js, md5.js, perfect.js, trace-test.js, trace.js)


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 * Names 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 JSType enumerators from jspubtd.h and to two boolean
76 * literals, false and true. The following assert insists that JSType defines
77 * exactly 8 types.
78 */
79 JS_STATIC_ASSERT(JSTYPE_LIMIT == 8);
80 const char *const js_common_atom_names[] = {
81 "", /* emptyAtom */
82 js_undefined_str, /* typeAtoms[JSTYPE_VOID] */
83 js_object_str, /* typeAtoms[JSTYPE_OBJECT] */
84 js_function_str, /* typeAtoms[JSTYPE_FUNCTION] */
85 "string", /* typeAtoms[JSTYPE_STRING] */
86 "number", /* typeAtoms[JSTYPE_NUMBER] */
87 "boolean", /* typeAtoms[JSTYPE_BOOLEAN] */
88 js_null_str, /* typeAtoms[JSTYPE_NULL] */
89 "xml", /* typeAtoms[JSTYPE_XML] */
90 js_false_str, /* booleanAtoms[0] */
91 js_true_str, /* booleanAtoms[1] */
92 js_null_str, /* nullAtom */
93
94 #define JS_PROTO(name,code,init) js_##name##_str,
95 #include "jsproto.tbl"
96 #undef JS_PROTO
97
98 js_anonymous_str, /* anonymousAtom */
99 js_arguments_str, /* argumentsAtom */
100 js_arity_str, /* arityAtom */
101 js_callee_str, /* calleeAtom */
102 js_caller_str, /* callerAtom */
103 js_class_prototype_str, /* classPrototypeAtom */
104 js_constructor_str, /* constructorAtom */
105 js_count_str, /* countAtom */
106 js_each_str, /* eachAtom */
107 js_eval_str, /* evalAtom */
108 js_fileName_str, /* fileNameAtom */
109 js_get_str, /* getAtom */
110 js_getter_str, /* getterAtom */
111 js_index_str, /* indexAtom */
112 js_input_str, /* inputAtom */
113 js_iterator_str, /* iteratorAtom */
114 js_length_str, /* lengthAtom */
115 js_lineNumber_str, /* lineNumberAtom */
116 js_message_str, /* messageAtom */
117 js_name_str, /* nameAtom */
118 js_next_str, /* nextAtom */
119 js_noSuchMethod_str, /* noSuchMethodAtom */
120 js_parent_str, /* parentAtom */
121 js_proto_str, /* protoAtom */
122 js_set_str, /* setAtom */
123 js_setter_str, /* setterAtom */
124 js_stack_str, /* stackAtom */
125 js_toLocaleString_str, /* toLocaleStringAtom */
126 js_toSource_str, /* toSourceAtom */
127 js_toString_str, /* toStringAtom */
128 js_valueOf_str, /* valueOfAtom */
129 js_toJSON_str, /* toJSONAtom */
130 "(void 0)", /* void0Atom */
131
132 #if JS_HAS_XML_SUPPORT
133 js_etago_str, /* etagoAtom */
134 js_namespace_str, /* namespaceAtom */
135 js_ptagc_str, /* ptagcAtom */
136 js_qualifier_str, /* qualifierAtom */
137 js_space_str, /* spaceAtom */
138 js_stago_str, /* stagoAtom */
139 js_star_str, /* starAtom */
140 js_starQualifier_str, /* starQualifierAtom */
141 js_tagc_str, /* tagcAtom */
142 js_xml_str, /* xmlAtom */
143 #endif
144
145 #ifdef NARCISSUS
146 js_call_str, /* callAtom */
147 js_construct_str, /* constructAtom */
148 js_hasInstance_str, /* hasInstanceAtom */
149 js_ExecutionContext_str, /* ExecutionContextAtom */
150 js_current_str, /* currentAtom */
151 #endif
152 };
153 JS_STATIC_ASSERT(JS_ARRAY_LENGTH(js_common_atom_names) * sizeof(JSAtom *) ==
154 LAZY_ATOM_OFFSET_START - ATOM_OFFSET_START);
155
156 const char js_anonymous_str[] = "anonymous";
157 const char js_arguments_str[] = "arguments";
158 const char js_arity_str[] = "arity";
159 const char js_callee_str[] = "callee";
160 const char js_caller_str[] = "caller";
161 const char js_class_prototype_str[] = "prototype";
162 const char js_constructor_str[] = "constructor";
163 const char js_count_str[] = "__count__";
164 const char js_each_str[] = "each";
165 const char js_eval_str[] = "eval";
166 const char js_fileName_str[] = "fileName";
167 const char js_get_str[] = "get";
168 const char js_getter_str[] = "getter";
169 const char js_index_str[] = "index";
170 const char js_input_str[] = "input";
171 const char js_iterator_str[] = "__iterator__";
172 const char js_length_str[] = "length";
173 const char js_lineNumber_str[] = "lineNumber";
174 const char js_message_str[] = "message";
175 const char js_name_str[] = "name";
176 const char js_next_str[] = "next";
177 const char js_noSuchMethod_str[] = "__noSuchMethod__";
178 const char js_object_str[] = "object";
179 const char js_parent_str[] = "__parent__";
180 const char js_proto_str[] = "__proto__";
181 const char js_setter_str[] = "setter";
182 const char js_set_str[] = "set";
183 const char js_stack_str[] = "stack";
184 const char js_toSource_str[] = "toSource";
185 const char js_toString_str[] = "toString";
186 const char js_toLocaleString_str[] = "toLocaleString";
187 const char js_undefined_str[] = "undefined";
188 const char js_valueOf_str[] = "valueOf";
189 const char js_toJSON_str[] = "toJSON";
190
191 #if JS_HAS_XML_SUPPORT
192 const char js_etago_str[] = "</";
193 const char js_namespace_str[] = "namespace";
194 const char js_ptagc_str[] = "/>";
195 const char js_qualifier_str[] = "::";
196 const char js_space_str[] = " ";
197 const char js_stago_str[] = "<";
198 const char js_star_str[] = "*";
199 const char js_starQualifier_str[] = "*::";
200 const char js_tagc_str[] = ">";
201 const char js_xml_str[] = "xml";
202 #endif
203
204 #if JS_HAS_GENERATORS
205 const char js_close_str[] = "close";
206 const char js_send_str[] = "send";
207 #endif
208
209 #ifdef NARCISSUS
210 const char js_call_str[] = "__call__";
211 const char js_construct_str[] = "__construct__";
212 const char js_hasInstance_str[] = "__hasInstance__";
213 const char js_ExecutionContext_str[] = "ExecutionContext";
214 const char js_current_str[] = "current";
215 #endif
216
217 /*
218 * JSAtomState.doubleAtoms and JSAtomState.stringAtoms hashtable entry. To
219 * support pinned and interned string atoms, we use the lowest bits of the
220 * keyAndFlags field to store ATOM_PINNED and ATOM_INTERNED flags.
221 */
222 typedef struct JSAtomHashEntry {
223 JSDHashEntryHdr hdr;
224 jsuword keyAndFlags;
225 } JSAtomHashEntry;
226
227 #define ATOM_ENTRY_FLAG_MASK (ATOM_PINNED | ATOM_INTERNED)
228
229 JS_STATIC_ASSERT(ATOM_ENTRY_FLAG_MASK < JSVAL_ALIGN);
230
231 /*
232 * Helper macros to access and modify JSAtomHashEntry.
233 */
234 #define TO_ATOM_ENTRY(hdr) ((JSAtomHashEntry *) hdr)
235 #define ATOM_ENTRY_KEY(entry) \
236 ((void *)((entry)->keyAndFlags & ~ATOM_ENTRY_FLAG_MASK))
237 #define ATOM_ENTRY_FLAGS(entry) \
238 ((uintN)((entry)->keyAndFlags & ATOM_ENTRY_FLAG_MASK))
239 #define INIT_ATOM_ENTRY(entry, key) \
240 ((void)((entry)->keyAndFlags = (jsuword)(key)))
241 #define ADD_ATOM_ENTRY_FLAGS(entry, flags) \
242 ((void)((entry)->keyAndFlags |= (jsuword)(flags)))
243 #define CLEAR_ATOM_ENTRY_FLAGS(entry, flags) \
244 ((void)((entry)->keyAndFlags &= ~(jsuword)(flags)))
245
246 static JSDHashNumber
247 HashDouble(JSDHashTable *table, const void *key);
248
249 static JSBool
250 MatchDouble(JSDHashTable *table, const JSDHashEntryHdr *hdr, const void *key);
251
252 static JSDHashNumber
253 HashString(JSDHashTable *table, const void *key);
254
255 static JSBool
256 MatchString(JSDHashTable *table, const JSDHashEntryHdr *hdr, const void *key);
257
258 static const JSDHashTableOps DoubleHashOps = {
259 JS_DHashAllocTable,
260 JS_DHashFreeTable,
261 HashDouble,
262 MatchDouble,
263 JS_DHashMoveEntryStub,
264 JS_DHashClearEntryStub,
265 JS_DHashFinalizeStub,
266 NULL
267 };
268
269 static const JSDHashTableOps StringHashOps = {
270 JS_DHashAllocTable,
271 JS_DHashFreeTable,
272 HashString,
273 MatchString,
274 JS_DHashMoveEntryStub,
275 JS_DHashClearEntryStub,
276 JS_DHashFinalizeStub,
277 NULL
278 };
279
280 #define IS_DOUBLE_TABLE(table) ((table)->ops == &DoubleHashOps)
281 #define IS_STRING_TABLE(table) ((table)->ops == &StringHashOps)
282
283 #define IS_INITIALIZED_STATE(state) IS_DOUBLE_TABLE(&(state)->doubleAtoms)
284
285 static JSDHashNumber
286 HashDouble(JSDHashTable *table, const void *key)
287 {
288 jsdouble d;
289
290 JS_ASSERT(IS_DOUBLE_TABLE(table));
291 d = *(jsdouble *)key;
292 return JSDOUBLE_HI32(d) ^ JSDOUBLE_LO32(d);
293 }
294
295 static JSDHashNumber
296 HashString(JSDHashTable *table, const void *key)
297 {
298 JS_ASSERT(IS_STRING_TABLE(table));
299 return js_HashString((JSString *)key);
300 }
301
302 static JSBool
303 MatchDouble(JSDHashTable *table, const JSDHashEntryHdr *hdr, const void *key)
304 {
305 JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr);
306 jsdouble d1, d2;
307
308 JS_ASSERT(IS_DOUBLE_TABLE(table));
309 if (entry->keyAndFlags == 0) {
310 /* See comments in MatchString. */
311 return JS_FALSE;
312 }
313
314 d1 = *(jsdouble *)ATOM_ENTRY_KEY(entry);
315 d2 = *(jsdouble *)key;
316 if (JSDOUBLE_IS_NaN(d1))
317 return JSDOUBLE_IS_NaN(d2);
318 #if defined(XP_WIN)
319 /* XXX MSVC miscompiles such that (NaN == 0) */
320 if (JSDOUBLE_IS_NaN(d2))
321 return JS_FALSE;
322 #endif
323 return d1 == d2;
324 }
325
326 static JSBool
327 MatchString(JSDHashTable *table, const JSDHashEntryHdr *hdr, const void *key)
328 {
329 JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr);
330
331 JS_ASSERT(IS_STRING_TABLE(table));
332 if (entry->keyAndFlags == 0) {
333 /*
334 * This happens when js_AtomizeString adds a new hash entry and
335 * releases the lock but before it takes the lock the second time to
336 * initialize keyAndFlags for the entry.
337 *
338 * We always return false for such entries so JS_DHashTableOperate
339 * never finds them. We clean them during GC's sweep phase.
340 *
341 * It means that with a contested lock or when GC is triggered outside
342 * the lock we may end up adding two entries, but this is a price for
343 * simpler code.
344 */
345 return JS_FALSE;
346 }
347 return js_EqualStrings((JSString *)ATOM_ENTRY_KEY(entry), (JSString *)key);
348 }
349
350 /*
351 * For a browser build from 2007-08-09 after the browser starts up there are
352 * just 55 double atoms, but over 15000 string atoms. Not to penalize more
353 * economical embeddings allocating too much memory initially we initialize
354 * atomized strings with just 1K entries.
355 */
356 #define JS_STRING_HASH_COUNT 1024
357 #define JS_DOUBLE_HASH_COUNT 64
358
359 JSBool
360 js_InitAtomState(JSRuntime *rt)
361 {
362 JSAtomState *state = &rt->atomState;
363
364 /*
365 * The caller must zero the state before calling this function.
366 */
367 JS_ASSERT(!state->stringAtoms.ops);
368 JS_ASSERT(!state->doubleAtoms.ops);
369
370 if (!JS_DHashTableInit(&state->stringAtoms, &StringHashOps,
371 NULL, sizeof(JSAtomHashEntry),
372 JS_DHASH_DEFAULT_CAPACITY(JS_STRING_HASH_COUNT))) {
373 state->stringAtoms.ops = NULL;
374 return JS_FALSE;
375 }
376 JS_ASSERT(IS_STRING_TABLE(&state->stringAtoms));
377
378 if (!JS_DHashTableInit(&state->doubleAtoms, &DoubleHashOps,
379 NULL, sizeof(JSAtomHashEntry),
380 JS_DHASH_DEFAULT_CAPACITY(JS_DOUBLE_HASH_COUNT))) {
381 state->doubleAtoms.ops = NULL;
382 JS_DHashTableFinish(&state->stringAtoms);
383 state->stringAtoms.ops = NULL;
384 return JS_FALSE;
385 }
386 JS_ASSERT(IS_DOUBLE_TABLE(&state->doubleAtoms));
387
388 #ifdef JS_THREADSAFE
389 js_InitLock(&state->lock);
390 #endif
391 JS_ASSERT(IS_INITIALIZED_STATE(state));
392 return JS_TRUE;
393 }
394
395 static JSDHashOperator
396 js_string_uninterner(JSDHashTable *table, JSDHashEntryHdr *hdr,
397 uint32 number, void *arg)
398 {
399 JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr);
400 JSRuntime *rt = (JSRuntime *)arg;
401 JSString *str;
402
403 /*
404 * Any string entry that remains at this point must be initialized, as the
405 * last GC should clean any uninitialized ones.
406 */
407 JS_ASSERT(IS_STRING_TABLE(table));
408 JS_ASSERT(entry->keyAndFlags != 0);
409 str = (JSString *)ATOM_ENTRY_KEY(entry);
410
411 /* Pass null as context. */
412 js_FinalizeStringRT(rt, str, js_GetExternalStringGCType(str), NULL);
413 return JS_DHASH_NEXT;
414 }
415
416 void
417 js_FinishAtomState(JSRuntime *rt)
418 {
419 JSAtomState *state = &rt->atomState;
420
421 if (!IS_INITIALIZED_STATE(state)) {
422 /*
423 * We are called with uninitialized state when JS_NewRuntime fails and
424 * calls JS_DestroyRuntime on a partially initialized runtime.
425 */
426 return;
427 }
428
429 JS_DHashTableEnumerate(&state->stringAtoms, js_string_uninterner, rt);
430 JS_DHashTableFinish(&state->stringAtoms);
431 JS_DHashTableFinish(&state->doubleAtoms);
432
433 #ifdef JS_THREADSAFE
434 js_FinishLock(&state->lock);
435 #endif
436 #ifdef DEBUG
437 memset(state, JS_FREE_PATTERN, sizeof *state);
438 #endif
439 }
440
441 JSBool
442 js_InitCommonAtoms(JSContext *cx)
443 {
444 JSAtomState *state = &cx->runtime->atomState;
445 uintN i;
446 JSAtom **atoms;
447
448 atoms = COMMON_ATOMS_START(state);
449 for (i = 0; i < JS_ARRAY_LENGTH(js_common_atom_names); i++, atoms++) {
450 *atoms = js_Atomize(cx, js_common_atom_names[i],
451 strlen(js_common_atom_names[i]), ATOM_PINNED);
452 if (!*atoms)
453 return JS_FALSE;
454 }
455 JS_ASSERT((uint8 *)atoms - (uint8 *)state == LAZY_ATOM_OFFSET_START);
456 memset(atoms, 0, ATOM_OFFSET_LIMIT - LAZY_ATOM_OFFSET_START);
457
458 return JS_TRUE;
459 }
460
461 static JSDHashOperator
462 js_atom_unpinner(JSDHashTable *table, JSDHashEntryHdr *hdr,
463 uint32 number, void *arg)
464 {
465 JS_ASSERT(IS_STRING_TABLE(table));
466 CLEAR_ATOM_ENTRY_FLAGS(TO_ATOM_ENTRY(hdr), ATOM_PINNED);
467 return JS_DHASH_NEXT;
468 }
469
470 void
471 js_FinishCommonAtoms(JSContext *cx)
472 {
473 JSAtomState *state = &cx->runtime->atomState;
474
475 JS_DHashTableEnumerate(&state->stringAtoms, js_atom_unpinner, NULL);
476 #ifdef DEBUG
477 memset(COMMON_ATOMS_START(state), JS_FREE_PATTERN,
478 ATOM_OFFSET_LIMIT - ATOM_OFFSET_START);
479 #endif
480 }
481
482 static JSDHashOperator
483 js_locked_atom_tracer(JSDHashTable *table, JSDHashEntryHdr *hdr,
484 uint32 number, void *arg)
485 {
486 JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr);
487 JSTracer *trc = (JSTracer *)arg;
488
489 if (entry->keyAndFlags == 0) {
490 /* Ignore uninitialized entries during tracing. */
491 return JS_DHASH_NEXT;
492 }
493 JS_SET_TRACING_INDEX(trc, "locked_atom", (size_t)number);
494 JS_CallTracer(trc, ATOM_ENTRY_KEY(entry),
495 IS_STRING_TABLE(table) ? JSTRACE_STRING : JSTRACE_DOUBLE);
496 return JS_DHASH_NEXT;
497 }
498
499 static JSDHashOperator
500 js_pinned_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 uintN flags = ATOM_ENTRY_FLAGS(entry);
506
507 JS_ASSERT(IS_STRING_TABLE(table));
508 if (flags & (ATOM_PINNED | ATOM_INTERNED)) {
509 JS_SET_TRACING_INDEX(trc,
510 flags & ATOM_PINNED
511 ? "pinned_atom"
512 : "interned_atom",
513 (size_t)number);
514 JS_CallTracer(trc, ATOM_ENTRY_KEY(entry), JSTRACE_STRING);
515 }
516 return JS_DHASH_NEXT;
517 }
518
519 void
520 js_TraceAtomState(JSTracer *trc, JSBool allAtoms)
521 {
522 JSAtomState *state;
523
524 state = &trc->context->runtime->atomState;
525 if (allAtoms) {
526 JS_DHashTableEnumerate(&state->doubleAtoms, js_locked_atom_tracer, trc);
527 JS_DHashTableEnumerate(&state->stringAtoms, js_locked_atom_tracer, trc);
528 } else {
529 JS_DHashTableEnumerate(&state->stringAtoms, js_pinned_atom_tracer, trc);
530 }
531 }
532
533 static JSDHashOperator
534 js_atom_sweeper(JSDHashTable *table, JSDHashEntryHdr *hdr,
535 uint32 number, void *arg)
536 {
537 JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr);
538 JSContext *cx = (JSContext *)arg;
539
540 /* Remove uninitialized entries. */
541 if (entry->keyAndFlags == 0)
542 return JS_DHASH_REMOVE;
543
544 if (ATOM_ENTRY_FLAGS(entry) & (ATOM_PINNED | ATOM_INTERNED)) {
545 /* Pinned or interned key cannot be finalized. */
546 JS_ASSERT(!js_IsAboutToBeFinalized(cx, ATOM_ENTRY_KEY(entry)));
547 } else if (js_IsAboutToBeFinalized(cx, ATOM_ENTRY_KEY(entry))) {
548 /* Remove entries with things about to be GC'ed. */
549 return JS_DHASH_REMOVE;
550 }
551 return JS_DHASH_NEXT;
552 }
553
554 void
555 js_SweepAtomState(JSContext *cx)
556 {
557 JSAtomState *state = &cx->runtime->atomState;
558
559 JS_DHashTableEnumerate(&state->doubleAtoms, js_atom_sweeper, cx);
560 JS_DHashTableEnumerate(&state->stringAtoms, js_atom_sweeper, cx);
561
562 /*
563 * Optimize for simplicity and mutate table generation numbers even if the
564 * sweeper has not removed any entries.
565 */
566 state->doubleAtoms.generation++;
567 state->stringAtoms.generation++;
568 }
569
570 JSAtom *
571 js_AtomizeDouble(JSContext *cx, jsdouble d)
572 {
573 JSAtomState *state;
574 JSDHashTable *table;
575 JSAtomHashEntry *entry;
576 uint32 gen;
577 jsdouble *key;
578 jsval v;
579
580 state = &cx->runtime->atomState;
581 table = &state->doubleAtoms;
582
583 JS_LOCK(cx, &state->lock);
584 entry = TO_ATOM_ENTRY(JS_DHashTableOperate(table, &d, JS_DHASH_ADD));
585 if (!entry)
586 goto failed_hash_add;
587 if (entry->keyAndFlags == 0) {
588 gen = ++table->generation;
589 JS_UNLOCK(cx, &state->lock);
590
591 key = js_NewWeaklyRootedDouble(cx, d);
592 if (!key)
593 return NULL;
594
595 JS_LOCK(cx, &state->lock);
596 if (table->generation == gen) {
597 JS_ASSERT(entry->keyAndFlags == 0);
598 } else {
599 entry = TO_ATOM_ENTRY(JS_DHashTableOperate(table, key,
600 JS_DHASH_ADD));
601 if (!entry)
602 goto failed_hash_add;
603 if (entry->keyAndFlags != 0)
604 goto finish;
605 ++table->generation;
606 }
607 INIT_ATOM_ENTRY(entry, key);
608 }
609
610 finish:
611 v = DOUBLE_TO_JSVAL((jsdouble *)ATOM_ENTRY_KEY(entry));
612 cx->weakRoots.lastAtom = v;
613 JS_UNLOCK(cx, &state->lock);
614
615 return (JSAtom *)v;
616
617 failed_hash_add:
618 JS_UNLOCK(cx, &state->lock);
619 JS_ReportOutOfMemory(cx);
620 return NULL;
621 }
622
623 JSAtom *
624 js_AtomizeString(JSContext *cx, JSString *str, uintN flags)
625 {
626 jsval v;
627 JSAtomState *state;
628 JSDHashTable *table;
629 JSAtomHashEntry *entry;
630 JSString *key;
631 uint32 gen;
632
633 JS_ASSERT(!(flags & ~(ATOM_PINNED|ATOM_INTERNED|ATOM_TMPSTR|ATOM_NOCOPY)));
634 JS_ASSERT_IF(flags & ATOM_NOCOPY, flags & ATOM_TMPSTR);
635
636 state = &cx->runtime->atomState;
637 table = &state->stringAtoms;
638
639 JS_LOCK(cx, &state->lock);
640 entry = TO_ATOM_ENTRY(JS_DHashTableOperate(table, str, JS_DHASH_ADD));
641 if (!entry)
642 goto failed_hash_add;
643 if (entry->keyAndFlags != 0) {
644 key = (JSString *)ATOM_ENTRY_KEY(entry);
645 } else {
646 /*
647 * We created a new hashtable entry. Unless str is already allocated
648 * from the GC heap and flat, we have to release state->lock as
649 * string construction is a complex operation. For example, it can
650 * trigger GC which may rehash the table and make the entry invalid.
651 */
652 ++table->generation;
653 if (!(flags & ATOM_TMPSTR) && JSSTRING_IS_FLAT(str)) {
654 JSFLATSTR_CLEAR_MUTABLE(str);
655 key = str;
656 } else {
657 gen = table->generation;
658 JS_UNLOCK(cx, &state->lock);
659
660 if (flags & ATOM_TMPSTR) {
661 if (flags & ATOM_NOCOPY) {
662 key = js_NewString(cx, JSFLATSTR_CHARS(str),
663 JSFLATSTR_LENGTH(str));
664 if (!key)
665 return NULL;
666
667 /* Finish handing off chars to the GC'ed key string. */
668 str->u.chars = NULL;
669 } else {
670 key = js_NewStringCopyN(cx, JSFLATSTR_CHARS(str),
671 JSFLATSTR_LENGTH(str));
672 if (!key)
673 return NULL;
674 }
675 } else {
676 JS_ASSERT(JSSTRING_IS_DEPENDENT(str));
677 if (!js_UndependString(cx, str))
678 return NULL;
679 key = str;
680 }
681
682 JS_LOCK(cx, &state->lock);
683 if (table->generation == gen) {
684 JS_ASSERT(entry->keyAndFlags == 0);
685 } else {
686 entry = TO_ATOM_ENTRY(JS_DHashTableOperate(table, key,
687 JS_DHASH_ADD));
688 if (!entry)
689 goto failed_hash_add;
690 if (entry->keyAndFlags != 0) {
691 key = (JSString *)ATOM_ENTRY_KEY(entry);
692 goto finish;
693 }
694 ++table->generation;
695 }
696 }
697 INIT_ATOM_ENTRY(entry, key);
698 JSFLATSTR_SET_ATOMIZED(key);
699 }
700
701 finish:
702 ADD_ATOM_ENTRY_FLAGS(entry, flags & (ATOM_PINNED | ATOM_INTERNED));
703 JS_ASSERT(JSSTRING_IS_ATOMIZED(key));
704 v = STRING_TO_JSVAL(key);
705 cx->weakRoots.lastAtom = v;
706 JS_UNLOCK(cx, &state->lock);
707 return (JSAtom *)v;
708
709 failed_hash_add:
710 JS_UNLOCK(cx, &state->lock);
711 JS_ReportOutOfMemory(cx);
712 return NULL;
713 }
714
715 JSAtom *
716 js_Atomize(JSContext *cx, const char *bytes, size_t length, uintN flags)
717 {
718 jschar *chars;
719 JSString str;
720 JSAtom *atom;
721
722 /*
723 * Avoiding the malloc in js_InflateString on shorter strings saves us
724 * over 20,000 malloc calls on mozilla browser startup. This compares to
725 * only 131 calls where the string is longer than a 31 char (net) buffer.
726 * The vast majority of atomized strings are already in the hashtable. So
727 * js_AtomizeString rarely has to copy the temp string we make.
728 */
729 #define ATOMIZE_BUF_MAX 32
730 jschar inflated[ATOMIZE_BUF_MAX];
731 size_t inflatedLength = ATOMIZE_BUF_MAX - 1;
732
733 if (length < ATOMIZE_BUF_MAX) {
734 js_InflateStringToBuffer(cx, bytes, length, inflated, &inflatedLength);
735 inflated[inflatedLength] = 0;
736 chars = inflated;
737 } else {
738 inflatedLength = length;
739 chars = js_InflateString(cx, bytes, &inflatedLength);
740 if (!chars)
741 return NULL;
742 flags |= ATOM_NOCOPY;
743 }
744
745 JSFLATSTR_INIT(&str, (jschar *)chars, inflatedLength);
746 atom = js_AtomizeString(cx, &str, ATOM_TMPSTR | flags);
747 if (chars != inflated && str.u.chars)
748 JS_free(cx, chars);
749 return atom;
750 }
751
752 JSAtom *
753 js_AtomizeChars(JSContext *cx, const jschar *chars, size_t length, uintN flags)
754 {
755 JSString str;
756
757 JSFLATSTR_INIT(&str, (jschar *)chars, length);
758 return js_AtomizeString(cx, &str, ATOM_TMPSTR | flags);
759 }
760
761 JSAtom *
762 js_GetExistingStringAtom(JSContext *cx, const jschar *chars, size_t length)
763 {
764 JSString str, *str2;
765 JSAtomState *state;
766 JSDHashEntryHdr *hdr;
767
768 JSFLATSTR_INIT(&str, (jschar *)chars, length);
769 state = &cx->runtime->atomState;
770
771 JS_LOCK(cx, &state->lock);
772 hdr = JS_DHashTableOperate(&state->stringAtoms, &str, JS_DHASH_LOOKUP);
773 str2 = JS_DHASH_ENTRY_IS_BUSY(hdr)
774 ? (JSString *)ATOM_ENTRY_KEY(TO_ATOM_ENTRY(hdr))
775 : NULL;
776 JS_UNLOCK(cx, &state->lock);
777
778 return str2 ? (JSAtom *)STRING_TO_JSVAL(str2) : NULL;
779 }
780
781 JSBool
782 js_AtomizePrimitiveValue(JSContext *cx, jsval v, JSAtom **atomp)
783 {
784 JSAtom *atom;
785
786 if (JSVAL_IS_STRING(v)) {
787 atom = js_AtomizeString(cx, JSVAL_TO_STRING(v), 0);
788 if (!atom)
789 return JS_FALSE;
790 } else if (JSVAL_IS_DOUBLE(v)) {
791 atom = js_AtomizeDouble(cx, *JSVAL_TO_DOUBLE(v));
792 if (!atom)
793 return JS_FALSE;
794 } else {
795 JS_ASSERT(JSVAL_IS_INT(v) || JSVAL_IS_BOOLEAN(v) ||
796 JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v));
797 atom = (JSAtom *)v;
798 }
799 *atomp = atom;
800 return JS_TRUE;
801 }
802
803 JSBool
804 js_ValueToStringId(JSContext *cx, jsval v, jsid *idp)
805 {
806 JSString *str;
807 JSAtom *atom;
808
809 /*
810 * Optimize for the common case where v is an already-atomized string. The
811 * comment in jsstr.h before the JSSTRING_SET_ATOMIZED macro's definition
812 * explains why this is thread-safe. The extra rooting via lastAtom (which
813 * would otherwise be done in js_js_AtomizeString) ensures the caller that
814 * the resulting id at is least weakly rooted.
815 */
816 if (JSVAL_IS_STRING(v)) {
817 str = JSVAL_TO_STRING(v);
818 if (JSSTRING_IS_ATOMIZED(str)) {
819 cx->weakRoots.lastAtom = v;
820 *idp = ATOM_TO_JSID((JSAtom *) v);
821 return JS_TRUE;
822 }
823 } else {
824 str = js_ValueToString(cx, v);
825 if (!str)
826 return JS_FALSE;
827 }
828 atom = js_AtomizeString(cx, str, 0);
829 if (!atom)
830 return JS_FALSE;
831 *idp = ATOM_TO_JSID(atom);
832 return JS_TRUE;
833 }
834
835 #ifdef DEBUG
836
837 static JSDHashOperator
838 atom_dumper(JSDHashTable *table, JSDHashEntryHdr *hdr,
839 uint32 number, void *arg)
840 {
841 JSAtomHashEntry *entry = TO_ATOM_ENTRY(hdr);
842 FILE *fp = (FILE *)arg;
843 void *key;
844 uintN flags;
845
846 fprintf(fp, "%3u %08x ", number, (uintN)entry->hdr.keyHash);
847 if (entry->keyAndFlags == 0) {
848 fputs("<uninitialized>", fp);
849 } else {
850 key = ATOM_ENTRY_KEY(entry);
851 if (IS_DOUBLE_TABLE(table)) {
852 fprintf(fp, "%.16g", *(jsdouble *)key);
853 } else {
854 JS_ASSERT(IS_STRING_TABLE(table));
855 js_FileEscapedString(fp, (JSString *)key, '"');
856 }
857 flags = ATOM_ENTRY_FLAGS(entry);
858 if (flags != 0) {
859 fputs((flags & (ATOM_PINNED | ATOM_INTERNED))
860 ? " pinned | interned"
861 : (flags & ATOM_PINNED) ? " pinned" : " interned",
862 fp);
863 }
864 }
865 putc('\n', fp);
866 return JS_DHASH_NEXT;
867 }
868
869 JS_FRIEND_API(void)
870 js_DumpAtoms(JSContext *cx, FILE *fp)
871 {
872 JSAtomState *state = &cx->runtime->atomState;
873
874 fprintf(fp, "stringAtoms table contents:\n");
875 JS_DHashTableEnumerate(&state->stringAtoms, atom_dumper, fp);
876 #ifdef JS_DHASHMETER
877 JS_DHashTableDumpMeter(&state->stringAtoms, atom_dumper, fp);
878 #endif
879 putc('\n', fp);
880
881 fprintf(fp, "doubleAtoms table contents:\n");
882 JS_DHashTableEnumerate(&state->doubleAtoms, atom_dumper, fp);
883 #ifdef JS_DHASHMETER
884 JS_DHashTableDumpMeter(&state->doubleAtoms, atom_dumper, fp);
885 #endif
886 putc('\n', fp);
887 }
888
889 #endif
890
891 static JSHashNumber
892 js_hash_atom_ptr(const void *key)
893 {
894 const JSAtom *atom = (const JSAtom *) key;
895 return ATOM_HASH(atom);
896 }
897
898 static void *
899 js_alloc_temp_space(void *priv, size_t size)
900 {
901 JSContext *cx = (JSContext *) priv;
902 void *space;
903
904 JS_ARENA_ALLOCATE(space, &cx->tempPool, size);
905 if (!space)
906 js_ReportOutOfScriptQuota(cx);
907 return space;
908 }
909
910 static void
911 js_free_temp_space(void *priv, void *item)
912 {
913 }
914
915 static JSHashEntry *
916 js_alloc_temp_entry(void *priv, const void *key)
917 {
918 JSContext *cx = (JSContext *) priv;
919 JSAtomListElement *ale;
920
921 JS_ARENA_ALLOCATE_TYPE(ale, JSAtomListElement, &cx->tempPool);
922 if (!ale) {
923 js_ReportOutOfScriptQuota(cx);
924 return NULL;
925 }
926 return &ale->entry;
927 }
928
929 static void
930 js_free_temp_entry(void *priv, JSHashEntry *he, uintN flag)
931 {
932 }
933
934 static JSHashAllocOps temp_alloc_ops = {
935 js_alloc_temp_space, js_free_temp_space,
936 js_alloc_temp_entry, js_free_temp_entry
937 };
938
939 JSAtomListElement *
940 js_IndexAtom(JSContext *cx, JSAtom *atom, JSAtomList *al)
941 {
942 JSAtomListElement *ale, *ale2, *next;
943 JSHashEntry **hep;
944
945 ATOM_LIST_LOOKUP(ale, hep, al, atom);
946 if (!ale) {
947 if (al->count < 10) {
948 /* Few enough for linear search, no hash table needed. */
949 JS_ASSERT(!al->table);
950 ale = (JSAtomListElement *)js_alloc_temp_entry(cx, atom);
951 if (!ale)
952 return NULL;
953 ALE_SET_ATOM(ale, atom);
954 ale->entry.next = al->list;
955 al->list = &ale->entry;
956 } else {
957 /* We want to hash. Have we already made a hash table? */
958 if (!al->table) {
959 /* No hash table yet, so hep had better be null! */
960 JS_ASSERT(!hep);
961 al->table = JS_NewHashTable(al->count + 1, js_hash_atom_ptr,
962 JS_CompareValues, JS_CompareValues,
963 &temp_alloc_ops, cx);
964 if (!al->table)
965 return NULL;
966
967 /*
968 * Set ht->nentries explicitly, because we are moving entries
969 * from al to ht, not calling JS_HashTable(Raw|)Add.
970 */
971 al->table->nentries = al->count;
972
973 /* Insert each ale on al->list into the new hash table. */
974 for (ale2 = (JSAtomListElement *)al->list; ale2; ale2 = next) {
975 next = ALE_NEXT(ale2);
976 ale2->entry.keyHash = ATOM_HASH(ALE_ATOM(ale2));
977 hep = JS_HashTableRawLookup(al->table, ale2->entry.keyHash,
978 ale2->entry.key);
979 ale2->entry.next = *hep;
980 *hep = &ale2->entry;
981 }
982 al->list = NULL;
983
984 /* Set hep for insertion of atom's ale, immediately below. */
985 hep = JS_HashTableRawLookup(al->table, ATOM_HASH(atom), atom);
986 }
987
988 /* Finally, add an entry for atom into the hash bucket at hep. */
989 ale = (JSAtomListElement *)
990 JS_HashTableRawAdd(al->table, hep, ATOM_HASH(atom), atom,
991 NULL);
992 if (!ale)
993 return NULL;
994 }
995
996 ALE_SET_INDEX(ale, al->count++);
997 }
998 return ale;
999 }
1000
1001 static intN
1002 js_map_atom(JSHashEntry *he, intN i, void *arg)
1003 {
1004 JSAtomListElement *ale = (JSAtomListElement *)he;
1005 JSAtom **vector = (JSAtom **) arg;
1006
1007 vector[ALE_INDEX(ale)] = ALE_ATOM(ale);
1008 return HT_ENUMERATE_NEXT;
1009 }
1010
1011 #ifdef DEBUG
1012 static jsrefcount js_atom_map_count;
1013 static jsrefcount js_atom_map_hash_table_count;
1014 #endif
1015
1016 void
1017 js_InitAtomMap(JSContext *cx, JSAtomMap *map, JSAtomList *al)
1018 {
1019 JSAtom **vector;
1020 JSAtomListElement *ale;
1021 uint32 count;
1022
1023 /* Map length must already be initialized. */
1024 JS_ASSERT(al->count == map->length);
1025 #ifdef DEBUG
1026 JS_ATOMIC_INCREMENT(&js_atom_map_count);
1027 #endif
1028 ale = (JSAtomListElement *)al->list;
1029 if (!ale && !al->table) {
1030 JS_ASSERT(!map->vector);
1031 return;
1032 }
1033
1034 count = al->count;
1035 vector = map->vector;
1036 if (al->table) {
1037 #ifdef DEBUG
1038 JS_ATOMIC_INCREMENT(&js_atom_map_hash_table_count);
1039 #endif
1040 JS_HashTableEnumerateEntries(al->table, js_map_atom, vector);
1041 } else {
1042 do {
1043 vector[ALE_INDEX(ale)] = ALE_ATOM(ale);
1044 } while ((ale = ALE_NEXT(ale)) != NULL);
1045 }
1046 ATOM_LIST_INIT(al);
1047 }

  ViewVC Help
Powered by ViewVC 1.1.24