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 |
|
|
} |