📄 jsobj.c
字号:
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * 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 object implementation. */#include "jsstddef.h"#include <stdlib.h>#include <string.h>#include "jstypes.h"#include "jsarena.h" /* Added by JSIFY */#include "jsbit.h"#include "jsutil.h" /* Added by JSIFY */#include "jshash.h" /* Added by JSIFY */#include "jsdhash.h"#include "jsprf.h"#include "jsapi.h"#include "jsarray.h"#include "jsatom.h"#include "jsbool.h"#include "jscntxt.h"#include "jsconfig.h"#include "jsfun.h"#include "jsgc.h"#include "jsinterp.h"#include "jslock.h"#include "jsnum.h"#include "jsobj.h"#include "jsscan.h"#include "jsscope.h"#include "jsscript.h"#include "jsstr.h"#include "jsopcode.h"#include "jsdbgapi.h" /* whether or not JS_HAS_OBJ_WATCHPOINT */#if JS_HAS_GENERATORS#include "jsiter.h"#endif#if JS_HAS_XML_SUPPORT#include "jsxml.h"#endif#if JS_HAS_XDR#include "jsxdrapi.h"#endif#ifdef JS_THREADSAFE#define NATIVE_DROP_PROPERTY js_DropPropertyextern voidjs_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop);#else#define NATIVE_DROP_PROPERTY NULL#endifJS_FRIEND_DATA(JSObjectOps) js_ObjectOps = { js_NewObjectMap, js_DestroyObjectMap, js_LookupProperty, js_DefineProperty, js_GetProperty, js_SetProperty, js_GetAttributes, js_SetAttributes, js_DeleteProperty, js_DefaultValue, js_Enumerate, js_CheckAccess, NULL, NATIVE_DROP_PROPERTY, js_Call, js_Construct, NULL, js_HasInstance, js_SetProtoOrParent, js_SetProtoOrParent, js_Mark, js_Clear, js_GetRequiredSlot, js_SetRequiredSlot};JSClass js_ObjectClass = { js_Object_str, JSCLASS_HAS_CACHED_PROTO(JSProto_Object), JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub, JSCLASS_NO_OPTIONAL_MEMBERS};#if JS_HAS_OBJ_PROTO_PROPstatic JSBoolobj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp);static JSBoolobj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp);static JSBoolobj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp);static JSPropertySpec object_props[] = { /* These two must come first; see object_props[slot].name usage below. */ {js_proto_str, JSSLOT_PROTO, JSPROP_PERMANENT|JSPROP_SHARED, obj_getSlot, obj_setSlot}, {js_parent_str,JSSLOT_PARENT,JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED, obj_getSlot, obj_setSlot}, {js_count_str, 0, JSPROP_PERMANENT,obj_getCount, obj_getCount}, {0,0,0,0,0}};/* NB: JSSLOT_PROTO and JSSLOT_PARENT are already indexes into object_props. */#define JSSLOT_COUNT 2static JSBoolReportStrictSlot(JSContext *cx, uint32 slot){ if (slot == JSSLOT_PROTO) return JS_TRUE; return JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING | JSREPORT_STRICT, js_GetErrorMessage, NULL, JSMSG_DEPRECATED_USAGE, object_props[slot].name);}static JSBoolobj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp){ uint32 slot; jsid propid; JSAccessMode mode; uintN attrs; JSObject *pobj; JSClass *clasp; JSExtendedClass *xclasp; slot = (uint32) JSVAL_TO_INT(id); if (id == INT_TO_JSVAL(JSSLOT_PROTO)) { propid = ATOM_TO_JSID(cx->runtime->atomState.protoAtom); mode = JSACC_PROTO; } else { propid = ATOM_TO_JSID(cx->runtime->atomState.parentAtom); mode = JSACC_PARENT; } /* Let OBJ_CHECK_ACCESS get the slot's value, based on the access mode. */ if (!OBJ_CHECK_ACCESS(cx, obj, propid, mode, vp, &attrs)) return JS_FALSE; pobj = JSVAL_TO_OBJECT(*vp); if (pobj) { clasp = OBJ_GET_CLASS(cx, pobj); if (clasp == &js_CallClass || clasp == &js_BlockClass) { /* Censor activations and lexical scopes per ECMA-262. */ *vp = JSVAL_NULL; } else if (clasp->flags & JSCLASS_IS_EXTENDED) { xclasp = (JSExtendedClass *) clasp; if (xclasp->outerObject) { pobj = xclasp->outerObject(cx, pobj); if (!pobj) return JS_FALSE; *vp = OBJECT_TO_JSVAL(pobj); } } } return JS_TRUE;}static JSBoolobj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp){ JSObject *pobj; uint32 slot; jsid propid; uintN attrs; if (!JSVAL_IS_OBJECT(*vp)) return JS_TRUE; pobj = JSVAL_TO_OBJECT(*vp); if (pobj) { /* * Innerize pobj here to avoid sticking unwanted properties on the * outer object. This ensures that any with statements only grant * access to the inner object. */ OBJ_TO_INNER_OBJECT(cx, pobj); if (!pobj) return JS_FALSE; } slot = (uint32) JSVAL_TO_INT(id); if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, slot)) return JS_FALSE; /* __parent__ is readonly and permanent, only __proto__ may be set. */ propid = ATOM_TO_JSID(cx->runtime->atomState.protoAtom); if (!OBJ_CHECK_ACCESS(cx, obj, propid, JSACC_PROTO|JSACC_WRITE, vp, &attrs)) return JS_FALSE; return js_SetProtoOrParent(cx, obj, slot, pobj);}static JSBoolobj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp){ jsval iter_state; jsid num_properties; JSBool ok; if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, JSSLOT_COUNT)) return JS_FALSE; /* Get the number of properties to enumerate. */ iter_state = JSVAL_NULL; ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, &num_properties); if (!ok) goto out; if (!JSVAL_IS_INT(num_properties)) { JS_ASSERT(0); *vp = JSVAL_ZERO; goto out; } *vp = num_properties;out: if (iter_state != JSVAL_NULL) ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_DESTROY, &iter_state, 0); return ok;}#else /* !JS_HAS_OBJ_PROTO_PROP */#define object_props NULL#endif /* !JS_HAS_OBJ_PROTO_PROP */JSBooljs_SetProtoOrParent(JSContext *cx, JSObject *obj, uint32 slot, JSObject *pobj){ JSRuntime *rt; JSObject *obj2, *oldproto; JSScope *scope, *newscope; /* * Serialize all proto and parent setting in order to detect cycles. * We nest locks in this function, and only here, in the following orders: * * (1) rt->setSlotLock < pobj's scope lock; * rt->setSlotLock < pobj's proto-or-parent's scope lock; * rt->setSlotLock < pobj's grand-proto-or-parent's scope lock; * etc... * (2) rt->setSlotLock < obj's scope lock < pobj's scope lock. * * We avoid AB-BA deadlock by restricting obj from being on pobj's parent * or proto chain (pobj may already be on obj's parent or proto chain; it * could be moving up or down). We finally order obj with respect to pobj * at the bottom of this routine (just before releasing rt->setSlotLock), * by making pobj be obj's prototype or parent. * * After we have set the slot and released rt->setSlotLock, another call * to js_SetProtoOrParent could nest locks according to the first order * list above, but it cannot deadlock with any other thread. For there * to be a deadlock, other parts of the engine would have to nest scope * locks in the opposite order. XXXbe ensure they don't! */ rt = cx->runtime;#ifdef JS_THREADSAFE JS_ACQUIRE_LOCK(rt->setSlotLock); while (rt->setSlotBusy) { jsrefcount saveDepth; /* Take pains to avoid nesting rt->gcLock inside rt->setSlotLock! */ JS_RELEASE_LOCK(rt->setSlotLock); saveDepth = JS_SuspendRequest(cx); JS_ACQUIRE_LOCK(rt->setSlotLock); if (rt->setSlotBusy) JS_WAIT_CONDVAR(rt->setSlotDone, JS_NO_TIMEOUT); JS_RELEASE_LOCK(rt->setSlotLock); JS_ResumeRequest(cx, saveDepth); JS_ACQUIRE_LOCK(rt->setSlotLock); } rt->setSlotBusy = JS_TRUE; JS_RELEASE_LOCK(rt->setSlotLock);#define SET_SLOT_DONE(rt) \ JS_BEGIN_MACRO \ JS_ACQUIRE_LOCK((rt)->setSlotLock); \ (rt)->setSlotBusy = JS_FALSE; \ JS_NOTIFY_ALL_CONDVAR((rt)->setSlotDone); \ JS_RELEASE_LOCK((rt)->setSlotLock); \ JS_END_MACRO#else#define SET_SLOT_DONE(rt) /* nothing */#endif obj2 = pobj; while (obj2) { if (obj2 == obj) { SET_SLOT_DONE(rt); JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CYCLIC_VALUE,#if JS_HAS_OBJ_PROTO_PROP object_props[slot].name#else (slot == JSSLOT_PROTO) ? js_proto_str : js_parent_str#endif ); return JS_FALSE; } obj2 = JSVAL_TO_OBJECT(OBJ_GET_SLOT(cx, obj2, slot)); } if (slot == JSSLOT_PROTO && OBJ_IS_NATIVE(obj)) { /* Check to see whether obj shares its prototype's scope. */ JS_LOCK_OBJ(cx, obj); scope = OBJ_SCOPE(obj); oldproto = JSVAL_TO_OBJECT(LOCKED_OBJ_GET_SLOT(obj, JSSLOT_PROTO)); if (oldproto && OBJ_SCOPE(oldproto) == scope) { /* Either obj needs a new empty scope, or it should share pobj's. */ if (!pobj || !OBJ_IS_NATIVE(pobj) || OBJ_GET_CLASS(cx, pobj) != LOCKED_OBJ_GET_CLASS(oldproto)) { /* * With no proto and no scope of its own, obj is truly empty. * * If pobj is not native, obj needs its own empty scope -- it * should not continue to share oldproto's scope once oldproto * is not on obj's prototype chain. That would put properties * from oldproto's scope ahead of properties defined by pobj, * in lookup order. * * If pobj's class differs from oldproto's, we may need a new * scope to handle differences in private and reserved slots, * so we suboptimally but safely make one. */ scope = js_GetMutableScope(cx, obj); if (!scope) { JS_UNLOCK_OBJ(cx, obj); SET_SLOT_DONE(rt); return JS_FALSE; } } else if (OBJ_SCOPE(pobj) != scope) {#ifdef JS_THREADSAFE /* * We are about to nest scope locks. Help jslock.c:ShareScope * keep scope->u.count balanced for the JS_UNLOCK_SCOPE, while * avoiding deadlock, by recording scope in rt->setSlotScope. */ if (scope->ownercx) { JS_ASSERT(scope->ownercx == cx); rt->setSlotScope = scope; }#endif /* We can't deadlock because we checked for cycles above (2). */ JS_LOCK_OBJ(cx, pobj); newscope = (JSScope *) js_HoldObjectMap(cx, pobj->map); obj->map = &newscope->map; js_DropObjectMap(cx, &scope->map, obj); JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope); scope = newscope;#ifdef JS_THREADSAFE rt->setSlotScope = NULL;#endif } } LOCKED_OBJ_SET_SLOT(obj, JSSLOT_PROTO, OBJECT_TO_JSVAL(pobj)); JS_UNLOCK_SCOPE(cx, scope); } else { OBJ_SET_SLOT(cx, obj, slot, OBJECT_TO_JSVAL(pobj)); } SET_SLOT_DONE(rt); return JS_TRUE;#undef SET_SLOT_DONE}JS_STATIC_DLL_CALLBACK(JSHashNumber)js_hash_object(const void *key){ return (JSHashNumber)JS_PTR_TO_UINT32(key) >> JSVAL_TAGBITS;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -