1 : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 : * vim: set ts=8 sw=4 et tw=78:
3 : *
4 : * ***** BEGIN LICENSE BLOCK *****
5 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 : *
7 : * The contents of this file are subject to the Mozilla Public License Version
8 : * 1.1 (the "License"); you may not use this file except in compliance with
9 : * the License. You may obtain a copy of the License at
10 : * http://www.mozilla.org/MPL/
11 : *
12 : * Software distributed under the License is distributed on an "AS IS" basis,
13 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 : * for the specific language governing rights and limitations under the
15 : * License.
16 : *
17 : * The Original Code is Mozilla Communicator client code, released
18 : * March 31, 1998.
19 : *
20 : * The Initial Developer of the Original Code is
21 : * Netscape Communications Corporation.
22 : * Portions created by the Initial Developer are Copyright (C) 1998
23 : * the Initial Developer. All Rights Reserved.
24 : *
25 : * Contributor(s):
26 : *
27 : * Alternatively, the contents of this file may be used under the terms of
28 : * either of the GNU General Public License Version 2 or later (the "GPL"),
29 : * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 : * in which case the provisions of the GPL or the LGPL are applicable instead
31 : * of those above. If you wish to allow use of your version of this file only
32 : * under the terms of either the GPL or the LGPL, and not to allow others to
33 : * use your version of this file under the terms of the MPL, indicate your
34 : * decision by deleting the provisions above and replace them with the notice
35 : * and other provisions required by the GPL or the LGPL. If you do not delete
36 : * the provisions above, a recipient may use your version of this file under
37 : * the terms of any one of the MPL, the GPL or the LGPL.
38 : *
39 : * ***** END LICENSE BLOCK ***** */
40 :
41 : /*
42 : * JS symbol tables.
43 : */
44 : #include <new>
45 : #include <stdlib.h>
46 : #include <string.h>
47 : #include "jstypes.h"
48 : #include "jsclist.h"
49 : #include "jsutil.h"
50 : #include "jsapi.h"
51 : #include "jsatom.h"
52 : #include "jscntxt.h"
53 : #include "jsdbgapi.h"
54 : #include "jslock.h"
55 : #include "jsnum.h"
56 : #include "jsobj.h"
57 : #include "jsscope.h"
58 : #include "jsstr.h"
59 :
60 : #include "js/MemoryMetrics.h"
61 :
62 : #include "jsatominlines.h"
63 : #include "jsobjinlines.h"
64 : #include "jsscopeinlines.h"
65 :
66 : using namespace js;
67 : using namespace js::gc;
68 :
69 : bool
70 127390 : PropertyTable::init(JSRuntime *rt, Shape *lastProp)
71 : {
72 : /*
73 : * Either we're creating a table for a large scope that was populated
74 : * via property cache hit logic under JSOP_INITPROP, JSOP_SETNAME, or
75 : * JSOP_SETPROP; or else calloc failed at least once already. In any
76 : * event, let's try to grow, overallocating to hold at least twice the
77 : * current population.
78 : */
79 127390 : uint32_t sizeLog2 = JS_CEILING_LOG2W(2 * entryCount);
80 127390 : if (sizeLog2 < MIN_SIZE_LOG2)
81 17127 : sizeLog2 = MIN_SIZE_LOG2;
82 :
83 : /*
84 : * Use rt->calloc_ for memory accounting and overpressure handling
85 : * without OOM reporting. See PropertyTable::change.
86 : */
87 127390 : entries = (Shape **) rt->calloc_(sizeOfEntries(JS_BIT(sizeLog2)));
88 127390 : if (!entries)
89 0 : return false;
90 :
91 127390 : hashShift = HASH_BITS - sizeLog2;
92 3066744 : for (Shape::Range r = lastProp->all(); !r.empty(); r.popFront()) {
93 2939354 : const Shape &shape = r.front();
94 2939354 : Shape **spp = search(shape.propid(), true);
95 :
96 : /*
97 : * Beware duplicate args and arg vs. var conflicts: the youngest shape
98 : * (nearest to lastProp) must win. See bug 600067.
99 : */
100 2939354 : if (!SHAPE_FETCH(spp))
101 2939354 : SHAPE_STORE_PRESERVING_COLLISION(spp, &shape);
102 : }
103 127390 : return true;
104 : }
105 :
106 : bool
107 127390 : Shape::makeOwnBaseShape(JSContext *cx)
108 : {
109 127390 : JS_ASSERT(!base()->isOwned());
110 127390 : assertSameCompartment(cx, compartment());
111 :
112 254780 : RootedVarShape self(cx, this);
113 :
114 127390 : BaseShape *nbase = js_NewGCBaseShape(cx);
115 127390 : if (!nbase)
116 0 : return false;
117 :
118 127390 : new (nbase) BaseShape(*self->base());
119 127390 : nbase->setOwned(self->base()->toUnowned());
120 :
121 127390 : self->base_ = nbase;
122 :
123 127390 : return true;
124 : }
125 :
126 : void
127 1965616 : Shape::handoffTableTo(Shape *shape)
128 : {
129 1965616 : JS_ASSERT(inDictionary() && shape->inDictionary());
130 :
131 1965616 : if (this == shape)
132 2133 : return;
133 :
134 1963483 : JS_ASSERT(base()->isOwned() && !shape->base()->isOwned());
135 :
136 1963483 : BaseShape *nbase = base();
137 :
138 1963483 : JS_ASSERT_IF(shape->hasSlot(), nbase->slotSpan() > shape->slot());
139 :
140 1963483 : this->base_ = nbase->baseUnowned();
141 1963483 : nbase->adoptUnowned(shape->base()->toUnowned());
142 :
143 1963483 : shape->base_ = nbase;
144 : }
145 :
146 : bool
147 127390 : Shape::hashify(JSContext *cx)
148 : {
149 127390 : JS_ASSERT(!hasTable());
150 :
151 254780 : RootedVarShape self(cx, this);
152 :
153 127390 : if (!ensureOwnBaseShape(cx))
154 0 : return false;
155 :
156 127390 : JSRuntime *rt = cx->runtime;
157 127390 : PropertyTable *table = rt->new_<PropertyTable>(self->entryCount());
158 127390 : if (!table)
159 0 : return false;
160 :
161 127390 : if (!table->init(rt, self)) {
162 0 : rt->free_(table);
163 0 : return false;
164 : }
165 :
166 127390 : self->base()->setTable(table);
167 127390 : return true;
168 : }
169 :
170 : /*
171 : * Double hashing needs the second hash code to be relatively prime to table
172 : * size, so we simply make hash2 odd.
173 : */
174 : #define HASH1(hash0,shift) ((hash0) >> (shift))
175 : #define HASH2(hash0,log2,shift) ((((hash0) << (log2)) >> (shift)) | 1)
176 :
177 : Shape **
178 55237604 : PropertyTable::search(jsid id, bool adding)
179 : {
180 : JSHashNumber hash0, hash1, hash2;
181 : int sizeLog2;
182 : Shape *stored, *shape, **spp, **firstRemoved;
183 : uint32_t sizeMask;
184 :
185 55237604 : JS_ASSERT(entries);
186 55237604 : JS_ASSERT(!JSID_IS_EMPTY(id));
187 :
188 : /* Compute the primary hash address. */
189 55237604 : hash0 = HashId(id);
190 55237604 : hash1 = HASH1(hash0, hashShift);
191 55237604 : spp = entries + hash1;
192 :
193 : /* Miss: return space for a new entry. */
194 55237604 : stored = *spp;
195 55237604 : if (SHAPE_IS_FREE(stored))
196 13840400 : return spp;
197 :
198 : /* Hit: return entry. */
199 41397204 : shape = SHAPE_CLEAR_COLLISION(stored);
200 41397204 : if (shape && shape->propid() == id)
201 25582412 : return spp;
202 :
203 : /* Collision: double hash. */
204 15814792 : sizeLog2 = HASH_BITS - hashShift;
205 15814792 : hash2 = HASH2(hash0, sizeLog2, hashShift);
206 15814792 : sizeMask = JS_BITMASK(sizeLog2);
207 :
208 : #ifdef DEBUG
209 15814792 : uintptr_t collision_flag = SHAPE_COLLISION;
210 : #endif
211 :
212 : /* Save the first removed entry pointer so we can recycle it if adding. */
213 15814792 : if (SHAPE_IS_REMOVED(stored)) {
214 16299 : firstRemoved = spp;
215 : } else {
216 15798493 : firstRemoved = NULL;
217 15798493 : if (adding && !SHAPE_HAD_COLLISION(stored))
218 855947 : SHAPE_FLAG_COLLISION(spp, shape);
219 : #ifdef DEBUG
220 15798493 : collision_flag &= uintptr_t(*spp) & SHAPE_COLLISION;
221 : #endif
222 : }
223 :
224 10128544 : for (;;) {
225 25943336 : hash1 -= hash2;
226 25943336 : hash1 &= sizeMask;
227 25943336 : spp = entries + hash1;
228 :
229 25943336 : stored = *spp;
230 25943336 : if (SHAPE_IS_FREE(stored))
231 6074524 : return (adding && firstRemoved) ? firstRemoved : spp;
232 :
233 19868812 : shape = SHAPE_CLEAR_COLLISION(stored);
234 19868812 : if (shape && shape->propid() == id) {
235 9740268 : JS_ASSERT(collision_flag);
236 9740268 : return spp;
237 : }
238 :
239 10128544 : if (SHAPE_IS_REMOVED(stored)) {
240 10242 : if (!firstRemoved)
241 8739 : firstRemoved = spp;
242 : } else {
243 10118302 : if (adding && !SHAPE_HAD_COLLISION(stored))
244 689321 : SHAPE_FLAG_COLLISION(spp, shape);
245 : #ifdef DEBUG
246 10118302 : collision_flag &= uintptr_t(*spp) & SHAPE_COLLISION;
247 : #endif
248 : }
249 : }
250 :
251 : /* NOTREACHED */
252 : return NULL;
253 : }
254 :
255 : bool
256 4797 : PropertyTable::change(int log2Delta, JSContext *cx)
257 : {
258 4797 : JS_ASSERT(entries);
259 :
260 : /*
261 : * Grow, shrink, or compress by changing this->entries.
262 : */
263 4797 : int oldlog2 = HASH_BITS - hashShift;
264 4797 : int newlog2 = oldlog2 + log2Delta;
265 4797 : uint32_t oldsize = JS_BIT(oldlog2);
266 4797 : uint32_t newsize = JS_BIT(newlog2);
267 4797 : Shape **newTable = (Shape **) cx->calloc_(sizeOfEntries(newsize));
268 4797 : if (!newTable)
269 0 : return false;
270 :
271 : /* Now that we have newTable allocated, update members. */
272 4797 : hashShift = HASH_BITS - newlog2;
273 4797 : removedCount = 0;
274 4797 : Shape **oldTable = entries;
275 4797 : entries = newTable;
276 :
277 : /* Copy only live entries, leaving removed and free ones behind. */
278 2560845 : for (Shape **oldspp = oldTable; oldsize != 0; oldspp++) {
279 2556048 : Shape *shape = SHAPE_FETCH(oldspp);
280 2556048 : if (shape) {
281 1917036 : Shape **spp = search(shape->propid(), true);
282 1917036 : JS_ASSERT(SHAPE_IS_FREE(*spp));
283 1917036 : *spp = shape;
284 : }
285 2556048 : oldsize--;
286 : }
287 :
288 : /* Finally, free the old entries storage. */
289 4797 : cx->free_(oldTable);
290 4797 : return true;
291 : }
292 :
293 : bool
294 4797 : PropertyTable::grow(JSContext *cx)
295 : {
296 4797 : JS_ASSERT(needsToGrow());
297 :
298 4797 : uint32_t size = capacity();
299 4797 : int delta = removedCount < size >> 2;
300 :
301 4797 : if (!change(delta, cx) && entryCount + removedCount == size - 1) {
302 0 : JS_ReportOutOfMemory(cx);
303 0 : return false;
304 : }
305 4797 : return true;
306 : }
307 :
308 : Shape *
309 355342 : Shape::getChildBinding(JSContext *cx, const StackShape &child)
310 : {
311 355342 : JS_ASSERT(!inDictionary());
312 :
313 355342 : Shape *shape = JS_PROPERTY_TREE(cx).getChild(cx, this, numFixedSlots(), child);
314 355342 : if (shape) {
315 355342 : JS_ASSERT(shape->parent == this);
316 :
317 : /*
318 : * Update the number of fixed slots which bindings of this shape will
319 : * have. Bindings are constructed as new properties come in, so the
320 : * call object allocation class is not known ahead of time. Compute
321 : * the fixed slot count here, which will feed into call objects created
322 : * off of the bindings.
323 : */
324 355342 : uint32_t slots = child.slotSpan() + 1; /* Add one for private data. */
325 355342 : gc::AllocKind kind = gc::GetGCObjectKind(slots);
326 :
327 : /*
328 : * Make sure that the arguments and variables in the call object all
329 : * end up in a contiguous range of slots. We need this to be able to
330 : * embed the args/vars arrays in the TypeScriptNesting for the function
331 : * after the call object's frame has finished.
332 : */
333 355342 : uint32_t nfixed = gc::GetGCKindSlots(kind);
334 355342 : if (nfixed < slots) {
335 225225 : nfixed = CallObject::RESERVED_SLOTS + 1;
336 225225 : JS_ASSERT(gc::GetGCKindSlots(gc::GetGCObjectKind(nfixed)) == CallObject::RESERVED_SLOTS + 1);
337 : }
338 :
339 355342 : shape->setNumFixedSlots(nfixed - 1);
340 : }
341 355342 : return shape;
342 : }
343 :
344 : /* static */ Shape *
345 1843423 : Shape::replaceLastProperty(JSContext *cx, const StackBaseShape &base, JSObject *proto, Shape *shape)
346 : {
347 1843423 : JS_ASSERT(!shape->inDictionary());
348 :
349 1843423 : if (!shape->parent) {
350 : /* Treat as resetting the initial property of the shape hierarchy. */
351 1058785 : AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots());
352 : return EmptyShape::getInitialShape(cx, base.clasp, proto,
353 : base.parent, kind,
354 1058785 : base.flags & BaseShape::OBJECT_FLAG_MASK);
355 : }
356 :
357 1569276 : RootShape root(cx, &shape);
358 :
359 784638 : UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base);
360 784638 : if (!nbase)
361 0 : return NULL;
362 :
363 784638 : StackShape child(shape);
364 784638 : child.base = nbase;
365 :
366 784638 : return JS_PROPERTY_TREE(cx).getChild(cx, shape->parent, shape->numFixedSlots(), child);
367 : }
368 :
369 : /*
370 : * Get or create a property-tree or dictionary child property of |parent|,
371 : * which must be lastProperty() if inDictionaryMode(), else parent must be
372 : * one of lastProperty() or lastProperty()->parent.
373 : */
374 : Shape *
375 30109677 : JSObject::getChildProperty(JSContext *cx, Shape *parent, StackShape &child)
376 : {
377 : /*
378 : * Shared properties have no slot, but slot_ will reflect that of parent.
379 : * Unshared properties allocate a slot here but may lose it due to a
380 : * JS_ClearScope call.
381 : */
382 30109677 : if (!child.hasSlot()) {
383 11505189 : child.setSlot(parent->maybeSlot());
384 : } else {
385 18604488 : if (child.hasMissingSlot()) {
386 : uint32_t slot;
387 18130374 : if (!allocSlot(cx, &slot))
388 0 : return NULL;
389 18130374 : child.setSlot(slot);
390 : } else {
391 : /* Slots can only be allocated out of order on objects in dictionary mode. */
392 1140413 : JS_ASSERT(inDictionaryMode() ||
393 : parent->hasMissingSlot() ||
394 1140413 : child.slot() == parent->maybeSlot() + 1);
395 : }
396 : }
397 :
398 : Shape *shape;
399 :
400 60219354 : RootedVarObject self(cx, this);
401 :
402 30109677 : if (inDictionaryMode()) {
403 1663231 : JS_ASSERT(parent == lastProperty());
404 3326462 : RootStackShape childRoot(cx, &child);
405 1663231 : shape = js_NewGCShape(cx);
406 1663231 : if (!shape)
407 0 : return NULL;
408 1663231 : if (child.hasSlot() && child.slot() >= self->lastProperty()->base()->slotSpan()) {
409 0 : if (!self->setSlotSpan(cx, child.slot() + 1))
410 0 : return NULL;
411 : }
412 3326462 : shape->initDictionaryShape(child, self->numFixedSlots(), &self->shape_);
413 : } else {
414 28446446 : shape = JS_PROPERTY_TREE(cx).getChild(cx, parent, self->numFixedSlots(), child);
415 28446446 : if (!shape)
416 0 : return NULL;
417 : //JS_ASSERT(shape->parent == parent);
418 : //JS_ASSERT_IF(parent != lastProperty(), parent == lastProperty()->parent);
419 28446446 : if (!self->setLastProperty(cx, shape))
420 0 : return NULL;
421 : }
422 :
423 30109677 : return shape;
424 : }
425 :
426 : bool
427 119895 : JSObject::toDictionaryMode(JSContext *cx)
428 : {
429 119895 : JS_ASSERT(!inDictionaryMode());
430 :
431 : /* We allocate the shapes from cx->compartment, so make sure it's right. */
432 119895 : JS_ASSERT(compartment() == cx->compartment);
433 :
434 119895 : uint32_t span = slotSpan();
435 :
436 239790 : RootedVarObject self(cx, this);
437 :
438 : /*
439 : * Clone the shapes into a new dictionary list. Don't update the
440 : * last property of this object until done, otherwise a GC
441 : * triggered while creating the dictionary will get the wrong
442 : * slot span for this object.
443 : */
444 239790 : RootedVarShape root(cx);
445 239790 : RootedVarShape dictionaryShape(cx);
446 :
447 239790 : RootedVarShape shape(cx);
448 119895 : shape = lastProperty();
449 :
450 3000569 : while (shape) {
451 2760779 : JS_ASSERT(!shape->inDictionary());
452 :
453 2760779 : Shape *dprop = js_NewGCShape(cx);
454 2760779 : if (!dprop) {
455 0 : js_ReportOutOfMemory(cx);
456 0 : return false;
457 : }
458 :
459 : HeapPtrShape *listp = dictionaryShape
460 2640884 : ? &dictionaryShape->parent
461 5401663 : : (HeapPtrShape *) root.address();
462 :
463 2760779 : StackShape child(shape);
464 2760779 : dprop->initDictionaryShape(child, self->numFixedSlots(), listp);
465 :
466 2760779 : JS_ASSERT(!dprop->hasTable());
467 2760779 : dictionaryShape = dprop;
468 2760779 : shape = shape->previous();
469 : }
470 :
471 119895 : if (!root->hashify(cx)) {
472 0 : js_ReportOutOfMemory(cx);
473 0 : return false;
474 : }
475 :
476 119895 : JS_ASSERT((Shape **) root->listp == root.address());
477 119895 : root->listp = &self->shape_;
478 119895 : self->shape_ = root;
479 :
480 119895 : JS_ASSERT(self->inDictionaryMode());
481 119895 : root->base()->setSlotSpan(span);
482 :
483 119895 : return true;
484 : }
485 :
486 : /*
487 : * Normalize stub getter and setter values for faster is-stub testing in the
488 : * SHAPE_CALL_[GS]ETTER macros.
489 : */
490 : static inline bool
491 31985760 : NormalizeGetterAndSetter(JSContext *cx, JSObject *obj,
492 : jsid id, unsigned attrs, unsigned flags,
493 : PropertyOp &getter,
494 : StrictPropertyOp &setter)
495 : {
496 31985760 : if (setter == JS_StrictPropertyStub) {
497 31074965 : JS_ASSERT(!(attrs & JSPROP_SETTER));
498 31074965 : setter = NULL;
499 : }
500 31985760 : if (getter == JS_PropertyStub) {
501 19598878 : JS_ASSERT(!(attrs & JSPROP_GETTER));
502 19598878 : getter = NULL;
503 : }
504 :
505 31985760 : return true;
506 : }
507 :
508 : Shape *
509 377836 : JSObject::addProperty(JSContext *cx, jsid id,
510 : PropertyOp getter, StrictPropertyOp setter,
511 : uint32_t slot, unsigned attrs,
512 : unsigned flags, int shortid, bool allowDictionary)
513 : {
514 377836 : JS_ASSERT(!JSID_IS_VOID(id));
515 :
516 377836 : if (!isExtensible()) {
517 0 : reportNotExtensible(cx);
518 0 : return NULL;
519 : }
520 :
521 377836 : NormalizeGetterAndSetter(cx, this, id, attrs, flags, getter, setter);
522 :
523 377836 : Shape **spp = NULL;
524 377836 : if (inDictionaryMode())
525 52689 : spp = lastProperty()->table().search(id, true);
526 :
527 : return addPropertyInternal(cx, id, getter, setter, slot, attrs, flags, shortid,
528 377836 : spp, allowDictionary);
529 : }
530 :
531 : Shape *
532 30109396 : JSObject::addPropertyInternal(JSContext *cx, jsid id,
533 : PropertyOp getter, StrictPropertyOp setter,
534 : uint32_t slot, unsigned attrs,
535 : unsigned flags, int shortid, Shape **spp,
536 : bool allowDictionary)
537 : {
538 30109396 : JS_ASSERT_IF(!allowDictionary, !inDictionaryMode());
539 :
540 60218792 : RootId idRoot(cx, &id);
541 60218792 : RootedVarObject self(cx, this);
542 :
543 30109396 : PropertyTable *table = NULL;
544 30109396 : if (!inDictionaryMode()) {
545 : bool stableSlot =
546 : (slot == SHAPE_INVALID_SLOT) ||
547 421397 : lastProperty()->hasMissingSlot() ||
548 28888654 : (slot == lastProperty()->maybeSlot() + 1);
549 28467257 : JS_ASSERT_IF(!allowDictionary, stableSlot);
550 84719216 : if (allowDictionary &&
551 56251959 : (!stableSlot || lastProperty()->entryCount() >= PropertyTree::MAX_HEIGHT)) {
552 21092 : if (!toDictionaryMode(cx))
553 0 : return NULL;
554 21092 : table = &lastProperty()->table();
555 21092 : spp = table->search(id, true);
556 : }
557 : } else {
558 1642139 : table = &lastProperty()->table();
559 1642139 : if (table->needsToGrow()) {
560 4797 : if (!table->grow(cx))
561 0 : return NULL;
562 4797 : spp = table->search(id, true);
563 4797 : JS_ASSERT(!SHAPE_FETCH(spp));
564 : }
565 : }
566 :
567 30109396 : JS_ASSERT(!!table == !!spp);
568 :
569 : /* Find or create a property tree node labeled by our arguments. */
570 : Shape *shape;
571 : {
572 30109396 : shape = self->lastProperty();
573 :
574 : uint32_t index;
575 30109396 : bool indexed = js_IdIsIndex(id, &index);
576 : UnownedBaseShape *nbase;
577 30109396 : if (shape->base()->matchesGetterSetter(getter, setter) && !indexed) {
578 16406365 : nbase = shape->base()->unowned();
579 : } else {
580 13703031 : StackBaseShape base(shape->base());
581 13703031 : base.updateGetterSetter(attrs, getter, setter);
582 13703031 : if (indexed)
583 1730848 : base.flags |= BaseShape::INDEXED;
584 13703031 : nbase = BaseShape::getUnowned(cx, base);
585 13703031 : if (!nbase)
586 0 : return NULL;
587 : }
588 :
589 30109396 : StackShape child(nbase, id, slot, self->numFixedSlots(), attrs, flags, shortid);
590 30109396 : shape = self->getChildProperty(cx, self->lastProperty(), child);
591 : }
592 :
593 30109396 : if (shape) {
594 30109396 : JS_ASSERT(shape == self->lastProperty());
595 :
596 30109396 : if (table) {
597 : /* Store the tree node pointer in the table entry for id. */
598 1663231 : SHAPE_STORE_PRESERVING_COLLISION(spp, shape);
599 1663231 : ++table->entryCount;
600 :
601 : /* Pass the table along to the new last property, namely shape. */
602 1663231 : JS_ASSERT(&shape->parent->table() == table);
603 1663231 : shape->parent->handoffTableTo(shape);
604 : }
605 :
606 30109396 : self->checkShapeConsistency();
607 30109396 : return shape;
608 : }
609 :
610 0 : self->checkShapeConsistency();
611 0 : return NULL;
612 : }
613 :
614 : /*
615 : * Check and adjust the new attributes for the shape to make sure that our
616 : * slot access optimizations are sound. It is responsibility of the callers to
617 : * enforce all restrictions from ECMA-262 v5 8.12.9 [[DefineOwnProperty]].
618 : */
619 : inline bool
620 2239267 : CheckCanChangeAttrs(JSContext *cx, JSObject *obj, const Shape *shape, unsigned *attrsp)
621 : {
622 2239267 : if (shape->configurable())
623 2079985 : return true;
624 :
625 : /* A permanent property must stay permanent. */
626 159282 : *attrsp |= JSPROP_PERMANENT;
627 :
628 : /* Reject attempts to remove a slot from the permanent data property. */
629 159282 : if (shape->isDataDescriptor() && shape->hasSlot() &&
630 : (*attrsp & (JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED))) {
631 0 : obj->reportNotConfigurable(cx, shape->propid());
632 0 : return false;
633 : }
634 :
635 159282 : return true;
636 : }
637 :
638 : Shape *
639 31607924 : JSObject::putProperty(JSContext *cx, jsid id,
640 : PropertyOp getter, StrictPropertyOp setter,
641 : uint32_t slot, unsigned attrs,
642 : unsigned flags, int shortid)
643 : {
644 31607924 : JS_ASSERT(!JSID_IS_VOID(id));
645 :
646 63215848 : RootId idRoot(cx, &id);
647 :
648 31607924 : NormalizeGetterAndSetter(cx, this, id, attrs, flags, getter, setter);
649 :
650 63215848 : RootedVarObject self(cx, this);
651 :
652 : /* Search for id in order to claim its entry if table has been allocated. */
653 : Shape **spp;
654 31607924 : Shape *shape = Shape::search(cx, lastProperty(), id, &spp, true);
655 31607924 : if (!shape) {
656 : /*
657 : * You can't add properties to a non-extensible object, but you can change
658 : * attributes of properties in such objects.
659 : */
660 29400779 : if (!self->isExtensible()) {
661 0 : self->reportNotExtensible(cx);
662 0 : return NULL;
663 : }
664 :
665 29400779 : return self->addPropertyInternal(cx, id, getter, setter, slot, attrs, flags, shortid, spp, true);
666 : }
667 :
668 : /* Property exists: search must have returned a valid *spp. */
669 2207145 : JS_ASSERT_IF(spp, !SHAPE_IS_REMOVED(*spp));
670 :
671 4414290 : RootShape shapeRoot(cx, &shape);
672 :
673 2207145 : if (!CheckCanChangeAttrs(cx, self, shape, &attrs))
674 0 : return NULL;
675 :
676 : /*
677 : * If the caller wants to allocate a slot, but doesn't care which slot,
678 : * copy the existing shape's slot into slot so we can match shape, if all
679 : * other members match.
680 : */
681 2207145 : bool hadSlot = shape->hasSlot();
682 2207145 : uint32_t oldSlot = shape->maybeSlot();
683 2207145 : if (!(attrs & JSPROP_SHARED) && slot == SHAPE_INVALID_SLOT && hadSlot)
684 2174887 : slot = oldSlot;
685 :
686 4414290 : RootedVar<UnownedBaseShape*> nbase(cx);
687 : {
688 : uint32_t index;
689 2207145 : bool indexed = js_IdIsIndex(id, &index);
690 2207145 : StackBaseShape base(self->lastProperty()->base());
691 2207145 : base.updateGetterSetter(attrs, getter, setter);
692 2207145 : if (indexed)
693 126 : base.flags |= BaseShape::INDEXED;
694 2207145 : nbase = BaseShape::getUnowned(cx, base);
695 2207145 : if (!nbase)
696 0 : return NULL;
697 : }
698 :
699 : /*
700 : * Now that we've possibly preserved slot, check whether all members match.
701 : * If so, this is a redundant "put" and we can return without more work.
702 : */
703 2207145 : if (shape->matchesParamsAfterId(nbase, slot, attrs, flags, shortid))
704 2128074 : return shape;
705 :
706 : /*
707 : * Overwriting a non-last property requires switching to dictionary mode.
708 : * The shape tree is shared immutable, and we can't removeProperty and then
709 : * addPropertyInternal because a failure under add would lose data.
710 : */
711 79071 : if (shape != self->lastProperty() && !self->inDictionaryMode()) {
712 46701 : if (!self->toDictionaryMode(cx))
713 0 : return NULL;
714 46701 : spp = self->lastProperty()->table().search(shape->propid(), false);
715 46701 : shape = SHAPE_FETCH(spp);
716 : }
717 :
718 79071 : JS_ASSERT_IF(shape->hasSlot() && !(attrs & JSPROP_SHARED), shape->slot() == slot);
719 :
720 79071 : if (self->inDictionaryMode()) {
721 : /*
722 : * Updating some property in a dictionary-mode object. Create a new
723 : * shape for the existing property, and also generate a new shape for
724 : * the last property of the dictionary (unless the modified property
725 : * is also the last property).
726 : */
727 78790 : bool updateLast = (shape == self->lastProperty());
728 78790 : shape = self->replaceWithNewEquivalentShape(cx, shape);
729 78790 : if (!shape)
730 0 : return NULL;
731 78790 : if (!updateLast && !self->generateOwnShape(cx))
732 0 : return NULL;
733 :
734 : /* FIXME bug 593129 -- slot allocation and JSObject *this must move out of here! */
735 78790 : if (slot == SHAPE_INVALID_SLOT && !(attrs & JSPROP_SHARED)) {
736 9 : if (!self->allocSlot(cx, &slot))
737 0 : return NULL;
738 : }
739 :
740 78790 : if (updateLast)
741 13014 : shape->base()->adoptUnowned(nbase);
742 : else
743 65776 : shape->base_ = nbase;
744 :
745 78790 : shape->setSlot(slot);
746 78790 : shape->attrs = uint8_t(attrs);
747 78790 : shape->flags = flags | Shape::IN_DICTIONARY;
748 78790 : shape->shortid_ = int16_t(shortid);
749 : } else {
750 : /*
751 : * Updating the last property in a non-dictionary-mode object. Find an
752 : * alternate shared child of the last property's previous shape.
753 : */
754 281 : StackBaseShape base(self->lastProperty()->base());
755 281 : base.updateGetterSetter(attrs, getter, setter);
756 281 : UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base);
757 281 : if (!nbase)
758 0 : return NULL;
759 :
760 281 : JS_ASSERT(shape == self->lastProperty());
761 :
762 : /* Find or create a property tree node labeled by our arguments. */
763 281 : StackShape child(nbase, id, slot, self->numFixedSlots(), attrs, flags, shortid);
764 281 : Shape *newShape = self->getChildProperty(cx, shape->parent, child);
765 :
766 281 : if (!newShape) {
767 0 : self->checkShapeConsistency();
768 0 : return NULL;
769 : }
770 :
771 281 : shape = newShape;
772 : }
773 :
774 : /*
775 : * Can't fail now, so free the previous incarnation's slot if the new shape
776 : * has no slot. But we do not need to free oldSlot (and must not, as trying
777 : * to will botch an assertion in JSObject::freeSlot) if the new last
778 : * property (shape here) has a slotSpan that does not cover it.
779 : */
780 79071 : if (hadSlot && !shape->hasSlot()) {
781 108 : if (oldSlot < self->slotSpan())
782 54 : self->freeSlot(cx, oldSlot);
783 108 : JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals);
784 : }
785 :
786 79071 : self->checkShapeConsistency();
787 :
788 79071 : return shape;
789 : }
790 :
791 : Shape *
792 32122 : JSObject::changeProperty(JSContext *cx, Shape *shape, unsigned attrs, unsigned mask,
793 : PropertyOp getter, StrictPropertyOp setter)
794 : {
795 32122 : JS_ASSERT(nativeContains(cx, *shape));
796 :
797 32122 : attrs |= shape->attrs & mask;
798 :
799 : /* Allow only shared (slotless) => unshared (slotful) transition. */
800 32122 : JS_ASSERT(!((attrs ^ shape->attrs) & JSPROP_SHARED) ||
801 32122 : !(attrs & JSPROP_SHARED));
802 :
803 32122 : types::MarkTypePropertyConfigured(cx, this, shape->propid());
804 32122 : if (attrs & (JSPROP_GETTER | JSPROP_SETTER))
805 208 : types::AddTypePropertyId(cx, this, shape->propid(), types::Type::UnknownType());
806 :
807 32122 : if (getter == JS_PropertyStub)
808 0 : getter = NULL;
809 32122 : if (setter == JS_StrictPropertyStub)
810 0 : setter = NULL;
811 :
812 32122 : if (!CheckCanChangeAttrs(cx, this, shape, &attrs))
813 0 : return NULL;
814 :
815 32122 : if (shape->attrs == attrs && shape->getter() == getter && shape->setter() == setter)
816 0 : return shape;
817 :
818 : /*
819 : * Let JSObject::putProperty handle this |overwriting| case, including
820 : * the conservation of shape->slot (if it's valid). We must not call
821 : * removeProperty because it will free an allocated shape->slot, and
822 : * putProperty won't re-allocate it.
823 : */
824 32122 : Shape *newShape = putProperty(cx, shape->propid(), getter, setter, shape->maybeSlot(),
825 64244 : attrs, shape->flags, shape->maybeShortid());
826 :
827 32122 : checkShapeConsistency();
828 32122 : return newShape;
829 : }
830 :
831 : bool
832 3591 : JSObject::removeProperty(JSContext *cx, jsid id)
833 : {
834 7182 : RootedVarObject self(cx, this);
835 :
836 7182 : RootId idRoot(cx, &id);
837 7182 : RootedVarShape shape(cx);
838 :
839 : Shape **spp;
840 3591 : shape = Shape::search(cx, lastProperty(), id, &spp);
841 3591 : if (!shape)
842 0 : return true;
843 :
844 : /*
845 : * If shape is not the last property added, or the last property cannot
846 : * be removed, switch to dictionary mode.
847 : */
848 3591 : if (!self->inDictionaryMode() && (shape != self->lastProperty() || !self->canRemoveLastProperty())) {
849 517 : if (!self->toDictionaryMode(cx))
850 0 : return false;
851 517 : spp = self->lastProperty()->table().search(shape->propid(), false);
852 517 : shape = SHAPE_FETCH(spp);
853 : }
854 :
855 : /*
856 : * If in dictionary mode, get a new shape for the last property after the
857 : * removal. We need a fresh shape for all dictionary deletions, even of
858 : * the last property. Otherwise, a shape could replay and caches might
859 : * return deleted DictionaryShapes! See bug 595365. Do this before changing
860 : * the object or table, so the remaining removal is infallible.
861 : */
862 3591 : Shape *spare = NULL;
863 3591 : if (self->inDictionaryMode()) {
864 2412 : spare = js_NewGCShape(cx);
865 2412 : if (!spare)
866 0 : return false;
867 2412 : new (spare) Shape(shape->base()->unowned(), 0);
868 2412 : if (shape == lastProperty()) {
869 : /*
870 : * Get an up to date unowned base shape for the new last property
871 : * when removing the dictionary's last property. Information in
872 : * base shapes for non-last properties may be out of sync with the
873 : * object's state.
874 : */
875 279 : Shape *previous = lastProperty()->parent;
876 279 : StackBaseShape base(lastProperty()->base());
877 279 : base.updateGetterSetter(previous->attrs, previous->getter(), previous->setter());
878 279 : BaseShape *nbase = BaseShape::getUnowned(cx, base);
879 279 : if (!nbase)
880 0 : return false;
881 279 : previous->base_ = nbase;
882 : }
883 : }
884 :
885 : /* If shape has a slot, free its slot number. */
886 3591 : if (shape->hasSlot()) {
887 2709 : self->freeSlot(cx, shape->slot());
888 2709 : JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals);
889 : }
890 :
891 : /*
892 : * A dictionary-mode object owns mutable, unique shapes on a non-circular
893 : * doubly linked list, hashed by lastProperty()->table. So we can edit the
894 : * list and hash in place.
895 : */
896 3591 : if (self->inDictionaryMode()) {
897 2412 : PropertyTable &table = self->lastProperty()->table();
898 :
899 2412 : if (SHAPE_HAD_COLLISION(*spp)) {
900 369 : *spp = SHAPE_REMOVED;
901 369 : ++table.removedCount;
902 369 : --table.entryCount;
903 : } else {
904 2043 : *spp = NULL;
905 2043 : --table.entryCount;
906 :
907 : #ifdef DEBUG
908 : /*
909 : * Check the consistency of the table but limit the number of
910 : * checks not to alter significantly the complexity of the
911 : * delete in debug builds, see bug 534493.
912 : */
913 2043 : const Shape *aprop = self->lastProperty();
914 30114 : for (int n = 50; --n >= 0 && aprop->parent; aprop = aprop->parent)
915 28071 : JS_ASSERT_IF(aprop != shape, self->nativeContains(cx, *aprop));
916 : #endif
917 : }
918 :
919 : /* Remove shape from its non-circular doubly linked list. */
920 2412 : Shape *oldLastProp = self->lastProperty();
921 2412 : shape->removeFromDictionary(self);
922 :
923 : /* Hand off table from the old to new last property. */
924 2412 : oldLastProp->handoffTableTo(self->lastProperty());
925 :
926 : /* Generate a new shape for the object, infallibly. */
927 2412 : JS_ALWAYS_TRUE(self->generateOwnShape(cx, spare));
928 :
929 : /* Consider shrinking table if its load factor is <= .25. */
930 2412 : uint32_t size = table.capacity();
931 2412 : if (size > PropertyTable::MIN_SIZE && table.entryCount <= size >> 2)
932 0 : (void) table.change(-1, cx);
933 : } else {
934 : /*
935 : * Non-dictionary-mode property tables are shared immutables, so all we
936 : * need do is retract the last property and we'll either get or else
937 : * lazily make via a later hashify the exact table for the new property
938 : * lineage.
939 : */
940 1179 : JS_ASSERT(shape == self->lastProperty());
941 1179 : self->removeLastProperty(cx);
942 : }
943 :
944 3591 : self->checkShapeConsistency();
945 3591 : return true;
946 : }
947 :
948 : void
949 10 : JSObject::clear(JSContext *cx)
950 : {
951 10 : Shape *shape = lastProperty();
952 10 : JS_ASSERT(inDictionaryMode() == shape->inDictionary());
953 :
954 20 : while (shape->parent) {
955 0 : shape = shape->parent;
956 0 : JS_ASSERT(inDictionaryMode() == shape->inDictionary());
957 : }
958 10 : JS_ASSERT(shape->isEmptyShape());
959 :
960 10 : if (inDictionaryMode())
961 0 : shape->listp = &shape_;
962 :
963 10 : JS_ALWAYS_TRUE(setLastProperty(cx, shape));
964 :
965 10 : JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals);
966 10 : checkShapeConsistency();
967 10 : }
968 :
969 : void
970 20 : JSObject::rollbackProperties(JSContext *cx, uint32_t slotSpan)
971 : {
972 : /*
973 : * Remove properties from this object until it has a matching slot span.
974 : * The object cannot have escaped in a way which would prevent safe
975 : * removal of the last properties.
976 : */
977 20 : JS_ASSERT(!inDictionaryMode() && slotSpan <= this->slotSpan());
978 85 : while (this->slotSpan() != slotSpan) {
979 45 : JS_ASSERT(lastProperty()->hasSlot() && getSlot(lastProperty()->slot()).isUndefined());
980 45 : removeLastProperty(cx);
981 : }
982 20 : }
983 :
984 : Shape *
985 365749 : JSObject::replaceWithNewEquivalentShape(JSContext *cx, Shape *oldShape, Shape *newShape)
986 : {
987 497301 : JS_ASSERT_IF(oldShape != lastProperty(),
988 : inDictionaryMode() &&
989 497301 : nativeLookup(cx, oldShape->propidRef()) == oldShape);
990 :
991 365749 : JSObject *self = this;
992 :
993 365749 : if (!inDictionaryMode()) {
994 98700 : RootObject selfRoot(cx, &self);
995 98700 : RootShape newRoot(cx, &newShape);
996 49350 : if (!toDictionaryMode(cx))
997 0 : return NULL;
998 98700 : oldShape = lastProperty();
999 : }
1000 :
1001 365749 : if (!newShape) {
1002 726674 : RootObject selfRoot(cx, &self);
1003 726674 : RootShape oldRoot(cx, &oldShape);
1004 363337 : newShape = js_NewGCShape(cx);
1005 363337 : if (!newShape)
1006 0 : return NULL;
1007 363337 : new (newShape) Shape(oldShape->base()->unowned(), 0);
1008 : }
1009 :
1010 365749 : PropertyTable &table = self->lastProperty()->table();
1011 365749 : Shape **spp = oldShape->isEmptyShape()
1012 : ? NULL
1013 365749 : : table.search(oldShape->propidRef(), false);
1014 :
1015 : /*
1016 : * Splice the new shape into the same position as the old shape, preserving
1017 : * enumeration order (see bug 601399).
1018 : */
1019 365749 : StackShape nshape(oldShape);
1020 365749 : newShape->initDictionaryShape(nshape, self->numFixedSlots(), oldShape->listp);
1021 :
1022 365749 : JS_ASSERT(newShape->parent == oldShape);
1023 365749 : oldShape->removeFromDictionary(self);
1024 :
1025 365749 : if (newShape == lastProperty())
1026 299973 : oldShape->handoffTableTo(newShape);
1027 :
1028 365749 : if (spp)
1029 352603 : SHAPE_STORE_PRESERVING_COLLISION(spp, newShape);
1030 365749 : return newShape;
1031 : }
1032 :
1033 : bool
1034 141972 : JSObject::shadowingShapeChange(JSContext *cx, const Shape &shape)
1035 : {
1036 141972 : return generateOwnShape(cx);
1037 : }
1038 :
1039 : bool
1040 2967 : JSObject::clearParent(JSContext *cx)
1041 : {
1042 2967 : return setParent(cx, NULL);
1043 : }
1044 :
1045 : bool
1046 66672 : JSObject::setParent(JSContext *cx, JSObject *parent)
1047 : {
1048 66672 : if (parent && !parent->setDelegate(cx))
1049 0 : return false;
1050 :
1051 66672 : if (inDictionaryMode()) {
1052 0 : StackBaseShape base(lastProperty());
1053 0 : base.parent = parent;
1054 0 : UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base);
1055 0 : if (!nbase)
1056 0 : return false;
1057 :
1058 0 : lastProperty()->base()->adoptUnowned(nbase);
1059 0 : return true;
1060 : }
1061 :
1062 66672 : Shape *newShape = Shape::setObjectParent(cx, parent, getProto(), shape_);
1063 66672 : if (!newShape)
1064 0 : return false;
1065 :
1066 66672 : shape_ = newShape;
1067 66672 : return true;
1068 : }
1069 :
1070 : /* static */ Shape *
1071 212710 : Shape::setObjectParent(JSContext *cx, JSObject *parent, JSObject *proto, Shape *last)
1072 : {
1073 212710 : if (last->getObjectParent() == parent)
1074 60190 : return last;
1075 :
1076 152520 : StackBaseShape base(last);
1077 152520 : base.parent = parent;
1078 :
1079 152520 : return replaceLastProperty(cx, base, proto, last);
1080 : }
1081 :
1082 : bool
1083 1243024 : JSObject::preventExtensions(JSContext *cx, js::AutoIdVector *props)
1084 : {
1085 1243024 : JS_ASSERT(isExtensible());
1086 :
1087 2486048 : RootedVarObject self(cx, this);
1088 :
1089 1243024 : if (props) {
1090 1243024 : if (js::FixOp fix = getOps()->fix) {
1091 : bool success;
1092 82 : if (!fix(cx, this, &success, props))
1093 0 : return false;
1094 82 : if (!success) {
1095 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_CHANGE_EXTENSIBILITY);
1096 0 : return false;
1097 : }
1098 : } else {
1099 1242942 : if (!js::GetPropertyNames(cx, this, JSITER_HIDDEN | JSITER_OWNONLY, props))
1100 0 : return false;
1101 : }
1102 : }
1103 :
1104 1243024 : return self->setFlag(cx, BaseShape::NOT_EXTENSIBLE, GENERATE_SHAPE);
1105 : }
1106 :
1107 : bool
1108 4228376 : JSObject::setFlag(JSContext *cx, /*BaseShape::Flag*/ uint32_t flag_, GenerateShape generateShape)
1109 : {
1110 4228376 : BaseShape::Flag flag = (BaseShape::Flag) flag_;
1111 :
1112 4228376 : if (lastProperty()->getObjectFlags() & flag)
1113 2499873 : return true;
1114 :
1115 3457006 : RootedVarObject self(cx, this);
1116 :
1117 1728503 : if (inDictionaryMode()) {
1118 39384 : if (generateShape == GENERATE_SHAPE && !generateOwnShape(cx))
1119 0 : return false;
1120 39384 : StackBaseShape base(self->lastProperty());
1121 39384 : base.flags |= flag;
1122 39384 : UnownedBaseShape *nbase = BaseShape::getUnowned(cx, base);
1123 39384 : if (!nbase)
1124 0 : return false;
1125 :
1126 39384 : self->lastProperty()->base()->adoptUnowned(nbase);
1127 39384 : return true;
1128 : }
1129 :
1130 1689119 : Shape *newShape = Shape::setObjectFlag(cx, flag, getProto(), lastProperty());
1131 1689119 : if (!newShape)
1132 0 : return false;
1133 :
1134 1689119 : self->shape_ = newShape;
1135 1689119 : return true;
1136 : }
1137 :
1138 : /* static */ Shape *
1139 1689119 : Shape::setObjectFlag(JSContext *cx, BaseShape::Flag flag, JSObject *proto, Shape *last)
1140 : {
1141 1689119 : if (last->getObjectFlags() & flag)
1142 0 : return last;
1143 :
1144 1689119 : StackBaseShape base(last);
1145 1689119 : base.flags |= flag;
1146 :
1147 1689119 : return replaceLastProperty(cx, base, proto, last);
1148 : }
1149 :
1150 : /* static */ inline HashNumber
1151 18950141 : StackBaseShape::hash(const StackBaseShape *base)
1152 : {
1153 18950141 : HashNumber hash = base->flags;
1154 18950141 : hash = JS_ROTATE_LEFT32(hash, 4) ^ (uintptr_t(base->clasp) >> 3);
1155 18950141 : hash = JS_ROTATE_LEFT32(hash, 4) ^ (uintptr_t(base->parent) >> 3);
1156 18950141 : hash = JS_ROTATE_LEFT32(hash, 4) ^ uintptr_t(base->rawGetter);
1157 18950141 : hash = JS_ROTATE_LEFT32(hash, 4) ^ uintptr_t(base->rawSetter);
1158 18950141 : return hash;
1159 : }
1160 :
1161 : /* static */ inline bool
1162 14988376 : StackBaseShape::match(UnownedBaseShape *key, const StackBaseShape *lookup)
1163 : {
1164 : return key->flags == lookup->flags
1165 : && key->clasp == lookup->clasp
1166 14987833 : && key->parent == lookup->parent
1167 : && key->rawGetter == lookup->rawGetter
1168 29976209 : && key->rawSetter == lookup->rawSetter;
1169 : }
1170 :
1171 : /* Root for stack allocated base shapes. */
1172 : class RootStackBaseShape
1173 3962586 : {
1174 : Root<const JSObject*> parentRoot;
1175 : Maybe<RootObject> getterRoot;
1176 : Maybe<RootObject> setterRoot;
1177 :
1178 : public:
1179 3962586 : RootStackBaseShape(JSContext *cx, const StackBaseShape *base)
1180 3962586 : : parentRoot(cx, &base->parent)
1181 : {
1182 3962586 : if (base->flags & BaseShape::HAS_GETTER_OBJECT)
1183 1044856 : getterRoot.construct(cx, (JSObject **) &base->rawGetter);
1184 3962586 : if (base->flags & BaseShape::HAS_SETTER_OBJECT)
1185 187437 : setterRoot.construct(cx, (JSObject **) &base->rawSetter);
1186 3962586 : }
1187 : };
1188 :
1189 : /* static */ UnownedBaseShape *
1190 18950141 : BaseShape::getUnowned(JSContext *cx, const StackBaseShape &base)
1191 : {
1192 18950141 : BaseShapeSet &table = cx->compartment->baseShapes;
1193 :
1194 18950141 : if (!table.initialized() && !table.init())
1195 0 : return NULL;
1196 :
1197 37900282 : BaseShapeSet::AddPtr p = table.lookupForAdd(&base);
1198 :
1199 18950141 : if (p)
1200 14987555 : return *p;
1201 :
1202 7925172 : RootStackBaseShape root(cx, &base);
1203 :
1204 3962586 : BaseShape *nbase_ = js_NewGCBaseShape(cx);
1205 3962586 : if (!nbase_)
1206 0 : return NULL;
1207 3962586 : new (nbase_) BaseShape(base);
1208 :
1209 3962586 : UnownedBaseShape *nbase = static_cast<UnownedBaseShape *>(nbase_);
1210 :
1211 3962586 : if (!table.relookupOrAdd(p, &base, nbase))
1212 0 : return NULL;
1213 :
1214 3962586 : return nbase;
1215 : }
1216 :
1217 : void
1218 83697 : JSCompartment::sweepBaseShapeTable()
1219 : {
1220 83697 : if (baseShapes.initialized()) {
1221 9766789 : for (BaseShapeSet::Enum e(baseShapes); !e.empty(); e.popFront()) {
1222 9721375 : UnownedBaseShape *base = e.front();
1223 9721375 : if (!base->isMarked())
1224 3962586 : e.removeFront();
1225 : }
1226 : }
1227 83697 : }
1228 :
1229 : void
1230 4089976 : BaseShape::finalize(FreeOp *fop)
1231 : {
1232 4089976 : if (table_) {
1233 127390 : fop->delete_(table_);
1234 127390 : table_ = NULL;
1235 : }
1236 4089976 : }
1237 :
1238 : /* static */ Shape *
1239 1784 : Shape::setExtensibleParents(JSContext *cx, Shape *shape)
1240 : {
1241 1784 : JS_ASSERT(!shape->inDictionary());
1242 :
1243 1784 : StackBaseShape base(shape);
1244 1784 : base.flags |= BaseShape::EXTENSIBLE_PARENTS;
1245 :
1246 : /* This is only used for Block and Call objects, which have a NULL proto. */
1247 1784 : return replaceLastProperty(cx, base, NULL, shape);
1248 : }
1249 :
1250 : bool
1251 559 : Bindings::setExtensibleParents(JSContext *cx)
1252 : {
1253 559 : if (!ensureShape(cx))
1254 0 : return false;
1255 559 : Shape *newShape = Shape::setExtensibleParents(cx, lastBinding);
1256 559 : if (!newShape)
1257 0 : return false;
1258 559 : lastBinding = newShape;
1259 559 : return true;
1260 : }
1261 :
1262 : bool
1263 146038 : Bindings::setParent(JSContext *cx, JSObject *obj)
1264 : {
1265 : /*
1266 : * This may be invoked on GC heap allocated bindings, in which case this
1267 : * is pointing to an internal value of a JSScript that can't itself be
1268 : * relocated. The script itself will be rooted, and will not be moved, so
1269 : * mark the stack value as non-relocatable for the stack root analysis.
1270 : */
1271 146038 : Bindings *self = this;
1272 292076 : CheckRoot root(cx, &self);
1273 :
1274 292076 : RootObject rootObj(cx, &obj);
1275 :
1276 146038 : if (!ensureShape(cx))
1277 0 : return false;
1278 :
1279 : /* This is only used for Block objects, which have a NULL proto. */
1280 146038 : Shape *newShape = Shape::setObjectParent(cx, obj, NULL, self->lastBinding);
1281 146038 : if (!newShape)
1282 0 : return false;
1283 146038 : self->lastBinding = newShape;
1284 146038 : return true;
1285 : }
1286 :
1287 : /* static */ inline HashNumber
1288 8611900 : InitialShapeEntry::hash(const Lookup &lookup)
1289 : {
1290 8611900 : HashNumber hash = uintptr_t(lookup.clasp) >> 3;
1291 8611900 : hash = JS_ROTATE_LEFT32(hash, 4) ^ (uintptr_t(lookup.proto) >> 3);
1292 8611900 : hash = JS_ROTATE_LEFT32(hash, 4) ^ (uintptr_t(lookup.parent) >> 3);
1293 8611900 : return hash + lookup.nfixed;
1294 : }
1295 :
1296 : /* static */ inline bool
1297 9537229 : InitialShapeEntry::match(const InitialShapeEntry &key, const Lookup &lookup)
1298 : {
1299 9537229 : return lookup.clasp == key.shape->getObjectClass()
1300 : && lookup.proto == key.proto
1301 9537132 : && lookup.parent == key.shape->getObjectParent()
1302 9537132 : && lookup.nfixed == key.shape->numFixedSlots()
1303 38148722 : && lookup.baseFlags == key.shape->getObjectFlags();
1304 : }
1305 :
1306 : /* static */ Shape *
1307 8605500 : EmptyShape::getInitialShape(JSContext *cx, Class *clasp, JSObject *proto, JSObject *parent,
1308 : AllocKind kind, uint32_t objectFlags)
1309 : {
1310 8605500 : InitialShapeSet &table = cx->compartment->initialShapes;
1311 :
1312 8605500 : if (!table.initialized() && !table.init())
1313 0 : return NULL;
1314 :
1315 8605500 : size_t nfixed = GetGCKindSlots(kind, clasp);
1316 8605500 : InitialShapeEntry::Lookup lookup(clasp, proto, parent, nfixed, objectFlags);
1317 :
1318 17211000 : InitialShapeSet::AddPtr p = table.lookupForAdd(lookup);
1319 :
1320 8605500 : if (p)
1321 6745450 : return p->shape;
1322 :
1323 3720100 : RootedVar<UnownedBaseShape*> nbase(cx);
1324 :
1325 1860050 : StackBaseShape base(clasp, parent, objectFlags);
1326 1860050 : nbase = BaseShape::getUnowned(cx, base);
1327 1860050 : if (!nbase)
1328 0 : return NULL;
1329 :
1330 1860050 : Shape *shape = JS_PROPERTY_TREE(cx).newShape(cx);
1331 1860050 : if (!shape)
1332 0 : return NULL;
1333 1860050 : new (shape) EmptyShape(nbase, nfixed);
1334 :
1335 1860050 : InitialShapeEntry entry;
1336 1860050 : entry.shape = shape;
1337 1860050 : entry.proto = proto;
1338 :
1339 1860050 : if (!table.relookupOrAdd(p, lookup, entry))
1340 0 : return NULL;
1341 :
1342 1860050 : return shape;
1343 : }
1344 :
1345 : void
1346 6400 : NewObjectCache::invalidateEntriesForShape(JSContext *cx, Shape *shape, JSObject *proto)
1347 : {
1348 6400 : Class *clasp = shape->getObjectClass();
1349 :
1350 6400 : gc::AllocKind kind = gc::GetGCObjectKind(shape->numFixedSlots());
1351 6400 : if (CanBeFinalizedInBackground(kind, clasp))
1352 6400 : kind = GetBackgroundAllocKind(kind);
1353 :
1354 6400 : GlobalObject *global = &shape->getObjectParent()->global();
1355 6400 : types::TypeObject *type = proto->getNewType(cx);
1356 :
1357 : EntryIndex entry;
1358 6400 : if (lookupGlobal(clasp, global, kind, &entry))
1359 2556 : PodZero(&entries[entry]);
1360 6400 : if (!proto->isGlobal() && lookupProto(clasp, proto, kind, &entry))
1361 3844 : PodZero(&entries[entry]);
1362 6400 : if (lookupType(clasp, type, kind, &entry))
1363 0 : PodZero(&entries[entry]);
1364 6400 : }
1365 :
1366 : /* static */ void
1367 6400 : EmptyShape::insertInitialShape(JSContext *cx, Shape *shape, JSObject *proto)
1368 : {
1369 : InitialShapeEntry::Lookup lookup(shape->getObjectClass(), proto, shape->getObjectParent(),
1370 6400 : shape->numFixedSlots(), shape->getObjectFlags());
1371 :
1372 6400 : InitialShapeSet::Ptr p = cx->compartment->initialShapes.lookup(lookup);
1373 6400 : JS_ASSERT(p);
1374 :
1375 6400 : InitialShapeEntry &entry = const_cast<InitialShapeEntry &>(*p);
1376 6400 : JS_ASSERT(entry.shape->isEmptyShape());
1377 :
1378 : /* The new shape had better be rooted at the old one. */
1379 : #ifdef DEBUG
1380 6400 : const Shape *nshape = shape;
1381 32595 : while (!nshape->isEmptyShape())
1382 19795 : nshape = nshape->previous();
1383 6400 : JS_ASSERT(nshape == entry.shape);
1384 : #endif
1385 :
1386 6400 : entry.shape = shape;
1387 :
1388 : /*
1389 : * This affects the shape that will be produced by the various NewObject
1390 : * methods, so clear any cache entry referring to the old shape. This is
1391 : * not required for correctness (though it may bust on the above asserts):
1392 : * the NewObject must always check for a nativeEmpty() result and generate
1393 : * the appropriate properties if found. Clearing the cache entry avoids
1394 : * this duplicate regeneration.
1395 : */
1396 6400 : cx->compartment->newObjectCache.invalidateEntriesForShape(cx, shape, proto);
1397 6400 : }
1398 :
1399 : void
1400 83697 : JSCompartment::sweepInitialShapeTable()
1401 : {
1402 83697 : if (initialShapes.initialized()) {
1403 4078121 : for (InitialShapeSet::Enum e(initialShapes); !e.empty(); e.popFront()) {
1404 4032707 : const InitialShapeEntry &entry = e.front();
1405 4032707 : if (!entry.shape->isMarked() || (entry.proto && !entry.proto->isMarked()))
1406 1860050 : e.removeFront();
1407 : }
1408 : }
1409 83697 : }
|