| |
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 |
| |
16 #include <string.h> |
| |
17 |
| |
18 #include "jscntxt.h" |
| |
19 #include "jsstr.h" |
| |
20 #include "jstypes.h" |
| |
21 |
| |
22 #include "gc/Marking.h" |
| |
23 #include "vm/Xdr.h" |
| |
24 |
| |
25 #include "jscntxtinlines.h" |
| |
26 #include "jscompartmentinlines.h" |
| |
27 #include "jsobjinlines.h" |
| |
28 |
| |
29 #include "vm/String-inl.h" |
| |
30 |
| |
31 using namespace js; |
| |
32 using namespace js::gc; |
| |
33 |
| |
34 using mozilla::ArrayEnd; |
| |
35 using mozilla::ArrayLength; |
| |
36 using mozilla::RangedPtr; |
| |
37 |
| |
38 const char * |
| |
39 js::AtomToPrintableString(ExclusiveContext *cx, JSAtom *atom, JSAutoByteString *bytes) |
| |
40 { |
| |
41 JSString *str = js_QuoteString(cx, atom, 0); |
| |
42 if (!str) |
| |
43 return nullptr; |
| |
44 return bytes->encodeLatin1(cx, str); |
| |
45 } |
| |
46 |
| |
47 const char * const js::TypeStrings[] = { |
| |
48 js_undefined_str, |
| |
49 js_object_str, |
| |
50 js_function_str, |
| |
51 js_string_str, |
| |
52 js_number_str, |
| |
53 js_boolean_str, |
| |
54 js_null_str, |
| |
55 }; |
| |
56 |
| |
57 #define DEFINE_PROTO_STRING(name,code,init,clasp) const char js_##name##_str[] = #name; |
| |
58 JS_FOR_EACH_PROTOTYPE(DEFINE_PROTO_STRING) |
| |
59 #undef DEFINE_PROTO_STRING |
| |
60 |
| |
61 #define CONST_CHAR_STR(idpart, id, text) const char js_##idpart##_str[] = text; |
| |
62 FOR_EACH_COMMON_PROPERTYNAME(CONST_CHAR_STR) |
| |
63 #undef CONST_CHAR_STR |
| |
64 |
| |
65 /* Constant strings that are not atomized. */ |
| |
66 const char js_break_str[] = "break"; |
| |
67 const char js_case_str[] = "case"; |
| |
68 const char js_catch_str[] = "catch"; |
| |
69 const char js_class_str[] = "class"; |
| |
70 const char js_close_str[] = "close"; |
| |
71 const char js_const_str[] = "const"; |
| |
72 const char js_continue_str[] = "continue"; |
| |
73 const char js_debugger_str[] = "debugger"; |
| |
74 const char js_default_str[] = "default"; |
| |
75 const char js_do_str[] = "do"; |
| |
76 const char js_else_str[] = "else"; |
| |
77 const char js_enum_str[] = "enum"; |
| |
78 const char js_export_str[] = "export"; |
| |
79 const char js_extends_str[] = "extends"; |
| |
80 const char js_finally_str[] = "finally"; |
| |
81 const char js_for_str[] = "for"; |
| |
82 const char js_getter_str[] = "getter"; |
| |
83 const char js_if_str[] = "if"; |
| |
84 const char js_implements_str[] = "implements"; |
| |
85 const char js_import_str[] = "import"; |
| |
86 const char js_in_str[] = "in"; |
| |
87 const char js_instanceof_str[] = "instanceof"; |
| |
88 const char js_interface_str[] = "interface"; |
| |
89 const char js_new_str[] = "new"; |
| |
90 const char js_package_str[] = "package"; |
| |
91 const char js_private_str[] = "private"; |
| |
92 const char js_protected_str[] = "protected"; |
| |
93 const char js_public_str[] = "public"; |
| |
94 const char js_send_str[] = "send"; |
| |
95 const char js_setter_str[] = "setter"; |
| |
96 const char js_static_str[] = "static"; |
| |
97 const char js_super_str[] = "super"; |
| |
98 const char js_switch_str[] = "switch"; |
| |
99 const char js_this_str[] = "this"; |
| |
100 const char js_try_str[] = "try"; |
| |
101 const char js_typeof_str[] = "typeof"; |
| |
102 const char js_void_str[] = "void"; |
| |
103 const char js_while_str[] = "while"; |
| |
104 const char js_with_str[] = "with"; |
| |
105 |
| |
106 // Use a low initial capacity for atom hash tables to avoid penalizing runtimes |
| |
107 // which create a small number of atoms. |
| |
108 static const uint32_t JS_STRING_HASH_COUNT = 64; |
| |
109 |
| |
110 struct CommonNameInfo |
| |
111 { |
| |
112 const char *str; |
| |
113 size_t length; |
| |
114 }; |
| |
115 |
| |
116 bool |
| |
117 JSRuntime::initializeAtoms(JSContext *cx) |
| |
118 { |
| |
119 atoms_ = cx->new_<AtomSet>(); |
| |
120 if (!atoms_ || !atoms_->init(JS_STRING_HASH_COUNT)) |
| |
121 return false; |
| |
122 |
| |
123 if (parentRuntime) { |
| |
124 staticStrings = parentRuntime->staticStrings; |
| |
125 commonNames = parentRuntime->commonNames; |
| |
126 emptyString = parentRuntime->emptyString; |
| |
127 permanentAtoms = parentRuntime->permanentAtoms; |
| |
128 return true; |
| |
129 } |
| |
130 |
| |
131 permanentAtoms = cx->new_<AtomSet>(); |
| |
132 if (!permanentAtoms || !permanentAtoms->init(JS_STRING_HASH_COUNT)) |
| |
133 return false; |
| |
134 |
| |
135 staticStrings = cx->new_<StaticStrings>(); |
| |
136 if (!staticStrings || !staticStrings->init(cx)) |
| |
137 return false; |
| |
138 |
| |
139 static const CommonNameInfo cachedNames[] = { |
| |
140 #define COMMON_NAME_INFO(idpart, id, text) { js_##idpart##_str, sizeof(text) - 1 }, |
| |
141 FOR_EACH_COMMON_PROPERTYNAME(COMMON_NAME_INFO) |
| |
142 #undef COMMON_NAME_INFO |
| |
143 #define COMMON_NAME_INFO(name, code, init, clasp) { js_##name##_str, sizeof(#name) - 1 }, |
| |
144 JS_FOR_EACH_PROTOTYPE(COMMON_NAME_INFO) |
| |
145 #undef COMMON_NAME_INFO |
| |
146 }; |
| |
147 |
| |
148 commonNames = cx->new_<JSAtomState>(); |
| |
149 if (!commonNames) |
| |
150 return false; |
| |
151 |
| |
152 FixedHeapPtr<PropertyName> *names = reinterpret_cast<FixedHeapPtr<PropertyName> *>(commonNames); |
| |
153 for (size_t i = 0; i < ArrayLength(cachedNames); i++, names++) { |
| |
154 JSAtom *atom = Atomize(cx, cachedNames[i].str, cachedNames[i].length, InternAtom); |
| |
155 if (!atom) |
| |
156 return false; |
| |
157 names->init(atom->asPropertyName()); |
| |
158 } |
| |
159 JS_ASSERT(uintptr_t(names) == uintptr_t(commonNames + 1)); |
| |
160 |
| |
161 emptyString = commonNames->empty; |
| |
162 return true; |
| |
163 } |
| |
164 |
| |
165 void |
| |
166 JSRuntime::finishAtoms() |
| |
167 { |
| |
168 if (atoms_) |
| |
169 js_delete(atoms_); |
| |
170 |
| |
171 if (!parentRuntime) { |
| |
172 if (staticStrings) |
| |
173 js_delete(staticStrings); |
| |
174 |
| |
175 if (commonNames) |
| |
176 js_delete(commonNames); |
| |
177 |
| |
178 if (permanentAtoms) |
| |
179 js_delete(permanentAtoms); |
| |
180 } |
| |
181 |
| |
182 atoms_ = nullptr; |
| |
183 staticStrings = nullptr; |
| |
184 commonNames = nullptr; |
| |
185 permanentAtoms = nullptr; |
| |
186 emptyString = nullptr; |
| |
187 } |
| |
188 |
| |
189 void |
| |
190 js::MarkAtoms(JSTracer *trc) |
| |
191 { |
| |
192 JSRuntime *rt = trc->runtime(); |
| |
193 for (AtomSet::Enum e(rt->atoms()); !e.empty(); e.popFront()) { |
| |
194 const AtomStateEntry &entry = e.front(); |
| |
195 if (!entry.isTagged()) |
| |
196 continue; |
| |
197 |
| |
198 JSAtom *atom = entry.asPtr(); |
| |
199 bool tagged = entry.isTagged(); |
| |
200 MarkStringRoot(trc, &atom, "interned_atom"); |
| |
201 if (entry.asPtr() != atom) |
| |
202 e.rekeyFront(AtomHasher::Lookup(atom), AtomStateEntry(atom, tagged)); |
| |
203 } |
| |
204 } |
| |
205 |
| |
206 void |
| |
207 js::MarkPermanentAtoms(JSTracer *trc) |
| |
208 { |
| |
209 JSRuntime *rt = trc->runtime(); |
| |
210 |
| |
211 // Permanent atoms only need to be marked in the runtime which owns them. |
| |
212 if (rt->parentRuntime) |
| |
213 return; |
| |
214 |
| |
215 // Static strings are not included in the permanent atoms table. |
| |
216 if (rt->staticStrings) |
| |
217 rt->staticStrings->trace(trc); |
| |
218 |
| |
219 if (rt->permanentAtoms) { |
| |
220 for (AtomSet::Enum e(*rt->permanentAtoms); !e.empty(); e.popFront()) { |
| |
221 const AtomStateEntry &entry = e.front(); |
| |
222 |
| |
223 JSAtom *atom = entry.asPtr(); |
| |
224 MarkPermanentAtom(trc, atom, "permanent_table"); |
| |
225 } |
| |
226 } |
| |
227 } |
| |
228 |
| |
229 void |
| |
230 JSRuntime::sweepAtoms() |
| |
231 { |
| |
232 if (!atoms_) |
| |
233 return; |
| |
234 |
| |
235 for (AtomSet::Enum e(*atoms_); !e.empty(); e.popFront()) { |
| |
236 AtomStateEntry entry = e.front(); |
| |
237 JSAtom *atom = entry.asPtr(); |
| |
238 bool isDying = IsStringAboutToBeFinalized(&atom); |
| |
239 |
| |
240 /* Pinned or interned key cannot be finalized. */ |
| |
241 JS_ASSERT_IF(hasContexts() && entry.isTagged(), !isDying); |
| |
242 |
| |
243 if (isDying) |
| |
244 e.removeFront(); |
| |
245 } |
| |
246 } |
| |
247 |
| |
248 bool |
| |
249 JSRuntime::transformToPermanentAtoms() |
| |
250 { |
| |
251 JS_ASSERT(!parentRuntime); |
| |
252 |
| |
253 // All static strings were created as permanent atoms, now move the contents |
| |
254 // of the atoms table into permanentAtoms and mark each as permanent. |
| |
255 |
| |
256 JS_ASSERT(permanentAtoms && permanentAtoms->empty()); |
| |
257 |
| |
258 AtomSet *temp = atoms_; |
| |
259 atoms_ = permanentAtoms; |
| |
260 permanentAtoms = temp; |
| |
261 |
| |
262 for (AtomSet::Enum e(*permanentAtoms); !e.empty(); e.popFront()) { |
| |
263 AtomStateEntry entry = e.front(); |
| |
264 JSAtom *atom = entry.asPtr(); |
| |
265 atom->morphIntoPermanentAtom(); |
| |
266 } |
| |
267 |
| |
268 return true; |
| |
269 } |
| |
270 |
| |
271 bool |
| |
272 AtomIsInterned(JSContext *cx, JSAtom *atom) |
| |
273 { |
| |
274 /* We treat static strings as interned because they're never collected. */ |
| |
275 if (StaticStrings::isStatic(atom)) |
| |
276 return true; |
| |
277 |
| |
278 AtomHasher::Lookup lookup(atom); |
| |
279 |
| |
280 /* Likewise, permanent strings are considered to be interned. */ |
| |
281 AtomSet::Ptr p = cx->permanentAtoms().readonlyThreadsafeLookup(lookup); |
| |
282 if (p) |
| |
283 return true; |
| |
284 |
| |
285 AutoLockForExclusiveAccess lock(cx); |
| |
286 |
| |
287 p = cx->runtime()->atoms().lookup(lookup); |
| |
288 if (!p) |
| |
289 return false; |
| |
290 |
| |
291 return p->isTagged(); |
| |
292 } |
| |
293 |
| |
294 /* |
| |
295 * When the jschars reside in a freshly allocated buffer the memory can be used |
| |
296 * as a new JSAtom's storage without copying. The contract is that the caller no |
| |
297 * longer owns the memory and this method is responsible for freeing the memory. |
| |
298 */ |
| |
299 MOZ_ALWAYS_INLINE |
| |
300 static JSAtom * |
| |
301 AtomizeAndtake(ExclusiveContext *cx, jschar *tbchars, size_t length, InternBehavior ib) |
| |
302 { |
| |
303 JS_ASSERT(tbchars[length] == 0); |
| |
304 |
| |
305 if (JSAtom *s = cx->staticStrings().lookup(tbchars, length)) { |
| |
306 js_free(tbchars); |
| |
307 return s; |
| |
308 } |
| |
309 |
| |
310 AtomHasher::Lookup lookup(tbchars, length); |
| |
311 |
| |
312 AtomSet::Ptr pp = cx->permanentAtoms().readonlyThreadsafeLookup(lookup); |
| |
313 if (pp) { |
| |
314 js_free(tbchars); |
| |
315 return pp->asPtr(); |
| |
316 } |
| |
317 |
| |
318 AutoLockForExclusiveAccess lock(cx); |
| |
319 |
| |
320 /* |
| |
321 * If a GC occurs at js_NewStringCopy then |p| will still have the correct |
| |
322 * hash, allowing us to avoid rehashing it. Even though the hash is |
| |
323 * unchanged, we need to re-lookup the table position because a last-ditch |
| |
324 * GC will potentially free some table entries. |
| |
325 */ |
| |
326 AtomSet& atoms = cx->atoms(); |
| |
327 AtomSet::AddPtr p = atoms.lookupForAdd(lookup); |
| |
328 if (p) { |
| |
329 JSAtom *atom = p->asPtr(); |
| |
330 p->setTagged(bool(ib)); |
| |
331 js_free(tbchars); |
| |
332 return atom; |
| |
333 } |
| |
334 |
| |
335 AutoCompartment ac(cx, cx->atomsCompartment()); |
| |
336 |
| |
337 JSFlatString *flat = js_NewString<NoGC>(cx, tbchars, length); |
| |
338 if (!flat) { |
| |
339 js_free(tbchars); |
| |
340 js_ReportOutOfMemory(cx); |
| |
341 return nullptr; |
| |
342 } |
| |
343 |
| |
344 JSAtom *atom = flat->morphAtomizedStringIntoAtom(); |
| |
345 |
| |
346 if (!atoms.relookupOrAdd(p, lookup, AtomStateEntry(atom, bool(ib)))) { |
| |
347 js_ReportOutOfMemory(cx); /* SystemAllocPolicy does not report OOM. */ |
| |
348 return nullptr; |
| |
349 } |
| |
350 |
| |
351 return atom; |
| |
352 } |
| |
353 |
| |
354 /* |tbchars| must not point into an inline or short string. */ |
| |
355 MOZ_ALWAYS_INLINE |
| |
356 static JSAtom * |
| |
357 AtomizeAndCopyChars(ExclusiveContext *cx, const jschar *tbchars, size_t length, InternBehavior ib) |
| |
358 { |
| |
359 if (JSAtom *s = cx->staticStrings().lookup(tbchars, length)) |
| |
360 return s; |
| |
361 |
| |
362 AtomHasher::Lookup lookup(tbchars, length); |
| |
363 |
| |
364 AtomSet::Ptr pp = cx->permanentAtoms().readonlyThreadsafeLookup(lookup); |
| |
365 if (pp) |
| |
366 return pp->asPtr(); |
| |
367 |
| |
368 /* |
| |
369 * If a GC occurs at js_NewStringCopy then |p| will still have the correct |
| |
370 * hash, allowing us to avoid rehashing it. Even though the hash is |
| |
371 * unchanged, we need to re-lookup the table position because a last-ditch |
| |
372 * GC will potentially free some table entries. |
| |
373 */ |
| |
374 |
| |
375 AutoLockForExclusiveAccess lock(cx); |
| |
376 |
| |
377 AtomSet& atoms = cx->atoms(); |
| |
378 AtomSet::AddPtr p = atoms.lookupForAdd(lookup); |
| |
379 if (p) { |
| |
380 JSAtom *atom = p->asPtr(); |
| |
381 p->setTagged(bool(ib)); |
| |
382 return atom; |
| |
383 } |
| |
384 |
| |
385 AutoCompartment ac(cx, cx->atomsCompartment()); |
| |
386 |
| |
387 JSFlatString *flat = js_NewStringCopyN<NoGC>(cx, tbchars, length); |
| |
388 if (!flat) { |
| |
389 js_ReportOutOfMemory(cx); |
| |
390 return nullptr; |
| |
391 } |
| |
392 |
| |
393 JSAtom *atom = flat->morphAtomizedStringIntoAtom(); |
| |
394 |
| |
395 if (!atoms.relookupOrAdd(p, lookup, AtomStateEntry(atom, bool(ib)))) { |
| |
396 js_ReportOutOfMemory(cx); /* SystemAllocPolicy does not report OOM. */ |
| |
397 return nullptr; |
| |
398 } |
| |
399 |
| |
400 return atom; |
| |
401 } |
| |
402 |
| |
403 JSAtom * |
| |
404 js::AtomizeString(ExclusiveContext *cx, JSString *str, |
| |
405 js::InternBehavior ib /* = js::DoNotInternAtom */) |
| |
406 { |
| |
407 if (str->isAtom()) { |
| |
408 JSAtom &atom = str->asAtom(); |
| |
409 /* N.B. static atoms are effectively always interned. */ |
| |
410 if (ib != InternAtom || js::StaticStrings::isStatic(&atom)) |
| |
411 return &atom; |
| |
412 |
| |
413 AtomHasher::Lookup lookup(&atom); |
| |
414 |
| |
415 /* Likewise, permanent atoms are always interned. */ |
| |
416 AtomSet::Ptr p = cx->permanentAtoms().readonlyThreadsafeLookup(lookup); |
| |
417 if (p) |
| |
418 return &atom; |
| |
419 |
| |
420 AutoLockForExclusiveAccess lock(cx); |
| |
421 |
| |
422 p = cx->atoms().lookup(lookup); |
| |
423 JS_ASSERT(p); /* Non-static atom must exist in atom state set. */ |
| |
424 JS_ASSERT(p->asPtr() == &atom); |
| |
425 JS_ASSERT(ib == InternAtom); |
| |
426 p->setTagged(bool(ib)); |
| |
427 return &atom; |
| |
428 } |
| |
429 |
| |
430 const jschar *chars = str->getChars(cx); |
| |
431 if (!chars) |
| |
432 return nullptr; |
| |
433 |
| |
434 return AtomizeAndCopyChars(cx, chars, str->length(), ib); |
| |
435 } |
| |
436 |
| |
437 JSAtom * |
| |
438 js::Atomize(ExclusiveContext *cx, const char *bytes, size_t length, InternBehavior ib) |
| |
439 { |
| |
440 CHECK_REQUEST(cx); |
| |
441 |
| |
442 if (!JSString::validateLength(cx, length)) |
| |
443 return nullptr; |
| |
444 |
| |
445 static const unsigned ATOMIZE_BUF_MAX = 32; |
| |
446 if (length < ATOMIZE_BUF_MAX) { |
| |
447 /* |
| |
448 * Avoiding the malloc in InflateString on shorter strings saves us |
| |
449 * over 20,000 malloc calls on mozilla browser startup. This compares to |
| |
450 * only 131 calls where the string is longer than a 31 char (net) buffer. |
| |
451 * The vast majority of atomized strings are already in the hashtable. So |
| |
452 * js::AtomizeString rarely has to copy the temp string we make. |
| |
453 */ |
| |
454 jschar inflated[ATOMIZE_BUF_MAX]; |
| |
455 InflateStringToBuffer(bytes, length, inflated); |
| |
456 return AtomizeAndCopyChars(cx, inflated, length, ib); |
| |
457 } |
| |
458 |
| |
459 jschar *tbcharsZ = InflateString(cx, bytes, &length); |
| |
460 if (!tbcharsZ) |
| |
461 return nullptr; |
| |
462 return AtomizeAndtake(cx, tbcharsZ, length, ib); |
| |
463 } |
| |
464 |
| |
465 JSAtom * |
| |
466 js::AtomizeChars(ExclusiveContext *cx, const jschar *chars, size_t length, InternBehavior ib) |
| |
467 { |
| |
468 CHECK_REQUEST(cx); |
| |
469 |
| |
470 if (!JSString::validateLength(cx, length)) |
| |
471 return nullptr; |
| |
472 |
| |
473 return AtomizeAndCopyChars(cx, chars, length, ib); |
| |
474 } |
| |
475 |
| |
476 bool |
| |
477 js::IndexToIdSlow(ExclusiveContext *cx, uint32_t index, MutableHandleId idp) |
| |
478 { |
| |
479 JS_ASSERT(index > JSID_INT_MAX); |
| |
480 |
| |
481 jschar buf[UINT32_CHAR_BUFFER_LENGTH]; |
| |
482 RangedPtr<jschar> end(ArrayEnd(buf), buf, ArrayEnd(buf)); |
| |
483 RangedPtr<jschar> start = BackfillIndexInCharBuffer(index, end); |
| |
484 |
| |
485 JSAtom *atom = AtomizeChars(cx, start.get(), end - start); |
| |
486 if (!atom) |
| |
487 return false; |
| |
488 |
| |
489 idp.set(JSID_FROM_BITS((size_t)atom)); |
| |
490 return true; |
| |
491 } |
| |
492 |
| |
493 template <AllowGC allowGC> |
| |
494 static JSAtom * |
| |
495 ToAtomSlow(ExclusiveContext *cx, typename MaybeRooted<Value, allowGC>::HandleType arg) |
| |
496 { |
| |
497 JS_ASSERT(!arg.isString()); |
| |
498 |
| |
499 Value v = arg; |
| |
500 if (!v.isPrimitive()) { |
| |
501 if (!cx->shouldBeJSContext() || !allowGC) |
| |
502 return nullptr; |
| |
503 RootedValue v2(cx, v); |
| |
504 if (!ToPrimitive(cx->asJSContext(), JSTYPE_STRING, &v2)) |
| |
505 return nullptr; |
| |
506 v = v2; |
| |
507 } |
| |
508 |
| |
509 if (v.isString()) |
| |
510 return AtomizeString(cx, v.toString()); |
| |
511 if (v.isInt32()) |
| |
512 return Int32ToAtom(cx, v.toInt32()); |
| |
513 if (v.isDouble()) |
| |
514 return NumberToAtom(cx, v.toDouble()); |
| |
515 if (v.isBoolean()) |
| |
516 return v.toBoolean() ? cx->names().true_ : cx->names().false_; |
| |
517 if (v.isNull()) |
| |
518 return cx->names().null; |
| |
519 return cx->names().undefined; |
| |
520 } |
| |
521 |
| |
522 template <AllowGC allowGC> |
| |
523 JSAtom * |
| |
524 js::ToAtom(ExclusiveContext *cx, typename MaybeRooted<Value, allowGC>::HandleType v) |
| |
525 { |
| |
526 if (!v.isString()) |
| |
527 return ToAtomSlow<allowGC>(cx, v); |
| |
528 |
| |
529 JSString *str = v.toString(); |
| |
530 if (str->isAtom()) |
| |
531 return &str->asAtom(); |
| |
532 |
| |
533 return AtomizeString(cx, str); |
| |
534 } |
| |
535 |
| |
536 template JSAtom * |
| |
537 js::ToAtom<CanGC>(ExclusiveContext *cx, HandleValue v); |
| |
538 |
| |
539 template JSAtom * |
| |
540 js::ToAtom<NoGC>(ExclusiveContext *cx, Value v); |
| |
541 |
| |
542 template<XDRMode mode> |
| |
543 bool |
| |
544 js::XDRAtom(XDRState<mode> *xdr, MutableHandleAtom atomp) |
| |
545 { |
| |
546 if (mode == XDR_ENCODE) { |
| |
547 uint32_t nchars = atomp->length(); |
| |
548 if (!xdr->codeUint32(&nchars)) |
| |
549 return false; |
| |
550 |
| |
551 jschar *chars = const_cast<jschar *>(atomp->getChars(xdr->cx())); |
| |
552 if (!chars) |
| |
553 return false; |
| |
554 |
| |
555 return xdr->codeChars(chars, nchars); |
| |
556 } |
| |
557 |
| |
558 /* Avoid JSString allocation for already existing atoms. See bug 321985. */ |
| |
559 uint32_t nchars; |
| |
560 if (!xdr->codeUint32(&nchars)) |
| |
561 return false; |
| |
562 |
| |
563 JSContext *cx = xdr->cx(); |
| |
564 JSAtom *atom; |
| |
565 #if IS_LITTLE_ENDIAN |
| |
566 /* Directly access the little endian chars in the XDR buffer. */ |
| |
567 const jschar *chars = reinterpret_cast<const jschar *>(xdr->buf.read(nchars * sizeof(jschar))); |
| |
568 atom = AtomizeChars(cx, chars, nchars); |
| |
569 #else |
| |
570 /* |
| |
571 * We must copy chars to a temporary buffer to convert between little and |
| |
572 * big endian data. |
| |
573 */ |
| |
574 jschar *chars; |
| |
575 jschar stackChars[256]; |
| |
576 if (nchars <= ArrayLength(stackChars)) { |
| |
577 chars = stackChars; |
| |
578 } else { |
| |
579 /* |
| |
580 * This is very uncommon. Don't use the tempLifoAlloc arena for this as |
| |
581 * most allocations here will be bigger than tempLifoAlloc's default |
| |
582 * chunk size. |
| |
583 */ |
| |
584 chars = cx->runtime()->pod_malloc<jschar>(nchars); |
| |
585 if (!chars) |
| |
586 return false; |
| |
587 } |
| |
588 |
| |
589 JS_ALWAYS_TRUE(xdr->codeChars(chars, nchars)); |
| |
590 atom = AtomizeChars(cx, chars, nchars); |
| |
591 if (chars != stackChars) |
| |
592 js_free(chars); |
| |
593 #endif /* !IS_LITTLE_ENDIAN */ |
| |
594 |
| |
595 if (!atom) |
| |
596 return false; |
| |
597 atomp.set(atom); |
| |
598 return true; |
| |
599 } |
| |
600 |
| |
601 template bool |
| |
602 js::XDRAtom(XDRState<XDR_ENCODE> *xdr, MutableHandleAtom atomp); |
| |
603 |
| |
604 template bool |
| |
605 js::XDRAtom(XDRState<XDR_DECODE> *xdr, MutableHandleAtom atomp); |
| |
606 |