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

Annotation of /trunk/js/jsatom.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 332 - (hide annotations)
Thu Oct 23 19:03:33 2008 UTC (12 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 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     * 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