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 +}