js/src/vm/ObjectImpl.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

     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/. */
     7 #include "vm/ObjectImpl-inl.h"
     9 #include "gc/Marking.h"
    10 #include "js/Value.h"
    11 #include "vm/Debugger.h"
    13 #include "jsobjinlines.h"
    14 #include "vm/Shape-inl.h"
    16 using namespace js;
    18 using JS::GenericNaN;
    20 PropDesc::PropDesc()
    21   : pd_(UndefinedValue()),
    22     value_(UndefinedValue()),
    23     get_(UndefinedValue()),
    24     set_(UndefinedValue()),
    25     attrs(0),
    26     hasGet_(false),
    27     hasSet_(false),
    28     hasValue_(false),
    29     hasWritable_(false),
    30     hasEnumerable_(false),
    31     hasConfigurable_(false),
    32     isUndefined_(true)
    33 {
    34 }
    36 bool
    37 PropDesc::checkGetter(JSContext *cx)
    38 {
    39     if (hasGet_) {
    40         if (!js_IsCallable(get_) && !get_.isUndefined()) {
    41             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_GET_SET_FIELD,
    42                                  js_getter_str);
    43             return false;
    44         }
    45     }
    46     return true;
    47 }
    49 bool
    50 PropDesc::checkSetter(JSContext *cx)
    51 {
    52     if (hasSet_) {
    53         if (!js_IsCallable(set_) && !set_.isUndefined()) {
    54             JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_BAD_GET_SET_FIELD,
    55                                  js_setter_str);
    56             return false;
    57         }
    58     }
    59     return true;
    60 }
    62 static bool
    63 CheckArgCompartment(JSContext *cx, JSObject *obj, HandleValue v,
    64                     const char *methodname, const char *propname)
    65 {
    66     if (v.isObject() && v.toObject().compartment() != obj->compartment()) {
    67         JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_DEBUG_COMPARTMENT_MISMATCH,
    68                              methodname, propname);
    69         return false;
    70     }
    71     return true;
    72 }
    74 /*
    75  * Convert Debugger.Objects in desc to debuggee values.
    76  * Reject non-callable getters and setters.
    77  */
    78 bool
    79 PropDesc::unwrapDebuggerObjectsInto(JSContext *cx, Debugger *dbg, HandleObject obj,
    80                                     PropDesc *unwrapped) const
    81 {
    82     MOZ_ASSERT(!isUndefined());
    84     *unwrapped = *this;
    86     if (unwrapped->hasValue()) {
    87         RootedValue value(cx, unwrapped->value_);
    88         if (!dbg->unwrapDebuggeeValue(cx, &value) ||
    89             !CheckArgCompartment(cx, obj, value, "defineProperty", "value"))
    90         {
    91             return false;
    92         }
    93         unwrapped->value_ = value;
    94     }
    96     if (unwrapped->hasGet()) {
    97         RootedValue get(cx, unwrapped->get_);
    98         if (!dbg->unwrapDebuggeeValue(cx, &get) ||
    99             !CheckArgCompartment(cx, obj, get, "defineProperty", "get"))
   100         {
   101             return false;
   102         }
   103         unwrapped->get_ = get;
   104     }
   106     if (unwrapped->hasSet()) {
   107         RootedValue set(cx, unwrapped->set_);
   108         if (!dbg->unwrapDebuggeeValue(cx, &set) ||
   109             !CheckArgCompartment(cx, obj, set, "defineProperty", "set"))
   110         {
   111             return false;
   112         }
   113         unwrapped->set_ = set;
   114     }
   116     return true;
   117 }
   119 /*
   120  * Rewrap *idp and the fields of *desc for the current compartment.  Also:
   121  * defining a property on a proxy requires pd_ to contain a descriptor object,
   122  * so reconstitute desc->pd_ if needed.
   123  */
   124 bool
   125 PropDesc::wrapInto(JSContext *cx, HandleObject obj, const jsid &id, jsid *wrappedId,
   126                    PropDesc *desc) const
   127 {
   128     MOZ_ASSERT(!isUndefined());
   130     JSCompartment *comp = cx->compartment();
   132     *wrappedId = id;
   133     if (!comp->wrapId(cx, wrappedId))
   134         return false;
   136     *desc = *this;
   137     RootedValue value(cx, desc->value_);
   138     RootedValue get(cx, desc->get_);
   139     RootedValue set(cx, desc->set_);
   141     if (!comp->wrap(cx, &value) || !comp->wrap(cx, &get) || !comp->wrap(cx, &set))
   142         return false;
   144     desc->value_ = value;
   145     desc->get_ = get;
   146     desc->set_ = set;
   147     return !obj->is<ProxyObject>() || desc->makeObject(cx);
   148 }
   150 static const ObjectElements emptyElementsHeader(0, 0);
   152 /* Objects with no elements share one empty set of elements. */
   153 HeapSlot *const js::emptyObjectElements =
   154     reinterpret_cast<HeapSlot *>(uintptr_t(&emptyElementsHeader) + sizeof(ObjectElements));
   156 #ifdef DEBUG
   158 bool
   159 ObjectImpl::canHaveNonEmptyElements()
   160 {
   161     JSObject *obj = static_cast<JSObject *>(this);
   162     return isNative() && !obj->is<TypedArrayObject>();
   163 }
   165 #endif // DEBUG
   167 /* static */ bool
   168 ObjectElements::ConvertElementsToDoubles(JSContext *cx, uintptr_t elementsPtr)
   169 {
   170     /*
   171      * This function is infallible, but has a fallible interface so that it can
   172      * be called directly from Ion code. Only arrays can have their dense
   173      * elements converted to doubles, and arrays never have empty elements.
   174      */
   175     HeapSlot *elementsHeapPtr = (HeapSlot *) elementsPtr;
   176     JS_ASSERT(elementsHeapPtr != emptyObjectElements);
   178     ObjectElements *header = ObjectElements::fromElements(elementsHeapPtr);
   179     JS_ASSERT(!header->shouldConvertDoubleElements());
   181     Value *vp = (Value *) elementsPtr;
   182     for (size_t i = 0; i < header->initializedLength; i++) {
   183         if (vp[i].isInt32())
   184             vp[i].setDouble(vp[i].toInt32());
   185     }
   187     header->setShouldConvertDoubleElements();
   188     return true;
   189 }
   191 #ifdef DEBUG
   192 void
   193 js::ObjectImpl::checkShapeConsistency()
   194 {
   195     static int throttle = -1;
   196     if (throttle < 0) {
   197         if (const char *var = getenv("JS_CHECK_SHAPE_THROTTLE"))
   198             throttle = atoi(var);
   199         if (throttle < 0)
   200             throttle = 0;
   201     }
   202     if (throttle == 0)
   203         return;
   205     MOZ_ASSERT(isNative());
   207     Shape *shape = lastProperty();
   208     Shape *prev = nullptr;
   210     if (inDictionaryMode()) {
   211         MOZ_ASSERT(shape->hasTable());
   213         ShapeTable &table = shape->table();
   214         for (uint32_t fslot = table.freelist; fslot != SHAPE_INVALID_SLOT;
   215              fslot = getSlot(fslot).toPrivateUint32()) {
   216             MOZ_ASSERT(fslot < slotSpan());
   217         }
   219         for (int n = throttle; --n >= 0 && shape->parent; shape = shape->parent) {
   220             MOZ_ASSERT_IF(lastProperty() != shape, !shape->hasTable());
   222             Shape **spp = table.search(shape->propid(), false);
   223             MOZ_ASSERT(SHAPE_FETCH(spp) == shape);
   224         }
   226         shape = lastProperty();
   227         for (int n = throttle; --n >= 0 && shape; shape = shape->parent) {
   228             MOZ_ASSERT_IF(shape->slot() != SHAPE_INVALID_SLOT, shape->slot() < slotSpan());
   229             if (!prev) {
   230                 MOZ_ASSERT(lastProperty() == shape);
   231                 MOZ_ASSERT(shape->listp == &shape_);
   232             } else {
   233                 MOZ_ASSERT(shape->listp == &prev->parent);
   234             }
   235             prev = shape;
   236         }
   237     } else {
   238         for (int n = throttle; --n >= 0 && shape->parent; shape = shape->parent) {
   239             if (shape->hasTable()) {
   240                 ShapeTable &table = shape->table();
   241                 MOZ_ASSERT(shape->parent);
   242                 for (Shape::Range<NoGC> r(shape); !r.empty(); r.popFront()) {
   243                     Shape **spp = table.search(r.front().propid(), false);
   244                     MOZ_ASSERT(SHAPE_FETCH(spp) == &r.front());
   245                 }
   246             }
   247             if (prev) {
   248                 MOZ_ASSERT(prev->maybeSlot() >= shape->maybeSlot());
   249                 shape->kids.checkConsistency(prev);
   250             }
   251             prev = shape;
   252         }
   253     }
   254 }
   255 #endif
   257 void
   258 js::ObjectImpl::initializeSlotRange(uint32_t start, uint32_t length)
   259 {
   260     /*
   261      * No bounds check, as this is used when the object's shape does not
   262      * reflect its allocated slots (updateSlotsForSpan).
   263      */
   264     HeapSlot *fixedStart, *fixedEnd, *slotsStart, *slotsEnd;
   265     getSlotRangeUnchecked(start, length, &fixedStart, &fixedEnd, &slotsStart, &slotsEnd);
   267     JSRuntime *rt = runtimeFromAnyThread();
   268     uint32_t offset = start;
   269     for (HeapSlot *sp = fixedStart; sp < fixedEnd; sp++)
   270         sp->init(rt, this->asObjectPtr(), HeapSlot::Slot, offset++, UndefinedValue());
   271     for (HeapSlot *sp = slotsStart; sp < slotsEnd; sp++)
   272         sp->init(rt, this->asObjectPtr(), HeapSlot::Slot, offset++, UndefinedValue());
   273 }
   275 void
   276 js::ObjectImpl::initSlotRange(uint32_t start, const Value *vector, uint32_t length)
   277 {
   278     JSRuntime *rt = runtimeFromAnyThread();
   279     HeapSlot *fixedStart, *fixedEnd, *slotsStart, *slotsEnd;
   280     getSlotRange(start, length, &fixedStart, &fixedEnd, &slotsStart, &slotsEnd);
   281     for (HeapSlot *sp = fixedStart; sp < fixedEnd; sp++)
   282         sp->init(rt, this->asObjectPtr(), HeapSlot::Slot, start++, *vector++);
   283     for (HeapSlot *sp = slotsStart; sp < slotsEnd; sp++)
   284         sp->init(rt, this->asObjectPtr(), HeapSlot::Slot, start++, *vector++);
   285 }
   287 void
   288 js::ObjectImpl::copySlotRange(uint32_t start, const Value *vector, uint32_t length)
   289 {
   290     JS::Zone *zone = this->zone();
   291     HeapSlot *fixedStart, *fixedEnd, *slotsStart, *slotsEnd;
   292     getSlotRange(start, length, &fixedStart, &fixedEnd, &slotsStart, &slotsEnd);
   293     for (HeapSlot *sp = fixedStart; sp < fixedEnd; sp++)
   294         sp->set(zone, this->asObjectPtr(), HeapSlot::Slot, start++, *vector++);
   295     for (HeapSlot *sp = slotsStart; sp < slotsEnd; sp++)
   296         sp->set(zone, this->asObjectPtr(), HeapSlot::Slot, start++, *vector++);
   297 }
   299 #ifdef DEBUG
   300 bool
   301 js::ObjectImpl::isProxy() const
   302 {
   303     return asObjectPtr()->is<ProxyObject>();
   304 }
   306 bool
   307 js::ObjectImpl::slotInRange(uint32_t slot, SentinelAllowed sentinel) const
   308 {
   309     uint32_t capacity = numFixedSlots() + numDynamicSlots();
   310     if (sentinel == SENTINEL_ALLOWED)
   311         return slot <= capacity;
   312     return slot < capacity;
   313 }
   314 #endif /* DEBUG */
   316 // See bug 844580.
   317 #if defined(_MSC_VER)
   318 # pragma optimize("g", off)
   319 #endif
   321 #if defined(_MSC_VER) && _MSC_VER >= 1500
   322 /*
   323  * Work around a compiler bug in MSVC9 and above, where inlining this function
   324  * causes stack pointer offsets to go awry and spp to refer to something higher
   325  * up the stack.
   326  */
   327 MOZ_NEVER_INLINE
   328 #endif
   329 Shape *
   330 js::ObjectImpl::nativeLookup(ExclusiveContext *cx, jsid id)
   331 {
   332     MOZ_ASSERT(isNative());
   333     Shape **spp;
   334     return Shape::search(cx, lastProperty(), id, &spp);
   335 }
   337 #if defined(_MSC_VER)
   338 # pragma optimize("", on)
   339 #endif
   341 Shape *
   342 js::ObjectImpl::nativeLookupPure(jsid id)
   343 {
   344     MOZ_ASSERT(isNative());
   345     return Shape::searchNoHashify(lastProperty(), id);
   346 }
   348 uint32_t
   349 js::ObjectImpl::dynamicSlotsCount(uint32_t nfixed, uint32_t span, const Class *clasp)
   350 {
   351     if (span <= nfixed)
   352         return 0;
   353     span -= nfixed;
   355     // Increase the slots to SLOT_CAPACITY_MIN to decrease the likelihood
   356     // the dynamic slots need to get increased again. ArrayObjects ignore
   357     // this because slots are uncommon in that case.
   358     if (clasp != &ArrayObject::class_ && span <= SLOT_CAPACITY_MIN)
   359         return SLOT_CAPACITY_MIN;
   361     uint32_t slots = mozilla::RoundUpPow2(span);
   362     MOZ_ASSERT(slots >= span);
   363     return slots;
   364 }
   366 void
   367 js::ObjectImpl::markChildren(JSTracer *trc)
   368 {
   369     MarkTypeObject(trc, &type_, "type");
   371     MarkShape(trc, &shape_, "shape");
   373     const Class *clasp = type_->clasp();
   374     JSObject *obj = asObjectPtr();
   375     if (clasp->trace)
   376         clasp->trace(trc, obj);
   378     if (shape_->isNative()) {
   379         MarkObjectSlots(trc, obj, 0, obj->slotSpan());
   380         gc::MarkArraySlots(trc, obj->getDenseInitializedLength(), obj->getDenseElements(), "objectElements");
   381     }
   382 }
   384 void
   385 AutoPropDescRooter::trace(JSTracer *trc)
   386 {
   387     gc::MarkValueRoot(trc, &propDesc.pd_, "AutoPropDescRooter pd");
   388     gc::MarkValueRoot(trc, &propDesc.value_, "AutoPropDescRooter value");
   389     gc::MarkValueRoot(trc, &propDesc.get_, "AutoPropDescRooter get");
   390     gc::MarkValueRoot(trc, &propDesc.set_, "AutoPropDescRooter set");
   391 }

mercurial