js/src/vm/ObjectImpl.cpp

branch
TOR_BUG_3246
changeset 7
129ffea94266
equal deleted inserted replaced
-1:000000000000 0:db0e607e4076
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/. */
6
7 #include "vm/ObjectImpl-inl.h"
8
9 #include "gc/Marking.h"
10 #include "js/Value.h"
11 #include "vm/Debugger.h"
12
13 #include "jsobjinlines.h"
14 #include "vm/Shape-inl.h"
15
16 using namespace js;
17
18 using JS::GenericNaN;
19
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 }
35
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 }
48
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 }
61
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 }
73
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());
83
84 *unwrapped = *this;
85
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 }
95
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 }
105
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 }
115
116 return true;
117 }
118
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());
129
130 JSCompartment *comp = cx->compartment();
131
132 *wrappedId = id;
133 if (!comp->wrapId(cx, wrappedId))
134 return false;
135
136 *desc = *this;
137 RootedValue value(cx, desc->value_);
138 RootedValue get(cx, desc->get_);
139 RootedValue set(cx, desc->set_);
140
141 if (!comp->wrap(cx, &value) || !comp->wrap(cx, &get) || !comp->wrap(cx, &set))
142 return false;
143
144 desc->value_ = value;
145 desc->get_ = get;
146 desc->set_ = set;
147 return !obj->is<ProxyObject>() || desc->makeObject(cx);
148 }
149
150 static const ObjectElements emptyElementsHeader(0, 0);
151
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));
155
156 #ifdef DEBUG
157
158 bool
159 ObjectImpl::canHaveNonEmptyElements()
160 {
161 JSObject *obj = static_cast<JSObject *>(this);
162 return isNative() && !obj->is<TypedArrayObject>();
163 }
164
165 #endif // DEBUG
166
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);
177
178 ObjectElements *header = ObjectElements::fromElements(elementsHeapPtr);
179 JS_ASSERT(!header->shouldConvertDoubleElements());
180
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 }
186
187 header->setShouldConvertDoubleElements();
188 return true;
189 }
190
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;
204
205 MOZ_ASSERT(isNative());
206
207 Shape *shape = lastProperty();
208 Shape *prev = nullptr;
209
210 if (inDictionaryMode()) {
211 MOZ_ASSERT(shape->hasTable());
212
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 }
218
219 for (int n = throttle; --n >= 0 && shape->parent; shape = shape->parent) {
220 MOZ_ASSERT_IF(lastProperty() != shape, !shape->hasTable());
221
222 Shape **spp = table.search(shape->propid(), false);
223 MOZ_ASSERT(SHAPE_FETCH(spp) == shape);
224 }
225
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
256
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);
266
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 }
274
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 }
286
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 }
298
299 #ifdef DEBUG
300 bool
301 js::ObjectImpl::isProxy() const
302 {
303 return asObjectPtr()->is<ProxyObject>();
304 }
305
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 */
315
316 // See bug 844580.
317 #if defined(_MSC_VER)
318 # pragma optimize("g", off)
319 #endif
320
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 }
336
337 #if defined(_MSC_VER)
338 # pragma optimize("", on)
339 #endif
340
341 Shape *
342 js::ObjectImpl::nativeLookupPure(jsid id)
343 {
344 MOZ_ASSERT(isNative());
345 return Shape::searchNoHashify(lastProperty(), id);
346 }
347
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;
354
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;
360
361 uint32_t slots = mozilla::RoundUpPow2(span);
362 MOZ_ASSERT(slots >= span);
363 return slots;
364 }
365
366 void
367 js::ObjectImpl::markChildren(JSTracer *trc)
368 {
369 MarkTypeObject(trc, &type_, "type");
370
371 MarkShape(trc, &shape_, "shape");
372
373 const Class *clasp = type_->clasp();
374 JSObject *obj = asObjectPtr();
375 if (clasp->trace)
376 clasp->trace(trc, obj);
377
378 if (shape_->isNative()) {
379 MarkObjectSlots(trc, obj, 0, obj->slotSpan());
380 gc::MarkArraySlots(trc, obj->getDenseInitializedLength(), obj->getDenseElements(), "objectElements");
381 }
382 }
383
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