js/src/jsatom.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/js/src/jsatom.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,606 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     1.5 + * vim: set ts=8 sts=4 et sw=4 tw=99:
     1.6 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +/*
    1.11 + * JS atom table.
    1.12 + */
    1.13 +
    1.14 +#include "jsatominlines.h"
    1.15 +
    1.16 +#include "mozilla/ArrayUtils.h"
    1.17 +#include "mozilla/RangedPtr.h"
    1.18 +
    1.19 +#include <string.h>
    1.20 +
    1.21 +#include "jscntxt.h"
    1.22 +#include "jsstr.h"
    1.23 +#include "jstypes.h"
    1.24 +
    1.25 +#include "gc/Marking.h"
    1.26 +#include "vm/Xdr.h"
    1.27 +
    1.28 +#include "jscntxtinlines.h"
    1.29 +#include "jscompartmentinlines.h"
    1.30 +#include "jsobjinlines.h"
    1.31 +
    1.32 +#include "vm/String-inl.h"
    1.33 +
    1.34 +using namespace js;
    1.35 +using namespace js::gc;
    1.36 +
    1.37 +using mozilla::ArrayEnd;
    1.38 +using mozilla::ArrayLength;
    1.39 +using mozilla::RangedPtr;
    1.40 +
    1.41 +const char *
    1.42 +js::AtomToPrintableString(ExclusiveContext *cx, JSAtom *atom, JSAutoByteString *bytes)
    1.43 +{
    1.44 +    JSString *str = js_QuoteString(cx, atom, 0);
    1.45 +    if (!str)
    1.46 +        return nullptr;
    1.47 +    return bytes->encodeLatin1(cx, str);
    1.48 +}
    1.49 +
    1.50 +const char * const js::TypeStrings[] = {
    1.51 +    js_undefined_str,
    1.52 +    js_object_str,
    1.53 +    js_function_str,
    1.54 +    js_string_str,
    1.55 +    js_number_str,
    1.56 +    js_boolean_str,
    1.57 +    js_null_str,
    1.58 +};
    1.59 +
    1.60 +#define DEFINE_PROTO_STRING(name,code,init,clasp) const char js_##name##_str[] = #name;
    1.61 +JS_FOR_EACH_PROTOTYPE(DEFINE_PROTO_STRING)
    1.62 +#undef DEFINE_PROTO_STRING
    1.63 +
    1.64 +#define CONST_CHAR_STR(idpart, id, text) const char js_##idpart##_str[] = text;
    1.65 +FOR_EACH_COMMON_PROPERTYNAME(CONST_CHAR_STR)
    1.66 +#undef CONST_CHAR_STR
    1.67 +
    1.68 +/* Constant strings that are not atomized. */
    1.69 +const char js_break_str[]           = "break";
    1.70 +const char js_case_str[]            = "case";
    1.71 +const char js_catch_str[]           = "catch";
    1.72 +const char js_class_str[]           = "class";
    1.73 +const char js_close_str[]           = "close";
    1.74 +const char js_const_str[]           = "const";
    1.75 +const char js_continue_str[]        = "continue";
    1.76 +const char js_debugger_str[]        = "debugger";
    1.77 +const char js_default_str[]         = "default";
    1.78 +const char js_do_str[]              = "do";
    1.79 +const char js_else_str[]            = "else";
    1.80 +const char js_enum_str[]            = "enum";
    1.81 +const char js_export_str[]          = "export";
    1.82 +const char js_extends_str[]         = "extends";
    1.83 +const char js_finally_str[]         = "finally";
    1.84 +const char js_for_str[]             = "for";
    1.85 +const char js_getter_str[]          = "getter";
    1.86 +const char js_if_str[]              = "if";
    1.87 +const char js_implements_str[]      = "implements";
    1.88 +const char js_import_str[]          = "import";
    1.89 +const char js_in_str[]              = "in";
    1.90 +const char js_instanceof_str[]      = "instanceof";
    1.91 +const char js_interface_str[]       = "interface";
    1.92 +const char js_new_str[]             = "new";
    1.93 +const char js_package_str[]         = "package";
    1.94 +const char js_private_str[]         = "private";
    1.95 +const char js_protected_str[]       = "protected";
    1.96 +const char js_public_str[]          = "public";
    1.97 +const char js_send_str[]            = "send";
    1.98 +const char js_setter_str[]          = "setter";
    1.99 +const char js_static_str[]          = "static";
   1.100 +const char js_super_str[]           = "super";
   1.101 +const char js_switch_str[]          = "switch";
   1.102 +const char js_this_str[]            = "this";
   1.103 +const char js_try_str[]             = "try";
   1.104 +const char js_typeof_str[]          = "typeof";
   1.105 +const char js_void_str[]            = "void";
   1.106 +const char js_while_str[]           = "while";
   1.107 +const char js_with_str[]            = "with";
   1.108 +
   1.109 +// Use a low initial capacity for atom hash tables to avoid penalizing runtimes
   1.110 +// which create a small number of atoms.
   1.111 +static const uint32_t JS_STRING_HASH_COUNT = 64;
   1.112 +
   1.113 +struct CommonNameInfo
   1.114 +{
   1.115 +    const char *str;
   1.116 +    size_t length;
   1.117 +};
   1.118 +
   1.119 +bool
   1.120 +JSRuntime::initializeAtoms(JSContext *cx)
   1.121 +{
   1.122 +    atoms_ = cx->new_<AtomSet>();
   1.123 +    if (!atoms_ || !atoms_->init(JS_STRING_HASH_COUNT))
   1.124 +        return false;
   1.125 +
   1.126 +    if (parentRuntime) {
   1.127 +        staticStrings = parentRuntime->staticStrings;
   1.128 +        commonNames = parentRuntime->commonNames;
   1.129 +        emptyString = parentRuntime->emptyString;
   1.130 +        permanentAtoms = parentRuntime->permanentAtoms;
   1.131 +        return true;
   1.132 +    }
   1.133 +
   1.134 +    permanentAtoms = cx->new_<AtomSet>();
   1.135 +    if (!permanentAtoms || !permanentAtoms->init(JS_STRING_HASH_COUNT))
   1.136 +        return false;
   1.137 +
   1.138 +    staticStrings = cx->new_<StaticStrings>();
   1.139 +    if (!staticStrings || !staticStrings->init(cx))
   1.140 +        return false;
   1.141 +
   1.142 +    static const CommonNameInfo cachedNames[] = {
   1.143 +#define COMMON_NAME_INFO(idpart, id, text) { js_##idpart##_str, sizeof(text) - 1 },
   1.144 +        FOR_EACH_COMMON_PROPERTYNAME(COMMON_NAME_INFO)
   1.145 +#undef COMMON_NAME_INFO
   1.146 +#define COMMON_NAME_INFO(name, code, init, clasp) { js_##name##_str, sizeof(#name) - 1 },
   1.147 +        JS_FOR_EACH_PROTOTYPE(COMMON_NAME_INFO)
   1.148 +#undef COMMON_NAME_INFO
   1.149 +    };
   1.150 +
   1.151 +    commonNames = cx->new_<JSAtomState>();
   1.152 +    if (!commonNames)
   1.153 +        return false;
   1.154 +
   1.155 +    FixedHeapPtr<PropertyName> *names = reinterpret_cast<FixedHeapPtr<PropertyName> *>(commonNames);
   1.156 +    for (size_t i = 0; i < ArrayLength(cachedNames); i++, names++) {
   1.157 +        JSAtom *atom = Atomize(cx, cachedNames[i].str, cachedNames[i].length, InternAtom);
   1.158 +        if (!atom)
   1.159 +            return false;
   1.160 +        names->init(atom->asPropertyName());
   1.161 +    }
   1.162 +    JS_ASSERT(uintptr_t(names) == uintptr_t(commonNames + 1));
   1.163 +
   1.164 +    emptyString = commonNames->empty;
   1.165 +    return true;
   1.166 +}
   1.167 +
   1.168 +void
   1.169 +JSRuntime::finishAtoms()
   1.170 +{
   1.171 +    if (atoms_)
   1.172 +        js_delete(atoms_);
   1.173 +
   1.174 +    if (!parentRuntime) {
   1.175 +        if (staticStrings)
   1.176 +            js_delete(staticStrings);
   1.177 +
   1.178 +        if (commonNames)
   1.179 +            js_delete(commonNames);
   1.180 +
   1.181 +        if (permanentAtoms)
   1.182 +            js_delete(permanentAtoms);
   1.183 +    }
   1.184 +
   1.185 +    atoms_ = nullptr;
   1.186 +    staticStrings = nullptr;
   1.187 +    commonNames = nullptr;
   1.188 +    permanentAtoms = nullptr;
   1.189 +    emptyString = nullptr;
   1.190 +}
   1.191 +
   1.192 +void
   1.193 +js::MarkAtoms(JSTracer *trc)
   1.194 +{
   1.195 +    JSRuntime *rt = trc->runtime();
   1.196 +    for (AtomSet::Enum e(rt->atoms()); !e.empty(); e.popFront()) {
   1.197 +        const AtomStateEntry &entry = e.front();
   1.198 +        if (!entry.isTagged())
   1.199 +            continue;
   1.200 +
   1.201 +        JSAtom *atom = entry.asPtr();
   1.202 +        bool tagged = entry.isTagged();
   1.203 +        MarkStringRoot(trc, &atom, "interned_atom");
   1.204 +        if (entry.asPtr() != atom)
   1.205 +            e.rekeyFront(AtomHasher::Lookup(atom), AtomStateEntry(atom, tagged));
   1.206 +    }
   1.207 +}
   1.208 +
   1.209 +void
   1.210 +js::MarkPermanentAtoms(JSTracer *trc)
   1.211 +{
   1.212 +    JSRuntime *rt = trc->runtime();
   1.213 +
   1.214 +    // Permanent atoms only need to be marked in the runtime which owns them.
   1.215 +    if (rt->parentRuntime)
   1.216 +        return;
   1.217 +
   1.218 +    // Static strings are not included in the permanent atoms table.
   1.219 +    if (rt->staticStrings)
   1.220 +        rt->staticStrings->trace(trc);
   1.221 +
   1.222 +    if (rt->permanentAtoms) {
   1.223 +        for (AtomSet::Enum e(*rt->permanentAtoms); !e.empty(); e.popFront()) {
   1.224 +            const AtomStateEntry &entry = e.front();
   1.225 +
   1.226 +            JSAtom *atom = entry.asPtr();
   1.227 +            MarkPermanentAtom(trc, atom, "permanent_table");
   1.228 +        }
   1.229 +    }
   1.230 +}
   1.231 +
   1.232 +void
   1.233 +JSRuntime::sweepAtoms()
   1.234 +{
   1.235 +    if (!atoms_)
   1.236 +        return;
   1.237 +
   1.238 +    for (AtomSet::Enum e(*atoms_); !e.empty(); e.popFront()) {
   1.239 +        AtomStateEntry entry = e.front();
   1.240 +        JSAtom *atom = entry.asPtr();
   1.241 +        bool isDying = IsStringAboutToBeFinalized(&atom);
   1.242 +
   1.243 +        /* Pinned or interned key cannot be finalized. */
   1.244 +        JS_ASSERT_IF(hasContexts() && entry.isTagged(), !isDying);
   1.245 +
   1.246 +        if (isDying)
   1.247 +            e.removeFront();
   1.248 +    }
   1.249 +}
   1.250 +
   1.251 +bool
   1.252 +JSRuntime::transformToPermanentAtoms()
   1.253 +{
   1.254 +    JS_ASSERT(!parentRuntime);
   1.255 +
   1.256 +    // All static strings were created as permanent atoms, now move the contents
   1.257 +    // of the atoms table into permanentAtoms and mark each as permanent.
   1.258 +
   1.259 +    JS_ASSERT(permanentAtoms && permanentAtoms->empty());
   1.260 +
   1.261 +    AtomSet *temp = atoms_;
   1.262 +    atoms_ = permanentAtoms;
   1.263 +    permanentAtoms = temp;
   1.264 +
   1.265 +    for (AtomSet::Enum e(*permanentAtoms); !e.empty(); e.popFront()) {
   1.266 +        AtomStateEntry entry = e.front();
   1.267 +        JSAtom *atom = entry.asPtr();
   1.268 +        atom->morphIntoPermanentAtom();
   1.269 +    }
   1.270 +
   1.271 +    return true;
   1.272 +}
   1.273 +
   1.274 +bool
   1.275 +AtomIsInterned(JSContext *cx, JSAtom *atom)
   1.276 +{
   1.277 +    /* We treat static strings as interned because they're never collected. */
   1.278 +    if (StaticStrings::isStatic(atom))
   1.279 +        return true;
   1.280 +
   1.281 +    AtomHasher::Lookup lookup(atom);
   1.282 +
   1.283 +    /* Likewise, permanent strings are considered to be interned. */
   1.284 +    AtomSet::Ptr p = cx->permanentAtoms().readonlyThreadsafeLookup(lookup);
   1.285 +    if (p)
   1.286 +        return true;
   1.287 +
   1.288 +    AutoLockForExclusiveAccess lock(cx);
   1.289 +
   1.290 +    p = cx->runtime()->atoms().lookup(lookup);
   1.291 +    if (!p)
   1.292 +        return false;
   1.293 +
   1.294 +    return p->isTagged();
   1.295 +}
   1.296 +
   1.297 +/*
   1.298 + * When the jschars reside in a freshly allocated buffer the memory can be used
   1.299 + * as a new JSAtom's storage without copying. The contract is that the caller no
   1.300 + * longer owns the memory and this method is responsible for freeing the memory.
   1.301 + */
   1.302 +MOZ_ALWAYS_INLINE
   1.303 +static JSAtom *
   1.304 +AtomizeAndtake(ExclusiveContext *cx, jschar *tbchars, size_t length, InternBehavior ib)
   1.305 +{
   1.306 +    JS_ASSERT(tbchars[length] == 0);
   1.307 +
   1.308 +    if (JSAtom *s = cx->staticStrings().lookup(tbchars, length)) {
   1.309 +        js_free(tbchars);
   1.310 +        return s;
   1.311 +    }
   1.312 +
   1.313 +    AtomHasher::Lookup lookup(tbchars, length);
   1.314 +
   1.315 +    AtomSet::Ptr pp = cx->permanentAtoms().readonlyThreadsafeLookup(lookup);
   1.316 +    if (pp) {
   1.317 +        js_free(tbchars);
   1.318 +        return pp->asPtr();
   1.319 +    }
   1.320 +
   1.321 +    AutoLockForExclusiveAccess lock(cx);
   1.322 +
   1.323 +    /*
   1.324 +     * If a GC occurs at js_NewStringCopy then |p| will still have the correct
   1.325 +     * hash, allowing us to avoid rehashing it. Even though the hash is
   1.326 +     * unchanged, we need to re-lookup the table position because a last-ditch
   1.327 +     * GC will potentially free some table entries.
   1.328 +     */
   1.329 +    AtomSet& atoms = cx->atoms();
   1.330 +    AtomSet::AddPtr p = atoms.lookupForAdd(lookup);
   1.331 +    if (p) {
   1.332 +        JSAtom *atom = p->asPtr();
   1.333 +        p->setTagged(bool(ib));
   1.334 +        js_free(tbchars);
   1.335 +        return atom;
   1.336 +    }
   1.337 +
   1.338 +    AutoCompartment ac(cx, cx->atomsCompartment());
   1.339 +
   1.340 +    JSFlatString *flat = js_NewString<NoGC>(cx, tbchars, length);
   1.341 +    if (!flat) {
   1.342 +        js_free(tbchars);
   1.343 +        js_ReportOutOfMemory(cx);
   1.344 +        return nullptr;
   1.345 +    }
   1.346 +
   1.347 +    JSAtom *atom = flat->morphAtomizedStringIntoAtom();
   1.348 +
   1.349 +    if (!atoms.relookupOrAdd(p, lookup, AtomStateEntry(atom, bool(ib)))) {
   1.350 +        js_ReportOutOfMemory(cx); /* SystemAllocPolicy does not report OOM. */
   1.351 +        return nullptr;
   1.352 +    }
   1.353 +
   1.354 +    return atom;
   1.355 +}
   1.356 +
   1.357 +/* |tbchars| must not point into an inline or short string. */
   1.358 +MOZ_ALWAYS_INLINE
   1.359 +static JSAtom *
   1.360 +AtomizeAndCopyChars(ExclusiveContext *cx, const jschar *tbchars, size_t length, InternBehavior ib)
   1.361 +{
   1.362 +    if (JSAtom *s = cx->staticStrings().lookup(tbchars, length))
   1.363 +         return s;
   1.364 +
   1.365 +    AtomHasher::Lookup lookup(tbchars, length);
   1.366 +
   1.367 +    AtomSet::Ptr pp = cx->permanentAtoms().readonlyThreadsafeLookup(lookup);
   1.368 +    if (pp)
   1.369 +        return pp->asPtr();
   1.370 +
   1.371 +    /*
   1.372 +     * If a GC occurs at js_NewStringCopy then |p| will still have the correct
   1.373 +     * hash, allowing us to avoid rehashing it. Even though the hash is
   1.374 +     * unchanged, we need to re-lookup the table position because a last-ditch
   1.375 +     * GC will potentially free some table entries.
   1.376 +     */
   1.377 +
   1.378 +    AutoLockForExclusiveAccess lock(cx);
   1.379 +
   1.380 +    AtomSet& atoms = cx->atoms();
   1.381 +    AtomSet::AddPtr p = atoms.lookupForAdd(lookup);
   1.382 +    if (p) {
   1.383 +        JSAtom *atom = p->asPtr();
   1.384 +        p->setTagged(bool(ib));
   1.385 +        return atom;
   1.386 +    }
   1.387 +
   1.388 +    AutoCompartment ac(cx, cx->atomsCompartment());
   1.389 +
   1.390 +    JSFlatString *flat = js_NewStringCopyN<NoGC>(cx, tbchars, length);
   1.391 +    if (!flat) {
   1.392 +        js_ReportOutOfMemory(cx);
   1.393 +        return nullptr;
   1.394 +    }
   1.395 +
   1.396 +    JSAtom *atom = flat->morphAtomizedStringIntoAtom();
   1.397 +
   1.398 +    if (!atoms.relookupOrAdd(p, lookup, AtomStateEntry(atom, bool(ib)))) {
   1.399 +        js_ReportOutOfMemory(cx); /* SystemAllocPolicy does not report OOM. */
   1.400 +        return nullptr;
   1.401 +    }
   1.402 +
   1.403 +    return atom;
   1.404 +}
   1.405 +
   1.406 +JSAtom *
   1.407 +js::AtomizeString(ExclusiveContext *cx, JSString *str,
   1.408 +                  js::InternBehavior ib /* = js::DoNotInternAtom */)
   1.409 +{
   1.410 +    if (str->isAtom()) {
   1.411 +        JSAtom &atom = str->asAtom();
   1.412 +        /* N.B. static atoms are effectively always interned. */
   1.413 +        if (ib != InternAtom || js::StaticStrings::isStatic(&atom))
   1.414 +            return &atom;
   1.415 +
   1.416 +        AtomHasher::Lookup lookup(&atom);
   1.417 +
   1.418 +        /* Likewise, permanent atoms are always interned. */
   1.419 +        AtomSet::Ptr p = cx->permanentAtoms().readonlyThreadsafeLookup(lookup);
   1.420 +        if (p)
   1.421 +            return &atom;
   1.422 +
   1.423 +        AutoLockForExclusiveAccess lock(cx);
   1.424 +
   1.425 +        p = cx->atoms().lookup(lookup);
   1.426 +        JS_ASSERT(p); /* Non-static atom must exist in atom state set. */
   1.427 +        JS_ASSERT(p->asPtr() == &atom);
   1.428 +        JS_ASSERT(ib == InternAtom);
   1.429 +        p->setTagged(bool(ib));
   1.430 +        return &atom;
   1.431 +    }
   1.432 +
   1.433 +    const jschar *chars = str->getChars(cx);
   1.434 +    if (!chars)
   1.435 +        return nullptr;
   1.436 +
   1.437 +    return AtomizeAndCopyChars(cx, chars, str->length(), ib);
   1.438 +}
   1.439 +
   1.440 +JSAtom *
   1.441 +js::Atomize(ExclusiveContext *cx, const char *bytes, size_t length, InternBehavior ib)
   1.442 +{
   1.443 +    CHECK_REQUEST(cx);
   1.444 +
   1.445 +    if (!JSString::validateLength(cx, length))
   1.446 +        return nullptr;
   1.447 +
   1.448 +    static const unsigned ATOMIZE_BUF_MAX = 32;
   1.449 +    if (length < ATOMIZE_BUF_MAX) {
   1.450 +        /*
   1.451 +         * Avoiding the malloc in InflateString on shorter strings saves us
   1.452 +         * over 20,000 malloc calls on mozilla browser startup. This compares to
   1.453 +         * only 131 calls where the string is longer than a 31 char (net) buffer.
   1.454 +         * The vast majority of atomized strings are already in the hashtable. So
   1.455 +         * js::AtomizeString rarely has to copy the temp string we make.
   1.456 +         */
   1.457 +        jschar inflated[ATOMIZE_BUF_MAX];
   1.458 +        InflateStringToBuffer(bytes, length, inflated);
   1.459 +        return AtomizeAndCopyChars(cx, inflated, length, ib);
   1.460 +    }
   1.461 +
   1.462 +    jschar *tbcharsZ = InflateString(cx, bytes, &length);
   1.463 +    if (!tbcharsZ)
   1.464 +        return nullptr;
   1.465 +    return AtomizeAndtake(cx, tbcharsZ, length, ib);
   1.466 +}
   1.467 +
   1.468 +JSAtom *
   1.469 +js::AtomizeChars(ExclusiveContext *cx, const jschar *chars, size_t length, InternBehavior ib)
   1.470 +{
   1.471 +    CHECK_REQUEST(cx);
   1.472 +
   1.473 +    if (!JSString::validateLength(cx, length))
   1.474 +        return nullptr;
   1.475 +
   1.476 +    return AtomizeAndCopyChars(cx, chars, length, ib);
   1.477 +}
   1.478 +
   1.479 +bool
   1.480 +js::IndexToIdSlow(ExclusiveContext *cx, uint32_t index, MutableHandleId idp)
   1.481 +{
   1.482 +    JS_ASSERT(index > JSID_INT_MAX);
   1.483 +
   1.484 +    jschar buf[UINT32_CHAR_BUFFER_LENGTH];
   1.485 +    RangedPtr<jschar> end(ArrayEnd(buf), buf, ArrayEnd(buf));
   1.486 +    RangedPtr<jschar> start = BackfillIndexInCharBuffer(index, end);
   1.487 +
   1.488 +    JSAtom *atom = AtomizeChars(cx, start.get(), end - start);
   1.489 +    if (!atom)
   1.490 +        return false;
   1.491 +
   1.492 +    idp.set(JSID_FROM_BITS((size_t)atom));
   1.493 +    return true;
   1.494 +}
   1.495 +
   1.496 +template <AllowGC allowGC>
   1.497 +static JSAtom *
   1.498 +ToAtomSlow(ExclusiveContext *cx, typename MaybeRooted<Value, allowGC>::HandleType arg)
   1.499 +{
   1.500 +    JS_ASSERT(!arg.isString());
   1.501 +
   1.502 +    Value v = arg;
   1.503 +    if (!v.isPrimitive()) {
   1.504 +        if (!cx->shouldBeJSContext() || !allowGC)
   1.505 +            return nullptr;
   1.506 +        RootedValue v2(cx, v);
   1.507 +        if (!ToPrimitive(cx->asJSContext(), JSTYPE_STRING, &v2))
   1.508 +            return nullptr;
   1.509 +        v = v2;
   1.510 +    }
   1.511 +
   1.512 +    if (v.isString())
   1.513 +        return AtomizeString(cx, v.toString());
   1.514 +    if (v.isInt32())
   1.515 +        return Int32ToAtom(cx, v.toInt32());
   1.516 +    if (v.isDouble())
   1.517 +        return NumberToAtom(cx, v.toDouble());
   1.518 +    if (v.isBoolean())
   1.519 +        return v.toBoolean() ? cx->names().true_ : cx->names().false_;
   1.520 +    if (v.isNull())
   1.521 +        return cx->names().null;
   1.522 +    return cx->names().undefined;
   1.523 +}
   1.524 +
   1.525 +template <AllowGC allowGC>
   1.526 +JSAtom *
   1.527 +js::ToAtom(ExclusiveContext *cx, typename MaybeRooted<Value, allowGC>::HandleType v)
   1.528 +{
   1.529 +    if (!v.isString())
   1.530 +        return ToAtomSlow<allowGC>(cx, v);
   1.531 +
   1.532 +    JSString *str = v.toString();
   1.533 +    if (str->isAtom())
   1.534 +        return &str->asAtom();
   1.535 +
   1.536 +    return AtomizeString(cx, str);
   1.537 +}
   1.538 +
   1.539 +template JSAtom *
   1.540 +js::ToAtom<CanGC>(ExclusiveContext *cx, HandleValue v);
   1.541 +
   1.542 +template JSAtom *
   1.543 +js::ToAtom<NoGC>(ExclusiveContext *cx, Value v);
   1.544 +
   1.545 +template<XDRMode mode>
   1.546 +bool
   1.547 +js::XDRAtom(XDRState<mode> *xdr, MutableHandleAtom atomp)
   1.548 +{
   1.549 +    if (mode == XDR_ENCODE) {
   1.550 +        uint32_t nchars = atomp->length();
   1.551 +        if (!xdr->codeUint32(&nchars))
   1.552 +            return false;
   1.553 +
   1.554 +        jschar *chars = const_cast<jschar *>(atomp->getChars(xdr->cx()));
   1.555 +        if (!chars)
   1.556 +            return false;
   1.557 +
   1.558 +        return xdr->codeChars(chars, nchars);
   1.559 +    }
   1.560 +
   1.561 +    /* Avoid JSString allocation for already existing atoms. See bug 321985. */
   1.562 +    uint32_t nchars;
   1.563 +    if (!xdr->codeUint32(&nchars))
   1.564 +        return false;
   1.565 +
   1.566 +    JSContext *cx = xdr->cx();
   1.567 +    JSAtom *atom;
   1.568 +#if IS_LITTLE_ENDIAN
   1.569 +    /* Directly access the little endian chars in the XDR buffer. */
   1.570 +    const jschar *chars = reinterpret_cast<const jschar *>(xdr->buf.read(nchars * sizeof(jschar)));
   1.571 +    atom = AtomizeChars(cx, chars, nchars);
   1.572 +#else
   1.573 +    /*
   1.574 +     * We must copy chars to a temporary buffer to convert between little and
   1.575 +     * big endian data.
   1.576 +     */
   1.577 +    jschar *chars;
   1.578 +    jschar stackChars[256];
   1.579 +    if (nchars <= ArrayLength(stackChars)) {
   1.580 +        chars = stackChars;
   1.581 +    } else {
   1.582 +        /*
   1.583 +         * This is very uncommon. Don't use the tempLifoAlloc arena for this as
   1.584 +         * most allocations here will be bigger than tempLifoAlloc's default
   1.585 +         * chunk size.
   1.586 +         */
   1.587 +        chars = cx->runtime()->pod_malloc<jschar>(nchars);
   1.588 +        if (!chars)
   1.589 +            return false;
   1.590 +    }
   1.591 +
   1.592 +    JS_ALWAYS_TRUE(xdr->codeChars(chars, nchars));
   1.593 +    atom = AtomizeChars(cx, chars, nchars);
   1.594 +    if (chars != stackChars)
   1.595 +        js_free(chars);
   1.596 +#endif /* !IS_LITTLE_ENDIAN */
   1.597 +
   1.598 +    if (!atom)
   1.599 +        return false;
   1.600 +    atomp.set(atom);
   1.601 +    return true;
   1.602 +}
   1.603 +
   1.604 +template bool
   1.605 +js::XDRAtom(XDRState<XDR_ENCODE> *xdr, MutableHandleAtom atomp);
   1.606 +
   1.607 +template bool
   1.608 +js::XDRAtom(XDRState<XDR_DECODE> *xdr, MutableHandleAtom atomp);
   1.609 +

mercurial