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