Eliminate JSClass::reserveSlots and streamline new-object paths (535416, r=gal).

This commit is contained in:
Brendan Eich 2010-06-18 17:43:02 -07:00
parent 1ddbd48078
commit b73234dea0
24 changed files with 462 additions and 357 deletions

View File

@ -1415,7 +1415,7 @@ struct JSClass {
JSXDRObjectOp xdrObject;
JSHasInstanceOp hasInstance;
JSMarkOp mark;
JSReserveSlotsOp reserveSlots;
void (*reserved0)(void);
};
struct JSExtendedClass {

View File

@ -3068,6 +3068,12 @@ static JSFunctionSpec array_static_methods[] = {
JS_FS_END
};
static inline JSObject *
NewDenseArrayObject(JSContext *cx)
{
return NewObject(cx, &js_ArrayClass, NULL, NULL);
}
JSBool
js_Array(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
@ -3076,7 +3082,7 @@ js_Array(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
/* If called without new, replace obj with a new Array object. */
if (!JS_IsConstructing(cx)) {
obj = NewObject(cx, &js_ArrayClass, NULL, NULL);
obj = NewDenseArrayObject(cx);
if (!obj)
return JS_FALSE;
*rval = OBJECT_TO_JSVAL(obj);
@ -3168,7 +3174,7 @@ js_InitArrayClass(JSContext *cx, JSObject *obj)
JSObject *
js_NewArrayObject(JSContext *cx, jsuint length, const jsval *vector, bool holey)
{
JSObject *obj = NewObject(cx, &js_ArrayClass, NULL, NULL);
JSObject *obj = NewDenseArrayObject(cx);
if (!obj)
return NULL;

View File

@ -216,7 +216,7 @@ AddPropertyHelper(JSContext* cx, JSObject* obj, JSScopeProperty* sprop, bool isD
}
if (!scope->table) {
if (slot < obj->numSlots() && !obj->getClass()->reserveSlots) {
if (slot < obj->numSlots()) {
JS_ASSERT(JSVAL_IS_VOID(obj->getSlot(scope->freeslot)));
++scope->freeslot;
} else {

View File

@ -1413,9 +1413,16 @@ struct JSRuntime {
/* Literal table maintained by jsatom.c functions. */
JSAtomState atomState;
/*
* Runtime-shared empty scopes for well-known built-in objects that lack
* class prototypes (the usual locus of an emptyScope). Mnemonic: ABCDEW
*/
JSEmptyScope *emptyArgumentsScope;
JSEmptyScope *emptyBlockScope;
JSEmptyScope *emptyCallScope;
JSEmptyScope *emptyDeclEnvScope;
JSEmptyScope *emptyEnumeratorScope;
JSEmptyScope *emptyWithScope;
/*
* Various metering fields are defined at the end of JSRuntime. In this

View File

@ -2343,7 +2343,7 @@ js_InitDateClass(JSContext *cx, JSObject *obj)
JS_FRIEND_API(JSObject *)
js_NewDateObjectMsec(JSContext *cx, jsdouble msec_time)
{
JSObject *obj = NewObject(cx, &js_DateClass, NULL, NULL);
JSObject *obj = NewBuiltinClassInstance(cx, &js_DateClass);
if (!obj || !SetUTCTime(cx, obj, msec_time))
return NULL;
return obj;

View File

@ -65,11 +65,13 @@
#include "jsregexp.h"
#include "jsscan.h"
#include "jsscope.h"
#include "jsscopeinlines.h"
#include "jsscript.h"
#include "jsautooplen.h" // generated headers last
#include "jsstaticcheck.h"
#include "jsobjinlines.h"
#include "jsscopeinlines.h"
/* Allocation chunk counts, must be powers of two in general. */
#define BYTECODE_CHUNK 256 /* code allocation increment */
#define SRCNOTE_CHUNK 64 /* initial srcnote allocation increment */
@ -1845,9 +1847,8 @@ EmitEnterBlock(JSContext *cx, JSParseNode *pn, JSCodeGenerator *cg)
if (depth < 0)
return false;
for (uintN slot = JSSLOT_FREE(&js_BlockClass),
limit = slot + OBJ_BLOCK_COUNT(cx, blockObj);
slot < limit; slot++) {
uintN base = JSSLOT_FREE(&js_BlockClass);
for (uintN slot = base, limit = base + OBJ_BLOCK_COUNT(cx, blockObj); slot < limit; slot++) {
jsval v = blockObj->getSlot(slot);
/* Beware the empty destructuring dummy. */
@ -1869,8 +1870,8 @@ EmitEnterBlock(JSContext *cx, JSParseNode *pn, JSCodeGenerator *cg)
#endif
}
blockObj->scope()->freeslot = JSSLOT_FREE(&js_BlockClass);
return blockObj->growSlots(cx, JSSLOT_FREE(&js_BlockClass));
blockObj->scope()->freeslot = base;
return blockObj->growSlots(cx, base);
}
/*

View File

@ -695,8 +695,8 @@ Exception(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
* ECMA ed. 3, 15.11.1 requires Error, etc., to construct even when
* called as functions, without operator new. But as we do not give
* each constructor a distinct JSClass, whose .name member is used by
* NewObject to find the class prototype, we must get the class
* prototype ourselves.
* NewNativeClassInstance to find the class prototype, we must get the
* class prototype ourselves.
*/
if (!JSVAL_TO_OBJECT(argv[-2])->getProperty(cx,
ATOM_TO_JSID(cx->runtime->atomState
@ -704,7 +704,8 @@ Exception(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
rval)) {
return JS_FALSE;
}
obj = NewObject(cx, &js_ErrorClass, JSVAL_TO_OBJECT(*rval), NULL);
JSObject *errProto = JSVAL_TO_OBJECT(*rval);
obj = NewNativeClassInstance(cx, &js_ErrorClass, errProto, errProto->getParent());
if (!obj)
return JS_FALSE;
*rval = OBJECT_TO_JSVAL(obj);
@ -1159,7 +1160,7 @@ js_ErrorToException(JSContext *cx, const char *message, JSErrorReport *reportp,
goto out;
tv[0] = OBJECT_TO_JSVAL(errProto);
errObject = NewObject(cx, &js_ErrorClass, errProto, NULL);
errObject = NewNativeClassInstance(cx, &js_ErrorClass, errProto, errProto->getParent());
if (!errObject) {
ok = JS_FALSE;
goto out;

View File

@ -223,22 +223,8 @@ js_GetArgsObject(JSContext *cx, JSStackFrame *fp)
if (argsobj)
return argsobj;
/*
* Give arguments an intrinsic scope chain link to fp's global object.
* Since the arguments object lacks a prototype because js_ArgumentsClass
* is not initialized, NewObject won't assign a default parent to it.
*
* Therefore if arguments is used as the head of an eval scope chain (via
* a direct or indirect call to eval(program, arguments)), any reference
* to a standard class object in the program will fail to resolve due to
* js_GetClassPrototype not being able to find a global object containing
* the standard prototype by starting from arguments and following parent.
*/
JSObject *global = fp->scopeChain;
while (JSObject *parent = global->getParent())
global = parent;
JS_ASSERT(fp->argv);
/* Compute the arguments object's parent slot from fp's scope chain. */
JSObject *global = fp->scopeChain->getGlobal();
argsobj = NewArguments(cx, global, fp->argc, JSVAL_TO_OBJECT(fp->argv[-2]));
if (!argsobj)
return argsobj;
@ -328,6 +314,11 @@ WrapEscapingClosure(JSContext *cx, JSStackFrame *fp, JSObject *funobj, JSFunctio
if (!scopeChain)
return NULL;
/*
* We must wrap funobj with a JSFunction, so use NewObjectWithGivenProto.
* This helper has a special case for js_FunctionClass, triggered here and
* also possibly via the JS_NewObjectForGivenProto and JS_NewObject APIs.
*/
JSObject *wfunobj = NewObjectWithGivenProto(cx, &js_FunctionClass,
funobj, scopeChain);
if (!wfunobj)
@ -674,12 +665,6 @@ args_or_call_trace(JSTracer *trc, JSObject *obj)
# define args_or_call_trace NULL
#endif
static uint32
args_reserveSlots(JSContext *cx, JSObject *obj)
{
return obj->getArgsLength();
}
/*
* The Arguments class is not initialized via JS_InitClass, and must not be,
* because its name is "Object". Per ECMA, that causes instances of it to
@ -703,7 +688,7 @@ JSClass js_ArgumentsClass = {
NULL, NULL,
NULL, NULL,
NULL, NULL,
JS_CLASS_TRACE(args_or_call_trace), args_reserveSlots
JS_CLASS_TRACE(args_or_call_trace), NULL
};
const uint32 JSSLOT_CALLEE = JSSLOT_PRIVATE + 1;
@ -775,7 +760,6 @@ NewCallObject(JSContext *cx, JSFunction *fun, JSObject *scopeChain)
/* Init immediately to avoid GC seeing a half-init'ed object. */
callobj->init(&js_CallClass, NULL, scopeChain, JSVAL_NULL);
callobj->map = cx->runtime->emptyCallScope->hold();
/* This must come after callobj->map has been set. */
@ -784,6 +768,19 @@ NewCallObject(JSContext *cx, JSFunction *fun, JSObject *scopeChain)
return callobj;
}
static inline JSObject *
NewDeclEnvObject(JSContext *cx, JSStackFrame *fp)
{
JSObject *envobj = js_NewGCObject(cx);
if (!envobj)
return NULL;
/* Init immediately to avoid GC seeing a half-init'ed object. */
envobj->init(&js_DeclEnvClass, NULL, fp->scopeChain, reinterpret_cast<jsval>(fp));
envobj->map = cx->runtime->emptyDeclEnvScope->hold();
return envobj;
}
JSObject *
js_GetCallObject(JSContext *cx, JSStackFrame *fp)
{
@ -812,14 +809,12 @@ js_GetCallObject(JSContext *cx, JSStackFrame *fp)
*/
JSAtom *lambdaName = (fp->fun->flags & JSFUN_LAMBDA) ? fp->fun->atom : NULL;
if (lambdaName) {
JSObject *env = NewObjectWithGivenProto(cx, &js_DeclEnvClass, NULL,
fp->scopeChain);
if (!env)
JSObject *envobj = NewDeclEnvObject(cx, fp);
if (!envobj)
return NULL;
env->setPrivate(fp);
/* Root env before js_DefineNativeProperty (-> JSClass.addProperty). */
fp->scopeChain = env;
/* Root envobj before js_DefineNativeProperty (-> JSClass.addProperty). */
fp->scopeChain = envobj;
JS_ASSERT(fp->argv);
if (!js_DefineNativeProperty(cx, fp->scopeChain, ATOM_TO_JSID(lambdaName),
fp->calleeValue(),
@ -1264,15 +1259,6 @@ call_resolve(JSContext *cx, JSObject *obj, jsval idval, uintN flags,
return JS_TRUE;
}
static uint32
call_reserveSlots(JSContext *cx, JSObject *obj)
{
JSFunction *fun;
fun = js_GetCallObjectFunction(obj);
return fun->countArgsAndVars();
}
JS_FRIEND_DATA(JSClass) js_CallClass = {
"Call",
JSCLASS_HAS_PRIVATE |
@ -1285,7 +1271,7 @@ JS_FRIEND_DATA(JSClass) js_CallClass = {
NULL, NULL,
NULL, NULL,
NULL, NULL,
JS_CLASS_TRACE(args_or_call_trace), call_reserveSlots
JS_CLASS_TRACE(args_or_call_trace), NULL
};
/* Generic function tinyids. */
@ -1463,10 +1449,14 @@ fun_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
return JS_TRUE;
/*
* Make the prototype object to have the same parent as the function
* object itself.
* Make the prototype object an instance of Object with the same parent
* as the function object itself.
*/
JSObject *proto = NewObject(cx, &js_ObjectClass, NULL, obj->getParent());
JSObject *parent = obj->getParent();
JSObject *proto;
if (!js_GetClassPrototype(cx, parent, JSProto_Object, &proto))
return JS_FALSE;
proto = NewNativeClassInstance(cx, &js_ObjectClass, proto, parent);
if (!proto)
return JS_FALSE;
@ -1809,20 +1799,6 @@ JSFunction::countInterpretedReservedSlots() const
return (u.i.nupvars == 0) ? 0 : u.i.script->upvars()->length;
}
static uint32
fun_reserveSlots(JSContext *cx, JSObject *obj)
{
/*
* We use getPrivate and not GET_FUNCTION_PRIVATE because during
* js_InitFunctionClass invocation the function is called before the
* private slot of the function object is set.
*/
JSFunction *fun = (JSFunction *) obj->getPrivate();
return (fun && FUN_INTERPRETED(fun))
? fun->countInterpretedReservedSlots()
: 0;
}
/*
* Reserve two slots in all function objects for XPConnect. Note that this
* does not bloat every instance, only those on which reserved slots are set,
@ -1839,7 +1815,7 @@ JS_FRIEND_DATA(JSClass) js_FunctionClass = {
NULL, NULL,
NULL, NULL,
js_XDRFunctionObject, fun_hasInstance,
JS_CLASS_TRACE(fun_trace), fun_reserveSlots
JS_CLASS_TRACE(fun_trace), NULL
};
static JSBool
@ -2441,8 +2417,7 @@ js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent,
* The cloned function object does not need the extra JSFunction members
* beyond JSObject as it points to fun via the private slot.
*/
JSObject *clone = NewObjectWithGivenProto(cx, &js_FunctionClass, proto,
parent, sizeof(JSObject));
JSObject *clone = NewNativeClassInstance(cx, &js_FunctionClass, proto, parent);
if (!clone)
return NULL;
clone->setPrivate(fun);
@ -2472,7 +2447,7 @@ js_AllocFlatClosure(JSContext *cx, JSFunction *fun, JSObject *scopeChain)
return closure;
uint32 nslots = fun->countInterpretedReservedSlots();
if (!nslots)
if (nslots == 0)
return closure;
if (!js_EnsureReservedSlots(cx, closure, nslots))
return NULL;

View File

@ -355,10 +355,18 @@ js_OnUnknownMethod(JSContext *cx, jsval *vp)
vp[0] = ID_TO_VALUE(id);
}
#endif
obj = NewObjectWithGivenProto(cx, &js_NoSuchMethodClass, NULL, NULL);
obj = js_NewGCObject(cx);
if (!obj)
return false;
obj->fslots[JSSLOT_FOUND_FUNCTION] = tvr.value();
/*
* Null map to cause prompt and safe crash if this object were to
* escape due to a bug. This will make the object appear to be a
* stillborn instance that needs no finalization, which is sound:
* NoSuchMethod helper objects own no manually allocated resources.
*/
obj->map = NULL;
obj->init(&js_NoSuchMethodClass, NULL, NULL, tvr.value());
obj->fslots[JSSLOT_SAVED_ID] = vp[0];
vp[0] = OBJECT_TO_JSVAL(obj);
}

View File

@ -388,12 +388,27 @@ Compare(T *a, T *b, size_t c)
return true;
}
static JSObject *
static inline JSObject *
NewIteratorObject(JSContext *cx, uintN flags)
{
return !(flags & JSITER_ENUMERATE)
? NewObject(cx, &js_IteratorClass.base, NULL, NULL)
: NewObjectWithGivenProto(cx, &js_IteratorClass.base, NULL, NULL);
if (flags & JSITER_ENUMERATE) {
/*
* Non-escaping native enumerator objects do not need map, proto, or
* parent. However, code in jstracer.cpp and elsewhere may find such a
* native enumerator object via the stack and (as for all objects that
* are not stillborn, with the exception of "NoSuchMethod" internal
* helper objects) expect it to have a non-null map pointer, so we
* share an empty Enumerator scope in the runtime.
*/
JSObject *obj = js_NewGCObject(cx);
if (!obj)
return false;
obj->map = cx->runtime->emptyEnumeratorScope->hold();
obj->init(&js_IteratorClass.base, NULL, NULL, JSVAL_NULL);
return obj;
}
return NewBuiltinClassInstance(cx, &js_IteratorClass.base);
}
static inline void
@ -912,7 +927,7 @@ JSExtendedClass js_GeneratorClass = {
JS_REQUIRES_STACK JSObject *
js_NewGenerator(JSContext *cx)
{
JSObject *obj = NewObject(cx, &js_GeneratorClass.base, NULL, NULL);
JSObject *obj = NewBuiltinClassInstance(cx, &js_GeneratorClass.base);
if (!obj)
return NULL;

View File

@ -316,7 +316,6 @@ js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap,
map->table = table;
JS_KEEP_ATOMS(cx->runtime);
}
/* From this point the control must flow either through out: or bad:. */
ida = NULL;
@ -1738,10 +1737,11 @@ obj_getPrototypeOf(JSContext *cx, uintN argc, jsval *vp)
}
extern JSBool
js_NewPropertyDescriptorObject(JSContext *cx, jsid id, uintN attrs, jsval getter, jsval setter, jsval value, jsval *vp)
js_NewPropertyDescriptorObject(JSContext *cx, jsid id, uintN attrs, jsval getter, jsval setter,
jsval value, jsval *vp)
{
/* We have our own property, so start creating the descriptor. */
JSObject *desc = NewObject(cx, &js_ObjectClass, NULL, NULL);
JSObject *desc = NewBuiltinClassInstance(cx, &js_ObjectClass);
if (!desc)
return false;
*vp = OBJECT_TO_JSVAL(desc); /* Root and return. */
@ -2501,7 +2501,7 @@ obj_create(JSContext *cx, uintN argc, jsval *vp)
}
jsval v = vp[2];
if (!JSVAL_IS_OBJECT(vp[2])) {
if (!JSVAL_IS_OBJECT(v)) {
char *bytes = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NULL);
if (!bytes)
return JS_FALSE;
@ -2512,11 +2512,11 @@ obj_create(JSContext *cx, uintN argc, jsval *vp)
}
/*
* It's plausible that it's safe to just use the context's global object,
* but since we're not completely sure, better safe than sorry.
* Use the callee's global as the parent of the new object to avoid dynamic
* scoping (i.e., using the caller's global).
*/
JSObject *obj =
NewObjectWithGivenProto(cx, &js_ObjectClass, JSVAL_TO_OBJECT(v), JS_GetScopeChain(cx));
JSObject *obj = NewObjectWithGivenProto(cx, &js_ObjectClass, JSVAL_TO_OBJECT(v),
JSVAL_TO_OBJECT(*vp)->getGlobal());
if (!obj)
return JS_FALSE;
*vp = OBJECT_TO_JSVAL(obj); /* Root and prepare for eventual return. */
@ -2612,7 +2612,7 @@ js_Object(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
JS_ASSERT(!argc || JSVAL_IS_NULL(argv[0]) || JSVAL_IS_VOID(argv[0]));
if (JS_IsConstructing(cx))
return JS_TRUE;
obj = NewObject(cx, &js_ObjectClass, NULL, NULL);
obj = NewBuiltinClassInstance(cx, &js_ObjectClass);
if (!obj)
return JS_FALSE;
}
@ -2673,23 +2673,10 @@ js_NonEmptyObject(JSContext* cx, JSObject* proto)
JS_DEFINE_CALLINFO_2(extern, CONSTRUCTOR_RETRY, js_NonEmptyObject, CONTEXT, CALLEE_PROTOTYPE, 0,
nanojit::ACC_STORE_ANY)
static inline JSObject*
NewNativeObject(JSContext* cx, JSClass* clasp, JSObject* proto,
JSObject *parent, jsval privateSlotValue)
{
JS_ASSERT(JS_ON_TRACE(cx));
JSObject* obj = js_NewGCObject(cx);
if (!obj)
return NULL;
obj->init(clasp, proto, parent, privateSlotValue);
return InitScopeForObject(cx, obj, clasp, proto, &js_ObjectOps) ? obj : NULL;
}
JSObject* FASTCALL
js_NewInstance(JSContext *cx, JSClass *clasp, JSObject *ctor)
{
JS_ASSERT(JS_ON_TRACE(cx));
JS_ASSERT(ctor->isFunction());
JSAtom *atom = cx->runtime->atomState.classPrototypeAtom;
@ -2708,27 +2695,37 @@ js_NewInstance(JSContext *cx, JSClass *clasp, JSObject *ctor)
JSScopeProperty *sprop = scope->lookup(ATOM_TO_JSID(atom));
jsval pval = sprop ? ctor->getSlot(sprop->slot) : JSVAL_HOLE;
JSObject *parent = ctor->getParent();
JSObject *proto;
if (!JSVAL_IS_PRIMITIVE(pval)) {
/* An object in ctor.prototype, let's use it as the new instance's proto. */
proto = JSVAL_TO_OBJECT(pval);
} else if (pval == JSVAL_HOLE) {
/* No ctor.prototype yet, inline and optimize fun_resolve's prototype code. */
proto = NewObject(cx, clasp, NULL, ctor->getParent());
if (!proto)
return NULL;
if (!js_SetClassPrototype(cx, ctor, proto, JSPROP_ENUMERATE | JSPROP_PERMANENT))
return NULL;
} else {
/* Primitive value in .prototype means we use Object.prototype for proto. */
if (!js_GetClassPrototype(cx, JSVAL_TO_OBJECT(ctor->fslots[JSSLOT_PARENT]),
JSProto_Object, &proto)) {
/* A hole or a primitive: either way, we need to get Object.prototype. */
if (!js_GetClassPrototype(cx, parent, JSProto_Object, &proto))
return NULL;
if (pval == JSVAL_HOLE) {
/*
* No ctor.prototype was set, so we inline-expand and optimize
* fun_resolve's prototype creation code.
*/
proto = NewNativeClassInstance(cx, clasp, proto, parent);
if (!proto)
return NULL;
if (!js_SetClassPrototype(cx, ctor, proto, JSPROP_ENUMERATE | JSPROP_PERMANENT))
return NULL;
} else {
/*
* A primitive value in .prototype means to use Object.prototype
* for proto. See ES5 13.2.2 step 7.
*/
}
}
return NewNativeObject(cx, clasp, proto, ctor->getParent(),
JSObject::defaultPrivate(clasp));
if (proto->isNative())
return NewNativeClassInstance(cx, clasp, proto, parent);
return NewObjectWithGivenProto(cx, clasp, proto, parent);
}
JS_DEFINE_CALLINFO_3(extern, CONSTRUCTOR_RETRY, js_NewInstance, CONTEXT, CLASS, OBJECT, 0,
@ -2954,11 +2951,13 @@ js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth)
{
JSObject *obj;
obj = NewObject(cx, &js_WithClass, proto, parent);
obj = js_NewGCObject(cx);
if (!obj)
return NULL;
obj->setPrivate(js_FloatingFrameIfGenerator(cx, cx->fp));
obj->init(&js_WithClass, proto, parent,
reinterpret_cast<jsval>(js_FloatingFrameIfGenerator(cx, cx->fp)));
OBJ_SET_BLOCK_DEPTH(cx, obj, depth);
obj->map = cx->runtime->emptyWithScope->hold();
AutoObjectRooter tvr(cx, obj);
JSObject *thisp = proto->thisObject(cx);
@ -2976,8 +2975,11 @@ js_NewBlockObject(JSContext *cx)
* Null obj's proto slot so that Object.prototype.* does not pollute block
* scopes and to give the block object its own scope.
*/
JSObject *blockObj = NewObjectWithGivenProto(cx, &js_BlockClass, NULL, NULL);
JS_ASSERT_IF(blockObj, !OBJ_IS_CLONED_BLOCK(blockObj));
JSObject *blockObj = js_NewGCObject(cx);
if (!blockObj)
return NULL;
blockObj->init(&js_BlockClass, NULL, NULL, JSVAL_NULL);
blockObj->map = cx->runtime->emptyBlockScope->hold();
return blockObj;
}
@ -2999,6 +3001,9 @@ js_CloneBlockObject(JSContext *cx, JSObject *proto, JSStackFrame *fp)
JS_ASSERT(cx->runtime->emptyBlockScope->freeslot == JSSLOT_BLOCK_DEPTH + 1);
clone->map = cx->runtime->emptyBlockScope->hold();
JS_ASSERT(OBJ_IS_CLONED_BLOCK(clone));
if (!js_EnsureReservedSlots(cx, clone, OBJ_BLOCK_COUNT(cx, proto)))
return NULL;
return clone;
}
@ -3022,12 +3027,12 @@ js_PutBlockObject(JSContext *cx, JSBool normalUnwind)
*/
JS_ASSERT(obj->scope()->object != obj);
/* Block objects should not have reserved slots before they are put. */
JS_ASSERT(obj->numSlots() == JS_INITIAL_NSLOTS);
/* Block objects should have all reserved slots allocated early. */
uintN count = OBJ_BLOCK_COUNT(cx, obj);
JS_ASSERT(obj->numSlots() == JSSLOT_BLOCK_DEPTH + 1 + count);
/* The block and its locals must be on the current stack for GC safety. */
uintN depth = OBJ_BLOCK_DEPTH(cx, obj);
uintN count = OBJ_BLOCK_COUNT(cx, obj);
JS_ASSERT(depth <= (size_t) (cx->regs->sp - StackBase(fp)));
JS_ASSERT(count <= (size_t) (cx->regs->sp - StackBase(fp) - depth));
@ -3038,12 +3043,7 @@ js_PutBlockObject(JSContext *cx, JSBool normalUnwind)
obj->fslots[JSSLOT_BLOCK_DEPTH + 1] = fp->slots()[depth];
if (normalUnwind && count > 1) {
--count;
JS_LOCK_OBJ(cx, obj);
if (!obj->allocSlots(cx, JS_INITIAL_NSLOTS + count))
normalUnwind = JS_FALSE;
else
memcpy(obj->dslots, fp->slots() + depth + 1, count * sizeof(jsval));
JS_UNLOCK_OBJ(cx, obj);
memcpy(obj->dslots, fp->slots() + depth + 1, count * sizeof(jsval));
}
/* We must clear the private slot even with errors. */
@ -3288,18 +3288,12 @@ js_XDRBlockObject(JSXDRState *xdr, JSObject **objp)
#endif
static uint32
block_reserveSlots(JSContext *cx, JSObject *obj)
{
return OBJ_IS_CLONED_BLOCK(obj) ? OBJ_BLOCK_COUNT(cx, obj) : 0;
}
JSClass js_BlockClass = {
"Block",
JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_IS_ANONYMOUS,
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, NULL,
NULL, NULL, NULL, NULL, NULL, NULL, NULL, block_reserveSlots
JSCLASS_NO_OPTIONAL_MEMBERS
};
JSObject *
@ -3407,7 +3401,27 @@ js_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto,
return NULL;
}
/* Create a prototype object for this class. */
/*
* Create a prototype object for this class.
*
* FIXME: lazy standard (built-in) class initialization and even older
* eager boostrapping code rely on all of these properties:
*
* 1. NewObject attempting to compute a default prototype object when
* passed null for proto; and
*
* 2. NewObject tolerating no default prototype (null proto slot value)
* due to this js_InitClass call coming from js_InitFunctionClass on an
* otherwise-uninitialized global.
*
* 3. NewObject allocating a JSFunction-sized GC-thing when clasp is
* &js_FunctionClass, not a JSObject-sized (smaller) GC-thing.
*
* The JS_NewObjectForGivenProto and JS_NewObject APIs also allow clasp to
* be &js_FunctionClass (we could break compatibility easily). But fixing
* (3) is not enough without addressing the bootstrapping dependency on (1)
* and (2).
*/
proto = NewObject(cx, clasp, parent_proto, obj);
if (!proto)
return NULL;
@ -3911,12 +3925,6 @@ js_AllocSlot(JSContext *cx, JSObject *obj, uint32 *slotp)
JSScope *scope = obj->scope();
JS_ASSERT(scope->object == obj);
JSClass *clasp = obj->getClass();
if (scope->freeslot == JSSLOT_FREE(clasp) && clasp->reserveSlots) {
/* Adjust scope->freeslot to include computed reserved slots, if any. */
scope->freeslot += clasp->reserveSlots(cx, obj);
}
if (scope->freeslot >= obj->numSlots() &&
!obj->growSlots(cx, scope->freeslot + 1)) {
return JS_FALSE;
@ -5690,6 +5698,28 @@ js_IsDelegate(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
return JS_TRUE;
}
bool
js::FindClassPrototype(JSContext *cx, JSObject *scope, JSProtoKey protoKey, JSObject **protop,
JSClass *clasp)
{
jsval v;
if (!js_FindClassObject(cx, scope, protoKey, &v, clasp))
return false;
if (VALUE_IS_FUNCTION(cx, v)) {
JSObject *ctor = JSVAL_TO_OBJECT(v);
if (!ctor->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), &v))
return false;
}
*protop = JSVAL_IS_OBJECT(v) ? JSVAL_TO_OBJECT(v) : NULL;
return true;
}
/*
* The first part of this function has been hand-expanded and optimized into
* NewBuiltinClassInstance in jsobjinlines.h.
*/
JSBool
js_GetClassPrototype(JSContext *cx, JSObject *scope, JSProtoKey protoKey,
JSObject **protop, JSClass *clasp)
@ -5720,29 +5750,7 @@ js_GetClassPrototype(JSContext *cx, JSObject *scope, JSProtoKey protoKey,
}
}
jsval v;
if (!js_FindClassObject(cx, scope, protoKey, &v, clasp))
return JS_FALSE;
if (VALUE_IS_FUNCTION(cx, v)) {
JSObject *ctor = JSVAL_TO_OBJECT(v);
if (!ctor->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), &v))
return JS_FALSE;
if (!JSVAL_IS_PRIMITIVE(v)) {
/*
* Set the newborn root in case v is otherwise unreferenced.
* It's ok to overwrite newborn roots here, since the getter
* called just above could have. Unlike the common GC rooting
* model, our callers do not have to protect protop thanks to
* this newborn root, since they all immediately create a new
* instance that delegates to this object, or just query the
* prototype for its class.
*/
cx->weakRoots.finalizableNewborns[FINALIZE_OBJECT] =
JSVAL_TO_OBJECT(v);
}
}
*protop = JSVAL_IS_OBJECT(v) ? JSVAL_TO_OBJECT(v) : NULL;
return JS_TRUE;
return FindClassPrototype(cx, scope, protoKey, protop, clasp);
}
/*
@ -5830,7 +5838,7 @@ js_PrimitiveToObject(JSContext *cx, jsval *vp)
JS_ASSERT(!JSVAL_IS_OBJECT(*vp));
JS_ASSERT(!JSVAL_IS_VOID(*vp));
clasp = PrimitiveClasses[JSVAL_TAG(*vp) - 1];
obj = NewObject(cx, clasp, NULL, NULL);
obj = NewBuiltinClassInstance(cx, clasp);
if (!obj)
return JS_FALSE;
obj->setPrimitiveThis(*vp);
@ -6162,25 +6170,6 @@ js_Clear(JSContext *cx, JSObject *obj)
JS_UNLOCK_OBJ(cx, obj);
}
/* On failure the function unlocks the object. */
static bool
ReservedSlotIndexOK(JSContext *cx, JSObject *obj, JSClass *clasp,
uint32 index, uint32 limit)
{
JS_ASSERT(JS_IS_OBJ_LOCKED(cx, obj));
/* Check the computed, possibly per-instance, upper bound. */
if (clasp->reserveSlots)
limit += clasp->reserveSlots(cx, obj);
if (index >= limit) {
JS_UNLOCK_OBJ(cx, obj);
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
JSMSG_RESERVED_SLOT_RANGE);
return false;
}
return true;
}
bool
js_GetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval *vp)
{
@ -6189,14 +6178,8 @@ js_GetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval *vp)
return true;
}
JSClass *clasp = obj->getClass();
uint32 limit = JSCLASS_RESERVED_SLOTS(clasp);
uint32 slot = JSSLOT_START(obj->getClass()) + index;
JS_LOCK_OBJ(cx, obj);
if (index >= limit && !ReservedSlotIndexOK(cx, obj, clasp, index, limit))
return false;
uint32 slot = JSSLOT_START(clasp) + index;
*vp = (slot < obj->numSlots()) ? obj->getSlot(slot) : JSVAL_VOID;
JS_UNLOCK_OBJ(cx, obj);
return true;
@ -6209,14 +6192,9 @@ js_SetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval v)
return true;
JSClass *clasp = obj->getClass();
uint32 slot = JSSLOT_START(clasp) + index;
JS_LOCK_OBJ(cx, obj);
#ifdef DEBUG
uint32 limit = JSCLASS_RESERVED_SLOTS(clasp);
JS_ASSERT(index < limit || ReservedSlotIndexOK(cx, obj, clasp, index, limit));
#endif
uint32 slot = JSSLOT_START(clasp) + index;
if (slot >= obj->numSlots()) {
/*
* At this point, obj may or may not own scope, and we may or may not
@ -6224,8 +6202,6 @@ js_SetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, jsval v)
* be accurate for obj (see comment below).
*/
uint32 nslots = JSSLOT_FREE(clasp);
if (clasp->reserveSlots)
nslots += clasp->reserveSlots(cx, obj);
JS_ASSERT(slot < nslots);
if (!obj->allocSlots(cx, nslots)) {
JS_UNLOCK_OBJ(cx, obj);

View File

@ -986,9 +986,7 @@ js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot);
* Ensure that the object has at least JSCLASS_RESERVED_SLOTS(clasp)+nreserved
* slots. The function can be called only for native objects just created with
* js_NewObject or its forms. In particular, the object should not be shared
* between threads and its dslots array must be null. nreserved must match the
* value that JSClass.reserveSlots (if any) would return after the object is
* fully initialized.
* between threads and its dslots array must be null.
*/
bool
js_EnsureReservedSlots(JSContext *cx, JSObject *obj, size_t nreserved);

View File

@ -46,6 +46,7 @@
#include "jsiter.h"
#include "jsobj.h"
#include "jsscope.h"
#include "jsstaticcheck.h"
#include "jsxml.h"
#include "jsdtracef.h"
@ -594,14 +595,15 @@ InitScopeForObject(JSContext* cx, JSObject* obj, JSClass *clasp, JSObject* proto
scope = JSScope::create(cx, ops, clasp, obj, js_GenerateShape(cx, false));
if (!scope)
goto bad;
/* Let JSScope::create set freeslot so as to reserve slots. */
JS_ASSERT(scope->freeslot >= JSSLOT_PRIVATE);
if (scope->freeslot > JS_INITIAL_NSLOTS &&
!obj->allocSlots(cx, scope->freeslot)) {
scope->destroy(cx);
uint32 freeslot = JSSLOT_FREE(clasp);
JS_ASSERT(freeslot >= scope->freeslot);
if (freeslot > JS_INITIAL_NSLOTS && !obj->allocSlots(cx, freeslot))
goto bad;
}
scope->freeslot = freeslot;
#ifdef DEBUG
if (freeslot < obj->numSlots())
obj->setSlot(freeslot, JSVAL_VOID);
#endif
}
obj->map = scope;
@ -613,9 +615,105 @@ InitScopeForObject(JSContext* cx, JSObject* obj, JSClass *clasp, JSObject* proto
return false;
}
/*
* Helper optimized for creating a native instance of the given class (not the
* class's prototype object). Use this in preference to NewObjectWithGivenProto
* and NewObject, but use NewBuiltinClassInstance if you need the default class
* prototype as proto, and its parent global as parent.
*/
static inline JSObject *
NewObjectWithGivenProto(JSContext *cx, JSClass *clasp, JSObject *proto,
JSObject *parent, size_t objectSize = 0)
NewNativeClassInstance(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent)
{
JS_ASSERT(proto);
JS_ASSERT(proto->isNative());
JS_ASSERT(parent);
DTrace::ObjectCreationScope objectCreationScope(cx, cx->fp, clasp);
/*
* Allocate an object from the GC heap and initialize all its fields before
* doing any operation that can potentially trigger GC. Functions have a
* larger non-standard allocation size.
*/
JSObject* obj = js_NewGCObject(cx);
if (obj) {
/*
* Default parent to the parent of the prototype, which was set from
* the parent of the prototype's constructor.
*/
obj->init(clasp, proto, parent, JSObject::defaultPrivate(clasp));
JS_LOCK_OBJ(cx, proto);
JSScope *scope = proto->scope();
JS_ASSERT(scope->canProvideEmptyScope(&js_ObjectOps, clasp));
scope = scope->getEmptyScope(cx, clasp);
JS_UNLOCK_OBJ(cx, proto);
if (!scope) {
obj = NULL;
} else {
obj->map = scope;
/*
* Do not call debug hooks on trace, because we might be in a non-_FAIL
* builtin. See bug 481444.
*/
if (cx->debugHooks->objectHook && !JS_ON_TRACE(cx)) {
AutoValueRooter tvr(cx, obj);
AutoKeepAtoms keep(cx->runtime);
cx->debugHooks->objectHook(cx, obj, JS_TRUE,
cx->debugHooks->objectHookData);
cx->weakRoots.finalizableNewborns[FINALIZE_OBJECT] = obj;
}
}
}
objectCreationScope.handleCreation(obj);
return obj;
}
bool
FindClassPrototype(JSContext *cx, JSObject *scope, JSProtoKey protoKey, JSObject **protop,
JSClass *clasp);
/*
* Helper used to create Boolean, Date, RegExp, etc. instances of built-in
* classes with class prototypes of the same JSClass. See, e.g., jsdate.cpp,
* jsregexp.cpp, and js_PrimitiveToObject in jsobj.cpp. Use this to get the
* right default proto and parent for clasp in cx.
*/
static inline JSObject *
NewBuiltinClassInstance(JSContext *cx, JSClass *clasp)
{
VOUCH_DOES_NOT_REQUIRE_STACK();
JSProtoKey protoKey = JSCLASS_CACHED_PROTO_KEY(clasp);
JS_ASSERT(protoKey != JSProto_Null);
/* NB: inline-expanded and specialized version of js_GetClassPrototype. */
JSObject *global = cx->fp ? cx->fp->scopeChain->getGlobal() : cx->globalObject;
JS_ASSERT(global->getClass()->flags & JSCLASS_IS_GLOBAL);
jsval v = global->getReservedSlot(JSProto_LIMIT + protoKey);
JSObject *proto;
if (!JSVAL_IS_PRIMITIVE(v)) {
proto = JSVAL_TO_OBJECT(v);
JS_ASSERT(proto->getParent() == global);
} else {
if (!FindClassPrototype(cx, global, protoKey, &proto, clasp))
return NULL;
}
return NewNativeClassInstance(cx, clasp, proto, global);
}
/*
* Like NewObject but with exactly the given proto. A null parent defaults to
* proto->getParent() if proto is non-null (else to null). NB: only this helper
* and NewObject can be used to construct full-sized JSFunction instances.
*/
static inline JSObject *
NewObjectWithGivenProto(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent)
{
DTrace::ObjectCreationScope objectCreationScope(cx, cx->fp, clasp);
@ -630,7 +728,7 @@ NewObjectWithGivenProto(JSContext *cx, JSClass *clasp, JSObject *proto,
* larger non-standard allocation size.
*/
JSObject* obj;
if (clasp == &js_FunctionClass && !objectSize) {
if (clasp == &js_FunctionClass) {
obj = (JSObject*) js_NewGCFunction(cx);
#ifdef DEBUG
if (obj) {
@ -639,7 +737,6 @@ NewObjectWithGivenProto(JSContext *cx, JSClass *clasp, JSObject *proto,
}
#endif
} else {
JS_ASSERT(!objectSize || objectSize == sizeof(JSObject));
obj = js_NewGCObject(cx);
}
if (!obj)
@ -692,24 +789,30 @@ GetClassProtoKey(JSClass *clasp)
return JSProto_Null;
}
/*
* Create an instance of any class, native or not, JSFunction-sized or not.
*
* If proto is null, use the memoized original value of the class constructor
* .prototype property object for a built-in class, else the current value of
* .prototype if available, else Object.prototype.
*
* Default parent is null to proto's parent (null if proto is null too).
*/
static inline JSObject *
NewObject(JSContext *cx, JSClass *clasp, JSObject *proto,
JSObject *parent, size_t objectSize = 0)
NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent)
{
/* Bootstrap the ur-object, and make it the default prototype object. */
if (!proto) {
JSProtoKey protoKey = GetClassProtoKey(clasp);
if (!js_GetClassPrototype(cx, parent, protoKey, &proto, clasp))
return NULL;
if (!proto &&
!js_GetClassPrototype(cx, parent, JSProto_Object, &proto)) {
if (!proto && !js_GetClassPrototype(cx, parent, JSProto_Object, &proto))
return NULL;
}
}
return NewObjectWithGivenProto(cx, clasp, proto, parent, objectSize);
return NewObjectWithGivenProto(cx, clasp, proto, parent);
}
}
} /* namespace js */
#endif /* jsobjinlines_h___ */

View File

@ -518,7 +518,7 @@ js_Stringify(JSContext *cx, jsval *vp, JSObject *replacer, jsval space,
if (!InitializeGap(cx, space, scx.gap))
return JS_FALSE;
JSObject *obj = NewObject(cx, &js_ObjectClass, NULL, NULL);
JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass);
if (!obj)
return JS_FALSE;
@ -610,7 +610,7 @@ static bool
Revive(JSContext *cx, jsval reviver, jsval *vp)
{
JSObject *obj = NewObject(cx, &js_ObjectClass, NULL, NULL);
JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass);
if (!obj)
return false;
@ -802,7 +802,7 @@ PushObject(JSContext *cx, JSONParser *jp, JSObject *obj)
static JSBool
OpenObject(JSContext *cx, JSONParser *jp)
{
JSObject *obj = NewObject(cx, &js_ObjectClass, NULL, NULL);
JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass);
if (!obj)
return JS_FALSE;

View File

@ -1739,17 +1739,12 @@ BEGIN_CASE(JSOP_SETMETHOD)
PCMETER(cache->pchits++);
PCMETER(cache->addpchits++);
/*
* Beware classes such as Function that use the
* reserveSlots hook to allocate a number of reserved
* slots that may vary with obj.
*/
if (slot < obj->numSlots() &&
!obj->getClass()->reserveSlots) {
if (slot < obj->numSlots()) {
++scope->freeslot;
} else {
if (!js_AllocSlot(cx, obj, &slot))
goto error;
JS_ASSERT(slot + 1 == scope->freeslot);
}
/*
@ -1757,12 +1752,16 @@ BEGIN_CASE(JSOP_SETMETHOD)
* if something created a hash table for scope, we must
* pay the price of JSScope::putProperty.
*
* (A reserveSlots hook can cause scopes of the same
* shape to have different freeslot values. This is
* what causes the slot != sprop->slot case. See
* js_GetMutableScope.)
* (A built-in object with a pre-allocated but not fixed
* population of reserved slots hook can cause scopes of the
* same shape to have different freeslot values. Arguments,
* Block, Call, and certain Function objects pre-allocate
* reserveds lots this way. This is what causes the slot !=
* sprop->slot case. See js_GetMutableScope. FIXME 558451)
*/
if (slot != sprop->slot || scope->table) {
if (slot == sprop->slot && !scope->table) {
scope->extend(cx, sprop);
} else {
JSScopeProperty *sprop2 =
scope->putProperty(cx, sprop->id,
sprop->getter(), sprop->setter(),
@ -1773,8 +1772,6 @@ BEGIN_CASE(JSOP_SETMETHOD)
goto error;
}
sprop = sprop2;
} else {
scope->extend(cx, sprop);
}
/*
@ -3207,7 +3204,7 @@ BEGIN_CASE(JSOP_NEWINIT)
if (!obj)
goto error;
} else {
obj = NewObject(cx, &js_ObjectClass, NULL, NULL);
obj = NewBuiltinClassInstance(cx, &js_ObjectClass);
if (!obj)
goto error;
@ -3252,7 +3249,6 @@ BEGIN_CASE(JSOP_INITMETHOD)
lval = FETCH_OPND(-2);
obj = JSVAL_TO_OBJECT(lval);
JS_ASSERT(obj->isNative());
JS_ASSERT(!obj->getClass()->reserveSlots);
JSScope *scope = obj->scope();
PropertyCacheEntry *entry;

View File

@ -3148,14 +3148,14 @@ BindLet(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
: js_variable_str,
name);
}
return JS_FALSE;
return false;
}
n = OBJ_BLOCK_COUNT(cx, blockObj);
if (n == JS_BIT(16)) {
ReportCompileErrorNumber(cx, TS(tc->parser), pn,
JSREPORT_ERROR, data->let.overflow);
return JS_FALSE;
return false;
}
/*
@ -3163,7 +3163,7 @@ BindLet(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
* This is balanced by PopStatement, defined immediately below.
*/
if (!Define(pn, atom, tc, true))
return JS_FALSE;
return false;
/*
* Assign block-local index to pn->pn_cookie right away, encoding it as an
@ -3177,11 +3177,11 @@ BindLet(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
pn->pn_dflags |= PND_LET | PND_BOUND;
/*
* Define the let binding's property before storing pn in a reserved slot,
* since block_reserveSlots depends on blockObj->scope()->entryCount.
* Define the let binding's property before storing pn in reserved slot at
* reserved slot index (NB: not slot number) n.
*/
if (!js_DefineBlockVariable(cx, blockObj, ATOM_TO_JSID(atom), n))
return JS_FALSE;
return false;
/*
* Store pn temporarily in what would be reserved slots in a cloned block
@ -3190,13 +3190,11 @@ BindLet(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc)
* slots in jsemit.cpp:EmitEnterBlock.
*/
uintN slot = JSSLOT_FREE(&js_BlockClass) + n;
if (slot >= blockObj->numSlots() &&
!blockObj->growSlots(cx, slot + 1)) {
return JS_FALSE;
}
if (slot >= blockObj->numSlots() && !blockObj->growSlots(cx, slot + 1))
return false;
blockObj->scope()->freeslot = slot + 1;
blockObj->setSlot(slot, PRIVATE_TO_JSVAL(pn));
return JS_TRUE;
return true;
}
static void

View File

@ -48,6 +48,7 @@
#include "jsscope.h"
#include "jsnum.h"
#include "jsobjinlines.h"
#include "jsscopeinlines.h"
using namespace js;

View File

@ -968,9 +968,16 @@ proxy_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rv
*/
if (!JSProxy::get(cx, proxy, obj, ATOM_TO_JSID(ATOM(classPrototype)), rval))
return false;
JSObject *proto = !JSVAL_IS_PRIMITIVE(*rval) ? JSVAL_TO_OBJECT(*rval) : NULL;
JSObject *newobj = NewObject(cx, &js_ObjectClass, proto, NULL);
JSObject *proto;
if (!JSVAL_IS_PRIMITIVE(*rval)) {
proto = JSVAL_TO_OBJECT(*rval);
} else {
if (!js_GetClassPrototype(cx, NULL, JSProto_Object, &proto))
return false;
}
JSObject *newobj = NewNativeClassInstance(cx, &js_ObjectClass, proto, proto->getParent());
*rval = OBJECT_TO_JSVAL(newobj);
/* If the call returns an object, return that, otherwise the original newobj. */
@ -1210,9 +1217,16 @@ callable_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval
/* callable is the constructor, so get callable.prototype is the proto of the new object. */
if (!callable->getProperty(cx, ATOM_TO_JSID(ATOM(classPrototype)), rval))
return false;
JSObject *proto = !JSVAL_IS_PRIMITIVE(*rval) ? JSVAL_TO_OBJECT(*rval) : NULL;
JSObject *newobj = NewObject(cx, &js_ObjectClass, proto, NULL);
JSObject *proto;
if (!JSVAL_IS_PRIMITIVE(*rval)) {
proto = JSVAL_TO_OBJECT(*rval);
} else {
if (!js_GetClassPrototype(cx, NULL, JSProto_Object, &proto))
return false;
}
JSObject *newobj = NewNativeClassInstance(cx, &js_ObjectClass, proto, proto->getParent());
*rval = OBJECT_TO_JSVAL(newobj);
/* If the call returns an object, return that, otherwise the original newobj. */
@ -1309,5 +1323,5 @@ js_InitProxyClass(JSContext *cx, JSObject *obj)
}
if (!JS_DefineFunctions(cx, module, static_methods))
return NULL;
return obj;
return module;
}

View File

@ -397,21 +397,6 @@ typedef void
typedef void
(* JSTraceNamePrinter)(JSTracer *trc, char *buf, size_t bufsize);
/*
* The optional JSClass.reserveSlots hook allows a class to make computed
* per-instance object slots reservations, in addition to or instead of using
* JSCLASS_HAS_RESERVED_SLOTS(n) in the JSClass.flags initializer to reserve
* a constant-per-class number of slots. Implementations of this hook should
* return the number of slots to reserve, not including any reserved by using
* JSCLASS_HAS_RESERVED_SLOTS(n) in JSClass.flags.
*
* NB: called with obj locked by the JSObjectOps-specific mutual exclusion
* mechanism appropriate for obj, so don't nest other operations that might
* also lock obj.
*/
typedef uint32
(* JSReserveSlotsOp)(JSContext *cx, JSObject *obj);
/* JSExtendedClass function pointer typedefs. */
typedef JSBool

View File

@ -5367,7 +5367,7 @@ js_XDRRegExpObject(JSXDRState *xdr, JSObject **objp)
return JS_FALSE;
}
if (xdr->mode == JSXDR_DECODE) {
obj = NewObject(xdr->cx, &js_RegExpClass, NULL, NULL);
obj = NewBuiltinClassInstance(xdr->cx, &js_RegExpClass);
if (!obj)
return JS_FALSE;
obj->clearParent();
@ -5733,7 +5733,7 @@ RegExp(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
}
/* Otherwise, replace obj with a new RegExp object. */
obj = NewObject(cx, &js_RegExpClass, NULL, NULL);
obj = NewBuiltinClassInstance(cx, &js_RegExpClass);
if (!obj)
return JS_FALSE;
@ -5788,7 +5788,7 @@ js_NewRegExpObject(JSContext *cx, TokenStream *ts,
re = js_NewRegExp(cx, ts, str, flags, JS_FALSE);
if (!re)
return NULL;
obj = NewObject(cx, &js_RegExpClass, NULL, NULL);
obj = NewBuiltinClassInstance(cx, &js_RegExpClass);
if (!obj) {
js_DestroyRegExp(cx, re);
return NULL;
@ -5804,7 +5804,7 @@ js_CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *proto)
JS_ASSERT(obj->getClass() == &js_RegExpClass);
JS_ASSERT(proto);
JS_ASSERT(proto->getClass() == &js_RegExpClass);
JSObject *clone = NewObjectWithGivenProto(cx, &js_RegExpClass, proto, NULL);
JSObject *clone = NewNativeClassInstance(cx, &js_RegExpClass, proto, proto->getParent());
if (!clone)
return NULL;
JSRegExp *re = static_cast<JSRegExp *>(obj->getPrivate());

View File

@ -63,6 +63,7 @@
#include "jsstr.h"
#include "jstracer.h"
#include "jsobjinlines.h"
#include "jsscopeinlines.h"
using namespace js;
@ -93,21 +94,12 @@ js_GenerateShape(JSContext *cx, bool gcLocked)
JSScope *
js_GetMutableScope(JSContext *cx, JSObject *obj)
{
JSScope *scope, *newscope;
JSClass *clasp;
uint32 freeslot;
scope = obj->scope();
JSScope *scope = obj->scope();
JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, scope));
if (!scope->isSharedEmpty())
return scope;
/*
* Compile-time block objects each have their own scope, created at
* birth, and runtime clone of a block objects are never mutated.
*/
JS_ASSERT(obj->getClass() != &js_BlockClass);
newscope = JSScope::create(cx, scope->ops, obj->getClass(), obj, scope->shape);
JSScope *newscope = JSScope::create(cx, scope->ops, obj->getClass(), obj, scope->shape);
if (!newscope)
return NULL;
@ -116,21 +108,33 @@ js_GetMutableScope(JSContext *cx, JSObject *obj)
JS_ASSERT(JS_IS_SCOPE_LOCKED(cx, newscope));
obj->map = newscope;
JS_ASSERT(newscope->freeslot == JSSLOT_FREE(obj->getClass()));
clasp = obj->getClass();
if (clasp->reserveSlots) {
/*
* FIXME: Here we change obj->scope()->freeslot without changing
* obj->shape(). If we strengthen the shape guarantees to cover
* freeslot, we can eliminate a check in JSOP_SETPROP and in
* js_AddProperty. See bug 535416.
*/
freeslot = JSSLOT_FREE(clasp) + clasp->reserveSlots(cx, obj);
if (freeslot > obj->numSlots())
freeslot = obj->numSlots();
if (newscope->freeslot < freeslot)
newscope->freeslot = freeslot;
/*
* Subtle dependency on objects that call js_EnsureReservedSlots either:
* (a) never escaping anywhere an ad-hoc property could be set on them;
* (b) having at least JSSLOT_FREE(obj->getClass()) >= JS_INITIAL_NSLOTS.
* Note that (b) depends on fine-tuning of JS_INITIAL_NSLOTS (5).
*
* Block objects fall into (a); Argument, Call, and Function objects (flat
* closures only) fall into (b). All of this goes away soon (FIXME 558451).
*/
JS_ASSERT(newscope->freeslot >= JSSLOT_START(obj->getClass()) &&
newscope->freeslot <= JSSLOT_FREE(obj->getClass()));
newscope->freeslot = JSSLOT_FREE(obj->getClass());
uint32 nslots = obj->numSlots();
if (newscope->freeslot > nslots && !obj->allocSlots(cx, newscope->freeslot)) {
newscope->destroy(cx);
obj->map = scope;
return NULL;
}
if (nslots > JS_INITIAL_NSLOTS && nslots > newscope->freeslot)
newscope->freeslot = nslots;
#ifdef DEBUG
if (newscope->freeslot < nslots)
obj->setSlot(newscope->freeslot, JSVAL_VOID);
#endif
JS_DROP_ALL_EMPTY_SCOPE_LOCKS(cx, scope);
static_cast<JSEmptyScope *>(scope)->drop(cx);
return newscope;
@ -217,7 +221,7 @@ JSScope::create(JSContext *cx, const JSObjectOps *ops, JSClass *clasp,
if (!scope)
return NULL;
scope->freeslot = JSSLOT_FREE(clasp);
scope->freeslot = JSSLOT_START(clasp);
scope->flags = cx->runtime->gcRegenShapesScopeFlag;
scope->initMinimal(cx, shape);
@ -238,7 +242,7 @@ JSEmptyScope::JSEmptyScope(JSContext *cx, const JSObjectOps *ops,
* getEmptyScope, also promises to incref on behalf of its caller.
*/
nrefs = 2;
freeslot = JSSLOT_FREE(clasp);
freeslot = JSSLOT_START(clasp);
flags = OWN_SHAPE | cx->runtime->gcRegenShapesScopeFlag;
initMinimal(cx, js_GenerateShape(cx, false));
@ -283,12 +287,23 @@ JSScope::initRuntimeState(JSContext *cx)
{
JSRuntime *rt = cx->runtime;
rt->emptyArgumentsScope = cx->create<JSEmptyScope>(cx, &js_ObjectOps, &js_ArgumentsClass);
if (!rt->emptyArgumentsScope)
return false;
JS_ASSERT(rt->emptyArgumentsScope->shape == JSScope::EMPTY_ARGUMENTS_SHAPE);
JS_ASSERT(rt->emptyArgumentsScope->nrefs == 2);
rt->emptyArgumentsScope->nrefs = 1;
#define SCOPE(Name) rt->empty##Name##Scope
#define CLASP(Name) &js_##Name##Class
#define INIT_EMPTY_SCOPE(Name,NAME,ops) \
INIT_EMPTY_SCOPE_WITH_CLASS(Name, NAME, ops, CLASP(Name))
#define INIT_EMPTY_SCOPE_WITH_CLASS(Name,NAME,ops,clasp) \
INIT_EMPTY_SCOPE_WITH_FREESLOT(Name, NAME, ops, clasp, JSSLOT_FREE(clasp))
#define INIT_EMPTY_SCOPE_WITH_FREESLOT(Name,NAME,ops,clasp,slot) \
SCOPE(Name) = cx->create<JSEmptyScope>(cx, ops, clasp); \
if (!SCOPE(Name)) \
return false; \
JS_ASSERT(SCOPE(Name)->shape == JSScope::EMPTY_##NAME##_SHAPE); \
JS_ASSERT(SCOPE(Name)->nrefs == 2); \
SCOPE(Name)->nrefs = 1; \
SCOPE(Name)->freeslot = slot
/*
* NewArguments allocates dslots to have enough room for the argc of the
@ -308,25 +323,10 @@ JSScope::initRuntimeState(JSContext *cx)
* arguments objects. This helps ensure that any arguments object needing
* its own mutable scope (with unique shape) is a rare event.
*/
rt->emptyArgumentsScope->freeslot = JS_INITIAL_NSLOTS + JS_ARGS_LENGTH_MAX;
INIT_EMPTY_SCOPE_WITH_FREESLOT(Arguments, ARGUMENTS, &js_ObjectOps, CLASP(Arguments),
JS_INITIAL_NSLOTS + JS_ARGS_LENGTH_MAX);
rt->emptyBlockScope = cx->create<JSEmptyScope>(cx, &js_ObjectOps, &js_BlockClass);
if (!rt->emptyBlockScope) {
JSScope::finishRuntimeState(cx);
return false;
}
JS_ASSERT(rt->emptyBlockScope->shape == JSScope::EMPTY_BLOCK_SHAPE);
JS_ASSERT(rt->emptyBlockScope->nrefs == 2);
rt->emptyBlockScope->nrefs = 1;
rt->emptyCallScope = cx->create<JSEmptyScope>(cx, &js_ObjectOps, &js_CallClass);
if (!rt->emptyCallScope) {
JSScope::finishRuntimeState(cx);
return false;
}
JS_ASSERT(rt->emptyCallScope->shape == JSScope::EMPTY_CALL_SHAPE);
JS_ASSERT(rt->emptyCallScope->nrefs == 2);
rt->emptyCallScope->nrefs = 1;
INIT_EMPTY_SCOPE(Block, BLOCK, &js_ObjectOps);
/*
* Initialize the shared scope for all empty Call objects so gets for args
@ -335,7 +335,23 @@ JSScope::initRuntimeState(JSContext *cx)
*
* See comment above for rt->emptyArgumentsScope->freeslot initialization.
*/
rt->emptyCallScope->freeslot = JS_INITIAL_NSLOTS + JSFunction::MAX_ARGS_AND_VARS;
INIT_EMPTY_SCOPE_WITH_FREESLOT(Call, CALL, &js_ObjectOps, CLASP(Call),
JS_INITIAL_NSLOTS + JSFunction::MAX_ARGS_AND_VARS);
/* A DeclEnv object holds the name binding for a named function expression. */
INIT_EMPTY_SCOPE(DeclEnv, DECL_ENV, &js_ObjectOps);
/* Non-escaping native enumerator objects share this empty scope. */
INIT_EMPTY_SCOPE_WITH_CLASS(Enumerator, ENUMERATOR, &js_ObjectOps, &js_IteratorClass.base);
/* Same drill for With objects. */
INIT_EMPTY_SCOPE(With, WITH, &js_WithObjectOps);
#undef SCOPE
#undef CLASP
#undef INIT_EMPTY_SCOPE
#undef INIT_EMPTY_SCOPE_WITH_CLASS
#undef INIT_EMPTY_SCOPE_WITH_FREESLOT
return true;
}
@ -345,18 +361,22 @@ void
JSScope::finishRuntimeState(JSContext *cx)
{
JSRuntime *rt = cx->runtime;
if (rt->emptyArgumentsScope) {
rt->emptyArgumentsScope->drop(cx);
rt->emptyArgumentsScope = NULL;
}
if (rt->emptyBlockScope) {
rt->emptyBlockScope->drop(cx);
rt->emptyBlockScope = NULL;
}
if (rt->emptyCallScope) {
rt->emptyCallScope->drop(cx);
rt->emptyCallScope = NULL;
#define FINISH_EMPTY_SCOPE(Name) \
if (rt->empty##Name##Scope) { \
rt->empty##Name##Scope->drop(cx); \
rt->empty##Name##Scope = NULL; \
}
/* Mnemonic: ABCDEW */
FINISH_EMPTY_SCOPE(Arguments);
FINISH_EMPTY_SCOPE(Block);
FINISH_EMPTY_SCOPE(Call);
FINISH_EMPTY_SCOPE(DeclEnv);
FINISH_EMPTY_SCOPE(Enumerator);
FINISH_EMPTY_SCOPE(With);
#undef FINISH_EMPTY_SCOPE
}
JS_STATIC_ASSERT(sizeof(JSHashNumber) == 4);

View File

@ -515,10 +515,13 @@ struct JSScope : public JSObjectMap
static void finishRuntimeState(JSContext *cx);
enum {
EMPTY_ARGUMENTS_SHAPE = 1,
EMPTY_BLOCK_SHAPE = 2,
EMPTY_CALL_SHAPE = 3,
LAST_RESERVED_SHAPE = 3
EMPTY_ARGUMENTS_SHAPE = 1,
EMPTY_BLOCK_SHAPE = 2,
EMPTY_CALL_SHAPE = 3,
EMPTY_DECL_ENV_SHAPE = 4,
EMPTY_ENUMERATOR_SHAPE = 5,
EMPTY_WITH_SHAPE = 6,
LAST_RESERVED_SHAPE = 6
};
};

View File

@ -108,7 +108,7 @@ ArrayBuffer::class_constructor(JSContext *cx, JSObject *obj,
uintN argc, jsval *argv, jsval *rval)
{
if (!JS_IsConstructing(cx)) {
obj = NewObject(cx, &ArrayBuffer::jsclass, NULL, NULL);
obj = NewBuiltinClassInstance(cx, &ArrayBuffer::jsclass);
if (!obj)
return false;
*rval = OBJECT_TO_JSVAL(obj);
@ -122,7 +122,7 @@ ArrayBuffer::create(JSContext *cx, JSObject *obj,
uintN argc, jsval *argv, jsval *rval)
{
if (!obj) {
obj = NewObject(cx, &ArrayBuffer::jsclass, NULL, NULL);
obj = NewBuiltinClassInstance(cx, &ArrayBuffer::jsclass);
if (!obj)
return false;
*rval = OBJECT_TO_JSVAL(obj);
@ -717,7 +717,7 @@ class TypedArrayTemplate
//
if (!JS_IsConstructing(cx)) {
obj = NewObject(cx, slowClass(), NULL, NULL);
obj = NewBuiltinClassInstance(cx, slowClass());
if (!obj)
return false;
*rval = OBJECT_TO_JSVAL(obj);
@ -730,7 +730,7 @@ class TypedArrayTemplate
create(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
if (!obj) {
obj = NewObject(cx, slowClass(), NULL, NULL);
obj = NewBuiltinClassInstance(cx, slowClass());
if (!obj)
return false;
*rval = OBJECT_TO_JSVAL(obj);

View File

@ -281,7 +281,7 @@ NewXMLNamespace(JSContext *cx, JSString *prefix, JSString *uri, JSBool declared)
{
JSObject *obj;
obj = NewObject(cx, &js_NamespaceClass.base, NULL, NULL);
obj = NewBuiltinClassInstance(cx, &js_NamespaceClass.base);
if (!obj)
return JS_FALSE;
JS_ASSERT(JSVAL_IS_VOID(obj->getNamePrefix()));
@ -471,7 +471,7 @@ static JSObject *
NewXMLQName(JSContext *cx, JSString *uri, JSString *prefix, JSString *localName,
JSClass *clasp = &js_QNameClass.base)
{
JSObject *obj = NewObject(cx, clasp, NULL, NULL);
JSObject *obj = NewBuiltinClassInstance(cx, clasp);
if (!obj)
return NULL;
JS_ASSERT(obj->isQName());
@ -584,7 +584,7 @@ NamespaceHelper(JSContext *cx, JSObject *obj, intN argc, jsval *argv,
return JS_TRUE;
}
obj = NewObject(cx, &js_NamespaceClass.base, NULL, NULL);
obj = NewBuiltinClassInstance(cx, &js_NamespaceClass.base);
if (!obj)
return JS_FALSE;
*rval = OBJECT_TO_JSVAL(obj);
@ -691,7 +691,7 @@ QNameHelper(JSContext *cx, JSObject *obj, JSClass *clasp, intN argc,
* Create and return a new QName or AttributeName object exactly as if
* constructed.
*/
obj = NewObject(cx, clasp, NULL, NULL);
obj = NewBuiltinClassInstance(cx, clasp);
if (!obj)
return JS_FALSE;
*rval = OBJECT_TO_JSVAL(obj);
@ -7305,8 +7305,7 @@ js_GetAnyName(JSContext *cx, jsval *vp)
return JS_FALSE;
do {
obj = NewObjectWithGivenProto(cx, &js_AnyNameClass, NULL,
NULL);
obj = NewObjectWithGivenProto(cx, &js_AnyNameClass, NULL, NULL);
if (!obj) {
ok = JS_FALSE;
break;
@ -7589,8 +7588,7 @@ js_StepXMLListFilter(JSContext *cx, JSBool initialized)
return JS_FALSE;
}
filterobj = NewObjectWithGivenProto(cx, &js_XMLFilterClass,
NULL, NULL);
filterobj = NewObjectWithGivenProto(cx, &js_XMLFilterClass, NULL, NULL);
if (!filterobj)
return JS_FALSE;