1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : *
3 : * ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Mozilla Communicator client code, released
17 : * March 31, 1998.
18 : *
19 : * The Initial Developer of the Original Code is
20 : * Netscape Communications Corporation.
21 : * Portions created by the Initial Developer are Copyright (C) 1998
22 : * the Initial Developer. All Rights Reserved.
23 : *
24 : * Contributor(s):
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either of the GNU General Public License Version 2 or later (the "GPL"),
28 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : /*
41 : * JS atom table.
42 : */
43 : #include <stdlib.h>
44 : #include <string.h>
45 :
46 : #include "mozilla/RangedPtr.h"
47 : #include "mozilla/Util.h"
48 :
49 : #include "jstypes.h"
50 : #include "jsutil.h"
51 : #include "jshash.h"
52 : #include "jsprf.h"
53 : #include "jsapi.h"
54 : #include "jsatom.h"
55 : #include "jscntxt.h"
56 : #include "jsgc.h"
57 : #include "jsgcmark.h"
58 : #include "jslock.h"
59 : #include "jsnum.h"
60 : #include "jsstr.h"
61 : #include "jsversion.h"
62 : #include "jsxml.h"
63 :
64 : #include "frontend/Parser.h"
65 :
66 : #include "jsstrinlines.h"
67 : #include "jsatominlines.h"
68 : #include "jsobjinlines.h"
69 :
70 : #include "vm/String-inl.h"
71 : #include "vm/Xdr.h"
72 :
73 : using namespace mozilla;
74 : using namespace js;
75 : using namespace js::gc;
76 :
77 : const size_t JSAtomState::commonAtomsOffset = offsetof(JSAtomState, emptyAtom);
78 : const size_t JSAtomState::lazyAtomsOffset = offsetof(JSAtomState, lazy);
79 :
80 : /*
81 : * ATOM_HASH assumes that JSHashNumber is 32-bit even on 64-bit systems.
82 : */
83 : JS_STATIC_ASSERT(sizeof(JSHashNumber) == 4);
84 : JS_STATIC_ASSERT(sizeof(JSAtom *) == JS_BYTES_PER_WORD);
85 :
86 : const char *
87 20539 : js_AtomToPrintableString(JSContext *cx, JSAtom *atom, JSAutoByteString *bytes)
88 : {
89 20539 : return js_ValueToPrintable(cx, StringValue(atom), bytes);
90 : }
91 :
92 : #define JS_PROTO(name,code,init) const char js_##name##_str[] = #name;
93 : #include "jsproto.tbl"
94 : #undef JS_PROTO
95 :
96 : /*
97 : * String constants for common atoms defined in JSAtomState starting from
98 : * JSAtomState.emptyAtom until JSAtomState.lazy.
99 : *
100 : * The elements of the array after the first empty string define strings
101 : * corresponding to the two boolean literals, false and true, followed by the
102 : * JSType enumerators from jspubtd.h starting with "undefined" for JSTYPE_VOID
103 : * (which is special-value 2) and continuing as initialized below. The static
104 : * asserts check these relations.
105 : */
106 : JS_STATIC_ASSERT(JSTYPE_LIMIT == 8);
107 : JS_STATIC_ASSERT(JSTYPE_VOID == 0);
108 :
109 : const char *const js_common_atom_names[] = {
110 : "", /* emptyAtom */
111 : js_false_str, /* booleanAtoms[0] */
112 : js_true_str, /* booleanAtoms[1] */
113 : js_undefined_str, /* typeAtoms[JSTYPE_VOID] */
114 : js_object_str, /* typeAtoms[JSTYPE_OBJECT] */
115 : js_function_str, /* typeAtoms[JSTYPE_FUNCTION] */
116 : "string", /* typeAtoms[JSTYPE_STRING] */
117 : "number", /* typeAtoms[JSTYPE_NUMBER] */
118 : "boolean", /* typeAtoms[JSTYPE_BOOLEAN] */
119 : js_null_str, /* typeAtoms[JSTYPE_NULL] */
120 : "xml", /* typeAtoms[JSTYPE_XML] */
121 : js_null_str /* nullAtom */
122 :
123 : #define JS_PROTO(name,code,init) ,js_##name##_str
124 : #include "jsproto.tbl"
125 : #undef JS_PROTO
126 :
127 : #define DEFINE_ATOM(id, text) ,js_##id##_str
128 : #define DEFINE_PROTOTYPE_ATOM(id) ,js_##id##_str
129 : #define DEFINE_KEYWORD_ATOM(id) ,js_##id##_str
130 : #include "jsatom.tbl"
131 : #undef DEFINE_ATOM
132 : #undef DEFINE_PROTOTYPE_ATOM
133 : #undef DEFINE_KEYWORD_ATOM
134 : };
135 :
136 : void
137 0 : JSAtomState::checkStaticInvariants()
138 : {
139 : /*
140 : * Start and limit offsets for atom pointers in JSAtomState must be aligned
141 : * on the word boundary.
142 : */
143 : JS_STATIC_ASSERT(commonAtomsOffset % sizeof(JSAtom *) == 0);
144 : JS_STATIC_ASSERT(sizeof(*this) % sizeof(JSAtom *) == 0);
145 :
146 : /*
147 : * JS_BOOLEAN_STR and JS_TYPE_STR assume that boolean names starts from the
148 : * index 1 and type name starts from the index 1+2 atoms in JSAtomState.
149 : */
150 : JS_STATIC_ASSERT(1 * sizeof(JSAtom *) ==
151 : offsetof(JSAtomState, booleanAtoms) - commonAtomsOffset);
152 : JS_STATIC_ASSERT((1 + 2) * sizeof(JSAtom *) ==
153 : offsetof(JSAtomState, typeAtoms) - commonAtomsOffset);
154 :
155 : JS_STATIC_ASSERT(JS_ARRAY_LENGTH(js_common_atom_names) * sizeof(JSAtom *) ==
156 : lazyAtomsOffset - commonAtomsOffset);
157 0 : }
158 :
159 : /*
160 : * Interpreter macros called by the trace recorder assume common atom indexes
161 : * fit in one byte of immediate operand.
162 : */
163 : JS_STATIC_ASSERT(JS_ARRAY_LENGTH(js_common_atom_names) < 256);
164 :
165 : const size_t js_common_atom_count = JS_ARRAY_LENGTH(js_common_atom_names);
166 :
167 : const char js_undefined_str[] = "undefined";
168 : const char js_object_str[] = "object";
169 :
170 : #define DEFINE_ATOM(id, text) const char js_##id##_str[] = text;
171 : #define DEFINE_PROTOTYPE_ATOM(id)
172 : #define DEFINE_KEYWORD_ATOM(id)
173 : #include "jsatom.tbl"
174 : #undef DEFINE_ATOM
175 : #undef DEFINE_PROTOTYPE_ATOM
176 : #undef DEFINE_KEYWORD_ATOM
177 :
178 : #if JS_HAS_GENERATORS
179 : const char js_close_str[] = "close";
180 : const char js_send_str[] = "send";
181 : #endif
182 :
183 : /* Constant strings that are not atomized. */
184 : const char js_getter_str[] = "getter";
185 : const char js_setter_str[] = "setter";
186 :
187 : /*
188 : * For a browser build from 2007-08-09 after the browser starts up there are
189 : * just 55 double atoms, but over 15000 string atoms. Not to penalize more
190 : * economical embeddings allocating too much memory initially we initialize
191 : * atomized strings with just 1K entries.
192 : */
193 : #define JS_STRING_HASH_COUNT 1024
194 :
195 : JSBool
196 18761 : js_InitAtomState(JSRuntime *rt)
197 : {
198 18761 : JSAtomState *state = &rt->atomState;
199 :
200 18761 : JS_ASSERT(!state->atoms.initialized());
201 18761 : if (!state->atoms.init(JS_STRING_HASH_COUNT))
202 0 : return false;
203 :
204 18761 : JS_ASSERT(state->atoms.initialized());
205 18761 : return true;
206 : }
207 :
208 : void
209 18761 : js_FinishAtomState(JSRuntime *rt)
210 : {
211 18761 : JSAtomState *state = &rt->atomState;
212 :
213 18761 : if (!state->atoms.initialized()) {
214 : /*
215 : * We are called with uninitialized state when JS_NewRuntime fails and
216 : * calls JS_DestroyRuntime on a partially initialized runtime.
217 : */
218 0 : return;
219 : }
220 :
221 18761 : FreeOp fop(rt, false, false);
222 2812080 : for (AtomSet::Range r = state->atoms.all(); !r.empty(); r.popFront())
223 2793319 : r.front().asPtr()->finalize(&fop);
224 : }
225 :
226 : bool
227 18761 : js::InitCommonAtoms(JSContext *cx)
228 : {
229 18761 : JSAtomState *state = &cx->runtime->atomState;
230 18761 : JSAtom **atoms = state->commonAtomsStart();
231 2570257 : for (size_t i = 0; i < ArrayLength(js_common_atom_names); i++, atoms++) {
232 : JSAtom *atom = js_Atomize(cx, js_common_atom_names[i], strlen(js_common_atom_names[i]),
233 2551496 : InternAtom);
234 2551496 : if (!atom)
235 0 : return false;
236 2551496 : *atoms = atom->asPropertyName();
237 : }
238 :
239 18761 : state->clearLazyAtoms();
240 18761 : cx->runtime->emptyString = state->emptyAtom;
241 18761 : return true;
242 : }
243 :
244 : void
245 18761 : js::FinishCommonAtoms(JSRuntime *rt)
246 : {
247 18761 : rt->emptyString = NULL;
248 18761 : rt->atomState.junkAtoms();
249 18761 : }
250 :
251 : void
252 41836 : js::MarkAtomState(JSTracer *trc, bool markAll)
253 : {
254 41836 : JSRuntime *rt = trc->runtime;
255 41836 : JSAtomState *state = &rt->atomState;
256 :
257 41836 : if (markAll) {
258 6358114 : for (AtomSet::Range r = state->atoms.all(); !r.empty(); r.popFront()) {
259 6347269 : JSAtom *tmp = r.front().asPtr();
260 6347269 : MarkStringRoot(trc, &tmp, "locked_atom");
261 6347269 : JS_ASSERT(tmp == r.front().asPtr());
262 : }
263 : } else {
264 19000010 : for (AtomSet::Range r = state->atoms.all(); !r.empty(); r.popFront()) {
265 18969019 : AtomStateEntry entry = r.front();
266 18969019 : if (!entry.isTagged())
267 14354441 : continue;
268 :
269 4614578 : JSAtom *tmp = entry.asPtr();
270 4614578 : MarkStringRoot(trc, &tmp, "interned_atom");
271 4614578 : JS_ASSERT(tmp == entry.asPtr());
272 : }
273 : }
274 41836 : }
275 :
276 : void
277 38427 : js::SweepAtomState(JSRuntime *rt)
278 : {
279 38427 : JSAtomState *state = &rt->atomState;
280 :
281 23385873 : for (AtomSet::Enum e(state->atoms); !e.empty(); e.popFront()) {
282 23347446 : AtomStateEntry entry = e.front();
283 :
284 23347446 : if (entry.isTagged()) {
285 : /* Pinned or interned key cannot be finalized. */
286 5722520 : JS_ASSERT(!IsAboutToBeFinalized(entry.asPtr()));
287 5722520 : continue;
288 : }
289 :
290 17624926 : if (IsAboutToBeFinalized(entry.asPtr()))
291 9071906 : e.removeFront();
292 : }
293 38427 : }
294 :
295 : bool
296 18 : AtomIsInterned(JSContext *cx, JSAtom *atom)
297 : {
298 : /* We treat static strings as interned because they're never collected. */
299 18 : if (StaticStrings::isStatic(atom))
300 5 : return true;
301 :
302 13 : AtomSet::Ptr p = cx->runtime->atomState.atoms.lookup(atom);
303 13 : if (!p)
304 0 : return false;
305 :
306 13 : return p->isTagged();
307 : }
308 :
309 : enum OwnCharsBehavior
310 : {
311 : CopyChars, /* in other words, do not take ownership */
312 : TakeCharOwnership
313 : };
314 :
315 : /*
316 : * Callers passing OwnChars have freshly allocated *pchars and thus this
317 : * memory can be used as a new JSAtom's buffer without copying. When this flag
318 : * is set, the contract is that callers will free *pchars iff *pchars == NULL.
319 : */
320 : JS_ALWAYS_INLINE
321 : static JSAtom *
322 34665169 : AtomizeInline(JSContext *cx, const jschar **pchars, size_t length,
323 : InternBehavior ib, OwnCharsBehavior ocb = CopyChars)
324 : {
325 34665169 : const jschar *chars = *pchars;
326 :
327 34665169 : if (JSAtom *s = cx->runtime->staticStrings.lookup(chars, length))
328 2694690 : return s;
329 :
330 31970479 : AtomSet &atoms = cx->runtime->atomState.atoms;
331 63940958 : AtomSet::AddPtr p = atoms.lookupForAdd(AtomHasher::Lookup(chars, length));
332 :
333 31970479 : if (p) {
334 20105254 : JSAtom *atom = p->asPtr();
335 20105254 : p->setTagged(bool(ib));
336 20105254 : return atom;
337 : }
338 :
339 23730450 : SwitchToCompartment sc(cx, cx->runtime->atomsCompartment);
340 :
341 : JSFixedString *key;
342 :
343 11865225 : if (ocb == TakeCharOwnership) {
344 1269289 : key = js_NewString(cx, const_cast<jschar *>(chars), length);
345 1269289 : if (!key)
346 0 : return NULL;
347 1269289 : *pchars = NULL; /* Called should not free *pchars. */
348 : } else {
349 10595936 : JS_ASSERT(ocb == CopyChars);
350 10595936 : key = js_NewStringCopyN(cx, chars, length);
351 10595936 : if (!key)
352 0 : return NULL;
353 : }
354 :
355 : /*
356 : * We have to relookup the key as the last ditch GC invoked from the
357 : * string allocation or OOM handling unlocks the atomsCompartment.
358 : *
359 : * N.B. this avoids recomputing the hash but still has a potential
360 : * (# collisions * # chars) comparison cost in the case of a hash
361 : * collision!
362 : */
363 11865225 : AtomHasher::Lookup lookup(chars, length);
364 11865225 : if (!atoms.relookupOrAdd(p, lookup, AtomStateEntry((JSAtom *) key, bool(ib)))) {
365 0 : JS_ReportOutOfMemory(cx); /* SystemAllocPolicy does not report */
366 0 : return NULL;
367 : }
368 :
369 11865225 : return key->morphAtomizedStringIntoAtom();
370 : }
371 :
372 : static JSAtom *
373 30834610 : Atomize(JSContext *cx, const jschar **pchars, size_t length,
374 : InternBehavior ib, OwnCharsBehavior ocb = CopyChars)
375 : {
376 30834610 : return AtomizeInline(cx, pchars, length, ib, ocb);
377 : }
378 :
379 : JSAtom *
380 1519121 : js_AtomizeString(JSContext *cx, JSString *str, InternBehavior ib)
381 : {
382 1519121 : if (str->isAtom()) {
383 559200 : JSAtom &atom = str->asAtom();
384 : /* N.B. static atoms are effectively always interned. */
385 559200 : if (ib != InternAtom || js::StaticStrings::isStatic(&atom))
386 559199 : return &atom;
387 :
388 1 : AtomSet &atoms = cx->runtime->atomState.atoms;
389 1 : AtomSet::Ptr p = atoms.lookup(AtomHasher::Lookup(&atom));
390 1 : JS_ASSERT(p); /* Non-static atom must exist in atom state set. */
391 1 : JS_ASSERT(p->asPtr() == &atom);
392 1 : JS_ASSERT(ib == InternAtom);
393 1 : p->setTagged(bool(ib));
394 1 : return &atom;
395 : }
396 :
397 959921 : if (str->isAtom())
398 0 : return &str->asAtom();
399 :
400 959921 : size_t length = str->length();
401 959921 : const jschar *chars = str->getChars(cx);
402 959921 : if (!chars)
403 0 : return NULL;
404 :
405 959921 : JS_ASSERT(length <= JSString::MAX_LENGTH);
406 959921 : return Atomize(cx, &chars, length, ib);
407 : }
408 :
409 : JSAtom *
410 29874689 : js_Atomize(JSContext *cx, const char *bytes, size_t length, InternBehavior ib, FlationCoding fc)
411 : {
412 59749378 : CHECK_REQUEST(cx);
413 :
414 29874689 : if (!JSString::validateLength(cx, length))
415 0 : return NULL;
416 :
417 : /*
418 : * Avoiding the malloc in InflateString on shorter strings saves us
419 : * over 20,000 malloc calls on mozilla browser startup. This compares to
420 : * only 131 calls where the string is longer than a 31 char (net) buffer.
421 : * The vast majority of atomized strings are already in the hashtable. So
422 : * js_AtomizeString rarely has to copy the temp string we make.
423 : */
424 : static const unsigned ATOMIZE_BUF_MAX = 32;
425 : jschar inflated[ATOMIZE_BUF_MAX];
426 29874689 : size_t inflatedLength = ATOMIZE_BUF_MAX - 1;
427 :
428 : const jschar *chars;
429 29874689 : OwnCharsBehavior ocb = CopyChars;
430 29874689 : if (length < ATOMIZE_BUF_MAX) {
431 28288384 : if (fc == CESU8Encoding)
432 0 : InflateUTF8StringToBuffer(cx, bytes, length, inflated, &inflatedLength, fc);
433 : else
434 28288384 : InflateStringToBuffer(cx, bytes, length, inflated, &inflatedLength);
435 28288384 : inflated[inflatedLength] = 0;
436 28288384 : chars = inflated;
437 : } else {
438 1586305 : inflatedLength = length;
439 1586305 : chars = InflateString(cx, bytes, &inflatedLength, fc);
440 1586305 : if (!chars)
441 0 : return NULL;
442 1586305 : ocb = TakeCharOwnership;
443 : }
444 :
445 29874689 : JSAtom *atom = Atomize(cx, &chars, inflatedLength, ib, ocb);
446 29874689 : if (ocb == TakeCharOwnership && chars)
447 317016 : cx->free_((void *)chars);
448 29874689 : return atom;
449 : }
450 :
451 : JSAtom *
452 3830559 : js_AtomizeChars(JSContext *cx, const jschar *chars, size_t length, InternBehavior ib)
453 : {
454 7661118 : CHECK_REQUEST(cx);
455 :
456 3830559 : if (!JSString::validateLength(cx, length))
457 0 : return NULL;
458 :
459 3830559 : return AtomizeInline(cx, &chars, length, ib);
460 : }
461 :
462 : JSAtom *
463 0 : js_GetExistingStringAtom(JSContext *cx, const jschar *chars, size_t length)
464 : {
465 0 : if (JSAtom *atom = cx->runtime->staticStrings.lookup(chars, length))
466 0 : return atom;
467 0 : if (AtomSet::Ptr p = cx->runtime->atomState.atoms.lookup(AtomHasher::Lookup(chars, length)))
468 0 : return p->asPtr();
469 0 : return NULL;
470 : }
471 :
472 : #ifdef DEBUG
473 : JS_FRIEND_API(void)
474 0 : js_DumpAtoms(JSContext *cx, FILE *fp)
475 : {
476 0 : JSAtomState *state = &cx->runtime->atomState;
477 :
478 0 : fprintf(fp, "atoms table contents:\n");
479 0 : unsigned number = 0;
480 0 : for (AtomSet::Range r = state->atoms.all(); !r.empty(); r.popFront()) {
481 0 : AtomStateEntry entry = r.front();
482 0 : fprintf(fp, "%3u ", number++);
483 0 : JSAtom *key = entry.asPtr();
484 0 : FileEscapedString(fp, key, '"');
485 0 : if (entry.isTagged())
486 0 : fputs(" interned", fp);
487 0 : putc('\n', fp);
488 : }
489 0 : putc('\n', fp);
490 0 : }
491 : #endif
492 :
493 : #if JS_BITS_PER_WORD == 32
494 : # define TEMP_SIZE_START_LOG2 5
495 : #else
496 : # define TEMP_SIZE_START_LOG2 6
497 : #endif
498 : #define TEMP_SIZE_LIMIT_LOG2 (TEMP_SIZE_START_LOG2 + NUM_TEMP_FREELISTS)
499 :
500 : #define TEMP_SIZE_START JS_BIT(TEMP_SIZE_START_LOG2)
501 : #define TEMP_SIZE_LIMIT JS_BIT(TEMP_SIZE_LIMIT_LOG2)
502 :
503 : JS_STATIC_ASSERT(TEMP_SIZE_START >= sizeof(JSHashTable));
504 :
505 : void
506 259010 : js_InitAtomMap(JSContext *cx, AtomIndexMap *indices, JSAtom **atoms)
507 : {
508 259010 : if (indices->isMap()) {
509 : typedef AtomIndexMap::WordMap WordMap;
510 648 : const WordMap &wm = indices->asMap();
511 40455 : for (WordMap::Range r = wm.all(); !r.empty(); r.popFront()) {
512 39807 : JSAtom *atom = r.front().key;
513 39807 : jsatomid index = r.front().value;
514 39807 : JS_ASSERT(index < indices->count());
515 39807 : atoms[index] = atom;
516 : }
517 : } else {
518 870704 : for (const AtomIndexMap::InlineElem *it = indices->asInline(), *end = indices->inlineEnd();
519 : it != end; ++it) {
520 612342 : JSAtom *atom = it->key;
521 612342 : if (!atom)
522 0 : continue;
523 612342 : JS_ASSERT(it->value < indices->count());
524 612342 : atoms[it->value] = atom;
525 : }
526 : }
527 259010 : }
528 :
529 : namespace js {
530 :
531 : bool
532 233 : IndexToIdSlow(JSContext *cx, uint32_t index, jsid *idp)
533 : {
534 233 : JS_ASSERT(index > JSID_INT_MAX);
535 :
536 : jschar buf[UINT32_CHAR_BUFFER_LENGTH];
537 233 : RangedPtr<jschar> end(ArrayEnd(buf), buf, ArrayEnd(buf));
538 233 : RangedPtr<jschar> start = BackfillIndexInCharBuffer(index, end);
539 :
540 233 : JSAtom *atom = js_AtomizeChars(cx, start.get(), end - start);
541 233 : if (!atom)
542 0 : return false;
543 :
544 233 : *idp = ATOM_TO_JSID(atom);
545 233 : JS_ASSERT(js_CheckForStringIndex(*idp) == *idp);
546 233 : return true;
547 : }
548 :
549 : } /* namespace js */
550 :
551 : /* JSBOXEDWORD_INT_MAX as a string */
552 : #define JSBOXEDWORD_INT_MAX_STRING "1073741823"
553 :
554 : /*
555 : * Convert string indexes that convert to int jsvals as ints to save memory.
556 : * Care must be taken to use this macro every time a property name is used, or
557 : * else double-sets, incorrect property cache misses, or other mistakes could
558 : * occur.
559 : */
560 : jsid
561 164617159 : js_CheckForStringIndex(jsid id)
562 : {
563 164617159 : if (!JSID_IS_ATOM(id))
564 17816541 : return id;
565 :
566 146800618 : JSAtom *atom = JSID_TO_ATOM(id);
567 146800618 : const jschar *s = atom->chars();
568 146800618 : jschar ch = *s;
569 :
570 146800618 : JSBool negative = (ch == '-');
571 146800618 : if (negative)
572 27276 : ch = *++s;
573 :
574 146800618 : if (!JS7_ISDEC(ch))
575 145783263 : return id;
576 :
577 1017355 : size_t n = atom->length() - negative;
578 1017355 : if (n > sizeof(JSBOXEDWORD_INT_MAX_STRING) - 1)
579 54 : return id;
580 :
581 1017301 : const jschar *cp = s;
582 1017301 : const jschar *end = s + n;
583 :
584 1017301 : uint32_t index = JS7_UNDEC(*cp++);
585 1017301 : uint32_t oldIndex = 0;
586 1017301 : uint32_t c = 0;
587 :
588 1017301 : if (index != 0) {
589 2047844 : while (JS7_ISDEC(*cp)) {
590 77810 : oldIndex = index;
591 77810 : c = JS7_UNDEC(*cp);
592 77810 : index = 10 * index + c;
593 77810 : cp++;
594 : }
595 : }
596 :
597 : /*
598 : * Non-integer indexes can't be represented as integers. Also, distinguish
599 : * index "-0" from "0", because JSBOXEDWORD_INT cannot.
600 : */
601 1017301 : if (cp != end || (negative && index == 0))
602 885694 : return id;
603 :
604 131607 : if (negative) {
605 3381 : if (oldIndex < -(JSID_INT_MIN / 10) ||
606 : (oldIndex == -(JSID_INT_MIN / 10) && c <= (-JSID_INT_MIN % 10)))
607 : {
608 2634 : id = INT_TO_JSID(-int32_t(index));
609 : }
610 : } else {
611 128226 : if (oldIndex < JSID_INT_MAX / 10 ||
612 : (oldIndex == JSID_INT_MAX / 10 && c <= (JSID_INT_MAX % 10)))
613 : {
614 127291 : id = INT_TO_JSID(int32_t(index));
615 : }
616 : }
617 :
618 131607 : return id;
619 : }
620 :
621 : #if JS_HAS_XML_SUPPORT
622 : bool
623 6 : js_InternNonIntElementIdSlow(JSContext *cx, JSObject *obj, const Value &idval,
624 : jsid *idp)
625 : {
626 6 : JS_ASSERT(idval.isObject());
627 6 : if (obj->isXML()) {
628 0 : *idp = OBJECT_TO_JSID(&idval.toObject());
629 0 : return true;
630 : }
631 :
632 6 : if (js_GetLocalNameFromFunctionQName(&idval.toObject(), idp, cx))
633 0 : return true;
634 :
635 6 : return js_ValueToStringId(cx, idval, idp);
636 : }
637 :
638 : bool
639 819 : js_InternNonIntElementIdSlow(JSContext *cx, JSObject *obj, const Value &idval,
640 : jsid *idp, Value *vp)
641 : {
642 819 : JS_ASSERT(idval.isObject());
643 819 : if (obj->isXML()) {
644 0 : JSObject &idobj = idval.toObject();
645 0 : *idp = OBJECT_TO_JSID(&idobj);
646 0 : vp->setObject(idobj);
647 0 : return true;
648 : }
649 :
650 819 : if (js_GetLocalNameFromFunctionQName(&idval.toObject(), idp, cx)) {
651 9 : *vp = IdToValue(*idp);
652 9 : return true;
653 : }
654 :
655 810 : if (js_ValueToStringId(cx, idval, idp)) {
656 810 : vp->setString(JSID_TO_STRING(*idp));
657 810 : return true;
658 : }
659 0 : return false;
660 : }
661 : #endif
662 :
663 : template<XDRMode mode>
664 : bool
665 15742 : js::XDRAtom(XDRState<mode> *xdr, JSAtom **atomp)
666 : {
667 : if (mode == XDR_ENCODE) {
668 7871 : JSString *str = *atomp;
669 7871 : return xdr->codeString(&str);
670 : }
671 :
672 : /*
673 : * Inline XDRState::codeString when decoding to avoid JSString allocation
674 : * for already existing atoms. See bug 321985.
675 : */
676 : uint32_t nchars;
677 7871 : if (!xdr->codeUint32(&nchars))
678 0 : return false;
679 :
680 7871 : JSContext *cx = xdr->cx();
681 : JSAtom *atom;
682 : #if IS_LITTLE_ENDIAN
683 : /* Directly access the little endian chars in the XDR buffer. */
684 7871 : const jschar *chars = reinterpret_cast<const jschar *>(xdr->buf.read(nchars * sizeof(jschar)));
685 7871 : atom = js_AtomizeChars(cx, chars, nchars);
686 : #else
687 : /*
688 : * We must copy chars to a temporary buffer to convert between little and
689 : * big endian data.
690 : */
691 : jschar *chars;
692 : jschar stackChars[256];
693 : if (nchars <= ArrayLength(stackChars)) {
694 : chars = stackChars;
695 : } else {
696 : /*
697 : * This is very uncommon. Don't use the tempLifoAlloc arena for this as
698 : * most allocations here will be bigger than tempLifoAlloc's default
699 : * chunk size.
700 : */
701 : chars = static_cast<jschar *>(cx->runtime->malloc_(nchars * sizeof(jschar)));
702 : if (!chars)
703 : return false;
704 : }
705 :
706 : JS_ALWAYS_TRUE(xdr->codeChars(chars, nchars));
707 : atom = js_AtomizeChars(cx, chars, nchars);
708 : if (chars != stackChars)
709 : Foreground::free_(chars);
710 : #endif /* !IS_LITTLE_ENDIAN */
711 :
712 7871 : if (!atom)
713 0 : return false;
714 7871 : *atomp = atom;
715 7871 : return true;
716 : }
717 :
718 : template bool
719 : js::XDRAtom(XDRState<XDR_ENCODE> *xdr, JSAtom **atomp);
720 :
721 : template bool
722 : js::XDRAtom(XDRState<XDR_DECODE> *xdr, JSAtom **atomp);
723 :
|