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

Annotation of /trunk/js/jsatom.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 399 - (hide annotations)
Tue Dec 9 03:37:47 2008 UTC (10 years, 11 months ago) by siliconforks
File size: 35403 byte(s)
Use SpiderMonkey from Firefox 3.1b2.

1 siliconforks 332 /* -*- 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 siliconforks 399 * String constants for common atoms defined in JSAtomState starting from
72 siliconforks 332 * JSAtomState.emptyAtom until JSAtomState.lazy.
73     *
74     * The elements of the array after the first empty string define strings
75 siliconforks 399 * 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 siliconforks 332 */
80     JS_STATIC_ASSERT(JSTYPE_LIMIT == 8);
81 siliconforks 399 JS_STATIC_ASSERT(JSVAL_TO_BOOLEAN(JSVAL_VOID) == 2);
82     JS_STATIC_ASSERT(JSTYPE_VOID == 0);
83    
84 siliconforks 332 const char *const js_common_atom_names[] = {
85     "", /* emptyAtom */
86 siliconforks 399 js_false_str, /* booleanAtoms[0] */
87     js_true_str, /* booleanAtoms[1] */
88 siliconforks 332 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 siliconforks 399 js_apply_str, /* applyAtom */
104 siliconforks 332 js_arguments_str, /* argumentsAtom */
105     js_arity_str, /* arityAtom */
106 siliconforks 399 js_call_str, /* callAtom */
107 siliconforks 332 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 siliconforks 399 js___call___str, /* __call__Atom */
153     js___construct___str, /* __construct__Atom */
154     js___hasInstance___str, /* __hasInstance__Atom */
155 siliconforks 332 js_ExecutionContext_str, /* ExecutionContextAtom */
156     js_current_str, /* currentAtom */
157     #endif
158     };
159 siliconforks 399
160 siliconforks 332 JS_STATIC_ASSERT(JS_ARRAY_LENGTH(js_common_atom_names) * sizeof(JSAtom *) ==
161     LAZY_ATOM_OFFSET_START - ATOM_OFFSET_START);
162    
163 siliconforks 399 /*
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 siliconforks 332 const char js_anonymous_str[] = "anonymous";
172 siliconforks 399 const char js_apply_str[] = "apply";
173 siliconforks 332 const char js_arguments_str[] = "arguments";
174     const char js_arity_str[] = "arity";
175 siliconforks 399 const char js_call_str[] = "call";
176 siliconforks 332 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 siliconforks 399 const char js___call___str[] = "__call__";
228     const char js___construct___str[] = "__construct__";
229     const char js___hasInstance___str[] = "__hasInstance__";
230 siliconforks 332 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     }

  ViewVC Help
Powered by ViewVC 1.1.24