2009-06-10 18:29:44 -07:00
|
|
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
2007-03-22 10:30:00 -07:00
|
|
|
* vim: set ts=8 sw=4 et tw=78:
|
|
|
|
*
|
|
|
|
* ***** BEGIN LICENSE BLOCK *****
|
|
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
|
|
* the License. You may obtain a copy of the License at
|
|
|
|
* http://www.mozilla.org/MPL/
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
* for the specific language governing rights and limitations under the
|
|
|
|
* License.
|
|
|
|
*
|
|
|
|
* The Original Code is Mozilla Communicator client code, released
|
|
|
|
* March 31, 1998.
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is
|
|
|
|
* Netscape Communications Corporation.
|
|
|
|
* Portions created by the Initial Developer are Copyright (C) 1998
|
|
|
|
* the Initial Developer. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s):
|
|
|
|
*
|
|
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
|
|
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
|
|
|
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
|
|
* the provisions above, a recipient may use your version of this file under
|
|
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
|
|
*
|
|
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
|
|
|
|
/*
|
|
|
|
* JS symbol tables.
|
|
|
|
*/
|
2009-09-14 17:29:46 -07:00
|
|
|
#include <new>
|
2007-03-22 10:30:00 -07:00
|
|
|
#include <stdlib.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include "jstypes.h"
|
2009-03-18 11:38:16 -07:00
|
|
|
#include "jsstdint.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
#include "jsarena.h"
|
|
|
|
#include "jsbit.h"
|
|
|
|
#include "jsclist.h"
|
|
|
|
#include "jsdhash.h"
|
2010-10-01 16:46:54 -07:00
|
|
|
#include "jsutil.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
#include "jsapi.h"
|
|
|
|
#include "jsatom.h"
|
|
|
|
#include "jscntxt.h"
|
|
|
|
#include "jsdbgapi.h"
|
|
|
|
#include "jslock.h"
|
|
|
|
#include "jsnum.h"
|
2010-03-30 00:44:28 -07:00
|
|
|
#include "jsobj.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
#include "jsscope.h"
|
|
|
|
#include "jsstr.h"
|
2009-09-14 17:29:46 -07:00
|
|
|
#include "jstracer.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2010-11-09 15:04:12 -08:00
|
|
|
#include "jsdbgapiinlines.h"
|
2010-06-18 17:43:02 -07:00
|
|
|
#include "jsobjinlines.h"
|
2009-10-26 13:39:39 -07:00
|
|
|
#include "jsscopeinlines.h"
|
|
|
|
|
2010-01-22 14:49:18 -08:00
|
|
|
using namespace js;
|
2010-09-24 10:54:39 -07:00
|
|
|
using namespace js::gc;
|
2010-01-22 14:49:18 -08:00
|
|
|
|
2009-07-09 13:27:21 -07:00
|
|
|
uint32
|
2011-02-04 10:59:07 -08:00
|
|
|
js_GenerateShape(JSRuntime *rt)
|
2009-07-09 13:27:21 -07:00
|
|
|
{
|
|
|
|
uint32 shape;
|
|
|
|
|
|
|
|
shape = JS_ATOMIC_INCREMENT(&rt->shapeGen);
|
|
|
|
JS_ASSERT(shape != 0);
|
|
|
|
if (shape >= SHAPE_OVERFLOW_BIT) {
|
|
|
|
/*
|
|
|
|
* FIXME bug 440834: The shape id space has overflowed. Currently we
|
|
|
|
* cope badly with this and schedule the GC on the every call. But
|
|
|
|
* first we make sure that increments from other threads would not
|
|
|
|
* have a chance to wrap around shapeGen to zero.
|
|
|
|
*/
|
|
|
|
rt->shapeGen = SHAPE_OVERFLOW_BIT;
|
2009-09-10 04:59:43 -07:00
|
|
|
shape = SHAPE_OVERFLOW_BIT;
|
2010-09-21 00:04:25 -07:00
|
|
|
|
2010-09-07 14:08:20 -07:00
|
|
|
#ifdef JS_THREADSAFE
|
2011-02-04 10:59:07 -08:00
|
|
|
AutoLockGC lockIf(rt);
|
2010-09-07 14:08:20 -07:00
|
|
|
#endif
|
2011-06-02 13:02:21 -07:00
|
|
|
GCREASON(SHAPE);
|
2010-09-07 14:08:20 -07:00
|
|
|
TriggerGC(rt);
|
2009-07-09 13:27:21 -07:00
|
|
|
}
|
|
|
|
return shape;
|
|
|
|
}
|
|
|
|
|
2011-02-04 10:59:07 -08:00
|
|
|
uint32
|
|
|
|
js_GenerateShape(JSContext *cx)
|
|
|
|
{
|
|
|
|
return js_GenerateShape(cx->runtime);
|
|
|
|
}
|
|
|
|
|
2010-08-29 11:57:08 -07:00
|
|
|
bool
|
|
|
|
JSObject::ensureClassReservedSlotsForEmptyObject(JSContext *cx)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2010-08-29 11:57:08 -07:00
|
|
|
JS_ASSERT(nativeEmpty());
|
2009-05-14 03:35:23 -07:00
|
|
|
|
2010-06-18 17:43:02 -07:00
|
|
|
/*
|
2010-08-29 11:57:08 -07:00
|
|
|
* Subtle rule: objects that call JSObject::ensureInstanceReservedSlots
|
2010-09-02 14:50:44 -07:00
|
|
|
* must either:
|
2010-08-29 11:57:08 -07:00
|
|
|
*
|
2010-09-02 14:50:44 -07:00
|
|
|
* (a) never escape anywhere an ad-hoc property could be set on them; or
|
2010-08-29 11:57:08 -07:00
|
|
|
*
|
2010-09-02 14:50:44 -07:00
|
|
|
* (b) protect their instance-reserved slots with shapes, at least a custom
|
2010-09-03 11:05:43 -07:00
|
|
|
* empty shape with the right slotSpan member.
|
2010-06-18 17:43:02 -07:00
|
|
|
*
|
2010-08-29 11:57:08 -07:00
|
|
|
* Block objects are the only objects that fall into category (a). While
|
|
|
|
* Call objects cannot escape, they can grow ad-hoc properties via eval
|
2010-09-02 14:50:44 -07:00
|
|
|
* of a var declaration, or due to a function statement being evaluated,
|
|
|
|
* but they have slots mapped by compiler-created shapes, and thus (b) no
|
|
|
|
* problem predicting first ad-hoc property slot. Bound Function objects
|
|
|
|
* have a custom empty shape.
|
2010-08-29 11:57:08 -07:00
|
|
|
*
|
2010-09-02 14:50:44 -07:00
|
|
|
* (Note that Block, Call, and bound Function objects are the only native
|
|
|
|
* class objects that are allowed to call ensureInstanceReservedSlots.)
|
2010-06-18 17:43:02 -07:00
|
|
|
*/
|
2010-08-29 11:57:08 -07:00
|
|
|
uint32 nfixed = JSSLOT_FREE(getClass());
|
2010-09-02 14:50:44 -07:00
|
|
|
if (nfixed > numSlots() && !allocSlots(cx, nfixed))
|
|
|
|
return false;
|
2010-06-18 17:43:02 -07:00
|
|
|
|
2010-08-29 11:57:08 -07:00
|
|
|
return true;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2009-07-09 13:27:21 -07:00
|
|
|
bool
|
2011-01-03 17:14:55 -08:00
|
|
|
PropertyTable::init(JSRuntime *rt, Shape *lastProp)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2011-01-03 17:14:55 -08:00
|
|
|
/*
|
|
|
|
* Either we're creating a table for a large scope that was populated
|
|
|
|
* via property cache hit logic under JSOP_INITPROP, JSOP_SETNAME, or
|
|
|
|
* JSOP_SETPROP; or else calloc failed at least once already. In any
|
|
|
|
* event, let's try to grow, overallocating to hold at least twice the
|
|
|
|
* current population.
|
|
|
|
*/
|
|
|
|
uint32 sizeLog2 = JS_CeilingLog2(2 * entryCount);
|
|
|
|
if (sizeLog2 < MIN_SIZE_LOG2)
|
2010-08-29 11:57:08 -07:00
|
|
|
sizeLog2 = MIN_SIZE_LOG2;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2010-10-24 19:24:53 -07:00
|
|
|
/*
|
2011-03-31 01:14:12 -07:00
|
|
|
* Use rt->calloc_ for memory accounting and overpressure handling
|
2010-10-24 19:24:53 -07:00
|
|
|
* without OOM reporting. See PropertyTable::change.
|
|
|
|
*/
|
2011-07-24 17:00:56 -07:00
|
|
|
entries = (Shape **) rt->calloc_(sizeOfEntries(JS_BIT(sizeLog2)));
|
2011-06-21 10:26:22 -07:00
|
|
|
if (!entries)
|
2009-07-09 13:27:21 -07:00
|
|
|
return false;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2009-07-09 13:27:21 -07:00
|
|
|
hashShift = JS_DHASH_BITS - sizeLog2;
|
2010-08-29 11:57:08 -07:00
|
|
|
for (Shape::Range r = lastProp->all(); !r.empty(); r.popFront()) {
|
|
|
|
const Shape &shape = r.front();
|
2011-05-04 12:01:39 -07:00
|
|
|
Shape **spp = search(shape.propid, true);
|
2010-09-29 16:15:43 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Beware duplicate args and arg vs. var conflicts: the youngest shape
|
|
|
|
* (nearest to lastProp) must win. See bug 600067.
|
|
|
|
*/
|
|
|
|
if (!SHAPE_FETCH(spp))
|
|
|
|
SHAPE_STORE_PRESERVING_COLLISION(spp, &shape);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
2009-07-09 13:27:21 -07:00
|
|
|
return true;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2010-08-29 11:57:08 -07:00
|
|
|
bool
|
2011-01-03 17:14:55 -08:00
|
|
|
Shape::hashify(JSRuntime *rt)
|
2009-07-21 14:25:11 -07:00
|
|
|
{
|
2011-02-09 15:18:03 -08:00
|
|
|
JS_ASSERT(!hasTable());
|
Bug 634155: Account for NewCompartment's memory, and change allocation APIs (r=nnethercote)
This changes the allocation API, in the following way:
js_malloc -> {cx->,rt->,OffTheBooks::}malloc
js_calloc -> {cx->,rt->,OffTheBooks::}calloc
js_realloc -> {cx->,rt->,OffTheBooks::}realloc
js_free -> {cx->,rt->,Foreground::,UnwantedForeground::}free
js_new -> {cx->,rt->,OffTheBooks::}new_
js_new_array -> {cx->,rt->,OffTheBooks::}new_array
js_delete -> {cx->,rt->,Foreground::,UnwantedForeground::}delete_
This is to move as many allocations as possible through a JSContext (so that they may be aken into account by gcMallocBytes) and to move as many deallocations to the background as possible (except on error paths).
2011-03-31 01:13:49 -07:00
|
|
|
PropertyTable *table = rt->new_<PropertyTable>(entryCount());
|
|
|
|
if (!table)
|
2011-01-03 17:14:55 -08:00
|
|
|
return false;
|
2011-06-02 10:06:36 -07:00
|
|
|
|
|
|
|
if (!table->init(rt, this)) {
|
|
|
|
rt->free_(table);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
Bug 634155: Account for NewCompartment's memory, and change allocation APIs (r=nnethercote)
This changes the allocation API, in the following way:
js_malloc -> {cx->,rt->,OffTheBooks::}malloc
js_calloc -> {cx->,rt->,OffTheBooks::}calloc
js_realloc -> {cx->,rt->,OffTheBooks::}realloc
js_free -> {cx->,rt->,Foreground::,UnwantedForeground::}free
js_new -> {cx->,rt->,OffTheBooks::}new_
js_new_array -> {cx->,rt->,OffTheBooks::}new_array
js_delete -> {cx->,rt->,Foreground::,UnwantedForeground::}delete_
This is to move as many allocations as possible through a JSContext (so that they may be aken into account by gcMallocBytes) and to move as many deallocations to the background as possible (except on error paths).
2011-03-31 01:13:49 -07:00
|
|
|
setTable(table);
|
2011-06-02 10:06:36 -07:00
|
|
|
return true;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2007-07-11 02:25:45 -07:00
|
|
|
JS_STATIC_ASSERT(sizeof(JSHashNumber) == 4);
|
|
|
|
JS_STATIC_ASSERT(sizeof(jsid) == JS_BYTES_PER_WORD);
|
|
|
|
|
|
|
|
#if JS_BYTES_PER_WORD == 4
|
2010-07-14 23:19:36 -07:00
|
|
|
# define HASH_ID(id) ((JSHashNumber)(JSID_BITS(id)))
|
2007-07-11 02:25:45 -07:00
|
|
|
#elif JS_BYTES_PER_WORD == 8
|
2010-07-14 23:19:36 -07:00
|
|
|
# define HASH_ID(id) ((JSHashNumber)(JSID_BITS(id)) ^ (JSHashNumber)((JSID_BITS(id)) >> 32))
|
2007-07-11 02:25:45 -07:00
|
|
|
#else
|
|
|
|
# error "Unsupported configuration"
|
|
|
|
#endif
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
/*
|
|
|
|
* Double hashing needs the second hash code to be relatively prime to table
|
|
|
|
* size, so we simply make hash2 odd. The inputs to multiplicative hash are
|
2007-07-11 02:25:45 -07:00
|
|
|
* the golden ratio, expressed as a fixed-point 32 bit fraction, and the id
|
|
|
|
* itself.
|
2007-03-22 10:30:00 -07:00
|
|
|
*/
|
2010-08-29 11:57:08 -07:00
|
|
|
#define HASH0(id) (HASH_ID(id) * JS_GOLDEN_RATIO)
|
|
|
|
#define HASH1(hash0,shift) ((hash0) >> (shift))
|
|
|
|
#define HASH2(hash0,log2,shift) ((((hash0) << (log2)) >> (shift)) | 1)
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2010-08-29 11:57:08 -07:00
|
|
|
Shape **
|
|
|
|
PropertyTable::search(jsid id, bool adding)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
JSHashNumber hash0, hash1, hash2;
|
2009-07-09 13:27:21 -07:00
|
|
|
int sizeLog2;
|
2010-08-29 11:57:08 -07:00
|
|
|
Shape *stored, *shape, **spp, **firstRemoved;
|
2007-03-22 10:30:00 -07:00
|
|
|
uint32 sizeMask;
|
|
|
|
|
2010-08-29 11:57:08 -07:00
|
|
|
JS_ASSERT(entries);
|
2010-07-14 23:19:36 -07:00
|
|
|
JS_ASSERT(!JSID_IS_VOID(id));
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
/* Compute the primary hash address. */
|
2010-08-29 11:57:08 -07:00
|
|
|
hash0 = HASH0(id);
|
|
|
|
hash1 = HASH1(hash0, hashShift);
|
|
|
|
spp = entries + hash1;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
/* Miss: return space for a new entry. */
|
|
|
|
stored = *spp;
|
2011-06-21 10:26:22 -07:00
|
|
|
if (SHAPE_IS_FREE(stored))
|
2007-03-22 10:30:00 -07:00
|
|
|
return spp;
|
|
|
|
|
|
|
|
/* Hit: return entry. */
|
2010-08-29 11:57:08 -07:00
|
|
|
shape = SHAPE_CLEAR_COLLISION(stored);
|
2011-06-21 10:26:22 -07:00
|
|
|
if (shape && shape->propid == id)
|
2007-03-22 10:30:00 -07:00
|
|
|
return spp;
|
|
|
|
|
|
|
|
/* Collision: double hash. */
|
|
|
|
sizeLog2 = JS_DHASH_BITS - hashShift;
|
2010-08-29 11:57:08 -07:00
|
|
|
hash2 = HASH2(hash0, sizeLog2, hashShift);
|
2007-03-22 10:30:00 -07:00
|
|
|
sizeMask = JS_BITMASK(sizeLog2);
|
|
|
|
|
2009-12-02 19:13:31 -08:00
|
|
|
#ifdef DEBUG
|
2010-08-29 11:57:08 -07:00
|
|
|
jsuword collision_flag = SHAPE_COLLISION;
|
2009-12-02 19:13:31 -08:00
|
|
|
#endif
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
/* Save the first removed entry pointer so we can recycle it if adding. */
|
2010-08-29 11:57:08 -07:00
|
|
|
if (SHAPE_IS_REMOVED(stored)) {
|
2007-03-22 10:30:00 -07:00
|
|
|
firstRemoved = spp;
|
|
|
|
} else {
|
|
|
|
firstRemoved = NULL;
|
2010-08-29 11:57:08 -07:00
|
|
|
if (adding && !SHAPE_HAD_COLLISION(stored))
|
|
|
|
SHAPE_FLAG_COLLISION(spp, shape);
|
2009-12-02 19:13:31 -08:00
|
|
|
#ifdef DEBUG
|
2010-08-29 11:57:08 -07:00
|
|
|
collision_flag &= jsuword(*spp) & SHAPE_COLLISION;
|
2009-12-02 19:13:31 -08:00
|
|
|
#endif
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
hash1 -= hash2;
|
|
|
|
hash1 &= sizeMask;
|
2010-08-29 11:57:08 -07:00
|
|
|
spp = entries + hash1;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
stored = *spp;
|
2011-06-21 10:26:22 -07:00
|
|
|
if (SHAPE_IS_FREE(stored))
|
2007-03-22 10:30:00 -07:00
|
|
|
return (adding && firstRemoved) ? firstRemoved : spp;
|
|
|
|
|
2010-08-29 11:57:08 -07:00
|
|
|
shape = SHAPE_CLEAR_COLLISION(stored);
|
2011-05-04 12:01:39 -07:00
|
|
|
if (shape && shape->propid == id) {
|
2009-12-02 19:13:31 -08:00
|
|
|
JS_ASSERT(collision_flag);
|
2007-03-22 10:30:00 -07:00
|
|
|
return spp;
|
|
|
|
}
|
|
|
|
|
2010-08-29 11:57:08 -07:00
|
|
|
if (SHAPE_IS_REMOVED(stored)) {
|
2007-03-22 10:30:00 -07:00
|
|
|
if (!firstRemoved)
|
|
|
|
firstRemoved = spp;
|
|
|
|
} else {
|
2010-08-29 11:57:08 -07:00
|
|
|
if (adding && !SHAPE_HAD_COLLISION(stored))
|
|
|
|
SHAPE_FLAG_COLLISION(spp, shape);
|
2009-12-02 19:13:31 -08:00
|
|
|
#ifdef DEBUG
|
2010-08-29 11:57:08 -07:00
|
|
|
collision_flag &= jsuword(*spp) & SHAPE_COLLISION;
|
2009-12-02 19:13:31 -08:00
|
|
|
#endif
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* NOTREACHED */
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2009-07-09 13:27:21 -07:00
|
|
|
bool
|
2010-10-24 19:24:53 -07:00
|
|
|
PropertyTable::change(int log2Delta, JSContext *cx)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2010-08-29 11:57:08 -07:00
|
|
|
JS_ASSERT(entries);
|
2008-02-26 17:59:36 -08:00
|
|
|
|
2010-10-24 19:24:53 -07:00
|
|
|
/*
|
Bug 634155: Account for NewCompartment's memory, and change allocation APIs (r=nnethercote)
This changes the allocation API, in the following way:
js_malloc -> {cx->,rt->,OffTheBooks::}malloc
js_calloc -> {cx->,rt->,OffTheBooks::}calloc
js_realloc -> {cx->,rt->,OffTheBooks::}realloc
js_free -> {cx->,rt->,Foreground::,UnwantedForeground::}free
js_new -> {cx->,rt->,OffTheBooks::}new_
js_new_array -> {cx->,rt->,OffTheBooks::}new_array
js_delete -> {cx->,rt->,Foreground::,UnwantedForeground::}delete_
This is to move as many allocations as possible through a JSContext (so that they may be aken into account by gcMallocBytes) and to move as many deallocations to the background as possible (except on error paths).
2011-03-31 01:13:49 -07:00
|
|
|
* Grow, shrink, or compress by changing this->entries.
|
2010-10-24 19:24:53 -07:00
|
|
|
*/
|
2011-07-24 17:00:56 -07:00
|
|
|
int oldlog2 = JS_DHASH_BITS - hashShift;
|
|
|
|
int newlog2 = oldlog2 + log2Delta;
|
|
|
|
uint32 oldsize = JS_BIT(oldlog2);
|
|
|
|
uint32 newsize = JS_BIT(newlog2);
|
|
|
|
Shape **newTable = (Shape **) cx->calloc_(sizeOfEntries(newsize));
|
2011-06-21 10:26:22 -07:00
|
|
|
if (!newTable)
|
2009-07-09 13:27:21 -07:00
|
|
|
return false;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2010-08-29 11:57:08 -07:00
|
|
|
/* Now that we have newTable allocated, update members. */
|
2009-07-09 13:27:21 -07:00
|
|
|
hashShift = JS_DHASH_BITS - newlog2;
|
|
|
|
removedCount = 0;
|
2011-07-24 17:00:56 -07:00
|
|
|
Shape **oldTable = entries;
|
2010-08-29 11:57:08 -07:00
|
|
|
entries = newTable;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
/* Copy only live entries, leaving removed and free ones behind. */
|
2011-07-24 17:00:56 -07:00
|
|
|
for (Shape **oldspp = oldTable; oldsize != 0; oldspp++) {
|
|
|
|
Shape *shape = SHAPE_FETCH(oldspp);
|
2010-08-29 11:57:08 -07:00
|
|
|
if (shape) {
|
2011-07-24 17:00:56 -07:00
|
|
|
Shape **spp = search(shape->propid, true);
|
2010-08-29 11:57:08 -07:00
|
|
|
JS_ASSERT(SHAPE_IS_FREE(*spp));
|
|
|
|
*spp = shape;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
oldsize--;
|
|
|
|
}
|
|
|
|
|
Bug 634155: Account for NewCompartment's memory, and change allocation APIs (r=nnethercote)
This changes the allocation API, in the following way:
js_malloc -> {cx->,rt->,OffTheBooks::}malloc
js_calloc -> {cx->,rt->,OffTheBooks::}calloc
js_realloc -> {cx->,rt->,OffTheBooks::}realloc
js_free -> {cx->,rt->,Foreground::,UnwantedForeground::}free
js_new -> {cx->,rt->,OffTheBooks::}new_
js_new_array -> {cx->,rt->,OffTheBooks::}new_array
js_delete -> {cx->,rt->,Foreground::,UnwantedForeground::}delete_
This is to move as many allocations as possible through a JSContext (so that they may be aken into account by gcMallocBytes) and to move as many deallocations to the background as possible (except on error paths).
2011-03-31 01:13:49 -07:00
|
|
|
/* Finally, free the old entries storage. */
|
2011-03-31 01:14:12 -07:00
|
|
|
cx->free_(oldTable);
|
2009-07-09 13:27:21 -07:00
|
|
|
return true;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2010-11-19 15:53:55 -08:00
|
|
|
bool
|
|
|
|
PropertyTable::grow(JSContext *cx)
|
|
|
|
{
|
|
|
|
JS_ASSERT(needsToGrow());
|
|
|
|
|
|
|
|
uint32 size = capacity();
|
|
|
|
int delta = removedCount < size >> 2;
|
|
|
|
|
|
|
|
if (!change(delta, cx) && entryCount + removedCount == size - 1) {
|
|
|
|
JS_ReportOutOfMemory(cx);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2010-08-29 11:57:08 -07:00
|
|
|
Shape *
|
|
|
|
Shape::getChild(JSContext *cx, const js::Shape &child, Shape **listp)
|
|
|
|
{
|
2011-05-04 12:01:39 -07:00
|
|
|
JS_ASSERT(!JSID_IS_VOID(child.propid));
|
2010-08-29 11:57:08 -07:00
|
|
|
JS_ASSERT(!child.inDictionary());
|
|
|
|
|
|
|
|
if (inDictionary()) {
|
2010-11-19 15:53:55 -08:00
|
|
|
Shape *oldShape = *listp;
|
2011-02-09 15:18:03 -08:00
|
|
|
PropertyTable *table = (oldShape && oldShape->hasTable()) ? oldShape->getTable() : NULL;
|
2010-11-19 15:53:55 -08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Attempt to grow table if needed before extending *listp, rather than
|
|
|
|
* risking OOM under table->grow after newDictionaryShape succeeds, and
|
|
|
|
* then have to fix up *listp.
|
|
|
|
*/
|
|
|
|
if (table && table->needsToGrow() && !table->grow(cx))
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (newDictionaryShape(cx, child, listp)) {
|
|
|
|
Shape *newShape = *listp;
|
|
|
|
|
|
|
|
JS_ASSERT(oldShape == newShape->parent);
|
|
|
|
if (table) {
|
|
|
|
/* Add newShape to the property table. */
|
2011-05-04 12:01:39 -07:00
|
|
|
Shape **spp = table->search(newShape->propid, true);
|
2010-11-19 15:53:55 -08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Beware duplicate formal parameters, allowed by ECMA-262 in
|
2010-12-29 23:46:50 -08:00
|
|
|
* non-strict mode. Otherwise we know that Bindings::add (our
|
|
|
|
* caller) won't pass an id already in the table to us. In the
|
|
|
|
* case of duplicate formals, the last one wins, so while we
|
|
|
|
* must not overcount entries, we must store newShape.
|
2010-11-19 15:53:55 -08:00
|
|
|
*/
|
|
|
|
if (!SHAPE_FETCH(spp))
|
|
|
|
++table->entryCount;
|
|
|
|
SHAPE_STORE_PRESERVING_COLLISION(spp, newShape);
|
|
|
|
|
|
|
|
/* Hand the table off from oldShape to newShape. */
|
|
|
|
oldShape->setTable(NULL);
|
|
|
|
newShape->setTable(table);
|
|
|
|
} else {
|
2011-02-09 15:18:03 -08:00
|
|
|
if (!newShape->hasTable())
|
2011-01-03 17:14:55 -08:00
|
|
|
newShape->hashify(cx->runtime);
|
2010-11-19 15:53:55 -08:00
|
|
|
}
|
|
|
|
return newShape;
|
|
|
|
}
|
|
|
|
|
2010-08-29 11:57:08 -07:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2010-09-13 18:44:34 -07:00
|
|
|
if ((*listp)->entryCount() >= PropertyTree::MAX_HEIGHT) {
|
|
|
|
Shape *dprop = Shape::newDictionaryList(cx, listp);
|
|
|
|
if (!dprop)
|
|
|
|
return NULL;
|
|
|
|
return dprop->getChild(cx, child, listp);
|
|
|
|
}
|
|
|
|
|
2010-08-29 11:57:08 -07:00
|
|
|
Shape *shape = JS_PROPERTY_TREE(cx).getChild(cx, this, child);
|
|
|
|
if (shape) {
|
|
|
|
JS_ASSERT(shape->parent == this);
|
|
|
|
JS_ASSERT(this == *listp);
|
|
|
|
*listp = shape;
|
|
|
|
}
|
|
|
|
return shape;
|
|
|
|
}
|
|
|
|
|
2009-12-01 12:49:15 -08:00
|
|
|
/*
|
|
|
|
* Get or create a property-tree or dictionary child property of parent, which
|
|
|
|
* must be lastProp if inDictionaryMode(), else parent must be one of lastProp
|
|
|
|
* or lastProp->parent.
|
|
|
|
*/
|
2010-08-29 11:57:08 -07:00
|
|
|
Shape *
|
|
|
|
JSObject::getChildProperty(JSContext *cx, Shape *parent, Shape &child)
|
2009-12-01 12:49:15 -08:00
|
|
|
{
|
2011-05-04 12:01:39 -07:00
|
|
|
JS_ASSERT(!JSID_IS_VOID(child.propid));
|
2010-02-05 16:11:13 -08:00
|
|
|
JS_ASSERT(!child.inDictionary());
|
2009-12-01 12:49:15 -08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Aliases share another property's slot, passed in the |slot| parameter.
|
|
|
|
* Shared properties have no slot. Unshared properties that do not alias
|
|
|
|
* another property's slot allocate a slot here, but may lose it due to a
|
|
|
|
* JS_ClearScope call.
|
|
|
|
*/
|
2010-02-05 16:11:13 -08:00
|
|
|
if (!child.isAlias()) {
|
2009-12-01 12:49:15 -08:00
|
|
|
if (child.attrs & JSPROP_SHARED) {
|
2010-08-29 11:57:08 -07:00
|
|
|
child.slot = SHAPE_INVALID_SLOT;
|
2009-12-01 12:49:15 -08:00
|
|
|
} else {
|
|
|
|
/*
|
2010-09-02 14:50:44 -07:00
|
|
|
* We may have set slot from a nearly-matching shape, above. If so,
|
|
|
|
* we're overwriting that nearly-matching shape, so we can reuse
|
|
|
|
* its slot -- we don't need to allocate a new one. Similarly, we
|
|
|
|
* use a specific slot if provided by the caller.
|
2009-12-01 12:49:15 -08:00
|
|
|
*/
|
2010-08-29 11:57:08 -07:00
|
|
|
if (child.slot == SHAPE_INVALID_SLOT && !allocSlot(cx, &child.slot))
|
2009-12-01 12:49:15 -08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-09-02 14:50:44 -07:00
|
|
|
Shape *shape;
|
|
|
|
|
2009-12-01 12:49:15 -08:00
|
|
|
if (inDictionaryMode()) {
|
|
|
|
JS_ASSERT(parent == lastProp);
|
2010-08-29 11:57:08 -07:00
|
|
|
if (parent->frozen()) {
|
|
|
|
parent = Shape::newDictionaryList(cx, &lastProp);
|
|
|
|
if (!parent)
|
|
|
|
return NULL;
|
|
|
|
JS_ASSERT(!parent->frozen());
|
|
|
|
}
|
2010-09-02 14:50:44 -07:00
|
|
|
shape = Shape::newDictionaryShape(cx, child, &lastProp);
|
|
|
|
if (!shape)
|
|
|
|
return NULL;
|
|
|
|
} else {
|
|
|
|
shape = JS_PROPERTY_TREE(cx).getChild(cx, parent, child);
|
2011-02-01 14:54:50 -08:00
|
|
|
if (!shape)
|
|
|
|
return NULL;
|
|
|
|
JS_ASSERT(shape->parent == parent);
|
|
|
|
JS_ASSERT_IF(parent != lastProp, parent == lastProp->parent);
|
|
|
|
setLastProperty(shape);
|
2009-12-01 12:49:15 -08:00
|
|
|
}
|
2010-01-14 15:20:27 -08:00
|
|
|
|
2010-09-02 14:50:44 -07:00
|
|
|
updateFlags(shape);
|
|
|
|
updateShape(cx);
|
2010-08-29 11:57:08 -07:00
|
|
|
return shape;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2010-08-29 11:57:08 -07:00
|
|
|
Shape *
|
|
|
|
Shape::newDictionaryShape(JSContext *cx, const Shape &child, Shape **listp)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2010-08-29 11:57:08 -07:00
|
|
|
Shape *dprop = JS_PROPERTY_TREE(cx).newShape(cx);
|
2010-03-22 11:11:44 -07:00
|
|
|
if (!dprop)
|
2009-12-01 12:49:15 -08:00
|
|
|
return NULL;
|
|
|
|
|
2011-05-04 12:01:39 -07:00
|
|
|
new (dprop) Shape(child.propid, child.rawGetter, child.rawSetter, child.slot, child.attrs,
|
2010-09-02 14:50:44 -07:00
|
|
|
(child.flags & ~FROZEN) | IN_DICTIONARY, child.shortid,
|
2011-02-04 10:59:07 -08:00
|
|
|
js_GenerateShape(cx), child.slotSpan);
|
2009-12-01 12:49:15 -08:00
|
|
|
|
2010-08-29 11:57:08 -07:00
|
|
|
dprop->listp = NULL;
|
|
|
|
dprop->insertIntoDictionary(listp);
|
2009-12-01 12:49:15 -08:00
|
|
|
return dprop;
|
|
|
|
}
|
|
|
|
|
2010-08-29 11:57:08 -07:00
|
|
|
Shape *
|
|
|
|
Shape::newDictionaryList(JSContext *cx, Shape **listp)
|
2009-12-01 12:49:15 -08:00
|
|
|
{
|
2010-08-29 11:57:08 -07:00
|
|
|
Shape *shape = *listp;
|
|
|
|
Shape *list = shape;
|
2009-12-01 12:49:15 -08:00
|
|
|
|
2011-03-23 11:57:19 -07:00
|
|
|
/*
|
|
|
|
* We temporarily create the dictionary shapes using a root located on the
|
|
|
|
* stack. This way, the GC doesn't see any intermediate state until we
|
|
|
|
* switch listp at the end.
|
|
|
|
*/
|
|
|
|
Shape *root = NULL;
|
|
|
|
Shape **childp = &root;
|
2009-12-01 12:49:15 -08:00
|
|
|
|
2010-08-29 11:57:08 -07:00
|
|
|
while (shape) {
|
2010-09-20 08:01:06 -07:00
|
|
|
JS_ASSERT_IF(!shape->frozen(), !shape->inDictionary());
|
2009-12-01 12:49:15 -08:00
|
|
|
|
2010-08-29 11:57:08 -07:00
|
|
|
Shape *dprop = Shape::newDictionaryShape(cx, *shape, childp);
|
2009-12-01 12:49:15 -08:00
|
|
|
if (!dprop) {
|
2010-08-29 11:57:08 -07:00
|
|
|
*listp = list;
|
|
|
|
return NULL;
|
2009-12-01 12:49:15 -08:00
|
|
|
}
|
|
|
|
|
2011-02-09 15:18:03 -08:00
|
|
|
JS_ASSERT(!dprop->hasTable());
|
2009-12-01 12:49:15 -08:00
|
|
|
childp = &dprop->parent;
|
2010-08-29 11:57:08 -07:00
|
|
|
shape = shape->parent;
|
2009-12-01 12:49:15 -08:00
|
|
|
}
|
|
|
|
|
2011-03-23 11:57:19 -07:00
|
|
|
*listp = root;
|
|
|
|
root->listp = listp;
|
|
|
|
|
|
|
|
JS_ASSERT(root->inDictionary());
|
|
|
|
root->hashify(cx->runtime);
|
|
|
|
return root;
|
2009-12-01 12:49:15 -08:00
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2010-08-29 11:57:08 -07:00
|
|
|
bool
|
|
|
|
JSObject::toDictionaryMode(JSContext *cx)
|
2009-12-01 12:49:15 -08:00
|
|
|
{
|
2010-08-29 11:57:08 -07:00
|
|
|
JS_ASSERT(!inDictionaryMode());
|
2011-03-23 11:57:44 -07:00
|
|
|
|
|
|
|
/* We allocate the shapes from cx->compartment, so make sure it's right. */
|
|
|
|
JS_ASSERT(compartment() == cx->compartment);
|
2010-08-29 11:57:08 -07:00
|
|
|
if (!Shape::newDictionaryList(cx, &lastProp))
|
|
|
|
return false;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2010-08-29 11:57:08 -07:00
|
|
|
clearOwnShape();
|
|
|
|
return true;
|
2009-12-01 12:49:15 -08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Normalize stub getter and setter values for faster is-stub testing in the
|
2010-08-29 11:57:08 -07:00
|
|
|
* SHAPE_CALL_[GS]ETTER macros.
|
2009-12-01 12:49:15 -08:00
|
|
|
*/
|
|
|
|
static inline bool
|
2010-08-29 11:57:08 -07:00
|
|
|
NormalizeGetterAndSetter(JSContext *cx, JSObject *obj,
|
2009-12-01 12:49:15 -08:00
|
|
|
jsid id, uintN attrs, uintN flags,
|
2010-07-14 23:19:36 -07:00
|
|
|
PropertyOp &getter,
|
2011-02-09 11:31:40 -08:00
|
|
|
StrictPropertyOp &setter)
|
2009-12-01 12:49:15 -08:00
|
|
|
{
|
2011-02-09 11:31:40 -08:00
|
|
|
if (setter == StrictPropertyStub) {
|
2010-04-30 16:03:37 -07:00
|
|
|
JS_ASSERT(!(attrs & JSPROP_SETTER));
|
2007-03-22 10:30:00 -07:00
|
|
|
setter = NULL;
|
2010-04-30 16:03:37 -07:00
|
|
|
}
|
2010-08-29 11:57:08 -07:00
|
|
|
if (flags & Shape::METHOD) {
|
2009-09-03 14:41:19 -07:00
|
|
|
/* Here, getter is the method, a function object reference. */
|
|
|
|
JS_ASSERT(getter);
|
|
|
|
JS_ASSERT(!setter || setter == js_watch_set);
|
|
|
|
JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
|
|
|
|
} else {
|
2010-07-14 23:19:36 -07:00
|
|
|
if (getter == PropertyStub) {
|
2010-04-30 16:03:37 -07:00
|
|
|
JS_ASSERT(!(attrs & JSPROP_GETTER));
|
2009-09-03 14:41:19 -07:00
|
|
|
getter = NULL;
|
2010-04-30 16:03:37 -07:00
|
|
|
}
|
2009-09-03 14:41:19 -07:00
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2009-12-01 12:49:15 -08:00
|
|
|
return true;
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2010-09-02 18:46:11 -07:00
|
|
|
#ifdef DEBUG
|
|
|
|
# define CHECK_SHAPE_CONSISTENCY(obj) obj->checkShapeConsistency()
|
|
|
|
|
|
|
|
void
|
|
|
|
JSObject::checkShapeConsistency()
|
|
|
|
{
|
|
|
|
static int throttle = -1;
|
|
|
|
if (throttle < 0) {
|
|
|
|
if (const char *var = getenv("JS_CHECK_SHAPE_THROTTLE"))
|
|
|
|
throttle = atoi(var);
|
|
|
|
if (throttle < 0)
|
|
|
|
throttle = 0;
|
|
|
|
}
|
|
|
|
if (throttle == 0)
|
|
|
|
return;
|
|
|
|
|
|
|
|
JS_ASSERT(isNative());
|
|
|
|
if (hasOwnShape())
|
2011-05-04 12:01:39 -07:00
|
|
|
JS_ASSERT(objShape != lastProp->shapeid);
|
2010-09-02 18:46:11 -07:00
|
|
|
else
|
2011-05-04 12:01:39 -07:00
|
|
|
JS_ASSERT(objShape == lastProp->shapeid);
|
2010-09-02 18:46:11 -07:00
|
|
|
|
|
|
|
Shape *shape = lastProp;
|
|
|
|
Shape *prev = NULL;
|
|
|
|
|
|
|
|
if (inDictionaryMode()) {
|
2011-02-09 15:18:03 -08:00
|
|
|
if (shape->hasTable()) {
|
|
|
|
PropertyTable *table = shape->getTable();
|
2010-09-03 11:05:43 -07:00
|
|
|
for (uint32 fslot = table->freelist; fslot != SHAPE_INVALID_SLOT;
|
2011-07-26 11:41:43 -07:00
|
|
|
fslot = getSlotRef(fslot).toPrivateUint32()) {
|
2010-09-03 11:05:43 -07:00
|
|
|
JS_ASSERT(fslot < shape->slotSpan);
|
2010-09-02 18:46:11 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
for (int n = throttle; --n >= 0 && shape->parent; shape = shape->parent) {
|
2011-02-09 15:18:03 -08:00
|
|
|
JS_ASSERT_IF(shape != lastProp, !shape->hasTable());
|
2010-09-02 18:46:11 -07:00
|
|
|
|
2011-05-04 12:01:39 -07:00
|
|
|
Shape **spp = table->search(shape->propid, false);
|
2010-09-02 18:46:11 -07:00
|
|
|
JS_ASSERT(SHAPE_FETCH(spp) == shape);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
shape = shape->parent;
|
|
|
|
for (int n = throttle; --n >= 0 && shape; shape = shape->parent)
|
2011-02-09 15:18:03 -08:00
|
|
|
JS_ASSERT(!shape->hasTable());
|
2010-09-02 18:46:11 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
shape = lastProp;
|
|
|
|
for (int n = throttle; --n >= 0 && shape; shape = shape->parent) {
|
2010-09-03 11:05:43 -07:00
|
|
|
JS_ASSERT_IF(shape->slot != SHAPE_INVALID_SLOT, shape->slot < shape->slotSpan);
|
2010-09-02 18:46:11 -07:00
|
|
|
if (!prev) {
|
|
|
|
JS_ASSERT(shape == lastProp);
|
|
|
|
JS_ASSERT(shape->listp == &lastProp);
|
|
|
|
} else {
|
|
|
|
JS_ASSERT(shape->listp == &prev->parent);
|
2010-09-03 11:05:43 -07:00
|
|
|
JS_ASSERT(prev->slotSpan >= shape->slotSpan);
|
2010-09-02 18:46:11 -07:00
|
|
|
}
|
|
|
|
prev = shape;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
for (int n = throttle; --n >= 0 && shape->parent; shape = shape->parent) {
|
2011-02-09 15:18:03 -08:00
|
|
|
if (shape->hasTable()) {
|
|
|
|
PropertyTable *table = shape->getTable();
|
2010-09-02 18:46:11 -07:00
|
|
|
JS_ASSERT(shape->parent);
|
|
|
|
for (Shape::Range r(shape); !r.empty(); r.popFront()) {
|
2011-05-04 12:01:39 -07:00
|
|
|
Shape **spp = table->search(r.front().propid, false);
|
2010-09-02 18:46:11 -07:00
|
|
|
JS_ASSERT(SHAPE_FETCH(spp) == &r.front());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (prev) {
|
2010-09-03 11:05:43 -07:00
|
|
|
JS_ASSERT(prev->slotSpan >= shape->slotSpan);
|
2010-09-12 09:34:56 -07:00
|
|
|
shape->kids.checkConsistency(prev);
|
2010-09-02 18:46:11 -07:00
|
|
|
}
|
|
|
|
prev = shape;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
# define CHECK_SHAPE_CONSISTENCY(obj) ((void)0)
|
|
|
|
#endif
|
|
|
|
|
2010-08-29 11:57:08 -07:00
|
|
|
const Shape *
|
|
|
|
JSObject::addProperty(JSContext *cx, jsid id,
|
2011-02-09 11:31:40 -08:00
|
|
|
PropertyOp getter, StrictPropertyOp setter,
|
2010-08-29 11:57:08 -07:00
|
|
|
uint32 slot, uintN attrs,
|
|
|
|
uintN flags, intN shortid)
|
2009-12-01 12:49:15 -08:00
|
|
|
{
|
2010-08-29 11:57:08 -07:00
|
|
|
JS_ASSERT(!JSID_IS_VOID(id));
|
|
|
|
|
2010-09-16 11:56:54 -07:00
|
|
|
if (!isExtensible()) {
|
|
|
|
reportNotExtensible(cx);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2009-12-01 12:49:15 -08:00
|
|
|
NormalizeGetterAndSetter(cx, this, id, attrs, flags, getter, setter);
|
|
|
|
|
2010-08-29 11:57:08 -07:00
|
|
|
/* Search for id with adding = true in order to claim its entry. */
|
|
|
|
Shape **spp = nativeSearch(id, true);
|
|
|
|
JS_ASSERT(!SHAPE_FETCH(spp));
|
2010-11-09 15:04:12 -08:00
|
|
|
const Shape *shape = addPropertyInternal(cx, id, getter, setter, slot, attrs,
|
|
|
|
flags, shortid, spp);
|
|
|
|
if (!shape)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
/* Update any watchpoints referring to this property. */
|
2011-06-21 10:26:22 -07:00
|
|
|
return js_UpdateWatchpointsForShape(cx, this, shape);
|
2010-08-29 11:57:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
const Shape *
|
2010-09-16 11:56:54 -07:00
|
|
|
JSObject::addPropertyInternal(JSContext *cx, jsid id,
|
2011-02-09 11:31:40 -08:00
|
|
|
PropertyOp getter, StrictPropertyOp setter,
|
2010-09-16 11:56:54 -07:00
|
|
|
uint32 slot, uintN attrs,
|
|
|
|
uintN flags, intN shortid,
|
|
|
|
Shape **spp)
|
2010-08-29 11:57:08 -07:00
|
|
|
{
|
2010-09-16 11:56:54 -07:00
|
|
|
JS_ASSERT_IF(inDictionaryMode(), !lastProp->frozen());
|
2010-09-21 11:35:29 -07:00
|
|
|
|
2010-08-29 11:57:08 -07:00
|
|
|
PropertyTable *table = NULL;
|
|
|
|
if (!inDictionaryMode()) {
|
2010-09-13 18:44:34 -07:00
|
|
|
if (lastProp->entryCount() >= PropertyTree::MAX_HEIGHT) {
|
2010-08-29 11:57:08 -07:00
|
|
|
if (!toDictionaryMode(cx))
|
|
|
|
return NULL;
|
|
|
|
spp = nativeSearch(id, true);
|
2011-02-09 15:18:03 -08:00
|
|
|
table = lastProp->getTable();
|
2010-08-29 11:57:08 -07:00
|
|
|
}
|
2011-02-09 15:18:03 -08:00
|
|
|
} else if (lastProp->hasTable()) {
|
|
|
|
table = lastProp->getTable();
|
2010-11-19 15:53:55 -08:00
|
|
|
if (table->needsToGrow()) {
|
|
|
|
if (!table->grow(cx))
|
2010-08-29 11:57:08 -07:00
|
|
|
return NULL;
|
2010-11-19 15:53:55 -08:00
|
|
|
|
2010-08-29 11:57:08 -07:00
|
|
|
spp = table->search(id, true);
|
|
|
|
JS_ASSERT(!SHAPE_FETCH(spp));
|
|
|
|
}
|
2009-12-01 12:49:15 -08:00
|
|
|
}
|
2009-11-20 16:14:42 -08:00
|
|
|
|
2009-12-01 12:49:15 -08:00
|
|
|
/* Find or create a property tree node labeled by our arguments. */
|
2010-08-29 11:57:08 -07:00
|
|
|
const Shape *shape;
|
2009-12-01 12:49:15 -08:00
|
|
|
{
|
2010-08-29 11:57:08 -07:00
|
|
|
Shape child(id, getter, setter, slot, attrs, flags, shortid);
|
|
|
|
shape = getChildProperty(cx, lastProp, child);
|
2009-11-20 16:14:42 -08:00
|
|
|
}
|
2009-03-31 17:55:43 -07:00
|
|
|
|
2010-08-29 11:57:08 -07:00
|
|
|
if (shape) {
|
|
|
|
JS_ASSERT(shape == lastProp);
|
|
|
|
|
|
|
|
if (table) {
|
|
|
|
/* Store the tree node pointer in the table entry for id. */
|
|
|
|
SHAPE_STORE_PRESERVING_COLLISION(spp, shape);
|
|
|
|
++table->entryCount;
|
|
|
|
|
|
|
|
/* Pass the table along to the new lastProp, namely shape. */
|
2011-02-09 15:18:03 -08:00
|
|
|
JS_ASSERT(shape->parent->getTable() == table);
|
2010-08-29 11:57:08 -07:00
|
|
|
shape->parent->setTable(NULL);
|
|
|
|
shape->setTable(table);
|
|
|
|
}
|
2011-04-13 13:43:33 -07:00
|
|
|
|
2010-09-02 18:46:11 -07:00
|
|
|
CHECK_SHAPE_CONSISTENCY(this);
|
2010-08-29 11:57:08 -07:00
|
|
|
return shape;
|
2009-12-01 12:49:15 -08:00
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2010-09-02 18:46:11 -07:00
|
|
|
CHECK_SHAPE_CONSISTENCY(this);
|
2009-12-01 12:49:15 -08:00
|
|
|
return NULL;
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2011-01-24 15:04:29 -08:00
|
|
|
/*
|
|
|
|
* Check and adjust the new attributes for the shape to make sure that our
|
|
|
|
* slot access optimizations are sound. It is responsibility of the callers to
|
|
|
|
* enforce all restrictions from ECMA-262 v5 8.12.9 [[DefineOwnProperty]].
|
|
|
|
*/
|
|
|
|
inline bool
|
|
|
|
CheckCanChangeAttrs(JSContext *cx, JSObject *obj, const Shape *shape, uintN *attrsp)
|
|
|
|
{
|
|
|
|
if (shape->configurable())
|
|
|
|
return true;
|
|
|
|
|
|
|
|
/* A permanent property must stay permanent. */
|
|
|
|
*attrsp |= JSPROP_PERMANENT;
|
|
|
|
|
|
|
|
/* Reject attempts to remove a slot from the permanent data property. */
|
|
|
|
if (shape->isDataDescriptor() && shape->hasSlot() &&
|
|
|
|
(*attrsp & (JSPROP_GETTER | JSPROP_SETTER | JSPROP_SHARED))) {
|
2011-05-04 12:01:39 -07:00
|
|
|
obj->reportNotConfigurable(cx, shape->propid);
|
2011-01-24 15:04:29 -08:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2010-08-29 11:57:08 -07:00
|
|
|
const Shape *
|
|
|
|
JSObject::putProperty(JSContext *cx, jsid id,
|
2011-02-09 11:31:40 -08:00
|
|
|
PropertyOp getter, StrictPropertyOp setter,
|
2010-08-29 11:57:08 -07:00
|
|
|
uint32 slot, uintN attrs,
|
|
|
|
uintN flags, intN shortid)
|
2009-12-01 12:49:15 -08:00
|
|
|
{
|
2010-07-14 23:19:36 -07:00
|
|
|
JS_ASSERT(!JSID_IS_VOID(id));
|
2009-11-20 16:14:42 -08:00
|
|
|
|
2010-09-16 11:56:54 -07:00
|
|
|
/*
|
|
|
|
* Horrid non-strict eval, debuggers, and |default xml namespace ...| may
|
|
|
|
* extend Call objects.
|
|
|
|
*/
|
|
|
|
if (lastProp->frozen()) {
|
|
|
|
if (!Shape::newDictionaryList(cx, &lastProp))
|
|
|
|
return NULL;
|
|
|
|
JS_ASSERT(!lastProp->frozen());
|
|
|
|
}
|
|
|
|
|
2010-08-29 11:57:08 -07:00
|
|
|
NormalizeGetterAndSetter(cx, this, id, attrs, flags, getter, setter);
|
2009-11-20 16:14:42 -08:00
|
|
|
|
2010-08-29 11:57:08 -07:00
|
|
|
/* Search for id in order to claim its entry if table has been allocated. */
|
2010-09-16 11:56:54 -07:00
|
|
|
Shape **spp = nativeSearch(id, true);
|
|
|
|
Shape *shape = SHAPE_FETCH(spp);
|
|
|
|
if (!shape) {
|
|
|
|
/*
|
|
|
|
* You can't add properties to a non-extensible object, but you can change
|
|
|
|
* attributes of properties in such objects.
|
|
|
|
*/
|
|
|
|
if (!isExtensible()) {
|
|
|
|
reportNotExtensible(cx);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
2011-02-08 13:45:12 -08:00
|
|
|
const Shape *newShape =
|
2010-11-09 15:04:12 -08:00
|
|
|
addPropertyInternal(cx, id, getter, setter, slot, attrs, flags, shortid, spp);
|
2011-02-08 13:45:12 -08:00
|
|
|
if (!newShape)
|
2010-11-09 15:04:12 -08:00
|
|
|
return NULL;
|
2011-06-21 10:26:22 -07:00
|
|
|
return js_UpdateWatchpointsForShape(cx, this, newShape);
|
2010-09-16 11:56:54 -07:00
|
|
|
}
|
2009-12-01 12:49:15 -08:00
|
|
|
|
2010-08-29 11:57:08 -07:00
|
|
|
/* Property exists: search must have returned a valid *spp. */
|
|
|
|
JS_ASSERT(!SHAPE_IS_REMOVED(*spp));
|
2009-12-01 12:49:15 -08:00
|
|
|
|
2011-01-24 15:04:29 -08:00
|
|
|
if (!CheckCanChangeAttrs(cx, this, shape, &attrs))
|
|
|
|
return NULL;
|
|
|
|
|
2009-12-01 12:49:15 -08:00
|
|
|
/*
|
2010-09-16 11:56:54 -07:00
|
|
|
* If the caller wants to allocate a slot, but doesn't care which slot,
|
|
|
|
* copy the existing shape's slot into slot so we can match shape, if all
|
|
|
|
* other members match.
|
2009-12-01 12:49:15 -08:00
|
|
|
*/
|
2010-09-16 11:56:54 -07:00
|
|
|
bool hadSlot = !shape->isAlias() && shape->hasSlot();
|
2010-09-21 00:04:25 -07:00
|
|
|
uint32 oldSlot = shape->slot;
|
|
|
|
if (!(attrs & JSPROP_SHARED) && slot == SHAPE_INVALID_SLOT && hadSlot)
|
|
|
|
slot = oldSlot;
|
2010-09-16 11:56:54 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Now that we've possibly preserved slot, check whether all members match.
|
|
|
|
* If so, this is a redundant "put" and we can return without more work.
|
|
|
|
*/
|
2011-06-21 10:26:22 -07:00
|
|
|
if (shape->matchesParamsAfterId(getter, setter, slot, attrs, flags, shortid))
|
2010-08-29 11:57:08 -07:00
|
|
|
return shape;
|
2009-12-01 12:49:15 -08:00
|
|
|
|
2010-09-16 11:56:54 -07:00
|
|
|
/*
|
|
|
|
* Overwriting a non-last property requires switching to dictionary mode.
|
|
|
|
* The shape tree is shared immutable, and we can't removeProperty and then
|
|
|
|
* addPropertyInternal because a failure under add would lose data.
|
|
|
|
*/
|
|
|
|
if (shape != lastProp && !inDictionaryMode()) {
|
|
|
|
if (!toDictionaryMode(cx))
|
2010-11-09 15:04:12 -08:00
|
|
|
return NULL;
|
2011-05-04 12:01:39 -07:00
|
|
|
spp = nativeSearch(shape->propid);
|
2010-09-16 11:56:54 -07:00
|
|
|
shape = SHAPE_FETCH(spp);
|
|
|
|
}
|
2010-08-29 11:57:08 -07:00
|
|
|
|
2009-12-01 12:49:15 -08:00
|
|
|
/*
|
2010-09-16 11:56:54 -07:00
|
|
|
* Now that we have passed the lastProp->frozen() check at the top of this
|
|
|
|
* method, and the non-last-property conditioning just above, we are ready
|
|
|
|
* to overwrite.
|
2009-12-01 12:49:15 -08:00
|
|
|
*
|
2010-09-16 11:56:54 -07:00
|
|
|
* Optimize the case of a non-frozen dictionary-mode object based on the
|
|
|
|
* property that dictionaries exclusively own their mutable shape structs,
|
|
|
|
* each of which has a unique shape number (not shared via a shape tree).
|
2010-10-26 19:12:40 -07:00
|
|
|
*
|
|
|
|
* This is more than an optimization: it is required to preserve for-in
|
|
|
|
* enumeration order (see bug 601399).
|
2009-12-01 12:49:15 -08:00
|
|
|
*/
|
2010-09-16 11:56:54 -07:00
|
|
|
if (inDictionaryMode()) {
|
|
|
|
/* FIXME bug 593129 -- slot allocation and JSObject *this must move out of here! */
|
|
|
|
if (slot == SHAPE_INVALID_SLOT && !(attrs & JSPROP_SHARED) && !(flags & Shape::ALIAS)) {
|
|
|
|
if (!allocSlot(cx, &slot))
|
2009-12-01 12:49:15 -08:00
|
|
|
return NULL;
|
2010-09-16 11:56:54 -07:00
|
|
|
}
|
2010-09-02 18:46:11 -07:00
|
|
|
|
2010-09-16 11:56:54 -07:00
|
|
|
shape->slot = slot;
|
2010-10-26 19:12:40 -07:00
|
|
|
if (slot != SHAPE_INVALID_SLOT && slot >= shape->slotSpan) {
|
|
|
|
shape->slotSpan = slot + 1;
|
2010-09-16 11:56:54 -07:00
|
|
|
|
2010-10-26 19:12:40 -07:00
|
|
|
for (Shape *temp = lastProp; temp != shape; temp = temp->parent) {
|
|
|
|
if (temp->slotSpan <= slot)
|
|
|
|
temp->slotSpan = slot + 1;
|
2010-09-16 11:56:54 -07:00
|
|
|
}
|
2009-12-01 06:56:16 -08:00
|
|
|
}
|
2009-12-01 12:49:15 -08:00
|
|
|
|
2010-09-16 11:56:54 -07:00
|
|
|
shape->rawGetter = getter;
|
|
|
|
shape->rawSetter = setter;
|
2010-11-23 14:40:29 -08:00
|
|
|
shape->attrs = uint8(attrs);
|
2010-09-16 11:56:54 -07:00
|
|
|
shape->flags = flags | Shape::IN_DICTIONARY;
|
2010-11-23 14:40:29 -08:00
|
|
|
shape->shortid = int16(shortid);
|
2010-09-02 14:50:44 -07:00
|
|
|
|
2010-09-16 11:56:54 -07:00
|
|
|
/*
|
|
|
|
* We are done updating shape and lastProp. Now we may need to update
|
2010-10-26 19:12:40 -07:00
|
|
|
* flags and we will need to update objShape, which is no longer "own".
|
|
|
|
* In the last non-dictionary property case in the else clause just
|
|
|
|
* below, getChildProperty handles this for us. First update flags.
|
2010-09-16 11:56:54 -07:00
|
|
|
*/
|
|
|
|
updateFlags(shape);
|
2010-10-26 19:12:40 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* We have just mutated shape in place, but nothing caches it based on
|
|
|
|
* shape->shape unless shape is lastProp and !hasOwnShape()). Therefore
|
|
|
|
* we regenerate only lastProp->shape. We will clearOwnShape(), which
|
|
|
|
* sets objShape to lastProp->shape.
|
|
|
|
*/
|
2011-05-04 12:01:39 -07:00
|
|
|
lastProp->shapeid = js_GenerateShape(cx);
|
2010-10-26 19:12:40 -07:00
|
|
|
clearOwnShape();
|
2010-09-16 11:56:54 -07:00
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Updating lastProp in a non-dictionary-mode object. Such objects
|
|
|
|
* share their shapes via a tree rooted at a prototype emptyShape, or
|
|
|
|
* perhaps a well-known compartment-wide singleton emptyShape.
|
|
|
|
*
|
|
|
|
* If any shape in the tree has a property hashtable, it is shared and
|
|
|
|
* immutable too, therefore we must not update *spp.
|
|
|
|
*/
|
|
|
|
JS_ASSERT(shape == lastProp);
|
|
|
|
removeLastProperty();
|
2009-12-01 12:49:15 -08:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
/* Find or create a property tree node labeled by our arguments. */
|
2010-08-29 11:57:08 -07:00
|
|
|
Shape child(id, getter, setter, slot, attrs, flags, shortid);
|
2008-02-07 15:18:45 -08:00
|
|
|
|
2010-09-16 11:56:54 -07:00
|
|
|
Shape *newShape = getChildProperty(cx, lastProp, child);
|
|
|
|
if (!newShape) {
|
|
|
|
setLastProperty(shape);
|
|
|
|
CHECK_SHAPE_CONSISTENCY(this);
|
|
|
|
return NULL;
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2010-09-16 11:56:54 -07:00
|
|
|
shape = newShape;
|
|
|
|
}
|
2009-12-01 06:56:16 -08:00
|
|
|
|
2010-09-16 11:56:54 -07:00
|
|
|
/*
|
|
|
|
* Can't fail now, so free the previous incarnation's slot if the new shape
|
|
|
|
* has no slot. But we do not need to free oldSlot (and must not, as trying
|
|
|
|
* to will botch an assertion in JSObject::freeSlot) if the new lastProp
|
|
|
|
* (shape here) has a slotSpan that does not cover it.
|
|
|
|
*/
|
|
|
|
if (hadSlot && !shape->hasSlot()) {
|
|
|
|
if (oldSlot < shape->slotSpan)
|
2010-09-21 00:04:25 -07:00
|
|
|
freeSlot(cx, oldSlot);
|
2010-09-16 11:56:54 -07:00
|
|
|
else
|
2011-07-26 11:41:43 -07:00
|
|
|
getSlotRef(oldSlot).setUndefined();
|
2010-09-16 11:56:54 -07:00
|
|
|
JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals);
|
2009-12-01 06:56:16 -08:00
|
|
|
}
|
2009-12-01 12:49:15 -08:00
|
|
|
|
2010-09-02 18:46:11 -07:00
|
|
|
CHECK_SHAPE_CONSISTENCY(this);
|
2010-11-09 15:04:12 -08:00
|
|
|
|
2011-06-21 10:26:22 -07:00
|
|
|
return js_UpdateWatchpointsForShape(cx, this, shape);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2010-08-29 11:57:08 -07:00
|
|
|
const Shape *
|
|
|
|
JSObject::changeProperty(JSContext *cx, const Shape *shape, uintN attrs, uintN mask,
|
2011-02-09 11:31:40 -08:00
|
|
|
PropertyOp getter, StrictPropertyOp setter)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2010-09-16 11:56:54 -07:00
|
|
|
JS_ASSERT_IF(inDictionaryMode(), !lastProp->frozen());
|
2011-05-04 12:01:39 -07:00
|
|
|
JS_ASSERT(!JSID_IS_VOID(shape->propid));
|
2010-08-29 11:57:08 -07:00
|
|
|
JS_ASSERT(nativeContains(*shape));
|
2009-11-20 16:14:42 -08:00
|
|
|
|
2010-08-29 11:57:08 -07:00
|
|
|
attrs |= shape->attrs & mask;
|
2009-12-01 12:49:15 -08:00
|
|
|
|
2010-09-02 14:50:44 -07:00
|
|
|
/* Allow only shared (slotless) => unshared (slotful) transition. */
|
2010-08-29 11:57:08 -07:00
|
|
|
JS_ASSERT(!((attrs ^ shape->attrs) & JSPROP_SHARED) ||
|
2007-03-22 10:30:00 -07:00
|
|
|
!(attrs & JSPROP_SHARED));
|
2009-12-01 12:49:15 -08:00
|
|
|
|
2010-02-22 15:30:35 -08:00
|
|
|
/* Don't allow method properties to be changed to have a getter. */
|
2010-08-29 11:57:08 -07:00
|
|
|
JS_ASSERT_IF(getter != shape->rawGetter, !shape->isMethod());
|
2009-12-01 12:49:15 -08:00
|
|
|
|
2010-07-14 23:19:36 -07:00
|
|
|
if (getter == PropertyStub)
|
2007-03-22 10:30:00 -07:00
|
|
|
getter = NULL;
|
2011-02-09 11:31:40 -08:00
|
|
|
if (setter == StrictPropertyStub)
|
2007-03-22 10:30:00 -07:00
|
|
|
setter = NULL;
|
2011-01-24 15:04:29 -08:00
|
|
|
|
|
|
|
if (!CheckCanChangeAttrs(cx, this, shape, &attrs))
|
|
|
|
return NULL;
|
|
|
|
|
2010-08-29 11:57:08 -07:00
|
|
|
if (shape->attrs == attrs && shape->getter() == getter && shape->setter() == setter)
|
|
|
|
return shape;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2010-09-16 11:56:54 -07:00
|
|
|
const Shape *newShape;
|
|
|
|
|
2010-12-06 16:27:39 -08:00
|
|
|
/*
|
|
|
|
* Dictionary-mode objects exclusively own their mutable shape structs, so
|
|
|
|
* we simply modify in place.
|
|
|
|
*/
|
2009-12-01 12:49:15 -08:00
|
|
|
if (inDictionaryMode()) {
|
2010-12-06 16:27:39 -08:00
|
|
|
/* FIXME bug 593129 -- slot allocation and JSObject *this must move out of here! */
|
|
|
|
uint32 slot = shape->slot;
|
|
|
|
if (slot == SHAPE_INVALID_SLOT && !(attrs & JSPROP_SHARED) && !(flags & Shape::ALIAS)) {
|
|
|
|
if (!allocSlot(cx, &slot))
|
|
|
|
return NULL;
|
|
|
|
}
|
2010-09-02 18:46:11 -07:00
|
|
|
|
2010-12-06 16:27:39 -08:00
|
|
|
Shape *mutableShape = const_cast<Shape *>(shape);
|
|
|
|
mutableShape->slot = slot;
|
|
|
|
if (slot != SHAPE_INVALID_SLOT && slot >= shape->slotSpan) {
|
|
|
|
mutableShape->slotSpan = slot + 1;
|
2010-08-29 11:57:08 -07:00
|
|
|
|
2010-12-06 16:27:39 -08:00
|
|
|
for (Shape *temp = lastProp; temp != shape; temp = temp->parent) {
|
|
|
|
if (temp->slotSpan <= slot)
|
|
|
|
temp->slotSpan = slot + 1;
|
2009-12-01 12:49:15 -08:00
|
|
|
}
|
2010-12-06 16:27:39 -08:00
|
|
|
}
|
2010-08-29 11:57:08 -07:00
|
|
|
|
2010-12-06 16:27:39 -08:00
|
|
|
mutableShape->rawGetter = getter;
|
|
|
|
mutableShape->rawSetter = setter;
|
|
|
|
mutableShape->attrs = uint8(attrs);
|
2010-11-09 15:04:12 -08:00
|
|
|
|
2010-12-06 16:27:39 -08:00
|
|
|
updateFlags(shape);
|
|
|
|
|
|
|
|
/* See the corresponding code in putProperty. */
|
2011-05-04 12:01:39 -07:00
|
|
|
lastProp->shapeid = js_GenerateShape(cx);
|
2010-12-06 16:27:39 -08:00
|
|
|
clearOwnShape();
|
|
|
|
|
2011-02-08 13:45:12 -08:00
|
|
|
shape = js_UpdateWatchpointsForShape(cx, this, shape);
|
2011-06-21 10:26:22 -07:00
|
|
|
if (!shape)
|
2010-12-06 16:27:39 -08:00
|
|
|
return NULL;
|
2011-02-08 13:45:12 -08:00
|
|
|
JS_ASSERT(shape == mutableShape);
|
2010-12-06 16:27:39 -08:00
|
|
|
newShape = mutableShape;
|
2010-08-29 11:57:08 -07:00
|
|
|
} else if (shape == lastProp) {
|
2011-05-04 12:01:39 -07:00
|
|
|
Shape child(shape->propid, getter, setter, shape->slot, attrs, shape->flags,
|
|
|
|
shape->shortid);
|
2010-12-06 16:27:39 -08:00
|
|
|
|
2010-08-29 11:57:08 -07:00
|
|
|
newShape = getChildProperty(cx, shape->parent, child);
|
|
|
|
#ifdef DEBUG
|
|
|
|
if (newShape) {
|
|
|
|
JS_ASSERT(newShape == lastProp);
|
2011-02-09 15:18:03 -08:00
|
|
|
if (newShape->hasTable()) {
|
2011-05-04 12:01:39 -07:00
|
|
|
Shape **spp = nativeSearch(shape->propid);
|
2010-08-29 11:57:08 -07:00
|
|
|
JS_ASSERT(SHAPE_FETCH(spp) == newShape);
|
2009-12-01 12:49:15 -08:00
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
2010-08-29 11:57:08 -07:00
|
|
|
#endif
|
2007-03-22 10:30:00 -07:00
|
|
|
} else {
|
|
|
|
/*
|
2010-08-29 11:57:08 -07:00
|
|
|
* Let JSObject::putProperty handle this |overwriting| case, including
|
|
|
|
* the conservation of shape->slot (if it's valid). We must not call
|
2010-09-16 11:56:54 -07:00
|
|
|
* removeProperty because it will free an allocated shape->slot, and
|
|
|
|
* putProperty won't re-allocate it.
|
2007-03-22 10:30:00 -07:00
|
|
|
*/
|
2011-05-04 12:01:39 -07:00
|
|
|
Shape child(shape->propid, getter, setter, shape->slot, attrs, shape->flags,
|
|
|
|
shape->shortid);
|
|
|
|
newShape = putProperty(cx, child.propid, child.rawGetter, child.rawSetter, child.slot,
|
2009-12-01 12:49:15 -08:00
|
|
|
child.attrs, child.flags, child.shortid);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2010-09-02 18:46:11 -07:00
|
|
|
CHECK_SHAPE_CONSISTENCY(this);
|
2010-08-29 11:57:08 -07:00
|
|
|
return newShape;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2009-07-09 13:27:21 -07:00
|
|
|
bool
|
2010-08-29 11:57:08 -07:00
|
|
|
JSObject::removeProperty(JSContext *cx, jsid id)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2010-08-29 11:57:08 -07:00
|
|
|
Shape **spp = nativeSearch(id);
|
|
|
|
Shape *shape = SHAPE_FETCH(spp);
|
2011-06-21 10:26:22 -07:00
|
|
|
if (!shape)
|
2009-07-09 13:27:21 -07:00
|
|
|
return true;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2010-09-16 11:56:54 -07:00
|
|
|
/* First, if shape is unshared and not has a slot, free its slot number. */
|
2010-12-06 16:27:39 -08:00
|
|
|
bool addedToFreelist = false;
|
2010-09-16 11:56:54 -07:00
|
|
|
bool hadSlot = !shape->isAlias() && shape->hasSlot();
|
2010-09-02 14:50:44 -07:00
|
|
|
if (hadSlot) {
|
2010-12-06 16:27:39 -08:00
|
|
|
addedToFreelist = freeSlot(cx, shape->slot);
|
2007-03-22 10:30:00 -07:00
|
|
|
JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals);
|
|
|
|
}
|
|
|
|
|
2010-09-16 11:56:54 -07:00
|
|
|
/* If shape is not the last property added, switch to dictionary mode. */
|
|
|
|
if (shape != lastProp && !inDictionaryMode()) {
|
|
|
|
if (!toDictionaryMode(cx))
|
|
|
|
return false;
|
2011-05-04 12:01:39 -07:00
|
|
|
spp = nativeSearch(shape->propid);
|
2010-09-16 11:56:54 -07:00
|
|
|
shape = SHAPE_FETCH(spp);
|
|
|
|
}
|
|
|
|
|
2010-08-29 11:57:08 -07:00
|
|
|
/*
|
2010-09-16 11:56:54 -07:00
|
|
|
* A dictionary-mode object owns mutable, unique shapes on a non-circular
|
|
|
|
* doubly linked list, optionally hashed by lastProp->table. So we can edit
|
|
|
|
* the list and hash in place.
|
2010-08-29 11:57:08 -07:00
|
|
|
*/
|
|
|
|
if (inDictionaryMode()) {
|
2011-02-09 15:18:03 -08:00
|
|
|
PropertyTable *table = lastProp->hasTable() ? lastProp->getTable() : NULL;
|
2010-08-29 11:57:08 -07:00
|
|
|
|
|
|
|
if (SHAPE_HAD_COLLISION(*spp)) {
|
|
|
|
JS_ASSERT(table);
|
|
|
|
*spp = SHAPE_REMOVED;
|
|
|
|
++table->removedCount;
|
|
|
|
--table->entryCount;
|
|
|
|
} else {
|
|
|
|
if (table) {
|
|
|
|
*spp = NULL;
|
|
|
|
--table->entryCount;
|
|
|
|
|
2009-12-02 19:13:31 -08:00
|
|
|
#ifdef DEBUG
|
2010-08-29 11:57:08 -07:00
|
|
|
/*
|
|
|
|
* Check the consistency of the table but limit the number of
|
|
|
|
* checks not to alter significantly the complexity of the
|
|
|
|
* delete in debug builds, see bug 534493.
|
|
|
|
*/
|
|
|
|
const Shape *aprop = lastProp;
|
2010-09-02 18:46:11 -07:00
|
|
|
for (int n = 50; --n >= 0 && aprop->parent; aprop = aprop->parent)
|
2010-08-29 11:57:08 -07:00
|
|
|
JS_ASSERT_IF(aprop != shape, nativeContains(*aprop));
|
2009-12-02 19:13:31 -08:00
|
|
|
#endif
|
2010-08-29 11:57:08 -07:00
|
|
|
}
|
2009-12-02 19:13:31 -08:00
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2009-12-01 12:49:15 -08:00
|
|
|
/*
|
2010-08-29 11:57:08 -07:00
|
|
|
* Remove shape from its non-circular doubly linked list, setting this
|
2010-09-16 11:56:54 -07:00
|
|
|
* object's OWN_SHAPE flag so the updateShape(cx) further below will
|
|
|
|
* generate a fresh shape id for this object, distinct from the id of
|
|
|
|
* any shape in the list. We need a fresh shape for all deletions, even
|
|
|
|
* of lastProp. Otherwise, a shape number could replay and caches might
|
|
|
|
* return get deleted DictionaryShapes! See bug 595365.
|
2009-12-01 12:49:15 -08:00
|
|
|
*/
|
2010-09-16 11:56:54 -07:00
|
|
|
flags |= OWN_SHAPE;
|
2010-09-02 14:50:44 -07:00
|
|
|
|
|
|
|
Shape *oldLastProp = lastProp;
|
2010-08-29 11:57:08 -07:00
|
|
|
shape->removeFromDictionary(this);
|
2010-09-02 14:50:44 -07:00
|
|
|
if (table) {
|
|
|
|
if (shape == oldLastProp) {
|
2011-02-09 15:18:03 -08:00
|
|
|
JS_ASSERT(shape->getTable() == table);
|
2010-09-02 14:50:44 -07:00
|
|
|
JS_ASSERT(shape->parent == lastProp);
|
2010-09-03 11:05:43 -07:00
|
|
|
JS_ASSERT(shape->slotSpan >= lastProp->slotSpan);
|
|
|
|
JS_ASSERT_IF(hadSlot, shape->slot + 1 <= shape->slotSpan);
|
2010-09-02 14:50:44 -07:00
|
|
|
|
|
|
|
/*
|
2011-05-11 08:24:57 -07:00
|
|
|
* Maintain slot freelist consistency. Slot numbers on the
|
|
|
|
* freelist are less than lastProp->slotSpan; so if the
|
|
|
|
* freelist is non-empty, then lastProp->slotSpan may not
|
|
|
|
* decrease.
|
|
|
|
*/
|
2010-12-06 16:27:39 -08:00
|
|
|
if (table->freelist != SHAPE_INVALID_SLOT) {
|
2010-09-03 11:05:43 -07:00
|
|
|
lastProp->slotSpan = shape->slotSpan;
|
2011-05-11 08:24:57 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Add the slot to the freelist if it wasn't added in
|
|
|
|
* freeSlot and it is not a reserved slot.
|
|
|
|
*/
|
|
|
|
if (hadSlot && !addedToFreelist && JSSLOT_FREE(clasp) <= shape->slot) {
|
2011-07-26 11:41:43 -07:00
|
|
|
getSlotRef(shape->slot).setPrivateUint32(table->freelist);
|
2010-12-06 16:27:39 -08:00
|
|
|
table->freelist = shape->slot;
|
|
|
|
}
|
|
|
|
}
|
2010-09-02 14:50:44 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Hand off table from old to new lastProp. */
|
|
|
|
oldLastProp->setTable(NULL);
|
|
|
|
lastProp->setTable(table);
|
|
|
|
}
|
2009-12-01 12:49:15 -08:00
|
|
|
} else {
|
2010-08-29 11:57:08 -07:00
|
|
|
/*
|
|
|
|
* Non-dictionary-mode property tables are shared immutables, so all we
|
|
|
|
* need do is retract lastProp and we'll either get or else lazily make
|
2011-01-03 17:14:55 -08:00
|
|
|
* via a later hashify the exact table for the new property lineage.
|
2010-08-29 11:57:08 -07:00
|
|
|
*/
|
|
|
|
JS_ASSERT(shape == lastProp);
|
2009-12-01 12:49:15 -08:00
|
|
|
removeLastProperty();
|
2010-10-13 11:49:22 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Revert to fixed slots if this was the first dynamically allocated slot,
|
|
|
|
* preserving invariant that objects with the same shape use the fixed
|
|
|
|
* slots in the same way.
|
|
|
|
*/
|
|
|
|
size_t fixed = numFixedSlots();
|
|
|
|
if (shape->slot == fixed) {
|
|
|
|
JS_ASSERT_IF(!lastProp->isEmptyShape() && lastProp->hasSlot(),
|
|
|
|
lastProp->slot == fixed - 1);
|
|
|
|
revertToFixedSlots(cx);
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
2009-12-01 12:49:15 -08:00
|
|
|
updateShape(cx);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2010-09-16 11:56:54 -07:00
|
|
|
/* On the way out, consider shrinking table if its load factor is <= .25. */
|
2011-02-09 15:18:03 -08:00
|
|
|
if (lastProp->hasTable()) {
|
|
|
|
PropertyTable *table = lastProp->getTable();
|
2010-08-29 11:57:08 -07:00
|
|
|
uint32 size = table->capacity();
|
2011-06-21 10:26:22 -07:00
|
|
|
if (size > PropertyTable::MIN_SIZE && table->entryCount <= size >> 2)
|
2010-10-24 19:24:53 -07:00
|
|
|
(void) table->change(-1, cx);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2011-03-09 00:53:56 -08:00
|
|
|
/* Also, consider shrinking object slots if 25% or more are unused. */
|
|
|
|
if (hasSlotsArray()) {
|
|
|
|
JS_ASSERT(slotSpan() <= numSlots());
|
|
|
|
if ((slotSpan() + (slotSpan() >> 2)) < numSlots())
|
|
|
|
shrinkSlots(cx, slotSpan());
|
|
|
|
}
|
|
|
|
|
2010-09-02 18:46:11 -07:00
|
|
|
CHECK_SHAPE_CONSISTENCY(this);
|
2009-07-09 13:27:21 -07:00
|
|
|
return true;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2010-08-29 11:57:08 -07:00
|
|
|
JSObject::clear(JSContext *cx)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2010-08-29 11:57:08 -07:00
|
|
|
Shape *shape = lastProp;
|
|
|
|
JS_ASSERT(inDictionaryMode() == shape->inDictionary());
|
|
|
|
|
|
|
|
while (shape->parent) {
|
|
|
|
shape = shape->parent;
|
|
|
|
JS_ASSERT(inDictionaryMode() == shape->inDictionary());
|
2009-09-02 15:58:25 -07:00
|
|
|
}
|
2010-08-29 11:57:08 -07:00
|
|
|
JS_ASSERT(shape->isEmptyShape());
|
|
|
|
|
|
|
|
if (inDictionaryMode())
|
|
|
|
shape->listp = &lastProp;
|
2009-09-02 15:58:25 -07:00
|
|
|
|
2010-10-13 11:49:22 -07:00
|
|
|
/*
|
|
|
|
* Revert to fixed slots if we have cleared below the first dynamically
|
|
|
|
* allocated slot, preserving invariant that objects with the same shape
|
|
|
|
* use the fixed slots in the same way.
|
|
|
|
*/
|
|
|
|
if (hasSlotsArray() && JSSLOT_FREE(getClass()) <= numFixedSlots())
|
|
|
|
revertToFixedSlots(cx);
|
|
|
|
|
2010-08-29 11:57:08 -07:00
|
|
|
/*
|
|
|
|
* We have rewound to a uniquely-shaped empty scope, so we don't need an
|
|
|
|
* override for this object's shape.
|
|
|
|
*/
|
|
|
|
clearOwnShape();
|
|
|
|
setMap(shape);
|
|
|
|
|
|
|
|
LeaveTraceIfGlobalObject(cx, this);
|
2007-03-22 10:30:00 -07:00
|
|
|
JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals);
|
2010-09-02 18:46:11 -07:00
|
|
|
CHECK_SHAPE_CONSISTENCY(this);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2009-07-09 13:27:21 -07:00
|
|
|
void
|
2010-08-29 11:57:08 -07:00
|
|
|
JSObject::generateOwnShape(JSContext *cx)
|
|
|
|
{
|
|
|
|
#ifdef JS_TRACER
|
2011-02-01 10:18:06 -08:00
|
|
|
JS_ASSERT_IF(!parent && JS_ON_TRACE(cx), JS_TRACE_MONITOR_ON_TRACE(cx)->bailExit);
|
2011-01-12 16:56:23 -08:00
|
|
|
LeaveTraceIfGlobalObject(cx, this);
|
2010-08-29 11:57:08 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If we are recording, here is where we forget already-guarded shapes.
|
|
|
|
* Any subsequent property operation upon object on the trace currently
|
|
|
|
* being recorded will re-guard (and re-memoize).
|
|
|
|
*/
|
2011-02-01 10:18:06 -08:00
|
|
|
if (TraceRecorder *tr = TRACE_RECORDER(cx))
|
2010-08-29 11:57:08 -07:00
|
|
|
tr->forgetGuardedShapesForObject(this);
|
|
|
|
#endif
|
|
|
|
|
2011-02-04 10:59:07 -08:00
|
|
|
setOwnShape(js_GenerateShape(cx));
|
2010-08-29 11:57:08 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
JSObject::deletingShapeChange(JSContext *cx, const Shape &shape)
|
2009-07-09 13:27:21 -07:00
|
|
|
{
|
2011-05-04 12:01:39 -07:00
|
|
|
JS_ASSERT(!JSID_IS_VOID(shape.propid));
|
2009-07-24 06:55:28 -07:00
|
|
|
generateOwnShape(cx);
|
2009-07-09 13:27:21 -07:00
|
|
|
}
|
|
|
|
|
2011-01-17 09:30:08 -08:00
|
|
|
const Shape *
|
2010-08-29 11:57:08 -07:00
|
|
|
JSObject::methodShapeChange(JSContext *cx, const Shape &shape)
|
2009-07-09 13:27:21 -07:00
|
|
|
{
|
2011-01-17 09:30:08 -08:00
|
|
|
const Shape *result = &shape;
|
|
|
|
|
2011-05-04 12:01:39 -07:00
|
|
|
JS_ASSERT(!JSID_IS_VOID(shape.propid));
|
2010-08-29 11:57:08 -07:00
|
|
|
if (shape.isMethod()) {
|
2009-09-03 14:41:19 -07:00
|
|
|
#ifdef DEBUG
|
2010-10-22 17:04:22 -07:00
|
|
|
const Value &prev = nativeGetSlot(shape.slot);
|
2011-04-08 10:52:48 -07:00
|
|
|
JS_ASSERT(shape.methodObject() == prev.toObject());
|
2010-08-29 11:57:08 -07:00
|
|
|
JS_ASSERT(canHaveMethodBarrier());
|
2009-09-03 14:41:19 -07:00
|
|
|
JS_ASSERT(hasMethodBarrier());
|
2010-08-29 11:57:08 -07:00
|
|
|
JS_ASSERT(!shape.rawSetter || shape.rawSetter == js_watch_set);
|
2009-09-03 14:41:19 -07:00
|
|
|
#endif
|
|
|
|
|
|
|
|
/*
|
2010-08-29 11:57:08 -07:00
|
|
|
* Pass null to make a stub getter, but pass along shape.rawSetter to
|
|
|
|
* preserve watchpoints. Clear Shape::METHOD from flags as we are
|
|
|
|
* despecializing from a method memoized in the property tree to a
|
2009-09-03 14:41:19 -07:00
|
|
|
* plain old function-valued property.
|
|
|
|
*/
|
2011-05-04 12:01:39 -07:00
|
|
|
result = putProperty(cx, shape.propid, NULL, shape.rawSetter, shape.slot,
|
2011-01-17 09:30:08 -08:00
|
|
|
shape.attrs,
|
|
|
|
shape.getFlags() & ~Shape::METHOD,
|
|
|
|
shape.shortid);
|
|
|
|
if (!result)
|
|
|
|
return NULL;
|
2009-09-03 14:41:19 -07:00
|
|
|
}
|
|
|
|
|
2010-11-22 17:58:53 -08:00
|
|
|
if (branded()) {
|
|
|
|
uintN thrashCount = getMethodThrashCount();
|
|
|
|
if (thrashCount < JSObject::METHOD_THRASH_COUNT_MAX) {
|
|
|
|
++thrashCount;
|
|
|
|
setMethodThrashCount(thrashCount);
|
|
|
|
if (thrashCount == JSObject::METHOD_THRASH_COUNT_MAX) {
|
|
|
|
unbrand(cx);
|
2011-01-17 09:30:08 -08:00
|
|
|
return result;
|
2010-11-22 17:58:53 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-07-24 06:55:28 -07:00
|
|
|
generateOwnShape(cx);
|
2011-01-17 09:30:08 -08:00
|
|
|
return result;
|
2009-09-03 14:41:19 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
2010-08-29 11:57:08 -07:00
|
|
|
JSObject::methodShapeChange(JSContext *cx, uint32 slot)
|
2009-09-03 14:41:19 -07:00
|
|
|
{
|
|
|
|
if (!hasMethodBarrier()) {
|
|
|
|
generateOwnShape(cx);
|
|
|
|
} else {
|
2010-08-29 11:57:08 -07:00
|
|
|
for (Shape::Range r = lastProp->all(); !r.empty(); r.popFront()) {
|
|
|
|
const Shape &shape = r.front();
|
2011-05-04 12:01:39 -07:00
|
|
|
JS_ASSERT(!JSID_IS_VOID(shape.propid));
|
2010-08-29 11:57:08 -07:00
|
|
|
if (shape.slot == slot)
|
2011-01-17 09:30:08 -08:00
|
|
|
return methodShapeChange(cx, shape) != NULL;
|
2009-09-03 14:41:19 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
2009-07-09 13:27:21 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2010-08-29 11:57:08 -07:00
|
|
|
JSObject::protoShapeChange(JSContext *cx)
|
2009-07-09 13:27:21 -07:00
|
|
|
{
|
2009-07-24 06:55:28 -07:00
|
|
|
generateOwnShape(cx);
|
2009-07-09 13:27:21 -07:00
|
|
|
}
|
|
|
|
|
2009-07-27 18:40:12 -07:00
|
|
|
void
|
2010-08-29 11:57:08 -07:00
|
|
|
JSObject::shadowingShapeChange(JSContext *cx, const Shape &shape)
|
2009-07-09 13:27:21 -07:00
|
|
|
{
|
2011-05-04 12:01:39 -07:00
|
|
|
JS_ASSERT(!JSID_IS_VOID(shape.propid));
|
2009-07-24 06:55:28 -07:00
|
|
|
generateOwnShape(cx);
|
2009-07-09 13:27:21 -07:00
|
|
|
}
|
|
|
|
|
2010-04-12 06:27:16 -07:00
|
|
|
bool
|
2010-08-29 11:57:08 -07:00
|
|
|
JSObject::globalObjectOwnShapeChange(JSContext *cx)
|
2010-04-12 06:27:16 -07:00
|
|
|
{
|
|
|
|
generateOwnShape(cx);
|
|
|
|
return !js_IsPropertyCacheDisabled(cx);
|
|
|
|
}
|