Line data Source code
1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sts=4 et sw=4 tw=99:
3 : * This Source Code Form is subject to the terms of the Mozilla Public
4 : * License, v. 2.0. If a copy of the MPL was not distributed with this
5 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 :
7 : /*
8 : * JS atom table.
9 : */
10 :
11 : #include "jsatominlines.h"
12 :
13 : #include "mozilla/ArrayUtils.h"
14 : #include "mozilla/RangedPtr.h"
15 : #include "mozilla/Unused.h"
16 :
17 : #include <string.h>
18 :
19 : #include "jscntxt.h"
20 : #include "jsstr.h"
21 : #include "jstypes.h"
22 :
23 : #include "gc/Marking.h"
24 : #include "vm/Symbol.h"
25 : #include "vm/Xdr.h"
26 :
27 : #include "jscntxtinlines.h"
28 : #include "jscompartmentinlines.h"
29 : #include "jsobjinlines.h"
30 :
31 : #include "gc/AtomMarking-inl.h"
32 : #include "vm/String-inl.h"
33 :
34 : using namespace js;
35 : using namespace js::gc;
36 :
37 : using mozilla::ArrayEnd;
38 : using mozilla::ArrayLength;
39 : using mozilla::Maybe;
40 : using mozilla::Nothing;
41 : using mozilla::RangedPtr;
42 :
43 : const char*
44 0 : js::AtomToPrintableString(JSContext* cx, JSAtom* atom, JSAutoByteString* bytes)
45 : {
46 0 : JSString* str = QuoteString(cx, atom, 0);
47 0 : if (!str)
48 0 : return nullptr;
49 0 : return bytes->encodeLatin1(cx, str);
50 : }
51 :
52 : #define DEFINE_PROTO_STRING(name,code,init,clasp) const char js_##name##_str[] = #name;
53 : JS_FOR_EACH_PROTOTYPE(DEFINE_PROTO_STRING)
54 : #undef DEFINE_PROTO_STRING
55 :
56 : #define CONST_CHAR_STR(idpart, id, text) const char js_##idpart##_str[] = text;
57 : FOR_EACH_COMMON_PROPERTYNAME(CONST_CHAR_STR)
58 : #undef CONST_CHAR_STR
59 :
60 : /* Constant strings that are not atomized. */
61 : const char js_getter_str[] = "getter";
62 : const char js_send_str[] = "send";
63 : const char js_setter_str[] = "setter";
64 :
65 : // Use a low initial capacity for atom hash tables to avoid penalizing runtimes
66 : // which create a small number of atoms.
67 : static const uint32_t JS_STRING_HASH_COUNT = 64;
68 :
69 : MOZ_ALWAYS_INLINE AtomSet::Ptr
70 124762 : js::FrozenAtomSet::readonlyThreadsafeLookup(const AtomSet::Lookup& l) const
71 : {
72 124762 : return mSet->readonlyThreadsafeLookup(l);
73 : }
74 :
75 : struct CommonNameInfo
76 : {
77 : const char* str;
78 : size_t length;
79 : };
80 :
81 : bool
82 4 : JSRuntime::initializeAtoms(JSContext* cx)
83 : {
84 4 : atoms_ = cx->new_<AtomSet>();
85 4 : if (!atoms_ || !atoms_->init(JS_STRING_HASH_COUNT))
86 0 : return false;
87 :
88 : // |permanentAtoms| hasn't been created yet.
89 4 : MOZ_ASSERT(!permanentAtoms);
90 :
91 4 : if (parentRuntime) {
92 1 : staticStrings = parentRuntime->staticStrings;
93 1 : commonNames = parentRuntime->commonNames;
94 1 : emptyString = parentRuntime->emptyString;
95 1 : permanentAtoms = parentRuntime->permanentAtoms;
96 1 : wellKnownSymbols = parentRuntime->wellKnownSymbols;
97 1 : return true;
98 : }
99 :
100 3 : staticStrings = cx->new_<StaticStrings>();
101 3 : if (!staticStrings || !staticStrings->init(cx))
102 0 : return false;
103 :
104 : static const CommonNameInfo cachedNames[] = {
105 : #define COMMON_NAME_INFO(idpart, id, text) { js_##idpart##_str, sizeof(text) - 1 },
106 : FOR_EACH_COMMON_PROPERTYNAME(COMMON_NAME_INFO)
107 : #undef COMMON_NAME_INFO
108 : #define COMMON_NAME_INFO(name, code, init, clasp) { js_##name##_str, sizeof(#name) - 1 },
109 : JS_FOR_EACH_PROTOTYPE(COMMON_NAME_INFO)
110 : #undef COMMON_NAME_INFO
111 : #define COMMON_NAME_INFO(name) { #name, sizeof(#name) - 1 },
112 : JS_FOR_EACH_WELL_KNOWN_SYMBOL(COMMON_NAME_INFO)
113 : #undef COMMON_NAME_INFO
114 : #define COMMON_NAME_INFO(name) { "Symbol." #name, sizeof("Symbol." #name) - 1 },
115 : JS_FOR_EACH_WELL_KNOWN_SYMBOL(COMMON_NAME_INFO)
116 : #undef COMMON_NAME_INFO
117 : };
118 :
119 3 : commonNames = cx->new_<JSAtomState>();
120 3 : if (!commonNames)
121 0 : return false;
122 :
123 3 : ImmutablePropertyNamePtr* names = reinterpret_cast<ImmutablePropertyNamePtr*>(commonNames.ref());
124 1428 : for (size_t i = 0; i < ArrayLength(cachedNames); i++, names++) {
125 1425 : JSAtom* atom = Atomize(cx, cachedNames[i].str, cachedNames[i].length, PinAtom);
126 1425 : if (!atom)
127 0 : return false;
128 1425 : names->init(atom->asPropertyName());
129 : }
130 3 : MOZ_ASSERT(uintptr_t(names) == uintptr_t(commonNames + 1));
131 :
132 3 : emptyString = commonNames->empty;
133 :
134 : // Create the well-known symbols.
135 3 : wellKnownSymbols = cx->new_<WellKnownSymbols>();
136 3 : if (!wellKnownSymbols)
137 0 : return false;
138 :
139 3 : ImmutablePropertyNamePtr* descriptions = commonNames->wellKnownSymbolDescriptions();
140 3 : ImmutableSymbolPtr* symbols = reinterpret_cast<ImmutableSymbolPtr*>(wellKnownSymbols.ref());
141 39 : for (size_t i = 0; i < JS::WellKnownSymbolLimit; i++) {
142 36 : JS::Symbol* symbol = JS::Symbol::new_(cx, JS::SymbolCode(i), descriptions[i]);
143 36 : if (!symbol) {
144 0 : ReportOutOfMemory(cx);
145 0 : return false;
146 : }
147 36 : symbols[i].init(symbol);
148 : }
149 :
150 3 : return true;
151 : }
152 :
153 : void
154 0 : JSRuntime::finishAtoms()
155 : {
156 0 : js_delete(atoms_.ref());
157 :
158 0 : if (!parentRuntime) {
159 0 : js_delete(staticStrings.ref());
160 0 : js_delete(commonNames.ref());
161 0 : js_delete(permanentAtoms.ref());
162 0 : js_delete(wellKnownSymbols.ref());
163 : }
164 :
165 0 : atoms_ = nullptr;
166 0 : staticStrings = nullptr;
167 0 : commonNames = nullptr;
168 0 : permanentAtoms = nullptr;
169 0 : wellKnownSymbols = nullptr;
170 0 : emptyString = nullptr;
171 0 : }
172 :
173 : static inline void
174 1 : TracePinnedAtoms(JSTracer* trc, const AtomSet& atoms)
175 : {
176 38981 : for (auto r = atoms.all(); !r.empty(); r.popFront()) {
177 38980 : const AtomStateEntry& entry = r.front();
178 38980 : if (entry.isPinned()) {
179 4802 : JSAtom* atom = entry.asPtrUnbarriered();
180 4802 : TraceRoot(trc, &atom, "interned_atom");
181 4802 : MOZ_ASSERT(entry.asPtrUnbarriered() == atom);
182 : }
183 : }
184 1 : }
185 :
186 : void
187 1 : js::TraceAtoms(JSTracer* trc, AutoLockForExclusiveAccess& lock)
188 : {
189 1 : JSRuntime* rt = trc->runtime();
190 :
191 1 : if (rt->atomsAreFinished())
192 0 : return;
193 :
194 1 : TracePinnedAtoms(trc, rt->atoms(lock));
195 1 : if (rt->atomsAddedWhileSweeping())
196 0 : TracePinnedAtoms(trc, *rt->atomsAddedWhileSweeping());
197 : }
198 :
199 : void
200 1 : js::TracePermanentAtoms(JSTracer* trc)
201 : {
202 1 : JSRuntime* rt = trc->runtime();
203 :
204 : // Permanent atoms only need to be traced in the runtime which owns them.
205 1 : if (rt->parentRuntime)
206 0 : return;
207 :
208 : // Static strings are not included in the permanent atoms table.
209 1 : if (rt->staticStrings)
210 1 : rt->staticStrings->trace(trc);
211 :
212 1 : if (rt->permanentAtoms) {
213 2891 : for (FrozenAtomSet::Range r(rt->permanentAtoms->all()); !r.empty(); r.popFront()) {
214 2890 : const AtomStateEntry& entry = r.front();
215 :
216 2890 : JSAtom* atom = entry.asPtrUnbarriered();
217 2890 : TraceProcessGlobalRoot(trc, atom, "permanent_table");
218 : }
219 : }
220 : }
221 :
222 : void
223 1 : js::TraceWellKnownSymbols(JSTracer* trc)
224 : {
225 1 : JSRuntime* rt = trc->runtime();
226 :
227 1 : if (rt->parentRuntime)
228 0 : return;
229 :
230 1 : if (WellKnownSymbols* wks = rt->wellKnownSymbols) {
231 13 : for (size_t i = 0; i < JS::WellKnownSymbolLimit; i++)
232 12 : TraceProcessGlobalRoot(trc, wks->get(i).get(), "well_known_symbol");
233 : }
234 : }
235 :
236 : bool
237 3 : JSRuntime::transformToPermanentAtoms(JSContext* cx)
238 : {
239 3 : MOZ_ASSERT(!parentRuntime);
240 :
241 : // All static strings were created as permanent atoms, now move the contents
242 : // of the atoms table into permanentAtoms and mark each as permanent.
243 :
244 3 : MOZ_ASSERT(!permanentAtoms);
245 3 : permanentAtoms = cx->new_<FrozenAtomSet>(atoms_); // takes ownership of atoms_
246 :
247 3 : atoms_ = cx->new_<AtomSet>();
248 3 : if (!atoms_ || !atoms_->init(JS_STRING_HASH_COUNT))
249 0 : return false;
250 :
251 8673 : for (FrozenAtomSet::Range r(permanentAtoms->all()); !r.empty(); r.popFront()) {
252 8670 : AtomStateEntry entry = r.front();
253 8670 : JSAtom* atom = entry.asPtr(cx);
254 8670 : atom->morphIntoPermanentAtom();
255 : }
256 :
257 3 : return true;
258 : }
259 :
260 : static inline AtomSet::Ptr
261 487368 : LookupAtomState(JSRuntime* rt, const AtomHasher::Lookup& lookup)
262 : {
263 487368 : MOZ_ASSERT(rt->currentThreadHasExclusiveAccess());
264 :
265 487368 : AtomSet::Ptr p = rt->unsafeAtoms().lookup(lookup); // Safe because we hold the lock.
266 487368 : if (!p && rt->atomsAddedWhileSweeping())
267 0 : p = rt->atomsAddedWhileSweeping()->lookup(lookup);
268 487367 : return p;
269 : }
270 :
271 : bool
272 32096 : AtomIsPinned(JSContext* cx, JSAtom* atom)
273 : {
274 : /* We treat static strings as interned because they're never collected. */
275 32096 : if (StaticStrings::isStatic(atom))
276 24 : return true;
277 :
278 64144 : AtomHasher::Lookup lookup(atom);
279 :
280 : /* Likewise, permanent strings are considered to be interned. */
281 32072 : MOZ_ASSERT(cx->isPermanentAtomsInitialized());
282 32072 : AtomSet::Ptr p = cx->permanentAtoms().readonlyThreadsafeLookup(lookup);
283 32072 : if (p)
284 676 : return true;
285 :
286 62792 : AutoLockForExclusiveAccess lock(cx);
287 :
288 31396 : p = LookupAtomState(cx->runtime(), lookup);
289 31396 : if (!p)
290 0 : return false;
291 :
292 31396 : return p->isPinned();
293 : }
294 :
295 : #ifdef DEBUG
296 :
297 : bool
298 455925 : AtomIsPinnedInRuntime(JSRuntime* rt, JSAtom* atom)
299 : {
300 911883 : Maybe<AutoLockForExclusiveAccess> lock;
301 455926 : if (!rt->currentThreadHasExclusiveAccess())
302 454644 : lock.emplace(rt);
303 :
304 911913 : AtomHasher::Lookup lookup(atom);
305 :
306 455958 : AtomSet::Ptr p = LookupAtomState(rt, lookup);
307 455957 : MOZ_ASSERT(p);
308 :
309 911913 : return p->isPinned();
310 : }
311 :
312 : #endif // DEBUG
313 :
314 : /* |tbchars| must not point into an inline or short string. */
315 : template <typename CharT>
316 : MOZ_ALWAYS_INLINE
317 : static JSAtom*
318 448576 : AtomizeAndCopyChars(JSContext* cx, const CharT* tbchars, size_t length, PinningBehavior pin,
319 : const Maybe<uint32_t>& indexValue)
320 : {
321 448576 : if (JSAtom* s = cx->staticStrings().lookup(tbchars, length))
322 23859 : return s;
323 :
324 849417 : AtomHasher::Lookup lookup(tbchars, length);
325 :
326 : // Try the per-Zone cache first. If we find the atom there we can avoid the
327 : // atoms lock, the markAtom call, and the multiple HashSet lookups below.
328 : // We don't use the per-Zone cache if we want a pinned atom: handling that
329 : // is more complicated and pinning atoms is relatively uncommon.
330 424718 : Zone* zone = cx->zone();
331 849421 : Maybe<AtomSet::AddPtr> zonePtr;
332 424718 : if (MOZ_LIKELY(zone && pin == DoNotPinAtom)) {
333 403485 : zonePtr.emplace(zone->atomCache().lookupForAdd(lookup));
334 403486 : if (zonePtr.ref()) {
335 : // The cache is purged on GC so if we're in the middle of an
336 : // incremental GC we should have barriered the atom when we put
337 : // it in the cache.
338 322785 : JSAtom* atom = zonePtr.ref()->asPtrUnbarriered();
339 322784 : MOZ_ASSERT(AtomIsMarked(zone, atom));
340 322771 : return atom;
341 : }
342 : }
343 :
344 : // Note: when this function is called while the permanent atoms table is
345 : // being initialized (in initializeAtoms()), |permanentAtoms| is not yet
346 : // initialized so this lookup is always skipped. Only once
347 : // transformToPermanentAtoms() is called does |permanentAtoms| get
348 : // initialized and then this lookup will go ahead.
349 101932 : if (cx->isPermanentAtomsInitialized()) {
350 92677 : AtomSet::Ptr pp = cx->permanentAtoms().readonlyThreadsafeLookup(lookup);
351 92676 : if (pp) {
352 6677 : JSAtom* atom = pp->asPtr(cx);
353 6677 : if (zonePtr)
354 6130 : mozilla::Unused << zone->atomCache().add(*zonePtr, AtomStateEntry(atom, false));
355 6677 : return atom;
356 : }
357 : }
358 :
359 : // Validate the length before taking the exclusive access lock, as throwing
360 : // an exception here may reenter this code.
361 95254 : if (MOZ_UNLIKELY(!JSString::validateLength(cx, length)))
362 0 : return nullptr;
363 :
364 190509 : AutoLockForExclusiveAccess lock(cx);
365 :
366 95255 : JSRuntime* rt = cx->runtime();
367 95255 : AtomSet& atoms = rt->atoms(lock);
368 95255 : AtomSet* atomsAddedWhileSweeping = rt->atomsAddedWhileSweeping();
369 95255 : AtomSet::AddPtr p;
370 :
371 95255 : if (!atomsAddedWhileSweeping) {
372 95255 : p = atoms.lookupForAdd(lookup);
373 : } else {
374 : // We're currently sweeping the main atoms table and all new atoms will
375 : // be added to a secondary table. Check this first.
376 0 : MOZ_ASSERT(rt->atomsZone(lock)->isGCSweeping());
377 0 : p = atomsAddedWhileSweeping->lookupForAdd(lookup);
378 :
379 : // If that fails check the main table but check if any atom found there
380 : // is dead.
381 0 : if (!p) {
382 0 : if (AtomSet::AddPtr p2 = atoms.lookupForAdd(lookup)) {
383 0 : JSAtom* atom = p2->asPtrUnbarriered();
384 0 : if (!IsAboutToBeFinalizedUnbarriered(&atom))
385 0 : p = p2;
386 : }
387 : }
388 : }
389 :
390 95255 : if (p) {
391 32693 : JSAtom* atom = p->asPtr(cx);
392 32693 : p->setPinned(bool(pin));
393 32693 : cx->atomMarking().inlinedMarkAtom(cx, atom);
394 32693 : if (zonePtr)
395 18439 : mozilla::Unused << zone->atomCache().add(*zonePtr, AtomStateEntry(atom, false));
396 32693 : return atom;
397 : }
398 :
399 : JSAtom* atom;
400 : {
401 125124 : AutoAtomsCompartment ac(cx, lock);
402 :
403 62562 : JSFlatString* flat = NewStringCopyN<NoGC>(cx, tbchars, length);
404 62562 : if (!flat) {
405 : // Grudgingly forgo last-ditch GC. The alternative would be to release
406 : // the lock, manually GC here, and retry from the top. If you fix this,
407 : // please also fix or comment the similar case in Symbol::new_.
408 0 : ReportOutOfMemory(cx);
409 0 : return nullptr;
410 : }
411 :
412 62562 : atom = flat->morphAtomizedStringIntoAtom(lookup.hash);
413 62562 : MOZ_ASSERT(atom->hash() == lookup.hash);
414 :
415 62562 : if (indexValue)
416 429 : atom->maybeInitializeIndex(*indexValue, true);
417 :
418 : // We have held the lock since looking up p, and the operations we've done
419 : // since then can't GC; therefore the atoms table has not been modified and
420 : // p is still valid.
421 62562 : AtomSet* addSet = atomsAddedWhileSweeping ? atomsAddedWhileSweeping : &atoms;
422 62562 : if (!addSet->add(p, AtomStateEntry(atom, bool(pin)))) {
423 0 : ReportOutOfMemory(cx); /* SystemAllocPolicy does not report OOM. */
424 0 : return nullptr;
425 : }
426 : }
427 :
428 62562 : cx->atomMarking().inlinedMarkAtom(cx, atom);
429 62562 : if (zonePtr)
430 56130 : mozilla::Unused << zone->atomCache().add(*zonePtr, AtomStateEntry(atom, false));
431 62562 : return atom;
432 : }
433 :
434 : template JSAtom*
435 : AtomizeAndCopyChars(JSContext* cx, const char16_t* tbchars, size_t length, PinningBehavior pin,
436 : const Maybe<uint32_t>& indexValue);
437 :
438 : template JSAtom*
439 : AtomizeAndCopyChars(JSContext* cx, const Latin1Char* tbchars, size_t length, PinningBehavior pin,
440 : const Maybe<uint32_t>& indexValue);
441 :
442 : JSAtom*
443 9975 : js::AtomizeString(JSContext* cx, JSString* str,
444 : js::PinningBehavior pin /* = js::DoNotPinAtom */)
445 : {
446 9975 : if (str->isAtom()) {
447 3155 : JSAtom& atom = str->asAtom();
448 : /* N.B. static atoms are effectively always interned. */
449 3155 : if (pin != PinAtom || js::StaticStrings::isStatic(&atom))
450 3141 : return &atom;
451 :
452 28 : AtomHasher::Lookup lookup(&atom);
453 :
454 : /* Likewise, permanent atoms are always interned. */
455 14 : MOZ_ASSERT(cx->isPermanentAtomsInitialized());
456 14 : AtomSet::Ptr p = cx->permanentAtoms().readonlyThreadsafeLookup(lookup);
457 14 : if (p)
458 0 : return &atom;
459 :
460 28 : AutoLockForExclusiveAccess lock(cx);
461 :
462 14 : p = LookupAtomState(cx->runtime(), lookup);
463 14 : MOZ_ASSERT(p); /* Non-static atom must exist in atom state set. */
464 14 : MOZ_ASSERT(p->asPtrUnbarriered() == &atom);
465 14 : MOZ_ASSERT(pin == PinAtom);
466 14 : p->setPinned(bool(pin));
467 14 : return &atom;
468 : }
469 :
470 6820 : JSLinearString* linear = str->ensureLinear(cx);
471 6820 : if (!linear)
472 0 : return nullptr;
473 :
474 13640 : Maybe<uint32_t> indexValue;
475 6820 : if (str->hasIndexValue())
476 0 : indexValue.emplace(str->getIndexValue());
477 :
478 13640 : JS::AutoCheckCannotGC nogc;
479 6820 : return linear->hasLatin1Chars()
480 7113 : ? AtomizeAndCopyChars(cx, linear->latin1Chars(nogc), linear->length(), pin, indexValue)
481 7113 : : AtomizeAndCopyChars(cx, linear->twoByteChars(nogc), linear->length(), pin, indexValue);
482 : }
483 :
484 : JSAtom*
485 105571 : js::Atomize(JSContext* cx, const char* bytes, size_t length, PinningBehavior pin,
486 : const Maybe<uint32_t>& indexValue)
487 : {
488 211142 : CHECK_REQUEST(cx);
489 :
490 105570 : const Latin1Char* chars = reinterpret_cast<const Latin1Char*>(bytes);
491 211141 : return AtomizeAndCopyChars(cx, chars, length, pin, indexValue);
492 : }
493 :
494 : template <typename CharT>
495 : JSAtom*
496 336185 : js::AtomizeChars(JSContext* cx, const CharT* chars, size_t length, PinningBehavior pin)
497 : {
498 672373 : CHECK_REQUEST(cx);
499 672374 : return AtomizeAndCopyChars(cx, chars, length, pin, Nothing());
500 : }
501 :
502 : template JSAtom*
503 : js::AtomizeChars(JSContext* cx, const Latin1Char* chars, size_t length, PinningBehavior pin);
504 :
505 : template JSAtom*
506 : js::AtomizeChars(JSContext* cx, const char16_t* chars, size_t length, PinningBehavior pin);
507 :
508 : JSAtom*
509 0 : js::AtomizeUTF8Chars(JSContext* cx, const char* utf8Chars, size_t utf8ByteLength)
510 : {
511 : // This could be optimized to hand the char16_t's directly to the JSAtom
512 : // instead of making a copy. UTF8CharsToNewTwoByteCharsZ should be
513 : // refactored to take an JSContext so that this function could also.
514 :
515 0 : UTF8Chars utf8(utf8Chars, utf8ByteLength);
516 :
517 : size_t length;
518 0 : UniqueTwoByteChars chars(JS::UTF8CharsToNewTwoByteCharsZ(cx, utf8, &length).get());
519 0 : if (!chars)
520 0 : return nullptr;
521 :
522 0 : return AtomizeChars(cx, chars.get(), length);
523 : }
524 :
525 : bool
526 0 : js::IndexToIdSlow(JSContext* cx, uint32_t index, MutableHandleId idp)
527 : {
528 0 : MOZ_ASSERT(index > JSID_INT_MAX);
529 :
530 : char16_t buf[UINT32_CHAR_BUFFER_LENGTH];
531 0 : RangedPtr<char16_t> end(ArrayEnd(buf), buf, ArrayEnd(buf));
532 0 : RangedPtr<char16_t> start = BackfillIndexInCharBuffer(index, end);
533 :
534 0 : JSAtom* atom = AtomizeChars(cx, start.get(), end - start);
535 0 : if (!atom)
536 0 : return false;
537 :
538 0 : idp.set(JSID_FROM_BITS((size_t)atom));
539 0 : return true;
540 : }
541 :
542 : template <AllowGC allowGC>
543 : static JSAtom*
544 34 : ToAtomSlow(JSContext* cx, typename MaybeRooted<Value, allowGC>::HandleType arg)
545 : {
546 34 : MOZ_ASSERT(!arg.isString());
547 :
548 34 : Value v = arg;
549 34 : if (!v.isPrimitive()) {
550 0 : MOZ_ASSERT(!cx->helperThread());
551 : if (!allowGC)
552 0 : return nullptr;
553 0 : RootedValue v2(cx, v);
554 0 : if (!ToPrimitive(cx, JSTYPE_STRING, &v2))
555 0 : return nullptr;
556 0 : v = v2;
557 : }
558 :
559 34 : if (v.isString()) {
560 0 : JSAtom* atom = AtomizeString(cx, v.toString());
561 0 : if (!allowGC && !atom)
562 0 : cx->recoverFromOutOfMemory();
563 0 : return atom;
564 : }
565 34 : if (v.isInt32()) {
566 0 : JSAtom* atom = Int32ToAtom(cx, v.toInt32());
567 0 : if (!allowGC && !atom)
568 0 : cx->recoverFromOutOfMemory();
569 0 : return atom;
570 : }
571 34 : if (v.isDouble()) {
572 25 : JSAtom* atom = NumberToAtom(cx, v.toDouble());
573 0 : if (!allowGC && !atom)
574 0 : cx->recoverFromOutOfMemory();
575 25 : return atom;
576 : }
577 9 : if (v.isBoolean())
578 2 : return v.toBoolean() ? cx->names().true_ : cx->names().false_;
579 7 : if (v.isNull())
580 0 : return cx->names().null;
581 7 : if (v.isSymbol()) {
582 0 : MOZ_ASSERT(!cx->helperThread());
583 : if (allowGC) {
584 0 : JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr,
585 : JSMSG_SYMBOL_TO_STRING);
586 : }
587 0 : return nullptr;
588 : }
589 7 : MOZ_ASSERT(v.isUndefined());
590 7 : return cx->names().undefined;
591 : }
592 :
593 : template <AllowGC allowGC>
594 : JSAtom*
595 18930 : js::ToAtom(JSContext* cx, typename MaybeRooted<Value, allowGC>::HandleType v)
596 : {
597 18930 : if (!v.isString())
598 34 : return ToAtomSlow<allowGC>(cx, v);
599 :
600 18896 : JSString* str = v.toString();
601 18896 : if (str->isAtom())
602 14339 : return &str->asAtom();
603 :
604 4557 : JSAtom* atom = AtomizeString(cx, str);
605 1 : if (!atom && !allowGC) {
606 0 : MOZ_ASSERT_IF(!cx->helperThread(), cx->isThrowingOutOfMemory());
607 0 : cx->recoverFromOutOfMemory();
608 : }
609 4557 : return atom;
610 : }
611 :
612 : template JSAtom*
613 : js::ToAtom<CanGC>(JSContext* cx, HandleValue v);
614 :
615 : template JSAtom*
616 : js::ToAtom<NoGC>(JSContext* cx, const Value& v);
617 :
618 : template<XDRMode mode>
619 : bool
620 185709 : js::XDRAtom(XDRState<mode>* xdr, MutableHandleAtom atomp)
621 : {
622 : if (mode == XDR_ENCODE) {
623 : static_assert(JSString::MAX_LENGTH <= INT32_MAX, "String length must fit in 31 bits");
624 44010 : uint32_t length = atomp->length();
625 44010 : uint32_t lengthAndEncoding = (length << 1) | uint32_t(atomp->hasLatin1Chars());
626 44010 : if (!xdr->codeUint32(&lengthAndEncoding))
627 0 : return false;
628 :
629 88020 : JS::AutoCheckCannotGC nogc;
630 44010 : return atomp->hasLatin1Chars()
631 44010 : ? xdr->codeChars(atomp->latin1Chars(nogc), length)
632 44010 : : xdr->codeChars(const_cast<char16_t*>(atomp->twoByteChars(nogc)), length);
633 : }
634 :
635 : /* Avoid JSString allocation for already existing atoms. See bug 321985. */
636 : uint32_t lengthAndEncoding;
637 141699 : if (!xdr->codeUint32(&lengthAndEncoding))
638 0 : return false;
639 :
640 141700 : uint32_t length = lengthAndEncoding >> 1;
641 141700 : bool latin1 = lengthAndEncoding & 0x1;
642 :
643 141700 : JSContext* cx = xdr->cx();
644 : JSAtom* atom;
645 141700 : if (latin1) {
646 141692 : const Latin1Char* chars = nullptr;
647 141692 : if (length)
648 141301 : chars = reinterpret_cast<const Latin1Char*>(xdr->buf.read(length));
649 141694 : atom = AtomizeChars(cx, chars, length);
650 : } else {
651 : #if MOZ_LITTLE_ENDIAN
652 : /* Directly access the little endian chars in the XDR buffer. */
653 8 : const char16_t* chars = nullptr;
654 8 : if (length)
655 8 : chars = reinterpret_cast<const char16_t*>(xdr->buf.read(length * sizeof(char16_t)));
656 8 : atom = AtomizeChars(cx, chars, length);
657 : #else
658 : /*
659 : * We must copy chars to a temporary buffer to convert between little and
660 : * big endian data.
661 : */
662 : char16_t* chars;
663 : char16_t stackChars[256];
664 : if (length <= ArrayLength(stackChars)) {
665 : chars = stackChars;
666 : } else {
667 : /*
668 : * This is very uncommon. Don't use the tempLifoAlloc arena for this as
669 : * most allocations here will be bigger than tempLifoAlloc's default
670 : * chunk size.
671 : */
672 : chars = cx->pod_malloc<char16_t>(length);
673 : if (!chars)
674 : return false;
675 : }
676 :
677 : JS_ALWAYS_TRUE(xdr->codeChars(chars, length));
678 : atom = AtomizeChars(cx, chars, length);
679 : if (chars != stackChars)
680 : js_free(chars);
681 : #endif /* !MOZ_LITTLE_ENDIAN */
682 : }
683 :
684 141700 : if (!atom)
685 0 : return false;
686 141700 : atomp.set(atom);
687 141700 : return true;
688 : }
689 :
690 : template bool
691 : js::XDRAtom(XDRState<XDR_ENCODE>* xdr, MutableHandleAtom atomp);
692 :
693 : template bool
694 : js::XDRAtom(XDRState<XDR_DECODE>* xdr, MutableHandleAtom atomp);
|