js/src/vm/ObjectImpl.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/js/src/vm/ObjectImpl.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,391 @@
     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 +#include "vm/ObjectImpl-inl.h"
    1.11 +
    1.12 +#include "gc/Marking.h"
    1.13 +#include "js/Value.h"
    1.14 +#include "vm/Debugger.h"
    1.15 +
    1.16 +#include "jsobjinlines.h"
    1.17 +#include "vm/Shape-inl.h"
    1.18 +
    1.19 +using namespace js;
    1.20 +
    1.21 +using JS::GenericNaN;
    1.22 +
    1.23 +PropDesc::PropDesc()
    1.24 +  : pd_(UndefinedValue()),
    1.25 +    value_(UndefinedValue()),
    1.26 +    get_(UndefinedValue()),
    1.27 +    set_(UndefinedValue()),
    1.28 +    attrs(0),
    1.29 +    hasGet_(false),
    1.30 +    hasSet_(false),
    1.31 +    hasValue_(false),
    1.32 +    hasWritable_(false),
    1.33 +    hasEnumerable_(false),
    1.34 +    hasConfigurable_(false),
    1.35 +    isUndefined_(true)
    1.36 +{
    1.37 +}
    1.38 +
    1.39 +bool
    1.40 +PropDesc::checkGetter(JSContext *cx)
    1.41 +{
    1.42 +    if (hasGet_) {
    1.43 +        if (!js_IsCallable(get_) && !get_.isUndefined()) {
    1.44 +            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_GET_SET_FIELD,
    1.45 +                                 js_getter_str);
    1.46 +            return false;
    1.47 +        }
    1.48 +    }
    1.49 +    return true;
    1.50 +}
    1.51 +
    1.52 +bool
    1.53 +PropDesc::checkSetter(JSContext *cx)
    1.54 +{
    1.55 +    if (hasSet_) {
    1.56 +        if (!js_IsCallable(set_) && !set_.isUndefined()) {
    1.57 +            JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_GET_SET_FIELD,
    1.58 +                                 js_setter_str);
    1.59 +            return false;
    1.60 +        }
    1.61 +    }
    1.62 +    return true;
    1.63 +}
    1.64 +
    1.65 +static bool
    1.66 +CheckArgCompartment(JSContext *cx, JSObject *obj, HandleValue v,
    1.67 +                    const char *methodname, const char *propname)
    1.68 +{
    1.69 +    if (v.isObject() && v.toObject().compartment() != obj->compartment()) {
    1.70 +        JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEBUG_COMPARTMENT_MISMATCH,
    1.71 +                             methodname, propname);
    1.72 +        return false;
    1.73 +    }
    1.74 +    return true;
    1.75 +}
    1.76 +
    1.77 +/*
    1.78 + * Convert Debugger.Objects in desc to debuggee values.
    1.79 + * Reject non-callable getters and setters.
    1.80 + */
    1.81 +bool
    1.82 +PropDesc::unwrapDebuggerObjectsInto(JSContext *cx, Debugger *dbg, HandleObject obj,
    1.83 +                                    PropDesc *unwrapped) const
    1.84 +{
    1.85 +    MOZ_ASSERT(!isUndefined());
    1.86 +
    1.87 +    *unwrapped = *this;
    1.88 +
    1.89 +    if (unwrapped->hasValue()) {
    1.90 +        RootedValue value(cx, unwrapped->value_);
    1.91 +        if (!dbg->unwrapDebuggeeValue(cx, &value) ||
    1.92 +            !CheckArgCompartment(cx, obj, value, "defineProperty", "value"))
    1.93 +        {
    1.94 +            return false;
    1.95 +        }
    1.96 +        unwrapped->value_ = value;
    1.97 +    }
    1.98 +
    1.99 +    if (unwrapped->hasGet()) {
   1.100 +        RootedValue get(cx, unwrapped->get_);
   1.101 +        if (!dbg->unwrapDebuggeeValue(cx, &get) ||
   1.102 +            !CheckArgCompartment(cx, obj, get, "defineProperty", "get"))
   1.103 +        {
   1.104 +            return false;
   1.105 +        }
   1.106 +        unwrapped->get_ = get;
   1.107 +    }
   1.108 +
   1.109 +    if (unwrapped->hasSet()) {
   1.110 +        RootedValue set(cx, unwrapped->set_);
   1.111 +        if (!dbg->unwrapDebuggeeValue(cx, &set) ||
   1.112 +            !CheckArgCompartment(cx, obj, set, "defineProperty", "set"))
   1.113 +        {
   1.114 +            return false;
   1.115 +        }
   1.116 +        unwrapped->set_ = set;
   1.117 +    }
   1.118 +
   1.119 +    return true;
   1.120 +}
   1.121 +
   1.122 +/*
   1.123 + * Rewrap *idp and the fields of *desc for the current compartment.  Also:
   1.124 + * defining a property on a proxy requires pd_ to contain a descriptor object,
   1.125 + * so reconstitute desc->pd_ if needed.
   1.126 + */
   1.127 +bool
   1.128 +PropDesc::wrapInto(JSContext *cx, HandleObject obj, const jsid &id, jsid *wrappedId,
   1.129 +                   PropDesc *desc) const
   1.130 +{
   1.131 +    MOZ_ASSERT(!isUndefined());
   1.132 +
   1.133 +    JSCompartment *comp = cx->compartment();
   1.134 +
   1.135 +    *wrappedId = id;
   1.136 +    if (!comp->wrapId(cx, wrappedId))
   1.137 +        return false;
   1.138 +
   1.139 +    *desc = *this;
   1.140 +    RootedValue value(cx, desc->value_);
   1.141 +    RootedValue get(cx, desc->get_);
   1.142 +    RootedValue set(cx, desc->set_);
   1.143 +
   1.144 +    if (!comp->wrap(cx, &value) || !comp->wrap(cx, &get) || !comp->wrap(cx, &set))
   1.145 +        return false;
   1.146 +
   1.147 +    desc->value_ = value;
   1.148 +    desc->get_ = get;
   1.149 +    desc->set_ = set;
   1.150 +    return !obj->is<ProxyObject>() || desc->makeObject(cx);
   1.151 +}
   1.152 +
   1.153 +static const ObjectElements emptyElementsHeader(0, 0);
   1.154 +
   1.155 +/* Objects with no elements share one empty set of elements. */
   1.156 +HeapSlot *const js::emptyObjectElements =
   1.157 +    reinterpret_cast<HeapSlot *>(uintptr_t(&emptyElementsHeader) + sizeof(ObjectElements));
   1.158 +
   1.159 +#ifdef DEBUG
   1.160 +
   1.161 +bool
   1.162 +ObjectImpl::canHaveNonEmptyElements()
   1.163 +{
   1.164 +    JSObject *obj = static_cast<JSObject *>(this);
   1.165 +    return isNative() && !obj->is<TypedArrayObject>();
   1.166 +}
   1.167 +
   1.168 +#endif // DEBUG
   1.169 +
   1.170 +/* static */ bool
   1.171 +ObjectElements::ConvertElementsToDoubles(JSContext *cx, uintptr_t elementsPtr)
   1.172 +{
   1.173 +    /*
   1.174 +     * This function is infallible, but has a fallible interface so that it can
   1.175 +     * be called directly from Ion code. Only arrays can have their dense
   1.176 +     * elements converted to doubles, and arrays never have empty elements.
   1.177 +     */
   1.178 +    HeapSlot *elementsHeapPtr = (HeapSlot *) elementsPtr;
   1.179 +    JS_ASSERT(elementsHeapPtr != emptyObjectElements);
   1.180 +
   1.181 +    ObjectElements *header = ObjectElements::fromElements(elementsHeapPtr);
   1.182 +    JS_ASSERT(!header->shouldConvertDoubleElements());
   1.183 +
   1.184 +    Value *vp = (Value *) elementsPtr;
   1.185 +    for (size_t i = 0; i < header->initializedLength; i++) {
   1.186 +        if (vp[i].isInt32())
   1.187 +            vp[i].setDouble(vp[i].toInt32());
   1.188 +    }
   1.189 +
   1.190 +    header->setShouldConvertDoubleElements();
   1.191 +    return true;
   1.192 +}
   1.193 +
   1.194 +#ifdef DEBUG
   1.195 +void
   1.196 +js::ObjectImpl::checkShapeConsistency()
   1.197 +{
   1.198 +    static int throttle = -1;
   1.199 +    if (throttle < 0) {
   1.200 +        if (const char *var = getenv("JS_CHECK_SHAPE_THROTTLE"))
   1.201 +            throttle = atoi(var);
   1.202 +        if (throttle < 0)
   1.203 +            throttle = 0;
   1.204 +    }
   1.205 +    if (throttle == 0)
   1.206 +        return;
   1.207 +
   1.208 +    MOZ_ASSERT(isNative());
   1.209 +
   1.210 +    Shape *shape = lastProperty();
   1.211 +    Shape *prev = nullptr;
   1.212 +
   1.213 +    if (inDictionaryMode()) {
   1.214 +        MOZ_ASSERT(shape->hasTable());
   1.215 +
   1.216 +        ShapeTable &table = shape->table();
   1.217 +        for (uint32_t fslot = table.freelist; fslot != SHAPE_INVALID_SLOT;
   1.218 +             fslot = getSlot(fslot).toPrivateUint32()) {
   1.219 +            MOZ_ASSERT(fslot < slotSpan());
   1.220 +        }
   1.221 +
   1.222 +        for (int n = throttle; --n >= 0 && shape->parent; shape = shape->parent) {
   1.223 +            MOZ_ASSERT_IF(lastProperty() != shape, !shape->hasTable());
   1.224 +
   1.225 +            Shape **spp = table.search(shape->propid(), false);
   1.226 +            MOZ_ASSERT(SHAPE_FETCH(spp) == shape);
   1.227 +        }
   1.228 +
   1.229 +        shape = lastProperty();
   1.230 +        for (int n = throttle; --n >= 0 && shape; shape = shape->parent) {
   1.231 +            MOZ_ASSERT_IF(shape->slot() != SHAPE_INVALID_SLOT, shape->slot() < slotSpan());
   1.232 +            if (!prev) {
   1.233 +                MOZ_ASSERT(lastProperty() == shape);
   1.234 +                MOZ_ASSERT(shape->listp == &shape_);
   1.235 +            } else {
   1.236 +                MOZ_ASSERT(shape->listp == &prev->parent);
   1.237 +            }
   1.238 +            prev = shape;
   1.239 +        }
   1.240 +    } else {
   1.241 +        for (int n = throttle; --n >= 0 && shape->parent; shape = shape->parent) {
   1.242 +            if (shape->hasTable()) {
   1.243 +                ShapeTable &table = shape->table();
   1.244 +                MOZ_ASSERT(shape->parent);
   1.245 +                for (Shape::Range<NoGC> r(shape); !r.empty(); r.popFront()) {
   1.246 +                    Shape **spp = table.search(r.front().propid(), false);
   1.247 +                    MOZ_ASSERT(SHAPE_FETCH(spp) == &r.front());
   1.248 +                }
   1.249 +            }
   1.250 +            if (prev) {
   1.251 +                MOZ_ASSERT(prev->maybeSlot() >= shape->maybeSlot());
   1.252 +                shape->kids.checkConsistency(prev);
   1.253 +            }
   1.254 +            prev = shape;
   1.255 +        }
   1.256 +    }
   1.257 +}
   1.258 +#endif
   1.259 +
   1.260 +void
   1.261 +js::ObjectImpl::initializeSlotRange(uint32_t start, uint32_t length)
   1.262 +{
   1.263 +    /*
   1.264 +     * No bounds check, as this is used when the object's shape does not
   1.265 +     * reflect its allocated slots (updateSlotsForSpan).
   1.266 +     */
   1.267 +    HeapSlot *fixedStart, *fixedEnd, *slotsStart, *slotsEnd;
   1.268 +    getSlotRangeUnchecked(start, length, &fixedStart, &fixedEnd, &slotsStart, &slotsEnd);
   1.269 +
   1.270 +    JSRuntime *rt = runtimeFromAnyThread();
   1.271 +    uint32_t offset = start;
   1.272 +    for (HeapSlot *sp = fixedStart; sp < fixedEnd; sp++)
   1.273 +        sp->init(rt, this->asObjectPtr(), HeapSlot::Slot, offset++, UndefinedValue());
   1.274 +    for (HeapSlot *sp = slotsStart; sp < slotsEnd; sp++)
   1.275 +        sp->init(rt, this->asObjectPtr(), HeapSlot::Slot, offset++, UndefinedValue());
   1.276 +}
   1.277 +
   1.278 +void
   1.279 +js::ObjectImpl::initSlotRange(uint32_t start, const Value *vector, uint32_t length)
   1.280 +{
   1.281 +    JSRuntime *rt = runtimeFromAnyThread();
   1.282 +    HeapSlot *fixedStart, *fixedEnd, *slotsStart, *slotsEnd;
   1.283 +    getSlotRange(start, length, &fixedStart, &fixedEnd, &slotsStart, &slotsEnd);
   1.284 +    for (HeapSlot *sp = fixedStart; sp < fixedEnd; sp++)
   1.285 +        sp->init(rt, this->asObjectPtr(), HeapSlot::Slot, start++, *vector++);
   1.286 +    for (HeapSlot *sp = slotsStart; sp < slotsEnd; sp++)
   1.287 +        sp->init(rt, this->asObjectPtr(), HeapSlot::Slot, start++, *vector++);
   1.288 +}
   1.289 +
   1.290 +void
   1.291 +js::ObjectImpl::copySlotRange(uint32_t start, const Value *vector, uint32_t length)
   1.292 +{
   1.293 +    JS::Zone *zone = this->zone();
   1.294 +    HeapSlot *fixedStart, *fixedEnd, *slotsStart, *slotsEnd;
   1.295 +    getSlotRange(start, length, &fixedStart, &fixedEnd, &slotsStart, &slotsEnd);
   1.296 +    for (HeapSlot *sp = fixedStart; sp < fixedEnd; sp++)
   1.297 +        sp->set(zone, this->asObjectPtr(), HeapSlot::Slot, start++, *vector++);
   1.298 +    for (HeapSlot *sp = slotsStart; sp < slotsEnd; sp++)
   1.299 +        sp->set(zone, this->asObjectPtr(), HeapSlot::Slot, start++, *vector++);
   1.300 +}
   1.301 +
   1.302 +#ifdef DEBUG
   1.303 +bool
   1.304 +js::ObjectImpl::isProxy() const
   1.305 +{
   1.306 +    return asObjectPtr()->is<ProxyObject>();
   1.307 +}
   1.308 +
   1.309 +bool
   1.310 +js::ObjectImpl::slotInRange(uint32_t slot, SentinelAllowed sentinel) const
   1.311 +{
   1.312 +    uint32_t capacity = numFixedSlots() + numDynamicSlots();
   1.313 +    if (sentinel == SENTINEL_ALLOWED)
   1.314 +        return slot <= capacity;
   1.315 +    return slot < capacity;
   1.316 +}
   1.317 +#endif /* DEBUG */
   1.318 +
   1.319 +// See bug 844580.
   1.320 +#if defined(_MSC_VER)
   1.321 +# pragma optimize("g", off)
   1.322 +#endif
   1.323 +
   1.324 +#if defined(_MSC_VER) && _MSC_VER >= 1500
   1.325 +/*
   1.326 + * Work around a compiler bug in MSVC9 and above, where inlining this function
   1.327 + * causes stack pointer offsets to go awry and spp to refer to something higher
   1.328 + * up the stack.
   1.329 + */
   1.330 +MOZ_NEVER_INLINE
   1.331 +#endif
   1.332 +Shape *
   1.333 +js::ObjectImpl::nativeLookup(ExclusiveContext *cx, jsid id)
   1.334 +{
   1.335 +    MOZ_ASSERT(isNative());
   1.336 +    Shape **spp;
   1.337 +    return Shape::search(cx, lastProperty(), id, &spp);
   1.338 +}
   1.339 +
   1.340 +#if defined(_MSC_VER)
   1.341 +# pragma optimize("", on)
   1.342 +#endif
   1.343 +
   1.344 +Shape *
   1.345 +js::ObjectImpl::nativeLookupPure(jsid id)
   1.346 +{
   1.347 +    MOZ_ASSERT(isNative());
   1.348 +    return Shape::searchNoHashify(lastProperty(), id);
   1.349 +}
   1.350 +
   1.351 +uint32_t
   1.352 +js::ObjectImpl::dynamicSlotsCount(uint32_t nfixed, uint32_t span, const Class *clasp)
   1.353 +{
   1.354 +    if (span <= nfixed)
   1.355 +        return 0;
   1.356 +    span -= nfixed;
   1.357 +
   1.358 +    // Increase the slots to SLOT_CAPACITY_MIN to decrease the likelihood
   1.359 +    // the dynamic slots need to get increased again. ArrayObjects ignore
   1.360 +    // this because slots are uncommon in that case.
   1.361 +    if (clasp != &ArrayObject::class_ && span <= SLOT_CAPACITY_MIN)
   1.362 +        return SLOT_CAPACITY_MIN;
   1.363 +
   1.364 +    uint32_t slots = mozilla::RoundUpPow2(span);
   1.365 +    MOZ_ASSERT(slots >= span);
   1.366 +    return slots;
   1.367 +}
   1.368 +
   1.369 +void
   1.370 +js::ObjectImpl::markChildren(JSTracer *trc)
   1.371 +{
   1.372 +    MarkTypeObject(trc, &type_, "type");
   1.373 +
   1.374 +    MarkShape(trc, &shape_, "shape");
   1.375 +
   1.376 +    const Class *clasp = type_->clasp();
   1.377 +    JSObject *obj = asObjectPtr();
   1.378 +    if (clasp->trace)
   1.379 +        clasp->trace(trc, obj);
   1.380 +
   1.381 +    if (shape_->isNative()) {
   1.382 +        MarkObjectSlots(trc, obj, 0, obj->slotSpan());
   1.383 +        gc::MarkArraySlots(trc, obj->getDenseInitializedLength(), obj->getDenseElements(), "objectElements");
   1.384 +    }
   1.385 +}
   1.386 +
   1.387 +void
   1.388 +AutoPropDescRooter::trace(JSTracer *trc)
   1.389 +{
   1.390 +    gc::MarkValueRoot(trc, &propDesc.pd_, "AutoPropDescRooter pd");
   1.391 +    gc::MarkValueRoot(trc, &propDesc.value_, "AutoPropDescRooter value");
   1.392 +    gc::MarkValueRoot(trc, &propDesc.get_, "AutoPropDescRooter get");
   1.393 +    gc::MarkValueRoot(trc, &propDesc.set_, "AutoPropDescRooter set");
   1.394 +}

mercurial