1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=79:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
17 * The Original Code is Mozilla Communicator client code, released
20 * The Initial Developer of the Original Code is
21 * Netscape Communications Corporation.
22 * Portions created by the Initial Developer are Copyright (C) 1998
23 * the Initial Developer. All Rights Reserved.
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
42 * JS object implementation.
48 #include "jsarena.h" /* Added by JSIFY */
50 #include "jsutil.h" /* Added by JSIFY */
51 #include "jshash.h" /* Added by JSIFY */
58 #include "jsbuiltins.h"
60 #include "jsversion.h"
74 #include "jsstaticcheck.h"
80 #include "jsscopeinlines.h"
81 #include "jsscriptinlines.h"
82 #include "jsobjinlines.h"
88 #if JS_HAS_XML_SUPPORT
96 #include "jsdtracef.h"
97 #include "jsatominlines.h"
98 #include "jsobjinlines.h"
99 #include "jsscriptinlines.h"
101 #include "jsautooplen.h"
105 JS_FRIEND_DATA(const JSObjectMap
) JSObjectMap::sharedNonNative(JSObjectMap::SHAPELESS
);
107 Class js_ObjectClass
= {
109 JSCLASS_HAS_CACHED_PROTO(JSProto_Object
) |
110 JSCLASS_FAST_CONSTRUCTOR
,
111 PropertyStub
, /* addProperty */
112 PropertyStub
, /* delProperty */
113 PropertyStub
, /* getProperty */
114 PropertyStub
, /* setProperty */
120 JS_FRIEND_API(JSObject
*)
121 js_ObjectToOuterObject(JSContext
*cx
, JSObject
*obj
)
123 OBJ_TO_OUTER_OBJECT(cx
, obj
);
127 #if JS_HAS_OBJ_PROTO_PROP
130 obj_getProto(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
);
133 obj_setProto(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
);
135 static JSPropertySpec object_props
[] = {
136 {js_proto_str
, 0, JSPROP_PERMANENT
|JSPROP_SHARED
, Jsvalify(obj_getProto
), Jsvalify(obj_setProto
)},
141 obj_getProto(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
143 /* Let CheckAccess get the slot's value, based on the access mode. */
145 id
= ATOM_TO_JSID(cx
->runtime
->atomState
.protoAtom
);
146 return CheckAccess(cx
, obj
, id
, JSACC_PROTO
, vp
, &attrs
);
150 obj_setProto(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
152 if (!vp
->isObjectOrNull())
155 JSObject
*pobj
= vp
->toObjectOrNull();
158 * Innerize pobj here to avoid sticking unwanted properties on the
159 * outer object. This ensures that any with statements only grant
160 * access to the inner object.
162 OBJ_TO_INNER_OBJECT(cx
, pobj
);
168 id
= ATOM_TO_JSID(cx
->runtime
->atomState
.protoAtom
);
169 if (!CheckAccess(cx
, obj
, id
, JSAccessMode(JSACC_PROTO
|JSACC_WRITE
), vp
, &attrs
))
172 return SetProto(cx
, obj
, pobj
, JS_TRUE
);
175 #else /* !JS_HAS_OBJ_PROTO_PROP */
177 #define object_props NULL
179 #endif /* !JS_HAS_OBJ_PROTO_PROP */
182 js_hash_object(const void *key
)
184 return JSHashNumber(uintptr_t(key
) >> JS_GCTHING_ALIGN
);
188 MarkSharpObjects(JSContext
*cx
, JSObject
*obj
, JSIdArray
**idap
)
190 JSSharpObjectMap
*map
;
193 JSHashEntry
**hep
, *he
;
202 JS_CHECK_RECURSION(cx
, return NULL
);
204 map
= &cx
->sharpObjectMap
;
205 JS_ASSERT(map
->depth
>= 1);
207 hash
= js_hash_object(obj
);
208 hep
= JS_HashTableRawLookup(table
, hash
, obj
);
212 he
= JS_HashTableRawAdd(table
, hep
, hash
, obj
, (void *) sharpid
);
214 JS_ReportOutOfMemory(cx
);
218 ida
= JS_Enumerate(cx
, obj
);
223 for (i
= 0, length
= ida
->length
; i
< length
; i
++) {
225 ok
= obj
->lookupProperty(cx
, id
, &obj2
, &prop
);
230 bool hasGetter
, hasSetter
;
231 AutoValueRooter
v(cx
);
232 AutoValueRooter
setter(cx
);
233 if (obj2
->isNative()) {
234 JSScopeProperty
*sprop
= (JSScopeProperty
*) prop
;
235 hasGetter
= sprop
->hasGetterValue();
236 hasSetter
= sprop
->hasSetterValue();
238 v
.set(sprop
->getterValue());
240 setter
.set(sprop
->setterValue());
241 JS_UNLOCK_OBJ(cx
, obj2
);
243 hasGetter
= hasSetter
= false;
246 /* Mark the getter, then set val to setter. */
247 if (hasGetter
&& v
.value().isObject()) {
248 ok
= !!MarkSharpObjects(cx
, &v
.value().toObject(), NULL
);
252 v
.set(setter
.value());
253 } else if (!hasGetter
) {
254 ok
= obj
->getProperty(cx
, id
, v
.addr());
258 if (v
.value().isObject() &&
259 !MarkSharpObjects(cx
, &v
.value().toObject(), NULL
)) {
265 JS_DestroyIdArray(cx
, ida
);
269 sharpid
= uintptr_t(he
->value
);
271 sharpid
= ++map
->sharpgen
<< SHARP_ID_SHIFT
;
272 he
->value
= (void *) sharpid
;
282 js_EnterSharpObject(JSContext
*cx
, JSObject
*obj
, JSIdArray
**idap
,
285 JSSharpObjectMap
*map
;
289 JSHashEntry
*he
, **hep
;
294 if (!JS_CHECK_OPERATION_LIMIT(cx
))
297 /* Set to null in case we return an early error. */
299 map
= &cx
->sharpObjectMap
;
302 table
= JS_NewHashTable(8, js_hash_object
, JS_CompareValues
,
303 JS_CompareValues
, NULL
, NULL
);
305 JS_ReportOutOfMemory(cx
);
309 JS_KEEP_ATOMS(cx
->runtime
);
312 /* From this point the control must flow either through out: or bad:. */
314 if (map
->depth
== 0) {
316 * Although MarkSharpObjects tries to avoid invoking getters,
317 * it ends up doing so anyway under some circumstances; for
318 * example, if a wrapped object has getters, the wrapper will
319 * prevent MarkSharpObjects from recognizing them as such.
320 * This could lead to js_LeaveSharpObject being called while
321 * MarkSharpObjects is still working.
323 * Increment map->depth while we call MarkSharpObjects, to
324 * ensure that such a call doesn't free the hash table we're
328 he
= MarkSharpObjects(cx
, obj
, &ida
);
332 JS_ASSERT((uintptr_t(he
->value
) & SHARP_BIT
) == 0);
334 JS_DestroyIdArray(cx
, ida
);
338 hash
= js_hash_object(obj
);
339 hep
= JS_HashTableRawLookup(table
, hash
, obj
);
343 * It's possible that the value of a property has changed from the
344 * first time the object's properties are traversed (when the property
345 * ids are entered into the hash table) to the second (when they are
346 * converted to strings), i.e., the JSObject::getProperty() call is not
350 he
= JS_HashTableRawAdd(table
, hep
, hash
, obj
, NULL
);
352 JS_ReportOutOfMemory(cx
);
360 sharpid
= uintptr_t(he
->value
);
362 len
= JS_snprintf(buf
, sizeof buf
, "#%u%c",
363 sharpid
>> SHARP_ID_SHIFT
,
364 (sharpid
& SHARP_BIT
) ? '#' : '=');
365 *sp
= js_InflateString(cx
, buf
, &len
);
368 JS_DestroyIdArray(cx
, ida
);
375 if ((sharpid
& SHARP_BIT
) == 0) {
377 ida
= JS_Enumerate(cx
, obj
);
394 /* Clean up the sharpObjectMap table on outermost error. */
395 if (map
->depth
== 0) {
396 JS_UNKEEP_ATOMS(cx
->runtime
);
398 JS_HashTableDestroy(map
->table
);
405 js_LeaveSharpObject(JSContext
*cx
, JSIdArray
**idap
)
407 JSSharpObjectMap
*map
;
410 map
= &cx
->sharpObjectMap
;
411 JS_ASSERT(map
->depth
> 0);
412 if (--map
->depth
== 0) {
413 JS_UNKEEP_ATOMS(cx
->runtime
);
415 JS_HashTableDestroy(map
->table
);
421 JS_DestroyIdArray(cx
, ida
);
428 gc_sharp_table_entry_marker(JSHashEntry
*he
, intN i
, void *arg
)
430 JS_CALL_OBJECT_TRACER((JSTracer
*)arg
, (JSObject
*)he
->key
,
431 "sharp table entry");
432 return JS_DHASH_NEXT
;
436 js_TraceSharpMap(JSTracer
*trc
, JSSharpObjectMap
*map
)
438 JS_ASSERT(map
->depth
> 0);
439 JS_ASSERT(map
->table
);
442 * During recursive calls to MarkSharpObjects a non-native object or
443 * object with a custom getProperty method can potentially return an
444 * unrooted value or even cut from the object graph an argument of one of
445 * MarkSharpObjects recursive invocations. So we must protect map->table
446 * entries against GC.
448 * We can not simply use JSTempValueRooter to mark the obj argument of
449 * MarkSharpObjects during recursion as we have to protect *all* entries
450 * in JSSharpObjectMap including those that contains otherwise unreachable
451 * objects just allocated through custom getProperty. Otherwise newer
452 * allocations can re-use the address of an object stored in the hashtable
453 * confusing js_EnterSharpObject. So to address the problem we simply
454 * mark all objects from map->table.
456 * An alternative "proper" solution is to use JSTempValueRooter in
457 * MarkSharpObjects with code to remove during finalization entries
458 * with otherwise unreachable objects. But this is way too complex
459 * to justify spending efforts.
461 JS_HashTableEnumerateEntries(map
->table
, gc_sharp_table_entry_marker
, trc
);
466 obj_toSource(JSContext
*cx
, uintN argc
, Value
*vp
)
468 JSBool ok
, outermost
;
472 jschar
*chars
, *ochars
, *vsharp
;
473 const jschar
*idstrchars
, *vchars
;
474 size_t nchars
, idstrlength
, gsoplength
, vlength
, vsharplength
, curlen
;
480 JSString
*idstr
, *valstr
, *str
;
482 JS_CHECK_RECURSION(cx
, return JS_FALSE
);
485 PodArrayZero(localroot
);
486 AutoArrayRooter
tvr(cx
, JS_ARRAY_LENGTH(localroot
), localroot
);
488 /* If outermost, we need parentheses to be an expression, not a block. */
489 outermost
= (cx
->sharpObjectMap
.depth
== 0);
490 obj
= ComputeThisFromVp(cx
, vp
);
491 if (!obj
|| !(he
= js_EnterSharpObject(cx
, obj
, &ida
, &chars
))) {
497 * We didn't enter -- obj is already "sharp", meaning we've visited it
498 * already in our depth first search, and therefore chars contains a
499 * string of the form "#n#".
502 #if JS_HAS_SHARP_VARS
503 nchars
= js_strlen(chars
);
516 /* If outermost, allocate 4 + 1 for "({})" and the terminator. */
517 chars
= (jschar
*) js_malloc(((outermost
? 4 : 2) + 1) * sizeof(jschar
));
522 chars
[nchars
++] = '(';
524 /* js_EnterSharpObject returned a string of the form "#n=" in chars. */
526 nchars
= js_strlen(chars
);
528 js_realloc((ochars
= chars
), (nchars
+ 2 + 1) * sizeof(jschar
));
535 * No need for parentheses around the whole shebang, because #n=
536 * unambiguously begins an object initializer, and never a block
539 outermost
= JS_FALSE
;
543 chars
[nchars
++] = '{';
548 * We have four local roots for cooked and raw value GC safety. Hoist the
549 * "localroot + 2" out of the loop using the val local, which refers to
550 * the raw (unconverted, "uncooked") values.
554 for (jsint i
= 0, length
= ida
->length
; i
< length
; i
++) {
555 /* Get strings for id and value and GC-root them via vp. */
556 jsid id
= ida
->vector
[i
];
558 ok
= obj
->lookupProperty(cx
, id
, &obj2
, &prop
);
563 * Convert id to a value and then to a string. Decide early whether we
564 * prefer get/set or old getter/setter syntax.
566 idstr
= js_ValueToString(cx
, IdToValue(id
));
569 obj2
->dropProperty(cx
, prop
);
572 vp
->setString(idstr
); /* local root */
577 if (obj2
->isNative()) {
578 JSScopeProperty
*sprop
= (JSScopeProperty
*) prop
;
579 unsigned attrs
= sprop
->attributes();
580 if (attrs
& JSPROP_GETTER
) {
582 val
[valcnt
] = sprop
->getterValue();
583 gsop
[valcnt
] = ATOM_TO_STRING(cx
->runtime
->atomState
.getAtom
);
586 if (attrs
& JSPROP_SETTER
) {
588 val
[valcnt
] = sprop
->setterValue();
589 gsop
[valcnt
] = ATOM_TO_STRING(cx
->runtime
->atomState
.setAtom
);
592 JS_UNLOCK_OBJ(cx
, obj2
);
597 ok
= obj
->getProperty(cx
, id
, &val
[0]);
604 * If id is a string that's not an identifier, or if it's a negative
605 * integer, then it must be quoted.
607 bool idIsLexicalIdentifier
= !!js_IsIdentifier(idstr
);
609 ? !idIsLexicalIdentifier
610 : (!JSID_IS_INT(id
) || JSID_TO_INT(id
) < 0)) {
611 idstr
= js_QuoteString(cx
, idstr
, jschar('\''));
616 vp
->setString(idstr
); /* local root */
618 idstr
->getCharsAndLength(idstrchars
, idstrlength
);
620 for (jsint j
= 0; j
< valcnt
; j
++) {
622 * Censor an accessor descriptor getter or setter part if it's
625 if (gsop
[j
] && val
[j
].isUndefined())
628 /* Convert val[j] to its canonical source form. */
629 valstr
= js_ValueToSource(cx
, val
[j
]);
634 localroot
[j
].setString(valstr
); /* local root */
635 valstr
->getCharsAndLength(vchars
, vlength
);
638 * If val[j] is a non-sharp object, and we're not serializing an
639 * accessor (ECMA syntax can't accommodate sharpened accessors),
640 * consider sharpening it.
644 #if JS_HAS_SHARP_VARS
645 if (!gsop
[j
] && val
[j
].isObject() && vchars
[0] != '#') {
646 he
= js_EnterSharpObject(cx
, &val
[j
].toObject(), NULL
, &vsharp
);
653 vlength
= js_strlen(vchars
);
656 vsharplength
= js_strlen(vsharp
);
659 js_LeaveSharpObject(cx
, NULL
);
665 * Remove '(function ' from the beginning of valstr and ')' from the
666 * end so that we can put "get" in front of the function definition.
668 if (gsop
[j
] && IsFunctionObject(val
[j
])) {
669 const jschar
*start
= vchars
;
670 const jschar
*end
= vchars
+ vlength
;
672 uint8 parenChomp
= 0;
673 if (vchars
[0] == '(') {
678 /* Try to jump "function" keyword. */
680 vchars
= js_strchr_limit(vchars
, ' ', end
);
683 * Jump over the function's name: it can't be encoded as part
684 * of an ECMA getter or setter.
687 vchars
= js_strchr_limit(vchars
, '(', end
);
692 vlength
= end
- vchars
- parenChomp
;
699 #define SAFE_ADD(n) \
710 SAFE_ADD(idstrlength
+ 1);
712 SAFE_ADD(gsop
[j
]->length() + 1);
713 SAFE_ADD(vsharplength
);
715 /* Account for the trailing null. */
716 SAFE_ADD((outermost
? 2 : 1) + 1);
719 if (curlen
> size_t(-1) / sizeof(jschar
))
722 /* Allocate 1 + 1 at end for closing brace and terminating 0. */
723 chars
= (jschar
*) js_realloc((ochars
= chars
), curlen
* sizeof(jschar
));
725 /* Save code space on error: let JS_free ignore null vsharp. */
732 chars
[nchars
++] = comma
[0];
733 chars
[nchars
++] = comma
[1];
738 gsoplength
= gsop
[j
]->length();
739 js_strncpy(&chars
[nchars
], gsop
[j
]->chars(),
741 nchars
+= gsoplength
;
742 chars
[nchars
++] = ' ';
744 js_strncpy(&chars
[nchars
], idstrchars
, idstrlength
);
745 nchars
+= idstrlength
;
746 /* Extraneous space after id here will be extracted later */
747 chars
[nchars
++] = gsop
[j
] ? ' ' : ':';
750 js_strncpy(&chars
[nchars
], vsharp
, vsharplength
);
751 nchars
+= vsharplength
;
753 js_strncpy(&chars
[nchars
], vchars
, vlength
);
761 chars
[nchars
++] = '}';
763 chars
[nchars
++] = ')';
767 js_LeaveSharpObject(cx
, &ida
);
776 JS_ReportOutOfMemory(cx
);
781 str
= js_NewString(cx
, chars
, nchars
);
798 #endif /* JS_HAS_TOSOURCE */
803 obj_toStringHelper(JSContext
*cx
, JSObject
*obj
)
806 return JSProxy::obj_toString(cx
, obj
);
808 const char *clazz
= obj
->wrappedObject(cx
)->getClass()->name
;
809 size_t nchars
= 9 + strlen(clazz
); /* 9 for "[object ]" */
810 jschar
*chars
= (jschar
*) cx
->malloc((nchars
+ 1) * sizeof(jschar
));
814 const char *prefix
= "[object ";
816 while ((chars
[nchars
] = (jschar
)*prefix
) != 0)
818 while ((chars
[nchars
] = (jschar
)*clazz
) != 0)
820 chars
[nchars
++] = ']';
823 JSString
*str
= js_NewString(cx
, chars
, nchars
);
832 obj_toString(JSContext
*cx
, uintN argc
, Value
*vp
)
834 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
838 JSString
*str
= js::obj_toStringHelper(cx
, obj
);
847 obj_toLocaleString(JSContext
*cx
, uintN argc
, Value
*vp
)
849 if (!ComputeThisFromVp(cx
, vp
))
852 JSString
*str
= js_ValueToString(cx
, vp
[1]);
861 obj_valueOf(JSContext
*cx
, uintN argc
, Value
*vp
)
863 if (!ComputeThisFromVp(cx
, vp
))
870 * Check if CSP allows new Function() or eval() to run in the current
874 js_CheckContentSecurityPolicy(JSContext
*cx
)
876 JSSecurityCallbacks
*callbacks
= JS_GetSecurityCallbacks(cx
);
878 // if there are callbacks, make sure that the CSP callback is installed and
879 // that it permits eval().
880 if (callbacks
&& callbacks
->contentSecurityPolicyAllows
)
881 return callbacks
->contentSecurityPolicyAllows(cx
);
887 * Check whether principals subsumes scopeobj's principals, and return true
888 * if so (or if scopeobj has no principals, for backward compatibility with
889 * the JS API, which does not require principals), and false otherwise.
892 js_CheckPrincipalsAccess(JSContext
*cx
, JSObject
*scopeobj
,
893 JSPrincipals
*principals
, JSAtom
*caller
)
895 JSSecurityCallbacks
*callbacks
;
896 JSPrincipals
*scopePrincipals
;
897 const char *callerstr
;
899 callbacks
= JS_GetSecurityCallbacks(cx
);
900 if (callbacks
&& callbacks
->findObjectPrincipals
) {
901 scopePrincipals
= callbacks
->findObjectPrincipals(cx
, scopeobj
);
902 if (!principals
|| !scopePrincipals
||
903 !principals
->subsume(principals
, scopePrincipals
)) {
904 callerstr
= js_AtomToPrintableString(cx
, caller
);
907 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
908 JSMSG_BAD_INDIRECT_CALL
, callerstr
);
916 CheckScopeChainValidity(JSContext
*cx
, JSObject
*scopeobj
, const char *caller
)
923 OBJ_TO_INNER_OBJECT(cx
, scopeobj
);
927 /* XXX This is an awful gross hack. */
930 JSObjectOp op
= scopeobj
->getClass()->ext
.innerObject
;
931 if (op
&& op(cx
, scopeobj
) != scopeobj
)
933 scopeobj
= scopeobj
->getParent();
939 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
940 JSMSG_BAD_INDIRECT_CALL
, caller
);
945 js_ComputeFilename(JSContext
*cx
, JSStackFrame
*caller
,
946 JSPrincipals
*principals
, uintN
*linenop
)
950 JSSecurityCallbacks
*callbacks
= JS_GetSecurityCallbacks(cx
);
953 JS_ASSERT(principals
|| !(callbacks
&& callbacks
->findObjectPrincipals
));
954 flags
= JS_GetScriptFilenameFlags(caller
->script
);
955 if ((flags
& JSFILENAME_PROTECTED
) &&
957 strcmp(principals
->codebase
, "[System Principal]")) {
959 return principals
->codebase
;
962 jsbytecode
*pc
= caller
->pc(cx
);
963 if (pc
&& js_GetOpcode(cx
, caller
->script
, pc
) == JSOP_EVAL
) {
964 JS_ASSERT(js_GetOpcode(cx
, caller
->script
, pc
+ JSOP_EVAL_LENGTH
) == JSOP_LINENO
);
965 *linenop
= GET_UINT16(pc
+ JSOP_EVAL_LENGTH
);
967 *linenop
= js_FramePCToLineNumber(cx
, caller
);
969 return caller
->script
->filename
;
972 #ifndef EVAL_CACHE_CHAIN_LIMIT
973 # define EVAL_CACHE_CHAIN_LIMIT 4
976 static inline JSScript
**
977 EvalCacheHash(JSContext
*cx
, JSString
*str
)
983 str
->getCharsAndLength(s
, n
);
986 for (h
= 0; n
; s
++, n
--)
987 h
= JS_ROTATE_LEFT32(h
, 4) ^ *s
;
989 h
*= JS_GOLDEN_RATIO
;
990 h
>>= 32 - JS_EVAL_CACHE_SHIFT
;
991 return &JS_SCRIPTS_TO_GC(cx
)[h
];
995 obj_eval(JSContext
*cx
, uintN argc
, Value
*vp
)
1002 JSStackFrame
*caller
= js_GetScriptedCaller(cx
, NULL
);
1004 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1005 JSMSG_BAD_INDIRECT_CALL
, js_eval_str
);
1009 jsbytecode
*callerPC
= caller
->pc(cx
);
1010 bool indirectCall
= (callerPC
&& *callerPC
!= JSOP_EVAL
);
1013 * This call to JSObject::wrappedObject is safe because of the security
1014 * checks we do below. However, the control flow below is confusing, so we
1015 * double check. There are two cases:
1016 * - Direct call: This object is never used. So unwrapping can't hurt.
1017 * - Indirect call: If this object isn't already the scope chain (which
1018 * we're guaranteed to be allowed to access) then we do a security
1021 Value
*argv
= JS_ARGV(cx
, vp
);
1022 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1025 obj
= obj
->wrappedObject(cx
);
1028 * Ban all indirect uses of eval (global.foo = eval; global.foo(...)) and
1029 * calls that attempt to use a non-global object as the "with" object in
1030 * the former indirect case.
1033 JSObject
*parent
= obj
->getParent();
1034 if (indirectCall
|| parent
) {
1035 uintN flags
= parent
1037 : JSREPORT_STRICT
| JSREPORT_WARNING
;
1038 if (!JS_ReportErrorFlagsAndNumber(cx
, flags
, js_GetErrorMessage
, NULL
,
1039 JSMSG_BAD_INDIRECT_CALL
,
1046 if (!argv
[0].isString()) {
1052 * We once supported a second argument to eval to use as the scope chain
1053 * when evaluating the code string. Warn when such uses are seen so that
1054 * authors will know that support for eval(s, o) has been removed.
1056 if (argc
> 1 && !caller
->script
->warnedAboutTwoArgumentEval
) {
1057 static const char TWO_ARGUMENT_WARNING
[] =
1058 "Support for eval(code, scopeObject) has been removed. "
1059 "Use |with (scopeObject) eval(code);| instead.";
1060 if (!JS_ReportWarning(cx
, TWO_ARGUMENT_WARNING
))
1062 caller
->script
->warnedAboutTwoArgumentEval
= true;
1065 /* From here on, control must exit through label out with ok set. */
1066 MUST_FLOW_THROUGH("out");
1067 uintN staticLevel
= caller
->script
->staticLevel
+ 1;
1070 * Bring fp->scopeChain up to date. We're either going to use
1071 * it (direct call) or save it and restore it (indirect call).
1073 JSObject
*callerScopeChain
= js_GetScopeChain(cx
, caller
);
1074 if (!callerScopeChain
)
1077 JSObject
*scopeobj
= NULL
;
1079 #if JS_HAS_EVAL_THIS_SCOPE
1081 * If we see an indirect call, then run eval in the global scope. We do
1082 * this so the compiler can make assumptions about what bindings may or
1083 * may not exist in the current frame if it doesn't see 'eval'.
1086 /* Pretend that we're top level. */
1089 OBJ_TO_INNER_OBJECT(cx
, obj
);
1093 if (!js_CheckPrincipalsAccess(cx
, obj
,
1094 JS_StackFramePrincipals(cx
, caller
),
1095 cx
->runtime
->atomState
.evalAtom
)) {
1099 /* NB: We know inner is a global object here. */
1100 JS_ASSERT(!obj
->getParent());
1104 * Compile using the caller's current scope object.
1106 * NB: This means that the C API must not be used to call eval.
1108 JS_ASSERT_IF(caller
->argv
, caller
->hasCallObj());
1109 scopeobj
= callerScopeChain
;
1113 /* Ensure we compile this eval with the right object in the scope chain. */
1114 JSObject
*result
= CheckScopeChainValidity(cx
, scopeobj
, js_eval_str
);
1115 JS_ASSERT_IF(result
, result
== scopeobj
);
1119 // CSP check: is eval() allowed at all?
1120 // report errors via CSP is done in the script security mgr.
1121 if (!js_CheckContentSecurityPolicy(cx
)) {
1122 JS_ReportError(cx
, "call to eval() blocked by CSP");
1126 JSObject
*callee
= &vp
[0].toObject();
1127 JSPrincipals
*principals
= js_EvalFramePrincipals(cx
, callee
, caller
);
1129 const char *file
= js_ComputeFilename(cx
, caller
, principals
, &line
);
1131 JSString
*str
= argv
[0].toString();
1132 JSScript
*script
= NULL
;
1135 * Cache local eval scripts indexed by source qualified by scope.
1137 * An eval cache entry should never be considered a hit unless its
1138 * strictness matches that of the new eval code. The existing code takes
1139 * care of this, because hits are qualified by the function from which
1140 * eval was called, whose strictness doesn't change. Scripts produced by
1141 * calls to eval from global code are not cached.
1143 JSScript
**bucket
= EvalCacheHash(cx
, str
);
1144 if (!indirectCall
&& caller
->fun
) {
1146 JSScript
**scriptp
= bucket
;
1148 EVAL_CACHE_METER(probe
);
1149 while ((script
= *scriptp
) != NULL
) {
1150 if (script
->savedCallerFun
&&
1151 script
->staticLevel
== staticLevel
&&
1152 script
->version
== cx
->version
&&
1153 (script
->principals
== principals
||
1154 (principals
->subsume(principals
, script
->principals
) &&
1155 script
->principals
->subsume(script
->principals
, principals
)))) {
1157 * Get the prior (cache-filling) eval's saved caller function.
1158 * See Compiler::compileScript in jsparse.cpp.
1160 JSFunction
*fun
= script
->getFunction(0);
1162 if (fun
== caller
->fun
) {
1164 * Get the source string passed for safekeeping in the
1165 * atom map by the prior eval to Compiler::compileScript.
1167 JSString
*src
= ATOM_TO_STRING(script
->atomMap
.vector
[0]);
1169 if (src
== str
|| js_EqualStrings(src
, str
)) {
1171 * Source matches, qualify by comparing scopeobj to the
1172 * COMPILE_N_GO-memoized parent of the first literal
1173 * function or regexp object if any. If none, then this
1174 * script has no compiled-in dependencies on the prior
1177 JSObjectArray
*objarray
= script
->objects();
1180 if (objarray
->length
== 1) {
1181 if (script
->regexpsOffset
!= 0) {
1182 objarray
= script
->regexps();
1185 EVAL_CACHE_METER(noscope
);
1190 objarray
->vector
[i
]->getParent() == scopeobj
) {
1191 JS_ASSERT(staticLevel
== script
->staticLevel
);
1192 EVAL_CACHE_METER(hit
);
1193 *scriptp
= script
->u
.nextToGC
;
1194 script
->u
.nextToGC
= NULL
;
1201 if (++count
== EVAL_CACHE_CHAIN_LIMIT
) {
1205 EVAL_CACHE_METER(step
);
1206 scriptp
= &script
->u
.nextToGC
;
1211 * We can't have a callerFrame (down in js_Execute's terms) if we're in
1212 * global code. This includes indirect eval and direct eval called with a
1213 * scope object parameter.
1215 JSStackFrame
*callerFrame
= (staticLevel
!= 0) ? caller
: NULL
;
1217 script
= Compiler::compileScript(cx
, scopeobj
, callerFrame
,
1219 TCF_COMPILE_N_GO
| TCF_NEED_MUTABLE_SCRIPT
,
1220 str
->chars(), str
->length(),
1221 NULL
, file
, line
, str
, staticLevel
);
1227 * Belt-and-braces: check that the lesser of eval's principals and the
1228 * caller's principals has access to scopeobj.
1230 JSBool ok
= js_CheckPrincipalsAccess(cx
, scopeobj
, principals
,
1231 cx
->runtime
->atomState
.evalAtom
) &&
1232 Execute(cx
, scopeobj
, script
, callerFrame
, JSFRAME_EVAL
, vp
);
1234 script
->u
.nextToGC
= *bucket
;
1236 #ifdef CHECK_SCRIPT_OWNER
1237 script
->owner
= NULL
;
1243 #if JS_HAS_OBJ_WATCHPOINT
1246 obj_watch_handler(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval old
,
1247 jsval
*nvp
, void *closure
)
1250 JSSecurityCallbacks
*callbacks
;
1251 JSStackFrame
*caller
;
1252 JSPrincipals
*subject
, *watcher
;
1254 JSResolvingEntry
*entry
;
1259 callable
= (JSObject
*) closure
;
1261 callbacks
= JS_GetSecurityCallbacks(cx
);
1262 if (callbacks
&& callbacks
->findObjectPrincipals
) {
1263 /* Skip over any obj_watch_* frames between us and the real subject. */
1264 caller
= js_GetScriptedCaller(cx
, NULL
);
1267 * Only call the watch handler if the watcher is allowed to watch
1268 * the currently executing script.
1270 watcher
= callbacks
->findObjectPrincipals(cx
, callable
);
1271 subject
= JS_StackFramePrincipals(cx
, caller
);
1273 if (watcher
&& subject
&& !watcher
->subsume(watcher
, subject
)) {
1274 /* Silently don't call the watch handler. */
1280 /* Avoid recursion on (obj, id) already being watched on cx. */
1283 if (!js_StartResolving(cx
, &key
, JSRESFLAG_WATCH
, &entry
))
1287 generation
= cx
->resolvingTable
->generation
;
1289 argv
[0] = IdToValue(id
);
1290 argv
[1] = Valueify(old
);
1291 argv
[2] = Valueify(*nvp
);
1292 ok
= InternalCall(cx
, obj
, ObjectOrNullValue(callable
), 3, argv
, Valueify(nvp
));
1293 js_StopResolving(cx
, &key
, JSRESFLAG_WATCH
, entry
, generation
);
1298 obj_watch(JSContext
*cx
, uintN argc
, Value
*vp
)
1301 js_ReportMissingArg(cx
, *vp
, 1);
1305 JSObject
*callable
= js_ValueToCallableObject(cx
, &vp
[3], 0);
1309 /* Compute the unique int/atom symbol id needed by js_LookupProperty. */
1311 if (!ValueToId(cx
, vp
[2], &propid
))
1314 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1317 if (!obj
|| !CheckAccess(cx
, obj
, propid
, JSACC_WATCH
, &tmp
, &attrs
))
1322 if (attrs
& JSPROP_READONLY
)
1324 if (obj
->isDenseArray() && !obj
->makeDenseArraySlow(cx
))
1326 return JS_SetWatchPoint(cx
, obj
, propid
, obj_watch_handler
, callable
);
1330 obj_unwatch(JSContext
*cx
, uintN argc
, Value
*vp
)
1332 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1338 if (!ValueToId(cx
, vp
[2], &id
))
1343 return JS_ClearWatchPoint(cx
, obj
, id
, NULL
, NULL
);
1346 #endif /* JS_HAS_OBJ_WATCHPOINT */
1349 * Prototype and property query methods, to complement the 'in' and
1350 * 'instanceof' operators.
1353 /* Proposed ECMA 15.2.4.5. */
1355 obj_hasOwnProperty(JSContext
*cx
, uintN argc
, Value
*vp
)
1357 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1359 js_HasOwnPropertyHelper(cx
, obj
->getOps()->lookupProperty
, argc
, vp
);
1363 js_HasOwnPropertyHelper(JSContext
*cx
, JSLookupPropOp lookup
, uintN argc
,
1367 if (!ValueToId(cx
, argc
!= 0 ? vp
[2] : UndefinedValue(), &id
))
1370 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1375 if (obj
->isProxy()) {
1377 if (!JSProxy::hasOwn(cx
, obj
, id
, &has
))
1379 vp
->setBoolean(has
);
1382 if (!js_HasOwnProperty(cx
, lookup
, obj
, id
, &obj2
, &prop
))
1385 vp
->setBoolean(true);
1386 obj2
->dropProperty(cx
, prop
);
1388 vp
->setBoolean(false);
1394 js_HasOwnProperty(JSContext
*cx
, JSLookupPropOp lookup
, JSObject
*obj
, jsid id
,
1395 JSObject
**objp
, JSProperty
**propp
)
1397 JSAutoResolveFlags
rf(cx
, JSRESOLVE_QUALIFIED
| JSRESOLVE_DETECTING
);
1398 if (!(lookup
? lookup
: js_LookupProperty
)(cx
, obj
, id
, objp
, propp
))
1406 Class
*clasp
= (*objp
)->getClass();
1407 JSObject
*outer
= NULL
;
1408 if (JSObjectOp op
= (*objp
)->getClass()->ext
.outerObject
) {
1409 outer
= op(cx
, *objp
);
1414 if (outer
!= *objp
) {
1415 if ((*objp
)->isNative() && obj
->getClass() == clasp
) {
1417 * The combination of JSPROP_SHARED and JSPROP_PERMANENT in a
1418 * delegated property makes that property appear to be direct in
1419 * all delegating instances of the same native class. This hack
1420 * avoids bloating every function instance with its own 'length'
1421 * (AKA 'arity') property. But it must not extend across class
1422 * boundaries, to avoid making hasOwnProperty lie (bug 320854).
1424 * It's not really a hack, of course: a permanent property can't
1425 * be deleted, and JSPROP_SHARED means "don't allocate a slot in
1426 * any instance, prototype or delegating". Without a slot, and
1427 * without the ability to remove and recreate (with differences)
1428 * the property, there is no way to tell whether it is directly
1429 * owned, or indirectly delegated.
1431 JSScopeProperty
*sprop
= reinterpret_cast<JSScopeProperty
*>(*propp
);
1432 if (sprop
->isSharedPermanent())
1436 (*objp
)->dropProperty(cx
, *propp
);
1442 /* Proposed ECMA 15.2.4.6. */
1444 obj_isPrototypeOf(JSContext
*cx
, uintN argc
, Value
*vp
)
1446 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1449 const Value
&v
= argc
!= 0 ? vp
[2] : UndefinedValue();
1450 vp
->setBoolean(js_IsDelegate(cx
, obj
, v
));
1454 /* Proposed ECMA 15.2.4.7. */
1456 obj_propertyIsEnumerable(JSContext
*cx
, uintN argc
, Value
*vp
)
1459 if (!ValueToId(cx
, argc
!= 0 ? vp
[2] : UndefinedValue(), &id
))
1462 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1463 return obj
&& js_PropertyIsEnumerable(cx
, obj
, id
, vp
);
1467 js_PropertyIsEnumerable(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
1471 if (!obj
->lookupProperty(cx
, id
, &pobj
, &prop
))
1475 vp
->setBoolean(false);
1480 * XXX ECMA spec error compatible: return false unless hasOwnProperty.
1481 * The ECMA spec really should be fixed so propertyIsEnumerable and the
1482 * for..in loop agree on whether prototype properties are enumerable,
1483 * obviously by fixing this method (not by breaking the for..in loop!).
1485 * We check here for shared permanent prototype properties, which should
1486 * be treated as if they are local to obj. They are an implementation
1487 * technique used to satisfy ECMA requirements; users should not be able
1488 * to distinguish a shared permanent proto-property from a local one.
1492 if (pobj
->isNative()) {
1493 JSScopeProperty
*sprop
= (JSScopeProperty
*) prop
;
1494 shared
= sprop
->isSharedPermanent();
1495 attrs
= sprop
->attributes();
1496 JS_UNLOCK_OBJ(cx
, pobj
);
1499 if (!pobj
->getAttributes(cx
, id
, &attrs
))
1502 if (pobj
!= obj
&& !shared
) {
1503 vp
->setBoolean(false);
1506 vp
->setBoolean((attrs
& JSPROP_ENUMERATE
) != 0);
1510 #if OLD_GETTER_SETTER_METHODS
1512 const char js_defineGetter_str
[] = "__defineGetter__";
1513 const char js_defineSetter_str
[] = "__defineSetter__";
1514 const char js_lookupGetter_str
[] = "__lookupGetter__";
1515 const char js_lookupSetter_str
[] = "__lookupSetter__";
1517 JS_FRIEND_API(JSBool
)
1518 js_obj_defineGetter(JSContext
*cx
, uintN argc
, Value
*vp
)
1520 if (argc
<= 1 || !js_IsCallable(vp
[3])) {
1521 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1522 JSMSG_BAD_GETTER_OR_SETTER
,
1526 PropertyOp getter
= CastAsPropertyOp(&vp
[3].toObject());
1529 if (!ValueToId(cx
, vp
[2], &id
))
1531 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1532 if (!obj
|| !CheckRedeclaration(cx
, obj
, id
, JSPROP_GETTER
, NULL
, NULL
))
1535 * Getters and setters are just like watchpoints from an access
1536 * control point of view.
1540 if (!CheckAccess(cx
, obj
, id
, JSACC_WATCH
, &junk
, &attrs
))
1543 return obj
->defineProperty(cx
, id
, UndefinedValue(), getter
, PropertyStub
,
1544 JSPROP_ENUMERATE
| JSPROP_GETTER
| JSPROP_SHARED
);
1547 JS_FRIEND_API(JSBool
)
1548 js_obj_defineSetter(JSContext
*cx
, uintN argc
, Value
*vp
)
1550 if (argc
<= 1 || !js_IsCallable(vp
[3])) {
1551 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1552 JSMSG_BAD_GETTER_OR_SETTER
,
1556 PropertyOp setter
= CastAsPropertyOp(&vp
[3].toObject());
1559 if (!ValueToId(cx
, vp
[2], &id
))
1561 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1562 if (!obj
|| !CheckRedeclaration(cx
, obj
, id
, JSPROP_SETTER
, NULL
, NULL
))
1565 * Getters and setters are just like watchpoints from an access
1566 * control point of view.
1570 if (!CheckAccess(cx
, obj
, id
, JSACC_WATCH
, &junk
, &attrs
))
1573 return obj
->defineProperty(cx
, id
, UndefinedValue(), PropertyStub
, setter
,
1574 JSPROP_ENUMERATE
| JSPROP_SETTER
| JSPROP_SHARED
);
1578 obj_lookupGetter(JSContext
*cx
, uintN argc
, Value
*vp
)
1581 if (!ValueToId(cx
, argc
!= 0 ? vp
[2] : UndefinedValue(), &id
))
1583 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1586 if (!obj
|| !obj
->lookupProperty(cx
, id
, &pobj
, &prop
))
1590 if (pobj
->isNative()) {
1591 JSScopeProperty
*sprop
= (JSScopeProperty
*) prop
;
1592 if (sprop
->hasGetterValue())
1593 *vp
= sprop
->getterValue();
1594 JS_UNLOCK_OBJ(cx
, pobj
);
1601 obj_lookupSetter(JSContext
*cx
, uintN argc
, Value
*vp
)
1604 if (!ValueToId(cx
, argc
!= 0 ? vp
[2] : UndefinedValue(), &id
))
1606 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1609 if (!obj
|| !obj
->lookupProperty(cx
, id
, &pobj
, &prop
))
1613 if (pobj
->isNative()) {
1614 JSScopeProperty
*sprop
= (JSScopeProperty
*) prop
;
1615 if (sprop
->hasSetterValue())
1616 *vp
= sprop
->setterValue();
1617 JS_UNLOCK_OBJ(cx
, pobj
);
1622 #endif /* OLD_GETTER_SETTER_METHODS */
1625 obj_getPrototypeOf(JSContext
*cx
, uintN argc
, Value
*vp
)
1628 js_ReportMissingArg(cx
, *vp
, 0);
1632 if (vp
[2].isPrimitive()) {
1633 char *bytes
= DecompileValueGenerator(cx
, JSDVG_SEARCH_STACK
, vp
[2], NULL
);
1636 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1637 JSMSG_UNEXPECTED_TYPE
, bytes
, "not an object");
1642 JSObject
*obj
= &vp
[2].toObject();
1644 return CheckAccess(cx
, obj
, ATOM_TO_JSID(cx
->runtime
->atomState
.protoAtom
),
1645 JSACC_PROTO
, vp
, &attrs
);
1649 js_NewPropertyDescriptorObject(JSContext
*cx
, jsid id
, uintN attrs
,
1650 const Value
&getter
, const Value
&setter
,
1651 const Value
&value
, Value
*vp
)
1653 /* We have our own property, so start creating the descriptor. */
1654 JSObject
*desc
= NewBuiltinClassInstance(cx
, &js_ObjectClass
);
1657 vp
->setObject(*desc
); /* Root and return. */
1659 const JSAtomState
&atomState
= cx
->runtime
->atomState
;
1660 if (attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)) {
1661 if (!desc
->defineProperty(cx
, ATOM_TO_JSID(atomState
.getAtom
), getter
,
1662 PropertyStub
, PropertyStub
, JSPROP_ENUMERATE
) ||
1663 !desc
->defineProperty(cx
, ATOM_TO_JSID(atomState
.setAtom
), setter
,
1664 PropertyStub
, PropertyStub
, JSPROP_ENUMERATE
)) {
1668 if (!desc
->defineProperty(cx
, ATOM_TO_JSID(atomState
.valueAtom
), value
,
1669 PropertyStub
, PropertyStub
, JSPROP_ENUMERATE
) ||
1670 !desc
->defineProperty(cx
, ATOM_TO_JSID(atomState
.writableAtom
),
1671 BooleanValue((attrs
& JSPROP_READONLY
) == 0),
1672 PropertyStub
, PropertyStub
, JSPROP_ENUMERATE
)) {
1677 return desc
->defineProperty(cx
, ATOM_TO_JSID(atomState
.enumerableAtom
),
1678 BooleanValue((attrs
& JSPROP_ENUMERATE
) != 0),
1679 PropertyStub
, PropertyStub
, JSPROP_ENUMERATE
) &&
1680 desc
->defineProperty(cx
, ATOM_TO_JSID(atomState
.configurableAtom
),
1681 BooleanValue((attrs
& JSPROP_PERMANENT
) == 0),
1682 PropertyStub
, PropertyStub
, JSPROP_ENUMERATE
);
1686 js_GetOwnPropertyDescriptor(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
1688 if (obj
->isProxy()) {
1689 if (!JSProxy::getOwnPropertyDescriptor(cx
, obj
, id
, vp
))
1695 if (!js_HasOwnProperty(cx
, obj
->getOps()->lookupProperty
, obj
, id
, &pobj
, &prop
))
1702 Value roots
[] = { UndefinedValue(), UndefinedValue(), UndefinedValue() };
1703 AutoArrayRooter
tvr(cx
, JS_ARRAY_LENGTH(roots
), roots
);
1706 if (pobj
->isNative()) {
1707 JSScopeProperty
*sprop
= (JSScopeProperty
*) prop
;
1708 attrs
= sprop
->attributes();
1709 if (attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)) {
1711 if (attrs
& JSPROP_GETTER
)
1712 roots
[0] = sprop
->getterValue();
1713 if (attrs
& JSPROP_SETTER
)
1714 roots
[1] = sprop
->setterValue();
1716 JS_UNLOCK_OBJ(cx
, pobj
);
1717 } else if (!pobj
->getAttributes(cx
, id
, &attrs
)) {
1721 if (doGet
&& !obj
->getProperty(cx
, id
, &roots
[2]))
1724 return js_NewPropertyDescriptorObject(cx
, id
,
1726 roots
[0], /* getter */
1727 roots
[1], /* setter */
1728 roots
[2], /* value */
1733 GetFirstArgumentAsObject(JSContext
*cx
, uintN argc
, Value
*vp
, const char *method
, JSObject
**objp
)
1736 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_MORE_ARGS_NEEDED
,
1741 const Value
&v
= vp
[2];
1742 if (v
.isPrimitive()) {
1743 char *bytes
= DecompileValueGenerator(cx
, JSDVG_SEARCH_STACK
, v
, NULL
);
1746 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_UNEXPECTED_TYPE
,
1747 bytes
, "not an object");
1752 *objp
= &v
.toObject();
1757 obj_getOwnPropertyDescriptor(JSContext
*cx
, uintN argc
, Value
*vp
)
1760 if (!GetFirstArgumentAsObject(cx
, argc
, vp
, "Object.getOwnPropertyDescriptor", &obj
))
1762 AutoIdRooter
nameidr(cx
);
1763 if (!ValueToId(cx
, argc
>= 2 ? vp
[3] : UndefinedValue(), nameidr
.addr()))
1765 return js_GetOwnPropertyDescriptor(cx
, obj
, nameidr
.id(), vp
);
1769 obj_keys(JSContext
*cx
, uintN argc
, Value
*vp
)
1772 if (!GetFirstArgumentAsObject(cx
, argc
, vp
, "Object.keys", &obj
))
1775 AutoIdVector
props(cx
);
1776 if (!GetPropertyNames(cx
, obj
, JSITER_OWNONLY
, props
))
1779 AutoValueVector
vals(cx
);
1780 vals
.resize(props
.length());
1781 for (size_t i
= 0, len
= props
.length(); i
< len
; i
++) {
1783 if (JSID_IS_STRING(id
)) {
1784 vals
[i
].setString(JSID_TO_STRING(id
));
1786 JS_ASSERT(JSID_IS_INT(id
));
1787 JSString
*str
= js_IntToString(cx
, JSID_TO_INT(id
));
1790 vals
[i
].setString(str
);
1794 JS_ASSERT(props
.length() <= UINT32_MAX
);
1795 JSObject
*aobj
= js_NewArrayObject(cx
, jsuint(vals
.length()), vals
.begin());
1798 vp
->setObject(*aobj
);
1804 HasProperty(JSContext
* cx
, JSObject
* obj
, jsid id
, Value
* vp
, JSBool
* answerp
)
1806 if (!JS_HasPropertyById(cx
, obj
, id
, answerp
))
1812 return JS_GetPropertyById(cx
, obj
, id
, Jsvalify(vp
));
1815 PropDesc::PropDesc()
1816 : pd(UndefinedValue()),
1818 value(UndefinedValue()),
1819 get(UndefinedValue()),
1820 set(UndefinedValue()),
1826 hasEnumerable(false),
1827 hasConfigurable(false)
1832 PropDesc::initialize(JSContext
* cx
, jsid id
, const Value
&origval
)
1838 if (v
.isPrimitive()) {
1839 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_NOT_NONNULL_OBJECT
);
1842 JSObject
* desc
= &v
.toObject();
1844 /* Make a copy of the descriptor. We might need it later. */
1847 /* Start with the proper defaults. */
1848 attrs
= JSPROP_PERMANENT
| JSPROP_READONLY
;
1853 if (!HasProperty(cx
, desc
, ATOM_TO_JSID(cx
->runtime
->atomState
.enumerableAtom
), &v
,
1858 hasEnumerable
= JS_TRUE
;
1859 if (js_ValueToBoolean(v
))
1860 attrs
|= JSPROP_ENUMERATE
;
1864 if (!HasProperty(cx
, desc
, ATOM_TO_JSID(cx
->runtime
->atomState
.configurableAtom
), &v
,
1869 hasConfigurable
= JS_TRUE
;
1870 if (js_ValueToBoolean(v
))
1871 attrs
&= ~JSPROP_PERMANENT
;
1875 if (!HasProperty(cx
, desc
, ATOM_TO_JSID(cx
->runtime
->atomState
.valueAtom
), &v
, &hasProperty
))
1883 if (!HasProperty(cx
, desc
, ATOM_TO_JSID(cx
->runtime
->atomState
.writableAtom
), &v
, &hasProperty
))
1886 hasWritable
= JS_TRUE
;
1887 if (js_ValueToBoolean(v
))
1888 attrs
&= ~JSPROP_READONLY
;
1892 if (!HasProperty(cx
, desc
, ATOM_TO_JSID(cx
->runtime
->atomState
.getAtom
), &v
, &hasProperty
))
1895 if ((v
.isPrimitive() || !js_IsCallable(v
)) && !v
.isUndefined()) {
1896 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_BAD_GET_SET_FIELD
,
1902 attrs
|= JSPROP_GETTER
| JSPROP_SHARED
;
1906 if (!HasProperty(cx
, desc
, ATOM_TO_JSID(cx
->runtime
->atomState
.setAtom
), &v
, &hasProperty
))
1909 if ((v
.isPrimitive() || !js_IsCallable(v
)) && !v
.isUndefined()) {
1910 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_BAD_GET_SET_FIELD
,
1916 attrs
|= JSPROP_SETTER
| JSPROP_SHARED
;
1920 if ((hasGet
|| hasSet
) && (hasValue
|| hasWritable
)) {
1921 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_INVALID_DESCRIPTOR
);
1929 Reject(JSContext
*cx
, uintN errorNumber
, bool throwError
, jsid id
, bool *rval
)
1933 if (!js_ValueToStringId(cx
, IdToValue(id
), &idstr
))
1935 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, errorNumber
,
1936 JS_GetStringBytes(JSID_TO_STRING(idstr
)));
1945 Reject(JSContext
*cx
, uintN errorNumber
, bool throwError
, bool *rval
)
1948 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, errorNumber
);
1957 Reject(JSContext
*cx
, JSObject
*obj
, JSProperty
*prop
, uintN errorNumber
, bool throwError
,
1958 jsid id
, bool *rval
)
1960 obj
->dropProperty(cx
, prop
);
1961 return Reject(cx
, errorNumber
, throwError
, id
, rval
);
1965 DefinePropertyOnObject(JSContext
*cx
, JSObject
*obj
, const PropDesc
&desc
,
1966 bool throwError
, bool *rval
)
1968 /* 8.12.9 step 1. */
1969 JSProperty
*current
;
1971 JS_ASSERT(!obj
->getOps()->lookupProperty
);
1972 if (!js_HasOwnProperty(cx
, NULL
, obj
, desc
.id
, &obj2
, ¤t
))
1975 JS_ASSERT(!obj
->getOps()->defineProperty
);
1977 /* 8.12.9 steps 2-4. */
1978 JSScope
*scope
= obj
->scope();
1980 if (scope
->sealed())
1981 return Reject(cx
, JSMSG_OBJECT_NOT_EXTENSIBLE
, throwError
, rval
);
1985 if (desc
.isGenericDescriptor() || desc
.isDataDescriptor()) {
1986 JS_ASSERT(!obj
->getOps()->defineProperty
);
1987 return js_DefineProperty(cx
, obj
, desc
.id
, &desc
.value
,
1988 PropertyStub
, PropertyStub
, desc
.attrs
);
1991 JS_ASSERT(desc
.isAccessorDescriptor());
1994 * Getters and setters are just like watchpoints from an access
1995 * control point of view.
1999 if (!CheckAccess(cx
, obj
, desc
.id
, JSACC_WATCH
, &dummy
, &dummyAttrs
))
2002 Value tmp
= UndefinedValue();
2003 return js_DefineProperty(cx
, obj
, desc
.id
, &tmp
,
2004 desc
.getter(), desc
.setter(), desc
.attrs
);
2007 /* 8.12.9 steps 5-6 (note 5 is merely a special case of 6). */
2008 Value v
= UndefinedValue();
2011 * In the special case of shared permanent properties, the "own" property
2012 * can be found on a different object. In that case the returned property
2013 * might not be native, except: the shared permanent property optimization
2014 * is not applied if the objects have different classes (bug 320854), as
2015 * must be enforced by js_HasOwnProperty for the JSScopeProperty cast below
2018 JS_ASSERT(obj
->getClass() == obj2
->getClass());
2020 JSScopeProperty
*sprop
= reinterpret_cast<JSScopeProperty
*>(current
);
2022 if (desc
.isAccessorDescriptor()) {
2023 if (!sprop
->isAccessorDescriptor())
2027 !SameValue(desc
.getterValue(), sprop
->getterOrUndefined(), cx
)) {
2032 !SameValue(desc
.setterValue(), sprop
->setterOrUndefined(), cx
)) {
2037 * Determine the current value of the property once, if the current
2038 * value might actually need to be used or preserved later. NB: we
2039 * guard on whether the current property is a data descriptor to
2040 * avoid calling a getter; we won't need the value if it's not a
2043 if (sprop
->isDataDescriptor()) {
2045 * Non-standard: if the property is non-configurable and is
2046 * represented by a native getter or setter, don't permit
2047 * redefinition. We expose properties with native getter/setter
2048 * as though they were data properties, for the most part, but
2049 * in this particular case we must worry about integrity
2050 * concerns for JSAPI users who expected that
2051 * permanent+getter/setter means precisely controlled behavior.
2052 * If we permitted such redefinitions, such a property could be
2053 * "fixed" to some specific previous value, no longer varying
2054 * according to the intent of the native getter/setter for the
2057 * Other engines expose properties of this nature using ECMA
2058 * getter/setter pairs, but we can't because we use them even
2059 * for properties which ECMA specifies as being true data
2060 * descriptors ([].length, Function.length, /regex/.lastIndex,
2061 * &c.). Longer-term perhaps we should convert such properties
2062 * to use data descriptors (at which point representing a
2063 * descriptor with native getter/setter as an accessor
2064 * descriptor would be fine) and take a small memory hit, but
2065 * for now we'll simply forbid their redefinition.
2067 if (!sprop
->configurable() &&
2068 (!sprop
->hasDefaultGetter() || !sprop
->hasDefaultSetter())) {
2069 return Reject(cx
, obj2
, current
, JSMSG_CANT_REDEFINE_UNCONFIGURABLE_PROP
,
2070 throwError
, desc
.id
, rval
);
2073 if (!js_NativeGet(cx
, obj
, obj2
, sprop
, JSGET_NO_METHOD_BARRIER
, &v
)) {
2074 /* current was dropped when the failure occurred. */
2079 if (desc
.isDataDescriptor()) {
2080 if (!sprop
->isDataDescriptor())
2083 if (desc
.hasValue
&& !SameValue(desc
.value
, v
, cx
))
2085 if (desc
.hasWritable
&& desc
.writable() != sprop
->writable())
2088 /* The only fields in desc will be handled below. */
2089 JS_ASSERT(desc
.isGenericDescriptor());
2093 if (desc
.hasConfigurable
&& desc
.configurable() != sprop
->configurable())
2095 if (desc
.hasEnumerable
&& desc
.enumerable() != sprop
->enumerable())
2098 /* The conditions imposed by step 5 or step 6 apply. */
2099 obj2
->dropProperty(cx
, current
);
2104 /* 8.12.9 step 7. */
2105 if (!sprop
->configurable()) {
2107 * Since [[Configurable]] defaults to false, we don't need to check
2108 * whether it was specified. We can't do likewise for [[Enumerable]]
2109 * because its putative value is used in a comparison -- a comparison
2110 * whose result must always be false per spec if the [[Enumerable]]
2111 * field is not present. Perfectly pellucid logic, eh?
2113 JS_ASSERT_IF(!desc
.hasConfigurable
, !desc
.configurable());
2114 if (desc
.configurable() ||
2115 (desc
.hasEnumerable
&& desc
.enumerable() != sprop
->enumerable())) {
2116 return Reject(cx
, obj2
, current
, JSMSG_CANT_REDEFINE_UNCONFIGURABLE_PROP
, throwError
,
2121 if (desc
.isGenericDescriptor()) {
2122 /* 8.12.9 step 8, no validation required */
2123 } else if (desc
.isDataDescriptor() != sprop
->isDataDescriptor()) {
2124 /* 8.12.9 step 9. */
2125 if (!sprop
->configurable()) {
2126 return Reject(cx
, obj2
, current
, JSMSG_CANT_REDEFINE_UNCONFIGURABLE_PROP
,
2127 throwError
, desc
.id
, rval
);
2129 } else if (desc
.isDataDescriptor()) {
2130 /* 8.12.9 step 10. */
2131 JS_ASSERT(sprop
->isDataDescriptor());
2132 if (!sprop
->configurable() && !sprop
->writable()) {
2133 if ((desc
.hasWritable
&& desc
.writable()) ||
2134 (desc
.hasValue
&& !SameValue(desc
.value
, v
, cx
))) {
2135 return Reject(cx
, obj2
, current
, JSMSG_CANT_REDEFINE_UNCONFIGURABLE_PROP
,
2136 throwError
, desc
.id
, rval
);
2140 /* 8.12.9 step 11. */
2141 JS_ASSERT(desc
.isAccessorDescriptor() && sprop
->isAccessorDescriptor());
2142 if (!sprop
->configurable()) {
2144 !SameValue(desc
.setterValue(), sprop
->setterOrUndefined(), cx
)) ||
2146 !SameValue(desc
.getterValue(), sprop
->getterOrUndefined(), cx
))) {
2147 return Reject(cx
, obj2
, current
, JSMSG_CANT_REDEFINE_UNCONFIGURABLE_PROP
,
2148 throwError
, desc
.id
, rval
);
2153 /* 8.12.9 step 12. */
2155 PropertyOp getter
, setter
;
2156 if (desc
.isGenericDescriptor()) {
2158 if (desc
.hasConfigurable
)
2159 changed
|= JSPROP_PERMANENT
;
2160 if (desc
.hasEnumerable
)
2161 changed
|= JSPROP_ENUMERATE
;
2163 attrs
= (sprop
->attributes() & ~changed
) | (desc
.attrs
& changed
);
2164 if (sprop
->isMethod()) {
2165 JS_ASSERT(!(attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)));
2166 getter
= setter
= PropertyStub
;
2168 getter
= sprop
->getter();
2169 setter
= sprop
->setter();
2171 } else if (desc
.isDataDescriptor()) {
2172 uintN unchanged
= 0;
2173 if (!desc
.hasConfigurable
)
2174 unchanged
|= JSPROP_PERMANENT
;
2175 if (!desc
.hasEnumerable
)
2176 unchanged
|= JSPROP_ENUMERATE
;
2177 if (!desc
.hasWritable
)
2178 unchanged
|= JSPROP_READONLY
;
2182 attrs
= (desc
.attrs
& ~unchanged
) | (sprop
->attributes() & unchanged
);
2183 getter
= setter
= PropertyStub
;
2185 JS_ASSERT(desc
.isAccessorDescriptor());
2188 * Getters and setters are just like watchpoints from an access
2189 * control point of view.
2192 if (!CheckAccess(cx
, obj2
, desc
.id
, JSACC_WATCH
, &dummy
, &attrs
)) {
2193 obj2
->dropProperty(cx
, current
);
2197 JS_ASSERT_IF(sprop
->isMethod(), !(attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)));
2199 /* 8.12.9 step 12. */
2201 if (desc
.hasConfigurable
)
2202 changed
|= JSPROP_PERMANENT
;
2203 if (desc
.hasEnumerable
)
2204 changed
|= JSPROP_ENUMERATE
;
2206 changed
|= JSPROP_GETTER
| JSPROP_SHARED
;
2208 changed
|= JSPROP_SETTER
| JSPROP_SHARED
;
2210 attrs
= (desc
.attrs
& changed
) | (sprop
->attributes() & ~changed
);
2212 getter
= desc
.getter();
2214 getter
= (sprop
->isMethod() || (sprop
->hasDefaultGetter() && !sprop
->hasGetterValue()))
2219 setter
= desc
.setter();
2221 setter
= (sprop
->hasDefaultSetter() && !sprop
->hasSetterValue())
2228 obj2
->dropProperty(cx
, current
);
2229 return js_DefineProperty(cx
, obj
, desc
.id
, &v
, getter
, setter
, attrs
);
2233 DefinePropertyOnArray(JSContext
*cx
, JSObject
*obj
, const PropDesc
&desc
,
2234 bool throwError
, bool *rval
)
2237 * We probably should optimize dense array property definitions where
2238 * the descriptor describes a traditional array property (enumerable,
2239 * configurable, writable, numeric index or length without altering its
2240 * attributes). Such definitions are probably unlikely, so we don't bother
2243 if (obj
->isDenseArray() && !obj
->makeDenseArraySlow(cx
))
2246 jsuint oldLen
= obj
->getArrayLength();
2248 if (JSID_IS_ATOM(desc
.id
, cx
->runtime
->atomState
.lengthAtom
)) {
2250 * Our optimization of storage of the length property of arrays makes
2251 * it very difficult to properly implement defining the property. For
2252 * now simply throw an exception (NB: not merely Reject) on any attempt
2253 * to define the "length" property, rather than attempting to implement
2254 * some difficult-for-authors-to-grasp subset of that functionality.
2256 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_DEFINE_ARRAY_LENGTH_UNSUPPORTED
);
2261 if (js_IdIsIndex(desc
.id
, &index
)) {
2263 // Disabled until we support defining "length":
2264 if (index >= oldLen && lengthPropertyNotWritable())
2265 return ThrowTypeError(cx, JSMSG_CANT_APPEND_PROPERTIES_TO_UNWRITABLE_LENGTH_ARRAY);
2267 if (!DefinePropertyOnObject(cx
, obj
, desc
, false, rval
))
2270 return Reject(cx
, JSMSG_CANT_DEFINE_ARRAY_INDEX
, throwError
, rval
);
2272 if (index
>= oldLen
) {
2273 JS_ASSERT(index
!= UINT32_MAX
);
2274 obj
->setArrayLength(index
+ 1);
2281 return DefinePropertyOnObject(cx
, obj
, desc
, throwError
, rval
);
2285 DefineProperty(JSContext
*cx
, JSObject
*obj
, const PropDesc
&desc
, bool throwError
,
2289 return DefinePropertyOnArray(cx
, obj
, desc
, throwError
, rval
);
2291 if (obj
->getOps()->lookupProperty
) {
2293 return JSProxy::defineProperty(cx
, obj
, desc
.id
, desc
.pd
);
2294 return Reject(cx
, JSMSG_OBJECT_NOT_EXTENSIBLE
, throwError
, rval
);
2297 return DefinePropertyOnObject(cx
, obj
, desc
, throwError
, rval
);
2301 js_DefineOwnProperty(JSContext
*cx
, JSObject
*obj
, jsid id
,
2302 const Value
&descriptor
, JSBool
*bp
)
2304 AutoPropDescArrayRooter
descs(cx
);
2305 PropDesc
*desc
= descs
.append();
2306 if (!desc
|| !desc
->initialize(cx
, id
, descriptor
))
2310 if (!DefineProperty(cx
, obj
, *desc
, true, &rval
))
2316 /* ES5 15.2.3.6: Object.defineProperty(O, P, Attributes) */
2318 obj_defineProperty(JSContext
* cx
, uintN argc
, Value
* vp
)
2320 /* 15.2.3.6 steps 1 and 5. */
2322 if (!GetFirstArgumentAsObject(cx
, argc
, vp
, "Object.defineProperty", &obj
))
2324 vp
->setObject(*obj
);
2326 /* 15.2.3.6 step 2. */
2327 AutoIdRooter
nameidr(cx
);
2328 if (!ValueToId(cx
, argc
>= 2 ? vp
[3] : UndefinedValue(), nameidr
.addr()))
2331 /* 15.2.3.6 step 3. */
2332 const Value
&descval
= argc
>= 3 ? vp
[4] : UndefinedValue();
2334 /* 15.2.3.6 step 4 */
2336 return js_DefineOwnProperty(cx
, obj
, nameidr
.id(), descval
, &junk
);
2340 DefineProperties(JSContext
*cx
, JSObject
*obj
, JSObject
*props
)
2342 AutoIdArray
ida(cx
, JS_Enumerate(cx
, props
));
2346 AutoPropDescArrayRooter
descs(cx
);
2347 size_t len
= ida
.length();
2348 for (size_t i
= 0; i
< len
; i
++) {
2350 PropDesc
* desc
= descs
.append();
2351 AutoValueRooter
tvr(cx
);
2353 !JS_GetPropertyById(cx
, props
, id
, tvr
.jsval_addr()) ||
2354 !desc
->initialize(cx
, id
, tvr
.value())) {
2360 for (size_t i
= 0; i
< len
; i
++) {
2361 if (!DefineProperty(cx
, obj
, descs
[i
], true, &dummy
))
2369 js_PopulateObject(JSContext
*cx
, JSObject
*newborn
, JSObject
*props
)
2371 return DefineProperties(cx
, newborn
, props
);
2374 /* ES5 15.2.3.7: Object.defineProperties(O, Properties) */
2376 obj_defineProperties(JSContext
* cx
, uintN argc
, Value
* vp
)
2378 /* 15.2.3.6 steps 1 and 5. */
2380 if (!GetFirstArgumentAsObject(cx
, argc
, vp
, "Object.defineProperties", &obj
))
2382 vp
->setObject(*obj
);
2385 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_MORE_ARGS_NEEDED
,
2386 "Object.defineProperties", "0", "s");
2390 JSObject
* props
= js_ValueToNonNullObject(cx
, vp
[3]);
2393 vp
[3].setObject(*props
);
2395 return DefineProperties(cx
, obj
, props
);
2398 /* ES5 15.2.3.5: Object.create(O [, Properties]) */
2400 obj_create(JSContext
*cx
, uintN argc
, Value
*vp
)
2403 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_MORE_ARGS_NEEDED
,
2404 "Object.create", "0", "s");
2408 const Value
&v
= vp
[2];
2409 if (!v
.isObjectOrNull()) {
2410 char *bytes
= DecompileValueGenerator(cx
, JSDVG_SEARCH_STACK
, v
, NULL
);
2413 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_UNEXPECTED_TYPE
,
2414 bytes
, "not an object or null");
2420 * Use the callee's global as the parent of the new object to avoid dynamic
2421 * scoping (i.e., using the caller's global).
2423 JSObject
*obj
= NewNonFunction
<WithProto::Given
>(cx
, &js_ObjectClass
, v
.toObjectOrNull(),
2424 vp
->toObject().getGlobal());
2427 vp
->setObject(*obj
); /* Root and prepare for eventual return. */
2429 /* 15.2.3.5 step 4. */
2430 if (argc
> 1 && !vp
[3].isUndefined()) {
2431 if (vp
[3].isPrimitive()) {
2432 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_NOT_NONNULL_OBJECT
);
2436 JSObject
*props
= &vp
[3].toObject();
2437 AutoIdArray
ida(cx
, JS_Enumerate(cx
, props
));
2441 AutoPropDescArrayRooter
descs(cx
);
2442 size_t len
= ida
.length();
2443 for (size_t i
= 0; i
< len
; i
++) {
2445 PropDesc
*desc
= descs
.append();
2446 if (!desc
|| !JS_GetPropertyById(cx
, props
, id
, Jsvalify(&vp
[1])) ||
2447 !desc
->initialize(cx
, id
, vp
[1])) {
2453 for (size_t i
= 0; i
< len
; i
++) {
2454 if (!DefineProperty(cx
, obj
, descs
[i
], true, &dummy
))
2459 /* 5. Return obj. */
2464 obj_getOwnPropertyNames(JSContext
*cx
, uintN argc
, Value
*vp
)
2467 if (!GetFirstArgumentAsObject(cx
, argc
, vp
, "Object.getOwnPropertyNames", &obj
))
2470 AutoIdVector
keys(cx
);
2471 if (!GetPropertyNames(cx
, obj
, JSITER_OWNONLY
| JSITER_HIDDEN
, keys
))
2474 AutoValueVector
vals(cx
);
2475 if (!vals
.resize(keys
.length()))
2478 for (size_t i
= 0, len
= keys
.length(); i
< len
; i
++) {
2480 if (JSID_IS_INT(id
)) {
2481 JSString
*str
= js_ValueToString(cx
, Int32Value(JSID_TO_INT(id
)));
2484 vals
[i
].setString(str
);
2485 } else if (JSID_IS_ATOM(id
)) {
2486 vals
[i
].setString(JSID_TO_STRING(id
));
2488 vals
[i
].setObject(*JSID_TO_OBJECT(id
));
2492 JSObject
*aobj
= js_NewArrayObject(cx
, vals
.length(), vals
.begin());
2496 vp
->setObject(*aobj
);
2501 #if JS_HAS_OBJ_WATCHPOINT
2502 const char js_watch_str
[] = "watch";
2503 const char js_unwatch_str
[] = "unwatch";
2505 const char js_hasOwnProperty_str
[] = "hasOwnProperty";
2506 const char js_isPrototypeOf_str
[] = "isPrototypeOf";
2507 const char js_propertyIsEnumerable_str
[] = "propertyIsEnumerable";
2509 static JSFunctionSpec object_methods
[] = {
2511 JS_FN(js_toSource_str
, obj_toSource
, 0,0),
2513 JS_FN(js_toString_str
, obj_toString
, 0,0),
2514 JS_FN(js_toLocaleString_str
, obj_toLocaleString
, 0,0),
2515 JS_FN(js_valueOf_str
, obj_valueOf
, 0,0),
2516 #if JS_HAS_OBJ_WATCHPOINT
2517 JS_FN(js_watch_str
, obj_watch
, 2,0),
2518 JS_FN(js_unwatch_str
, obj_unwatch
, 1,0),
2520 JS_FN(js_hasOwnProperty_str
, obj_hasOwnProperty
, 1,0),
2521 JS_FN(js_isPrototypeOf_str
, obj_isPrototypeOf
, 1,0),
2522 JS_FN(js_propertyIsEnumerable_str
, obj_propertyIsEnumerable
, 1,0),
2523 #if OLD_GETTER_SETTER_METHODS
2524 JS_FN(js_defineGetter_str
, js_obj_defineGetter
, 2,0),
2525 JS_FN(js_defineSetter_str
, js_obj_defineSetter
, 2,0),
2526 JS_FN(js_lookupGetter_str
, obj_lookupGetter
, 1,0),
2527 JS_FN(js_lookupSetter_str
, obj_lookupSetter
, 1,0),
2532 static JSFunctionSpec object_static_methods
[] = {
2533 JS_FN("getPrototypeOf", obj_getPrototypeOf
, 1,0),
2534 JS_FN("getOwnPropertyDescriptor", obj_getOwnPropertyDescriptor
,2,0),
2535 JS_FN("keys", obj_keys
, 1,0),
2536 JS_FN("defineProperty", obj_defineProperty
, 3,0),
2537 JS_FN("defineProperties", obj_defineProperties
, 2,0),
2538 JS_FN("create", obj_create
, 2,0),
2539 JS_FN("getOwnPropertyNames", obj_getOwnPropertyNames
, 1,0),
2544 js_Object(JSContext
*cx
, uintN argc
, Value
*vp
)
2548 /* Trigger logic below to construct a blank object. */
2551 /* If argv[0] is null or undefined, obj comes back null. */
2552 if (!js_ValueToObjectOrNull(cx
, vp
[2], &obj
))
2556 /* Make an object whether this was called with 'new' or not. */
2557 JS_ASSERT(!argc
|| vp
[2].isNull() || vp
[2].isUndefined());
2558 obj
= NewBuiltinClassInstance(cx
, &js_ObjectClass
);
2562 vp
->setObject(*obj
);
2569 js_NewObjectWithClassProto(JSContext
*cx
, Class
*clasp
, JSObject
*proto
,
2570 const Value
&privateSlotValue
)
2572 JS_ASSERT(clasp
->isNative());
2574 JSObject
* obj
= js_NewGCObject(cx
);
2578 obj
->initSharingEmptyScope(clasp
, proto
, proto
->getParent(), privateSlotValue
);
2583 js_Object_tn(JSContext
* cx
, JSObject
* proto
)
2585 JS_ASSERT(!(js_ObjectClass
.flags
& JSCLASS_HAS_PRIVATE
));
2586 return js_NewObjectWithClassProto(cx
, &js_ObjectClass
, proto
, UndefinedValue());
2589 JS_DEFINE_TRCINFO_1(js_Object
,
2590 (2, (extern, CONSTRUCTOR_RETRY
, js_Object_tn
, CONTEXT
, CALLEE_PROTOTYPE
, 0,
2591 nanojit::ACCSET_STORE_ANY
)))
2594 js_NonEmptyObject(JSContext
* cx
, JSObject
* proto
)
2596 JS_ASSERT(!(js_ObjectClass
.flags
& JSCLASS_HAS_PRIVATE
));
2597 JSObject
*obj
= js_NewObjectWithClassProto(cx
, &js_ObjectClass
, proto
, UndefinedValue());
2600 JS_LOCK_OBJ(cx
, obj
);
2601 JSScope
*scope
= js_GetMutableScope(cx
, obj
);
2603 JS_UNLOCK_OBJ(cx
, obj
);
2608 * See comments in the JSOP_NEWINIT case of jsinterp.cpp why we cannot
2609 * assume that cx owns the scope and skip the unlock call.
2611 JS_UNLOCK_SCOPE(cx
, scope
);
2615 JS_DEFINE_CALLINFO_2(extern, CONSTRUCTOR_RETRY
, js_NonEmptyObject
, CONTEXT
, CALLEE_PROTOTYPE
, 0,
2616 nanojit::ACCSET_STORE_ANY
)
2619 js_NewInstance(JSContext
*cx
, Class
*clasp
, JSObject
*ctor
)
2621 JS_ASSERT(JS_ON_TRACE(cx
));
2622 JS_ASSERT(ctor
->isFunction());
2624 JSAtom
*atom
= cx
->runtime
->atomState
.classPrototypeAtom
;
2626 JSScope
*scope
= ctor
->scope();
2627 #ifdef JS_THREADSAFE
2628 if (scope
->title
.ownercx
!= cx
)
2631 if (scope
->isSharedEmpty()) {
2632 scope
= js_GetMutableScope(cx
, ctor
);
2637 JSScopeProperty
*sprop
= scope
->lookup(ATOM_TO_JSID(atom
));
2638 Value pval
= sprop
? ctor
->getSlot(sprop
->slot
) : MagicValue(JS_GENERIC_MAGIC
);
2640 JSObject
*parent
= ctor
->getParent();
2642 if (pval
.isObject()) {
2643 /* An object in ctor.prototype, let's use it as the new instance's proto. */
2644 proto
= &pval
.toObject();
2646 /* A hole or a primitive: either way, we need to get Object.prototype. */
2647 if (!js_GetClassPrototype(cx
, parent
, JSProto_Object
, &proto
))
2650 if (pval
.isMagic(JS_GENERIC_MAGIC
)) {
2652 * No ctor.prototype was set, so we inline-expand and optimize
2653 * fun_resolve's prototype creation code.
2655 proto
= NewNativeClassInstance(cx
, clasp
, proto
, parent
);
2658 if (!js_SetClassPrototype(cx
, ctor
, proto
, JSPROP_ENUMERATE
| JSPROP_PERMANENT
))
2662 * A primitive value in .prototype means to use Object.prototype
2663 * for proto. See ES5 13.2.2 step 7.
2669 * FIXME: 561785 at least. Quasi-natives including XML objects prevent us
2670 * from easily or unconditionally calling NewNativeClassInstance here.
2672 return NewNonFunction
<WithProto::Given
>(cx
, clasp
, proto
, parent
);
2675 JS_DEFINE_CALLINFO_3(extern, CONSTRUCTOR_RETRY
, js_NewInstance
, CONTEXT
, CLASS
, OBJECT
, 0,
2676 nanojit::ACCSET_STORE_ANY
)
2678 #else /* !JS_TRACER */
2680 # define js_Object_trcinfo NULL
2682 #endif /* !JS_TRACER */
2685 * Given pc pointing after a property accessing bytecode, return true if the
2686 * access is "object-detecting" in the sense used by web scripts, e.g., when
2687 * checking whether document.all is defined.
2689 JS_REQUIRES_STACK JSBool
2690 Detecting(JSContext
*cx
, jsbytecode
*pc
)
2697 script
= cx
->fp
->script
;
2698 endpc
= script
->code
+ script
->length
;
2699 for (;; pc
+= js_CodeSpec
[op
].length
) {
2700 JS_ASSERT_IF(!cx
->fp
->hasIMacroPC(), script
->code
<= pc
&& pc
< endpc
);
2702 /* General case: a branch or equality op follows the access. */
2703 op
= js_GetOpcode(cx
, script
, pc
);
2704 if (js_CodeSpec
[op
].format
& JOF_DETECTING
)
2710 * Special case #1: handle (document.all == null). Don't sweat
2711 * about JS1.2's revision of the equality operators here.
2714 op
= js_GetOpcode(cx
, script
, pc
);
2715 return *pc
== JSOP_EQ
|| *pc
== JSOP_NE
;
2721 * Special case #2: handle (document.all == undefined). Don't
2722 * worry about someone redefining undefined, which was added by
2723 * Edition 3, so is read/write for backward compatibility.
2725 GET_ATOM_FROM_BYTECODE(script
, pc
, 0, atom
);
2726 if (atom
== cx
->runtime
->atomState
.typeAtoms
[JSTYPE_VOID
] &&
2727 (pc
+= js_CodeSpec
[op
].length
) < endpc
) {
2728 op
= js_GetOpcode(cx
, script
, pc
);
2729 return op
== JSOP_EQ
|| op
== JSOP_NE
||
2730 op
== JSOP_STRICTEQ
|| op
== JSOP_STRICTNE
;
2736 * At this point, anything but an extended atom index prefix means
2737 * we're not detecting.
2739 if (!(js_CodeSpec
[op
].format
& JOF_INDEXBASE
))
2747 * Infer lookup flags from the currently executing bytecode. This does
2748 * not attempt to infer JSRESOLVE_WITH, because the current bytecode
2749 * does not indicate whether we are in a with statement. Return defaultFlags
2750 * if a currently executing bytecode cannot be determined.
2753 js_InferFlags(JSContext
*cx
, uintN defaultFlags
)
2756 if (JS_ON_TRACE(cx
))
2757 return cx
->bailExit
->lookupFlags
;
2760 JS_ASSERT_NOT_ON_TRACE(cx
);
2763 const JSCodeSpec
*cs
;
2767 JSStackFrame
*const fp
= js_GetTopStackFrame(cx
);
2768 if (!fp
|| !(pc
= cx
->regs
->pc
))
2769 return defaultFlags
;
2770 cs
= &js_CodeSpec
[js_GetOpcode(cx
, fp
->script
, pc
)];
2771 format
= cs
->format
;
2772 if (JOF_MODE(format
) != JOF_NAME
)
2773 flags
|= JSRESOLVE_QUALIFIED
;
2774 if ((format
& (JOF_SET
| JOF_FOR
)) ||
2775 (fp
->flags
& JSFRAME_ASSIGNING
)) {
2776 flags
|= JSRESOLVE_ASSIGNING
;
2777 } else if (cs
->length
>= 0) {
2779 if (pc
< cx
->fp
->script
->code
+ cx
->fp
->script
->length
&& Detecting(cx
, pc
))
2780 flags
|= JSRESOLVE_DETECTING
;
2782 if (format
& JOF_DECLARING
)
2783 flags
|= JSRESOLVE_DECLARING
;
2788 * ObjectOps and Class for with-statement stack objects.
2791 with_LookupProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, JSObject
**objp
,
2794 /* Fixes bug 463997 */
2795 uintN flags
= cx
->resolveFlags
;
2796 if (flags
== JSRESOLVE_INFER
)
2797 flags
= js_InferFlags(cx
, flags
);
2798 flags
|= JSRESOLVE_WITH
;
2799 JSAutoResolveFlags
rf(cx
, flags
);
2800 return obj
->getProto()->lookupProperty(cx
, id
, objp
, propp
);
2804 with_GetProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
2806 return obj
->getProto()->getProperty(cx
, id
, vp
);
2810 with_SetProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
2812 return obj
->getProto()->setProperty(cx
, id
, vp
);
2816 with_GetAttributes(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN
*attrsp
)
2818 return obj
->getProto()->getAttributes(cx
, id
, attrsp
);
2822 with_SetAttributes(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN
*attrsp
)
2824 return obj
->getProto()->setAttributes(cx
, id
, attrsp
);
2828 with_DeleteProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*rval
)
2830 return obj
->getProto()->deleteProperty(cx
, id
, rval
);
2834 with_Enumerate(JSContext
*cx
, JSObject
*obj
, JSIterateOp enum_op
,
2835 Value
*statep
, jsid
*idp
)
2837 return obj
->getProto()->enumerate(cx
, enum_op
, statep
, idp
);
2841 with_TypeOf(JSContext
*cx
, JSObject
*obj
)
2843 return JSTYPE_OBJECT
;
2847 with_ThisObject(JSContext
*cx
, JSObject
*obj
)
2849 return obj
->getWithThis();
2852 Class js_WithClass
= {
2854 JSCLASS_HAS_PRIVATE
| JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS
,
2855 PropertyStub
, /* addProperty */
2856 PropertyStub
, /* delProperty */
2857 PropertyStub
, /* getProperty */
2858 PropertyStub
, /* setProperty */
2862 NULL
, /* finalize */
2863 NULL
, /* reserved */
2864 NULL
, /* checkAccess */
2866 NULL
, /* construct */
2867 NULL
, /* xdrObject */
2868 NULL
, /* hasInstance */
2872 with_LookupProperty
,
2873 NULL
, /* defineProperty */
2878 with_DeleteProperty
,
2887 JS_REQUIRES_STACK JSObject
*
2888 js_NewWithObject(JSContext
*cx
, JSObject
*proto
, JSObject
*parent
, jsint depth
)
2892 obj
= js_NewGCObject(cx
);
2895 obj
->init(&js_WithClass
, proto
, parent
,
2896 PrivateValue(js_FloatingFrameIfGenerator(cx
, cx
->fp
)));
2897 OBJ_SET_BLOCK_DEPTH(cx
, obj
, depth
);
2898 obj
->map
= cx
->runtime
->emptyWithScope
->hold();
2900 AutoObjectRooter
tvr(cx
, obj
);
2901 JSObject
*thisp
= proto
->thisObject(cx
);
2904 obj
->setWithThis(thisp
);
2910 js_NewBlockObject(JSContext
*cx
)
2913 * Null obj's proto slot so that Object.prototype.* does not pollute block
2914 * scopes and to give the block object its own scope.
2916 JSObject
*blockObj
= js_NewGCObject(cx
);
2919 blockObj
->init(&js_BlockClass
, NULL
, NULL
, NullValue());
2920 blockObj
->map
= cx
->runtime
->emptyBlockScope
->hold();
2925 js_CloneBlockObject(JSContext
*cx
, JSObject
*proto
, JSStackFrame
*fp
)
2927 JS_ASSERT(!OBJ_IS_CLONED_BLOCK(proto
));
2928 JS_ASSERT(proto
->getClass() == &js_BlockClass
);
2930 JSObject
*clone
= js_NewGCObject(cx
);
2934 Value privateValue
= PrivateValue(js_FloatingFrameIfGenerator(cx
, fp
));
2936 /* The caller sets parent on its own. */
2937 clone
->init(&js_BlockClass
, proto
, NULL
, privateValue
);
2938 clone
->fslots
[JSSLOT_BLOCK_DEPTH
] = proto
->fslots
[JSSLOT_BLOCK_DEPTH
];
2940 JS_ASSERT(cx
->runtime
->emptyBlockScope
->freeslot
== JSSLOT_BLOCK_DEPTH
+ 1);
2941 clone
->map
= cx
->runtime
->emptyBlockScope
->hold();
2942 JS_ASSERT(OBJ_IS_CLONED_BLOCK(clone
));
2944 if (!js_EnsureReservedSlots(cx
, clone
, OBJ_BLOCK_COUNT(cx
, proto
)))
2949 JS_REQUIRES_STACK JSBool
2950 js_PutBlockObject(JSContext
*cx
, JSBool normalUnwind
)
2952 /* Blocks have one fixed slot available for the first local.*/
2953 JS_STATIC_ASSERT(JS_INITIAL_NSLOTS
== JSSLOT_BLOCK_DEPTH
+ 2);
2955 JSStackFrame
*const fp
= cx
->fp
;
2956 JSObject
*obj
= fp
->getScopeChain();
2957 JS_ASSERT(obj
->getClass() == &js_BlockClass
);
2958 JS_ASSERT(obj
->getPrivate() == js_FloatingFrameIfGenerator(cx
, cx
->fp
));
2959 JS_ASSERT(OBJ_IS_CLONED_BLOCK(obj
));
2962 * Block objects should never be exposed to scripts. Thus the clone should
2963 * not own the property map and rather always share it with the prototype
2964 * object. This allows us to skip updating obj->scope()->freeslot after
2965 * we copy the stack slots into reserved slots.
2967 JS_ASSERT(obj
->scope()->object
!= obj
);
2969 /* Block objects should have all reserved slots allocated early. */
2970 uintN count
= OBJ_BLOCK_COUNT(cx
, obj
);
2971 JS_ASSERT(obj
->numSlots() == JSSLOT_BLOCK_DEPTH
+ 1 + count
);
2973 /* The block and its locals must be on the current stack for GC safety. */
2974 uintN depth
= OBJ_BLOCK_DEPTH(cx
, obj
);
2975 JS_ASSERT(depth
<= (size_t) (cx
->regs
->sp
- fp
->base()));
2976 JS_ASSERT(count
<= (size_t) (cx
->regs
->sp
- fp
->base() - depth
));
2978 /* See comments in CheckDestructuring from jsparse.cpp. */
2979 JS_ASSERT(count
>= 1);
2981 depth
+= fp
->script
->nfixed
;
2982 obj
->fslots
[JSSLOT_BLOCK_DEPTH
+ 1] = fp
->slots()[depth
];
2983 if (normalUnwind
&& count
> 1) {
2985 memcpy(obj
->dslots
, fp
->slots() + depth
+ 1, count
* sizeof(Value
));
2988 /* We must clear the private slot even with errors. */
2989 obj
->setPrivate(NULL
);
2990 fp
->setScopeChain(obj
->getParent());
2991 return normalUnwind
;
2995 block_getProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
2998 * Block objects are never exposed to script, and the engine handles them
2999 * with care. So unlike other getters, this one can assert (rather than
3000 * check) certain invariants about obj.
3002 JS_ASSERT(obj
->getClass() == &js_BlockClass
);
3003 JS_ASSERT(OBJ_IS_CLONED_BLOCK(obj
));
3004 uintN index
= (uintN
) JSID_TO_INT(id
);
3005 JS_ASSERT(index
< OBJ_BLOCK_COUNT(cx
, obj
));
3007 JSStackFrame
*fp
= (JSStackFrame
*) obj
->getPrivate();
3009 fp
= js_LiveFrameIfGenerator(fp
);
3010 index
+= fp
->script
->nfixed
+ OBJ_BLOCK_DEPTH(cx
, obj
);
3011 JS_ASSERT(index
< fp
->script
->nslots
);
3012 *vp
= fp
->slots()[index
];
3016 /* Values are in reserved slots immediately following DEPTH. */
3017 uint32 slot
= JSSLOT_BLOCK_DEPTH
+ 1 + index
;
3018 JS_LOCK_OBJ(cx
, obj
);
3019 JS_ASSERT(slot
< obj
->numSlots());
3020 *vp
= obj
->getSlot(slot
);
3021 JS_UNLOCK_OBJ(cx
, obj
);
3026 block_setProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
3028 JS_ASSERT(obj
->getClass() == &js_BlockClass
);
3029 JS_ASSERT(OBJ_IS_CLONED_BLOCK(obj
));
3030 uintN index
= (uintN
) JSID_TO_INT(id
);
3031 JS_ASSERT(index
< OBJ_BLOCK_COUNT(cx
, obj
));
3033 JSStackFrame
*fp
= (JSStackFrame
*) obj
->getPrivate();
3035 fp
= js_LiveFrameIfGenerator(fp
);
3036 index
+= fp
->script
->nfixed
+ OBJ_BLOCK_DEPTH(cx
, obj
);
3037 JS_ASSERT(index
< fp
->script
->nslots
);
3038 fp
->slots()[index
] = *vp
;
3042 /* Values are in reserved slots immediately following DEPTH. */
3043 uint32 slot
= JSSLOT_BLOCK_DEPTH
+ 1 + index
;
3044 JS_LOCK_OBJ(cx
, obj
);
3045 JS_ASSERT(slot
< obj
->numSlots());
3046 obj
->setSlot(slot
, *vp
);
3047 JS_UNLOCK_OBJ(cx
, obj
);
3052 js_DefineBlockVariable(JSContext
*cx
, JSObject
*obj
, jsid id
, intN index
)
3054 JS_ASSERT(obj
->getClass() == &js_BlockClass
);
3055 JS_ASSERT(!OBJ_IS_CLONED_BLOCK(obj
));
3057 /* Use JSPROP_ENUMERATE to aid the disassembler. */
3058 return js_DefineNativeProperty(cx
, obj
, id
, UndefinedValue(),
3061 JSPROP_ENUMERATE
| JSPROP_PERMANENT
| JSPROP_SHARED
,
3062 JSScopeProperty::HAS_SHORTID
, index
, NULL
);
3066 GetObjectSize(JSObject
*obj
)
3068 return (obj
->isFunction() && !obj
->getPrivate())
3069 ? sizeof(JSFunction
)
3074 * Use this method with extreme caution. It trades the guts of two objects and updates
3075 * scope ownership. This operation is not thread-safe, just as fast array to slow array
3076 * transitions are inherently not thread-safe. Don't perform a swap operation on objects
3077 * shared across threads or, or bad things will happen. You have been warned.
3080 JSObject::swap(JSObject
*other
)
3082 /* For both objects determine whether they own their respective scopes. */
3083 bool thisOwns
= this->isNative() && scope()->object
== this;
3084 bool otherOwns
= other
->isNative() && other
->scope()->object
== other
;
3086 size_t size
= GetObjectSize(this);
3087 JS_ASSERT(size
== GetObjectSize(other
));
3089 /* Trade the guts of the objects. */
3090 char tmp
[tl::Max
<sizeof(JSFunction
), sizeof(JSObject
)>::result
];
3091 memcpy(tmp
, this, size
);
3092 memcpy(this, other
, size
);
3093 memcpy(other
, tmp
, size
);
3095 /* Fixup scope ownerships. */
3097 scope()->object
= this;
3099 other
->scope()->object
= other
;
3104 #define NO_PARENT_INDEX ((uint32)-1)
3107 FindObjectIndex(JSObjectArray
*array
, JSObject
*obj
)
3115 if (array
->vector
[--i
] == obj
)
3120 return NO_PARENT_INDEX
;
3124 js_XDRBlockObject(JSXDRState
*xdr
, JSObject
**objp
)
3128 JSObject
*obj
, *parent
;
3129 uint16 depth
, count
, i
;
3131 JSScopeProperty
*sprop
;
3139 obj
= NULL
; /* quell GCC overwarning */
3142 if (xdr
->mode
== JSXDR_ENCODE
) {
3144 parent
= obj
->getParent();
3145 parentId
= (xdr
->script
->objectsOffset
== 0)
3147 : FindObjectIndex(xdr
->script
->objects(), parent
);
3148 depth
= (uint16
)OBJ_BLOCK_DEPTH(cx
, obj
);
3149 count
= (uint16
)OBJ_BLOCK_COUNT(cx
, obj
);
3150 tmp
= (uint32
)(depth
<< 16) | count
;
3152 #ifdef __GNUC__ /* suppress bogus gcc warnings */
3156 /* First, XDR the parent atomid. */
3157 if (!JS_XDRUint32(xdr
, &parentId
))
3160 if (xdr
->mode
== JSXDR_DECODE
) {
3161 obj
= js_NewBlockObject(cx
);
3167 * If there's a parent id, then get the parent out of our script's
3168 * object array. We know that we XDR block object in outer-to-inner
3169 * order, which means that getting the parent now will work.
3171 if (parentId
== NO_PARENT_INDEX
)
3174 parent
= xdr
->script
->getObject(parentId
);
3175 obj
->setParent(parent
);
3178 AutoObjectRooter
tvr(cx
, obj
);
3180 if (!JS_XDRUint32(xdr
, &tmp
))
3183 if (xdr
->mode
== JSXDR_DECODE
) {
3184 depth
= (uint16
)(tmp
>> 16);
3185 count
= (uint16
)tmp
;
3186 obj
->setSlot(JSSLOT_BLOCK_DEPTH
, Value(Int32Value(depth
)));
3190 * XDR the block object's properties. We know that there are 'count'
3191 * properties to XDR, stored as id/shortid pairs. We do not XDR any
3192 * non-native properties, only those that the compiler created.
3196 for (i
= 0; i
< count
; i
++) {
3197 if (xdr
->mode
== JSXDR_ENCODE
) {
3198 /* Find a property to XDR. */
3200 /* If sprop is NULL, this is the first property. */
3201 sprop
= sprop
? sprop
->parent
: obj
->scope()->lastProperty();
3202 } while (!sprop
->hasShortID());
3204 JS_ASSERT(sprop
->getter() == block_getProperty
);
3206 JS_ASSERT(JSID_IS_ATOM(propid
));
3207 atom
= JSID_TO_ATOM(propid
);
3208 shortid
= sprop
->shortid
;
3209 JS_ASSERT(shortid
>= 0);
3212 /* XDR the real id, then the shortid. */
3213 if (!js_XDRAtom(xdr
, &atom
) ||
3214 !JS_XDRUint16(xdr
, (uint16
*)&shortid
)) {
3218 if (xdr
->mode
== JSXDR_DECODE
) {
3219 if (!js_DefineBlockVariable(cx
, obj
, ATOM_TO_JSID(atom
), shortid
))
3228 Class js_BlockClass
= {
3230 JSCLASS_HAS_PRIVATE
| JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_IS_ANONYMOUS
,
3231 PropertyStub
, /* addProperty */
3232 PropertyStub
, /* delProperty */
3233 PropertyStub
, /* getProperty */
3234 PropertyStub
, /* setProperty */
3241 js_InitObjectClass(JSContext
*cx
, JSObject
*obj
)
3243 JSObject
*proto
= js_InitClass(cx
, obj
, NULL
, &js_ObjectClass
, (Native
) js_Object
, 1,
3244 object_props
, object_methods
, NULL
, object_static_methods
);
3248 /* ECMA (15.1.2.1) says 'eval' is a property of the global object. */
3249 if (!js_DefineFunction(cx
, obj
, cx
->runtime
->atomState
.evalAtom
,
3250 (Native
)obj_eval
, 1,
3251 JSFUN_FAST_NATIVE
| JSFUN_STUB_GSOPS
)) {
3259 DefineStandardSlot(JSContext
*cx
, JSObject
*obj
, JSProtoKey key
, JSAtom
*atom
,
3260 const Value
&v
, uint32 attrs
, bool &named
)
3262 jsid id
= ATOM_TO_JSID(atom
);
3264 if (key
!= JSProto_Null
) {
3266 * Initializing an actual standard class on a global object. If the
3267 * property is not yet present, force it into a new one bound to a
3268 * reserved slot. Otherwise, go through the normal property path.
3270 JS_ASSERT(obj
->getClass()->flags
& JSCLASS_IS_GLOBAL
);
3271 JS_ASSERT(obj
->isNative());
3273 JS_LOCK_OBJ(cx
, obj
);
3275 JSScope
*scope
= js_GetMutableScope(cx
, obj
);
3277 JS_UNLOCK_OBJ(cx
, obj
);
3281 JSScopeProperty
*sprop
= scope
->lookup(id
);
3283 uint32 index
= 2 * JSProto_LIMIT
+ key
;
3284 if (!js_SetReservedSlot(cx
, obj
, index
, v
)) {
3285 JS_UNLOCK_SCOPE(cx
, scope
);
3289 uint32 slot
= JSSLOT_START(obj
->getClass()) + index
;
3290 sprop
= scope
->addProperty(cx
, id
, PropertyStub
, PropertyStub
,
3293 JS_UNLOCK_SCOPE(cx
, scope
);
3300 JS_UNLOCK_SCOPE(cx
, scope
);
3303 named
= obj
->defineProperty(cx
, id
, v
, PropertyStub
, PropertyStub
, attrs
);
3308 js_InitClass(JSContext
*cx
, JSObject
*obj
, JSObject
*parent_proto
,
3309 Class
*clasp
, Native constructor
, uintN nargs
,
3310 JSPropertySpec
*ps
, JSFunctionSpec
*fs
,
3311 JSPropertySpec
*static_ps
, JSFunctionSpec
*static_fs
)
3318 atom
= js_Atomize(cx
, clasp
->name
, strlen(clasp
->name
), 0);
3323 * When initializing a standard class, if no parent_proto (grand-proto of
3324 * instances of the class, parent-proto of the class's prototype object)
3325 * is given, we must use Object.prototype if it is available. Otherwise,
3326 * we could look up the wrong binding for a class name in obj. Example:
3329 * print("hi there".join);
3331 * should print undefined, not Array.prototype.join. This is required by
3332 * ECMA-262, alas. It might have been better to make String readonly and
3333 * permanent in the global object, instead -- but that's too big a change
3334 * to swallow at this point.
3336 key
= JSCLASS_CACHED_PROTO_KEY(clasp
);
3337 if (key
!= JSProto_Null
&&
3339 !js_GetClassPrototype(cx
, obj
, JSProto_Object
, &parent_proto
)) {
3344 * Create a prototype object for this class.
3346 * FIXME: lazy standard (built-in) class initialization and even older
3347 * eager boostrapping code rely on all of these properties:
3349 * 1. NewObject attempting to compute a default prototype object when
3350 * passed null for proto; and
3352 * 2. NewObject tolerating no default prototype (null proto slot value)
3353 * due to this js_InitClass call coming from js_InitFunctionClass on an
3354 * otherwise-uninitialized global.
3356 * 3. NewObject allocating a JSFunction-sized GC-thing when clasp is
3357 * &js_FunctionClass, not a JSObject-sized (smaller) GC-thing.
3359 * The JS_NewObjectForGivenProto and JS_NewObject APIs also allow clasp to
3360 * be &js_FunctionClass (we could break compatibility easily). But fixing
3361 * (3) is not enough without addressing the bootstrapping dependency on (1)
3364 JSObject
*proto
= NewObject
<WithProto::Class
>(cx
, clasp
, parent_proto
, obj
);
3368 /* After this point, control must exit via label bad or out. */
3369 AutoObjectRooter
tvr(cx
, proto
);
3374 * Lacking a constructor, name the prototype (e.g., Math) unless this
3375 * class (a) is anonymous, i.e. for internal use only; (b) the class
3376 * of obj (the global object) is has a reserved slot indexed by key;
3377 * and (c) key is not the null key.
3379 if (!(clasp
->flags
& JSCLASS_IS_ANONYMOUS
) ||
3380 !(obj
->getClass()->flags
& JSCLASS_IS_GLOBAL
) ||
3381 key
== JSProto_Null
)
3383 uint32 attrs
= (clasp
->flags
& JSCLASS_IS_ANONYMOUS
)
3384 ? JSPROP_READONLY
| JSPROP_PERMANENT
3386 if (!DefineStandardSlot(cx
, obj
, key
, atom
, ObjectValue(*proto
), attrs
, named
))
3393 if (clasp
->flags
& JSCLASS_FAST_CONSTRUCTOR
)
3394 flags
|= JSFUN_FAST_NATIVE
| JSFUN_FAST_NATIVE_CTOR
;
3396 fun
= js_NewFunction(cx
, NULL
, constructor
, nargs
, flags
, obj
, atom
);
3400 AutoValueRooter
tvr2(cx
, ObjectValue(*fun
));
3401 if (!DefineStandardSlot(cx
, obj
, key
, atom
, tvr2
.value(), 0, named
))
3405 * Remember the class this function is a constructor for so that
3406 * we know to create an object of this class when we call the
3409 FUN_CLASP(fun
) = clasp
;
3412 * Optionally construct the prototype object, before the class has
3413 * been fully initialized. Allow the ctor to replace proto with a
3414 * different object, as is done for operator new -- and as at least
3415 * XML support requires.
3417 ctor
= FUN_OBJECT(fun
);
3418 if (clasp
->flags
& JSCLASS_CONSTRUCT_PROTOTYPE
) {
3420 if (!InternalConstruct(cx
, proto
, ObjectOrNullValue(ctor
), 0, NULL
, &rval
))
3422 if (rval
.isObject() && &rval
.toObject() != proto
)
3423 proto
= &rval
.toObject();
3426 /* Connect constructor and prototype by named properties. */
3427 if (!js_SetClassPrototype(cx
, ctor
, proto
,
3428 JSPROP_READONLY
| JSPROP_PERMANENT
)) {
3432 /* Bootstrap Function.prototype (see also JS_InitStandardClasses). */
3433 if (ctor
->getClass() == clasp
)
3434 ctor
->setProto(proto
);
3437 /* Add properties and methods to the prototype and the constructor. */
3438 if ((ps
&& !JS_DefineProperties(cx
, proto
, ps
)) ||
3439 (fs
&& !JS_DefineFunctions(cx
, proto
, fs
)) ||
3440 (static_ps
&& !JS_DefineProperties(cx
, ctor
, static_ps
)) ||
3441 (static_fs
&& !JS_DefineFunctions(cx
, ctor
, static_fs
))) {
3446 * Make sure proto's scope's emptyScope is available to be shared by
3447 * objects of this class. JSScope::emptyScope is a one-slot cache. If we
3448 * omit this, some other class could snap it up. (The risk is particularly
3449 * great for Object.prototype.)
3451 * All callers of JSObject::initSharingEmptyScope depend on this.
3455 JS_LOCK_OBJ(cx
, proto
);
3456 scope
= js_GetMutableScope(cx
, proto
);
3457 ok
= scope
&& scope
->ensureEmptyScope(cx
, clasp
);
3458 JS_UNLOCK_OBJ(cx
, proto
);
3462 /* If this is a standard class, cache its prototype. */
3463 if (key
!= JSProto_Null
&& !js_SetClassObject(cx
, obj
, key
, ctor
, proto
))
3471 obj
->deleteProperty(cx
, ATOM_TO_JSID(atom
), &rval
);
3477 JSObject::allocSlots(JSContext
*cx
, size_t nslots
)
3480 JS_ASSERT(nslots
> JS_INITIAL_NSLOTS
);
3482 size_t nwords
= slotsToDynamicWords(nslots
);
3483 dslots
= (Value
*) cx
->malloc(nwords
* sizeof(Value
));
3488 dslots
[-1].setPrivateUint32(nslots
);
3489 SetValueRangeToUndefined(dslots
, nslots
- JS_INITIAL_NSLOTS
);
3494 JSObject::growSlots(JSContext
*cx
, size_t nslots
)
3497 * Minimal number of dynamic slots to allocate.
3499 const size_t MIN_DYNAMIC_WORDS
= 4;
3502 * The limit to switch to linear allocation strategy from the power of 2
3503 * growth no to waste too much memory.
3505 const size_t LINEAR_GROWTH_STEP
= JS_BIT(16);
3507 /* If we are allocating fslots, there is nothing to do. */
3508 if (nslots
<= JS_INITIAL_NSLOTS
)
3511 size_t nwords
= slotsToDynamicWords(nslots
);
3514 * Round up nslots so the number of bytes in dslots array is power
3515 * of 2 to ensure exponential grouth.
3518 if (nwords
<= MIN_DYNAMIC_WORDS
) {
3519 nwords
= MIN_DYNAMIC_WORDS
;
3520 } else if (nwords
< LINEAR_GROWTH_STEP
) {
3521 JS_CEILING_LOG2(log
, nwords
);
3522 nwords
= JS_BIT(log
);
3524 nwords
= JS_ROUNDUP(nwords
, LINEAR_GROWTH_STEP
);
3526 nslots
= dynamicWordsToSlots(nwords
);
3529 * If nothing was allocated yet, treat it as initial allocation (but with
3530 * the exponential growth algorithm applied).
3533 return allocSlots(cx
, nslots
);
3535 size_t oldnslots
= dslots
[-1].toPrivateUint32();
3537 Value
*tmpdslots
= (Value
*) cx
->realloc(dslots
- 1, nwords
* sizeof(Value
));
3539 return false; /* Leave dslots at its old size. */
3543 dslots
[-1].setPrivateUint32(nslots
);
3545 /* Initialize the additional slots we added. */
3546 JS_ASSERT(nslots
> oldnslots
);
3547 Value
*beg
= dslots
+ (oldnslots
- JS_INITIAL_NSLOTS
);
3548 Value
*end
= dslots
+ (nslots
- JS_INITIAL_NSLOTS
);
3549 SetValueRangeToUndefined(beg
, end
);
3555 JSObject::shrinkSlots(JSContext
*cx
, size_t nslots
)
3557 /* Nothing to shrink? */
3561 JS_ASSERT(dslots
[-1].toPrivateUint32() > JS_INITIAL_NSLOTS
);
3562 JS_ASSERT(nslots
<= dslots
[-1].toPrivateUint32());
3564 if (nslots
<= JS_INITIAL_NSLOTS
) {
3568 size_t nwords
= slotsToDynamicWords(nslots
);
3569 Value
*tmpdslots
= (Value
*) cx
->realloc(dslots
- 1, nwords
* sizeof(Value
));
3571 return; /* Leave dslots at its old size. */
3575 dslots
[-1].setPrivateUint32(nslots
);
3580 js_EnsureReservedSlots(JSContext
*cx
, JSObject
*obj
, size_t nreserved
)
3582 uintN nslots
= JSSLOT_FREE(obj
->getClass()) + nreserved
;
3583 if (nslots
> obj
->numSlots() && !obj
->allocSlots(cx
, nslots
))
3586 if (obj
->isNative()) {
3587 JSScope
*scope
= obj
->scope();
3588 if (!scope
->isSharedEmpty()) {
3589 #ifdef JS_THREADSAFE
3590 JS_ASSERT(scope
->title
.ownercx
->thread
== cx
->thread
);
3592 JS_ASSERT(scope
->freeslot
== JSSLOT_FREE(obj
->getClass()));
3593 if (scope
->freeslot
< nslots
)
3594 scope
->freeslot
= nslots
;
3601 js_InitNullClass(JSContext
*cx
, JSObject
*obj
)
3607 #define JS_PROTO(name,code,init) extern JSObject *init(JSContext *, JSObject *);
3608 #include "jsproto.tbl"
3611 static JSObjectOp lazy_prototype_init
[JSProto_LIMIT
] = {
3612 #define JS_PROTO(name,code,init) init,
3613 #include "jsproto.tbl"
3620 SetProto(JSContext
*cx
, JSObject
*obj
, JSObject
*proto
, bool checkForCycles
)
3622 JS_ASSERT_IF(!checkForCycles
, obj
!= proto
);
3624 if (obj
->isNative()) {
3625 JS_LOCK_OBJ(cx
, obj
);
3626 bool ok
= !!js_GetMutableScope(cx
, obj
);
3627 JS_UNLOCK_OBJ(cx
, obj
);
3633 * Regenerate property cache shape ids for all of the scopes along the
3634 * old prototype chain to invalidate their property cache entries, in
3635 * case any entries were filled by looking up through obj.
3637 JSObject
*oldproto
= obj
;
3638 while (oldproto
&& oldproto
->isNative()) {
3639 JS_LOCK_OBJ(cx
, oldproto
);
3640 JSScope
*scope
= oldproto
->scope();
3641 scope
->protoShapeChange(cx
);
3642 JSObject
*tmp
= oldproto
->getProto();
3643 JS_UNLOCK_OBJ(cx
, oldproto
);
3647 if (!proto
|| !checkForCycles
) {
3648 obj
->setProto(proto
);
3649 } else if (!SetProtoCheckingForCycles(cx
, obj
, proto
)) {
3650 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_CYCLIC_VALUE
, js_proto_str
);
3659 js_GetClassObject(JSContext
*cx
, JSObject
*obj
, JSProtoKey key
,
3662 JSObject
*tmp
, *cobj
;
3663 JSResolvingKey rkey
;
3664 JSResolvingEntry
*rentry
;
3669 while ((tmp
= obj
->getParent()) != NULL
)
3671 if (!(obj
->getClass()->flags
& JSCLASS_IS_GLOBAL
)) {
3676 v
= obj
->getReservedSlot(key
);
3678 *objp
= &v
.toObject();
3683 rkey
.id
= ATOM_TO_JSID(cx
->runtime
->atomState
.classAtoms
[key
]);
3684 if (!js_StartResolving(cx
, &rkey
, JSRESFLAG_LOOKUP
, &rentry
))
3687 /* Already caching key in obj -- suppress recursion. */
3691 generation
= cx
->resolvingTable
->generation
;
3695 init
= lazy_prototype_init
[key
];
3697 if (!init(cx
, obj
)) {
3700 v
= obj
->getReservedSlot(key
);
3702 cobj
= &v
.toObject();
3706 js_StopResolving(cx
, &rkey
, JSRESFLAG_LOOKUP
, rentry
, generation
);
3712 js_SetClassObject(JSContext
*cx
, JSObject
*obj
, JSProtoKey key
, JSObject
*cobj
, JSObject
*proto
)
3714 JS_ASSERT(!obj
->getParent());
3715 if (!(obj
->getClass()->flags
& JSCLASS_IS_GLOBAL
))
3718 return js_SetReservedSlot(cx
, obj
, key
, ObjectOrNullValue(cobj
)) &&
3719 js_SetReservedSlot(cx
, obj
, JSProto_LIMIT
+ key
, ObjectOrNullValue(proto
));
3723 js_FindClassObject(JSContext
*cx
, JSObject
*start
, JSProtoKey protoKey
,
3724 Value
*vp
, Class
*clasp
)
3727 JSObject
*obj
, *cobj
, *pobj
;
3730 JSScopeProperty
*sprop
;
3733 * Find the global object. Use cx->fp directly to avoid falling off
3734 * trace; all JIT-elided stack frames have the same global object as
3737 VOUCH_DOES_NOT_REQUIRE_STACK();
3738 if (!start
&& (fp
= cx
->fp
) != NULL
)
3739 start
= fp
->maybeScopeChain();
3742 /* Find the topmost object in the scope chain. */
3745 start
= obj
->getParent();
3748 obj
= cx
->globalObject
;
3755 OBJ_TO_INNER_OBJECT(cx
, obj
);
3759 if (protoKey
!= JSProto_Null
) {
3760 JS_ASSERT(JSProto_Null
< protoKey
);
3761 JS_ASSERT(protoKey
< JSProto_LIMIT
);
3762 if (!js_GetClassObject(cx
, obj
, protoKey
, &cobj
))
3765 vp
->setObject(*cobj
);
3768 id
= ATOM_TO_JSID(cx
->runtime
->atomState
.classAtoms
[protoKey
]);
3770 JSAtom
*atom
= js_Atomize(cx
, clasp
->name
, strlen(clasp
->name
), 0);
3773 id
= ATOM_TO_JSID(atom
);
3776 JS_ASSERT(obj
->isNative());
3777 if (js_LookupPropertyWithFlags(cx
, obj
, id
, JSRESOLVE_CLASSNAME
,
3778 &pobj
, &prop
) < 0) {
3781 Value v
= UndefinedValue();
3782 if (prop
&& pobj
->isNative()) {
3783 sprop
= (JSScopeProperty
*) prop
;
3784 if (SPROP_HAS_VALID_SLOT(sprop
, pobj
->scope())) {
3785 v
= pobj
->lockedGetSlot(sprop
->slot
);
3786 if (v
.isPrimitive())
3789 JS_UNLOCK_OBJ(cx
, pobj
);
3796 js_ConstructObject(JSContext
*cx
, Class
*clasp
, JSObject
*proto
, JSObject
*parent
,
3797 uintN argc
, Value
*argv
)
3799 AutoArrayRooter
argtvr(cx
, argc
, argv
);
3801 JSProtoKey protoKey
= GetClassProtoKey(clasp
);
3803 /* Protect constructor in case a crazy getter for .prototype uproots it. */
3804 AutoValueRooter
tvr(cx
);
3805 if (!js_FindClassObject(cx
, parent
, protoKey
, tvr
.addr(), clasp
))
3808 const Value
&cval
= tvr
.value();
3809 if (tvr
.value().isPrimitive()) {
3810 js_ReportIsNotFunction(cx
, tvr
.addr(), JSV2F_CONSTRUCT
| JSV2F_SEARCH_STACK
);
3815 * If proto is NULL, set it to Constructor.prototype, just like JSOP_NEW
3816 * does, likewise for the new object's parent.
3818 JSObject
*ctor
= &cval
.toObject();
3820 parent
= ctor
->getParent();
3823 if (!ctor
->getProperty(cx
, ATOM_TO_JSID(cx
->runtime
->atomState
.classPrototypeAtom
),
3827 if (rval
.isObjectOrNull())
3828 proto
= rval
.toObjectOrNull();
3831 JSObject
*obj
= NewObject
<WithProto::Class
>(cx
, clasp
, proto
, parent
);
3836 if (!InternalConstruct(cx
, obj
, cval
, argc
, argv
, &rval
))
3839 if (rval
.isPrimitive())
3843 * If the instance's class differs from what was requested, throw a type
3844 * error. If the given class has both the JSCLASS_HAS_PRIVATE and the
3845 * JSCLASS_CONSTRUCT_PROTOTYPE flags, and the instance does not have its
3846 * private data set at this point, then the constructor was replaced and
3847 * we should throw a type error.
3849 obj
= &rval
.toObject();
3850 if (obj
->getClass() != clasp
||
3851 (!(~clasp
->flags
& (JSCLASS_HAS_PRIVATE
|
3852 JSCLASS_CONSTRUCT_PROTOTYPE
)) &&
3853 !obj
->getPrivate())) {
3854 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
3855 JSMSG_WRONG_CONSTRUCTOR
, clasp
->name
);
3862 * FIXME bug 535629: If one adds props, deletes earlier props, adds more, the
3863 * last added won't recycle the deleted props' slots.
3866 js_AllocSlot(JSContext
*cx
, JSObject
*obj
, uint32
*slotp
)
3868 JSScope
*scope
= obj
->scope();
3869 JS_ASSERT(scope
->object
== obj
);
3871 if (scope
->freeslot
>= obj
->numSlots() &&
3872 !obj
->growSlots(cx
, scope
->freeslot
+ 1)) {
3876 /* js_ReallocSlots or js_FreeSlot should set the free slots to void. */
3877 JS_ASSERT(obj
->getSlot(scope
->freeslot
).isUndefined());
3878 *slotp
= scope
->freeslot
++;
3883 js_FreeSlot(JSContext
*cx
, JSObject
*obj
, uint32 slot
)
3885 JSScope
*scope
= obj
->scope();
3886 JS_ASSERT(scope
->object
== obj
);
3887 obj
->lockedSetSlot(slot
, UndefinedValue());
3888 if (scope
->freeslot
== slot
+ 1)
3889 scope
->freeslot
= slot
;
3893 /* JSBOXEDWORD_INT_MAX as a string */
3894 #define JSBOXEDWORD_INT_MAX_STRING "1073741823"
3897 * Convert string indexes that convert to int jsvals as ints to save memory.
3898 * Care must be taken to use this macro every time a property name is used, or
3899 * else double-sets, incorrect property cache misses, or other mistakes could
3903 js_CheckForStringIndex(jsid id
)
3905 if (!JSID_IS_ATOM(id
))
3908 JSAtom
*atom
= JSID_TO_ATOM(id
);
3909 JSString
*str
= ATOM_TO_STRING(atom
);
3910 const jschar
*s
= str
->flatChars();
3913 JSBool negative
= (ch
== '-');
3920 size_t n
= str
->flatLength() - negative
;
3921 if (n
> sizeof(JSBOXEDWORD_INT_MAX_STRING
) - 1)
3924 const jschar
*cp
= s
;
3925 const jschar
*end
= s
+ n
;
3927 jsuint index
= JS7_UNDEC(*cp
++);
3928 jsuint oldIndex
= 0;
3932 while (JS7_ISDEC(*cp
)) {
3935 index
= 10 * index
+ c
;
3941 * Non-integer indexes can't be represented as integers. Also, distinguish
3942 * index "-0" from "0", because JSBOXEDWORD_INT cannot.
3944 if (cp
!= end
|| (negative
&& index
== 0))
3947 if (oldIndex
< JSID_INT_MAX
/ 10 ||
3948 (oldIndex
== JSID_INT_MAX
/ 10 && c
<= (JSID_INT_MAX
% 10))) {
3951 id
= INT_TO_JSID((jsint
)index
);
3958 PurgeProtoChain(JSContext
*cx
, JSObject
*obj
, jsid id
)
3961 JSScopeProperty
*sprop
;
3964 if (!obj
->isNative()) {
3965 obj
= obj
->getProto();
3968 JS_LOCK_OBJ(cx
, obj
);
3969 scope
= obj
->scope();
3970 sprop
= scope
->lookup(id
);
3972 PCMETER(JS_PROPERTY_CACHE(cx
).pcpurges
++);
3973 scope
->shadowingShapeChange(cx
, sprop
);
3974 JS_UNLOCK_SCOPE(cx
, scope
);
3976 if (!obj
->getParent()) {
3978 * All scope chains end in a global object, so this will change
3979 * the global shape. jstracer.cpp assumes that the global shape
3980 * never changes on trace, so we must deep-bail here.
3986 obj
= obj
->getProto();
3987 JS_UNLOCK_SCOPE(cx
, scope
);
3993 js_PurgeScopeChainHelper(JSContext
*cx
, JSObject
*obj
, jsid id
)
3995 JS_ASSERT(obj
->isDelegate());
3996 PurgeProtoChain(cx
, obj
->getProto(), id
);
3999 * We must purge the scope chain only for Call objects as they are the only
4000 * kind of cacheable non-global object that can gain properties after outer
4001 * properties with the same names have been cached or traced. Call objects
4002 * may gain such properties via eval introducing new vars; see bug 490364.
4004 if (obj
->getClass() == &js_CallClass
) {
4005 while ((obj
= obj
->getParent()) != NULL
) {
4006 if (PurgeProtoChain(cx
, obj
, id
))
4013 js_AddNativeProperty(JSContext
*cx
, JSObject
*obj
, jsid id
,
4014 PropertyOp getter
, PropertyOp setter
, uint32 slot
,
4015 uintN attrs
, uintN flags
, intN shortid
)
4018 JSScopeProperty
*sprop
;
4020 JS_ASSERT(!(flags
& JSScopeProperty::METHOD
));
4023 * Purge the property cache of now-shadowed id in obj's scope chain. Do
4024 * this optimistically (assuming no failure below) before locking obj, so
4025 * we can lock the shadowed scope.
4027 js_PurgeScopeChain(cx
, obj
, id
);
4029 JS_LOCK_OBJ(cx
, obj
);
4030 scope
= js_GetMutableScope(cx
, obj
);
4034 /* Convert string indices to integers if appropriate. */
4035 id
= js_CheckForStringIndex(id
);
4036 sprop
= scope
->putProperty(cx
, id
, getter
, setter
, slot
, attrs
, flags
, shortid
);
4038 JS_UNLOCK_OBJ(cx
, obj
);
4043 js_ChangeNativePropertyAttrs(JSContext
*cx
, JSObject
*obj
,
4044 JSScopeProperty
*sprop
, uintN attrs
, uintN mask
,
4045 PropertyOp getter
, PropertyOp setter
)
4049 JS_LOCK_OBJ(cx
, obj
);
4050 scope
= js_GetMutableScope(cx
, obj
);
4054 sprop
= scope
->changeProperty(cx
, sprop
, attrs
, mask
, getter
, setter
);
4056 JS_UNLOCK_OBJ(cx
, obj
);
4061 js_DefineProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, const Value
*value
,
4062 PropertyOp getter
, PropertyOp setter
, uintN attrs
)
4064 return js_DefineNativeProperty(cx
, obj
, id
, *value
, getter
, setter
, attrs
,
4069 * Backward compatibility requires allowing addProperty hooks to mutate the
4070 * nominal initial value of a slot-full property, while GC safety wants that
4071 * value to be stored before the call-out through the hook. Optimize to do
4072 * both while saving cycles for classes that stub their addProperty hook.
4075 AddPropertyHelper(JSContext
*cx
, Class
*clasp
, JSObject
*obj
, JSScope
*scope
,
4076 JSScopeProperty
*sprop
, Value
*vp
)
4078 if (clasp
->addProperty
!= PropertyStub
) {
4079 Value nominal
= *vp
;
4081 if (!callJSPropertyOp(cx
, clasp
->addProperty
, obj
, SPROP_USERID(sprop
), vp
))
4083 if (*vp
!= nominal
) {
4084 if (SPROP_HAS_VALID_SLOT(sprop
, scope
))
4085 obj
->lockedSetSlot(sprop
->slot
, *vp
);
4092 js_DefineNativeProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, const Value
&value
,
4093 PropertyOp getter
, PropertyOp setter
, uintN attrs
,
4094 uintN flags
, intN shortid
, JSProperty
**propp
,
4095 uintN defineHow
/* = 0 */)
4099 JSScopeProperty
*sprop
;
4103 JS_ASSERT((defineHow
& ~(JSDNP_CACHE_RESULT
| JSDNP_DONT_PURGE
| JSDNP_SET_METHOD
)) == 0);
4104 LeaveTraceIfGlobalObject(cx
, obj
);
4106 /* Convert string indices to integers if appropriate. */
4107 id
= js_CheckForStringIndex(id
);
4110 * If defining a getter or setter, we must check for its counterpart and
4111 * update the attributes and property ops. A getter or setter is really
4112 * only half of a property.
4115 if (attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)) {
4120 * If JS_THREADSAFE and id is found, js_LookupProperty returns with
4121 * sprop non-null and pobj locked. If pobj == obj, the property is
4122 * already in obj and obj has its own (mutable) scope. So if we are
4123 * defining a getter whose setter was already defined, or vice versa,
4124 * finish the job via js_ChangeScopePropertyAttributes, and refresh
4125 * the property cache line for (obj, id) to map sprop.
4127 if (!js_LookupProperty(cx
, obj
, id
, &pobj
, &prop
))
4129 sprop
= (JSScopeProperty
*) prop
;
4130 if (sprop
&& pobj
== obj
&& sprop
->isAccessorDescriptor()) {
4131 sprop
= obj
->scope()->changeProperty(cx
, sprop
, attrs
,
4132 JSPROP_GETTER
| JSPROP_SETTER
,
4133 (attrs
& JSPROP_GETTER
)
4136 (attrs
& JSPROP_SETTER
)
4140 /* NB: obj == pobj, so we can share unlock code at the bottom. */
4144 pobj
->dropProperty(cx
, prop
);
4151 * Purge the property cache of any properties named by id that are about
4152 * to be shadowed in obj's scope chain unless it is known a priori that it
4153 * is not possible. We do this before locking obj to avoid nesting locks.
4155 if (!(defineHow
& JSDNP_DONT_PURGE
))
4156 js_PurgeScopeChain(cx
, obj
, id
);
4159 * Check whether a readonly property or setter is being defined on a known
4160 * prototype object. See the comment in jscntxt.h before protoHazardShape's
4161 * member declaration.
4163 if (obj
->isDelegate() && (attrs
& (JSPROP_READONLY
| JSPROP_SETTER
)))
4164 cx
->runtime
->protoHazardShape
= js_GenerateShape(cx
, false);
4166 /* Lock if object locking is required by this implementation. */
4167 JS_LOCK_OBJ(cx
, obj
);
4169 /* Use the object's class getter and setter by default. */
4170 clasp
= obj
->getClass();
4171 if (!(defineHow
& JSDNP_SET_METHOD
)) {
4172 if (!getter
&& !(attrs
& JSPROP_GETTER
))
4173 getter
= clasp
->getProperty
;
4174 if (!setter
&& !(attrs
& JSPROP_SETTER
))
4175 setter
= clasp
->setProperty
;
4178 /* Get obj's own scope if it has one, or create a new one for obj. */
4179 scope
= js_GetMutableScope(cx
, obj
);
4185 /* Add a new property, or replace an existing one of the same id. */
4186 if (defineHow
& JSDNP_SET_METHOD
) {
4187 JS_ASSERT(clasp
== &js_ObjectClass
);
4188 JS_ASSERT(IsFunctionObject(value
));
4189 JS_ASSERT(!(attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)));
4190 JS_ASSERT(!getter
&& !setter
);
4192 JSObject
*funobj
= &value
.toObject();
4193 if (FUN_OBJECT(GET_FUNCTION_PRIVATE(cx
, funobj
)) == funobj
) {
4194 flags
|= JSScopeProperty::METHOD
;
4195 getter
= CastAsPropertyOp(funobj
);
4199 added
= !scope
->hasProperty(id
);
4200 uint32 oldShape
= scope
->shape
;
4201 sprop
= scope
->putProperty(cx
, id
, getter
, setter
, SPROP_INVALID_SLOT
,
4202 attrs
, flags
, shortid
);
4207 * If sprop is a method, the above call to putProperty suffices to
4208 * update the shape if necessary. But if scope->branded(), the shape
4209 * may not have changed and we may be overwriting a function-valued
4210 * property. See bug 560998.
4212 if (scope
->shape
== oldShape
&& scope
->branded() && sprop
->slot
!= SPROP_INVALID_SLOT
)
4213 scope
->methodWriteBarrier(cx
, sprop
->slot
, value
);
4216 /* Store value before calling addProperty, in case the latter GC's. */
4217 if (SPROP_HAS_VALID_SLOT(sprop
, scope
))
4218 obj
->lockedSetSlot(sprop
->slot
, value
);
4220 /* XXXbe called with lock held */
4222 if (!AddPropertyHelper(cx
, clasp
, obj
, scope
, sprop
, &valueCopy
)) {
4223 scope
->removeProperty(cx
, id
);
4227 if (defineHow
& JSDNP_CACHE_RESULT
) {
4229 JS_ASSERT_NOT_ON_TRACE(cx
);
4230 PropertyCacheEntry
*entry
=
4232 JS_PROPERTY_CACHE(cx
).fill(cx
, obj
, 0, 0, obj
, sprop
, added
);
4233 TRACE_2(SetPropHit
, entry
, sprop
);
4236 *propp
= (JSProperty
*) sprop
;
4238 JS_UNLOCK_OBJ(cx
, obj
);
4241 error
: // TRACE_2 jumps here on error, as well.
4242 JS_UNLOCK_OBJ(cx
, obj
);
4246 JS_FRIEND_API(JSBool
)
4247 js_LookupProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, JSObject
**objp
,
4250 return js_LookupPropertyWithFlags(cx
, obj
, id
, cx
->resolveFlags
,
4254 #define SCOPE_DEPTH_ACCUM(bs,val) \
4255 JS_SCOPE_DEPTH_METERING(JS_BASIC_STATS_ACCUM(bs, val))
4258 * Call obj's resolve hook. obj is a native object and the caller holds its
4261 * cx, start, id, and flags are the parameters initially passed to the ongoing
4262 * lookup; objp and propp are its out parameters. obj is an object along
4263 * start's prototype chain.
4265 * There are four possible outcomes:
4267 * - On failure, report an error or exception, unlock obj, and return false.
4269 * - If we are alrady resolving a property of *curobjp, set *recursedp = true,
4270 * unlock obj, and return true.
4272 * - If the resolve hook finds or defines the sought property, set *objp and
4273 * *propp appropriately, set *recursedp = false, and return true with *objp's
4276 * - Otherwise no property was resolved. Set *propp = NULL and *recursedp = false
4280 CallResolveOp(JSContext
*cx
, JSObject
*start
, JSObject
*obj
, jsid id
, uintN flags
,
4281 JSObject
**objp
, JSProperty
**propp
, bool *recursedp
)
4283 Class
*clasp
= obj
->getClass();
4284 JSResolveOp resolve
= clasp
->resolve
;
4285 JSScope
*scope
= obj
->scope();
4288 * Avoid recursion on (obj, id) already being resolved on cx.
4290 * Once we have successfully added an entry for (obj, key) to
4291 * cx->resolvingTable, control must go through cleanup: before
4292 * returning. But note that JS_DHASH_ADD may find an existing
4293 * entry, in which case we bail to suppress runaway recursion.
4295 JSResolvingKey key
= {obj
, id
};
4296 JSResolvingEntry
*entry
;
4297 if (!js_StartResolving(cx
, &key
, JSRESFLAG_LOOKUP
, &entry
)) {
4298 JS_UNLOCK_OBJ(cx
, obj
);
4302 /* Already resolving id in obj -- suppress recursion. */
4303 JS_UNLOCK_OBJ(cx
, obj
);
4307 uint32 generation
= cx
->resolvingTable
->generation
;
4313 JSScopeProperty
*sprop
= NULL
;
4314 if (clasp
->flags
& JSCLASS_NEW_RESOLVE
) {
4315 JSNewResolveOp newresolve
= (JSNewResolveOp
)resolve
;
4316 if (flags
== JSRESOLVE_INFER
)
4317 flags
= js_InferFlags(cx
, flags
);
4318 JSObject
*obj2
= (clasp
->flags
& JSCLASS_NEW_RESOLVE_GETS_START
) ? start
: NULL
;
4319 JS_UNLOCK_OBJ(cx
, obj
);
4322 /* Protect id and all atoms from a GC nested in resolve. */
4323 AutoKeepAtoms
keep(cx
->runtime
);
4324 ok
= newresolve(cx
, obj
, id
, flags
, &obj2
);
4329 JS_LOCK_OBJ(cx
, obj
);
4331 /* Resolved: juggle locks and lookup id again. */
4333 JS_UNLOCK_OBJ(cx
, obj
);
4334 if (obj2
->isNative())
4335 JS_LOCK_OBJ(cx
, obj2
);
4337 if (!obj2
->isNative()) {
4338 /* Whoops, newresolve handed back a foreign obj2. */
4339 JS_ASSERT(obj2
!= obj
);
4340 ok
= obj2
->lookupProperty(cx
, id
, objp
, propp
);
4343 JS_LOCK_OBJ(cx
, obj2
);
4346 * Require that obj2 have its own scope now, as we
4347 * do for old-style resolve. If it doesn't, then
4348 * id was not truly resolved, and we'll find it in
4349 * the proto chain, or miss it if obj2's proto is
4350 * not on obj's proto chain. That last case is a
4353 scope
= obj2
->scope();
4354 if (!scope
->isSharedEmpty())
4355 sprop
= scope
->lookup(id
);
4358 JS_ASSERT(scope
== obj2
->scope());
4359 JS_ASSERT(!scope
->isSharedEmpty());
4361 } else if (obj2
!= obj
) {
4362 if (obj2
->isNative())
4363 JS_UNLOCK_OBJ(cx
, obj2
);
4364 JS_LOCK_OBJ(cx
, obj
);
4369 * Old resolve always requires id re-lookup if obj owns
4370 * its scope after resolve returns.
4372 JS_UNLOCK_OBJ(cx
, obj
);
4373 ok
= resolve(cx
, obj
, id
);
4376 JS_LOCK_OBJ(cx
, obj
);
4377 JS_ASSERT(obj
->isNative());
4378 scope
= obj
->scope();
4379 if (!scope
->isSharedEmpty())
4380 sprop
= scope
->lookup(id
);
4385 JS_ASSERT(obj
->scope() == scope
);
4387 *propp
= (JSProperty
*) sprop
;
4389 js_StopResolving(cx
, &key
, JSRESFLAG_LOOKUP
, entry
, generation
);
4394 js_LookupPropertyWithFlags(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN flags
,
4395 JSObject
**objp
, JSProperty
**propp
)
4397 /* Convert string indices to integers if appropriate. */
4398 id
= js_CheckForStringIndex(id
);
4400 /* Search scopes starting with obj and following the prototype link. */
4401 JSObject
*start
= obj
;
4403 for (protoIndex
= 0; ; protoIndex
++) {
4404 JS_LOCK_OBJ(cx
, obj
);
4405 JSScopeProperty
*sprop
= obj
->scope()->lookup(id
);
4407 SCOPE_DEPTH_ACCUM(&cx
->runtime
->protoLookupDepthStats
, protoIndex
);
4409 *propp
= (JSProperty
*) sprop
;
4413 /* Try obj's class resolve hook if id was not found in obj's scope. */
4414 if (!sprop
&& obj
->getClass()->resolve
!= JS_ResolveStub
) {
4416 if (!CallResolveOp(cx
, start
, obj
, id
, flags
, objp
, propp
, &recursed
))
4421 /* Recalculate protoIndex in case it was resolved on some other object. */
4423 for (JSObject
*proto
= start
; proto
&& proto
!= *objp
; proto
= proto
->getProto())
4425 SCOPE_DEPTH_ACCUM(&cx
->runtime
->protoLookupDepthStats
, protoIndex
);
4430 JSObject
*proto
= obj
->getProto();
4431 JS_UNLOCK_OBJ(cx
, obj
);
4434 if (!proto
->isNative()) {
4435 if (!proto
->lookupProperty(cx
, id
, objp
, propp
))
4437 return protoIndex
+ 1;
4448 PropertyCacheEntry
*
4449 js_FindPropertyHelper(JSContext
*cx
, jsid id
, JSBool cacheResult
,
4450 JSObject
**objp
, JSObject
**pobjp
, JSProperty
**propp
)
4452 JSObject
*scopeChain
, *obj
, *parent
, *pobj
;
4453 PropertyCacheEntry
*entry
;
4454 int scopeIndex
, protoIndex
;
4457 JS_ASSERT_IF(cacheResult
, !JS_ON_TRACE(cx
));
4458 scopeChain
= js_GetTopStackFrame(cx
)->getScopeChain();
4460 /* Scan entries on the scope chain that we can cache across. */
4461 entry
= JS_NO_PROP_CACHE_FILL
;
4463 parent
= obj
->getParent();
4464 for (scopeIndex
= 0;
4466 ? js_IsCacheableNonGlobalScope(obj
)
4467 : !obj
->getOps()->lookupProperty
;
4470 js_LookupPropertyWithFlags(cx
, obj
, id
, cx
->resolveFlags
,
4478 Class
*clasp
= obj
->getClass();
4479 JS_ASSERT(pobj
->isNative());
4480 JS_ASSERT(pobj
->getClass() == clasp
);
4481 if (clasp
== &js_BlockClass
) {
4483 * A block instance on the scope chain is immutable and
4484 * the compile-time prototype provides all its properties.
4486 JS_ASSERT(pobj
== obj
->getProto());
4487 JS_ASSERT(protoIndex
== 1);
4489 /* Call and DeclEnvClass objects have no prototypes. */
4490 JS_ASSERT(!obj
->getProto());
4491 JS_ASSERT(protoIndex
== 0);
4494 JS_ASSERT(obj
->isNative());
4498 * We must check if pobj is native as a global object can have
4499 * non-native prototype.
4501 if (cacheResult
&& pobj
->isNative()) {
4502 entry
= JS_PROPERTY_CACHE(cx
).fill(cx
, scopeChain
, scopeIndex
,
4504 (JSScopeProperty
*) prop
);
4506 SCOPE_DEPTH_ACCUM(&cx
->runtime
->scopeSearchDepthStats
, scopeIndex
);
4515 parent
= obj
->getParent();
4519 if (!obj
->lookupProperty(cx
, id
, &pobj
, &prop
))
4522 PCMETER(JS_PROPERTY_CACHE(cx
).nofills
++);
4527 * We conservatively assume that a resolve hook could mutate the scope
4528 * chain during JSObject::lookupProperty. So we read parent here again.
4530 parent
= obj
->getParent();
4539 JS_ASSERT(!!pobj
== !!prop
);
4546 JS_FRIEND_API(JSBool
)
4547 js_FindProperty(JSContext
*cx
, jsid id
, JSObject
**objp
, JSObject
**pobjp
,
4550 return !!js_FindPropertyHelper(cx
, id
, false, objp
, pobjp
, propp
);
4554 js_FindIdentifierBase(JSContext
*cx
, JSObject
*scopeChain
, jsid id
)
4557 * This function should not be called for a global object or from the
4558 * trace and should have a valid cache entry for native scopeChain.
4560 JS_ASSERT(scopeChain
->getParent());
4561 JS_ASSERT(!JS_ON_TRACE(cx
));
4563 JSObject
*obj
= scopeChain
;
4566 * Loop over cacheable objects on the scope chain until we find a
4567 * property. We also stop when we reach the global object skipping any
4568 * farther checks or lookups. For details see the JSOP_BINDNAME case of
4571 * The test order here matters because js_IsCacheableNonGlobalScope
4572 * must not be passed a global object (i.e. one with null parent).
4574 for (int scopeIndex
= 0;
4575 !obj
->getParent() || js_IsCacheableNonGlobalScope(obj
);
4579 int protoIndex
= js_LookupPropertyWithFlags(cx
, obj
, id
,
4585 if (!pobj
->isNative()) {
4586 JS_ASSERT(!obj
->getParent());
4589 JS_ASSERT_IF(obj
->getParent(), pobj
->getClass() == obj
->getClass());
4591 PropertyCacheEntry
*entry
=
4593 JS_PROPERTY_CACHE(cx
).fill(cx
, scopeChain
, scopeIndex
, protoIndex
, pobj
,
4594 (JSScopeProperty
*) prop
);
4596 JS_UNLOCK_OBJ(cx
, pobj
);
4600 JSObject
*parent
= obj
->getParent();
4606 /* Loop until we find a property or reach the global object. */
4610 if (!obj
->lookupProperty(cx
, id
, &pobj
, &prop
))
4613 pobj
->dropProperty(cx
, prop
);
4618 * We conservatively assume that a resolve hook could mutate the scope
4619 * chain during JSObject::lookupProperty. So we must check if parent is
4620 * not null here even if it wasn't before the lookup.
4622 JSObject
*parent
= obj
->getParent();
4626 } while (obj
->getParent());
4631 js_NativeGet(JSContext
*cx
, JSObject
*obj
, JSObject
*pobj
,
4632 JSScopeProperty
*sprop
, uintN getHow
, Value
*vp
)
4634 LeaveTraceIfGlobalObject(cx
, pobj
);
4640 JS_ASSERT(pobj
->isNative());
4641 JS_ASSERT(JS_IS_OBJ_LOCKED(cx
, pobj
));
4642 scope
= pobj
->scope();
4645 if (slot
!= SPROP_INVALID_SLOT
)
4646 *vp
= pobj
->lockedGetSlot(slot
);
4649 if (sprop
->hasDefaultGetter())
4652 if (JS_UNLIKELY(sprop
->isMethod()) && (getHow
& JSGET_NO_METHOD_BARRIER
)) {
4653 JS_ASSERT(&sprop
->methodObject() == &vp
->toObject());
4657 sample
= cx
->runtime
->propertyRemovals
;
4658 JS_UNLOCK_SCOPE(cx
, scope
);
4660 AutoScopePropertyRooter
tvr(cx
, sprop
);
4661 AutoObjectRooter
tvr2(cx
, pobj
);
4662 if (!sprop
->get(cx
, obj
, pobj
, vp
))
4665 JS_LOCK_SCOPE(cx
, scope
);
4667 if (SLOT_IN_SCOPE(slot
, scope
) &&
4668 (JS_LIKELY(cx
->runtime
->propertyRemovals
== sample
) ||
4669 scope
->hasProperty(sprop
))) {
4670 if (!scope
->methodWriteBarrier(cx
, sprop
, *vp
)) {
4671 JS_UNLOCK_SCOPE(cx
, scope
);
4674 pobj
->lockedSetSlot(slot
, *vp
);
4681 js_NativeSet(JSContext
*cx
, JSObject
*obj
, JSScopeProperty
*sprop
, bool added
,
4684 LeaveTraceIfGlobalObject(cx
, obj
);
4690 JS_ASSERT(obj
->isNative());
4691 JS_ASSERT(JS_IS_OBJ_LOCKED(cx
, obj
));
4692 scope
= obj
->scope();
4695 if (slot
!= SPROP_INVALID_SLOT
) {
4696 OBJ_CHECK_SLOT(obj
, slot
);
4698 /* If sprop has a stub setter, keep scope locked and just store *vp. */
4699 if (sprop
->hasDefaultSetter()) {
4700 if (!added
&& !scope
->methodWriteBarrier(cx
, sprop
, *vp
)) {
4701 JS_UNLOCK_SCOPE(cx
, scope
);
4704 obj
->lockedSetSlot(slot
, *vp
);
4709 * Allow API consumers to create shared properties with stub setters.
4710 * Such properties effectively function as data descriptors which are
4711 * not writable, so attempting to set such a property should do nothing
4712 * or throw if we're in strict mode.
4714 if (!sprop
->hasGetterValue() && sprop
->hasDefaultSetter())
4715 return js_ReportGetterOnlyAssignment(cx
);
4718 sample
= cx
->runtime
->propertyRemovals
;
4719 JS_UNLOCK_SCOPE(cx
, scope
);
4721 AutoScopePropertyRooter
tvr(cx
, sprop
);
4722 if (!sprop
->set(cx
, obj
, vp
))
4726 JS_LOCK_SCOPE(cx
, scope
);
4727 if (SLOT_IN_SCOPE(slot
, scope
) &&
4728 (JS_LIKELY(cx
->runtime
->propertyRemovals
== sample
) ||
4729 scope
->hasProperty(sprop
))) {
4730 if (!added
&& !scope
->methodWriteBarrier(cx
, sprop
, *vp
)) {
4731 JS_UNLOCK_SCOPE(cx
, scope
);
4734 obj
->lockedSetSlot(slot
, *vp
);
4741 js_GetPropertyHelper(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN getHow
,
4744 JSObject
*aobj
, *obj2
;
4747 JSScopeProperty
*sprop
;
4749 JS_ASSERT_IF(getHow
& JSGET_CACHE_RESULT
, !JS_ON_TRACE(cx
));
4751 /* Convert string indices to integers if appropriate. */
4752 id
= js_CheckForStringIndex(id
);
4754 aobj
= js_GetProtoIfDenseArray(obj
);
4755 protoIndex
= js_LookupPropertyWithFlags(cx
, aobj
, id
, cx
->resolveFlags
,
4762 if (!callJSPropertyOp(cx
, obj
->getClass()->getProperty
, obj
, id
, vp
))
4765 PCMETER(getHow
& JSGET_CACHE_RESULT
&& JS_PROPERTY_CACHE(cx
).nofills
++);
4768 * Give a strict warning if foo.bar is evaluated by a script for an
4769 * object foo with no property named 'bar'.
4772 if (vp
->isUndefined() && ((pc
= js_GetCurrentBytecodePC(cx
)) != NULL
)) {
4777 if (op
== JSOP_TRAP
) {
4778 JS_ASSERT_NOT_ON_TRACE(cx
);
4779 op
= JS_GetTrapOpcode(cx
, cx
->fp
->script
, pc
);
4781 if (op
== JSOP_GETXPROP
) {
4782 flags
= JSREPORT_ERROR
;
4784 if (!JS_HAS_STRICT_OPTION(cx
) ||
4785 (op
!= JSOP_GETPROP
&& op
!= JSOP_GETELEM
) ||
4786 js_CurrentPCIsInImacro(cx
)) {
4791 * XXX do not warn about missing __iterator__ as the function
4792 * may be called from JS_GetMethodById. See bug 355145.
4794 if (JSID_IS_ATOM(id
, cx
->runtime
->atomState
.iteratorAtom
))
4797 /* Do not warn about tests like (obj[prop] == undefined). */
4798 if (cx
->resolveFlags
== JSRESOLVE_INFER
) {
4800 pc
+= js_CodeSpec
[op
].length
;
4801 if (Detecting(cx
, pc
))
4803 } else if (cx
->resolveFlags
& JSRESOLVE_DETECTING
) {
4807 flags
= JSREPORT_WARNING
| JSREPORT_STRICT
;
4810 /* Ok, bad undefined property reference: whine about it. */
4811 if (!js_ReportValueErrorFlags(cx
, flags
, JSMSG_UNDEFINED_PROP
,
4812 JSDVG_IGNORE_STACK
, IdToValue(id
),
4813 NULL
, NULL
, NULL
)) {
4820 if (!obj2
->isNative())
4821 return obj2
->getProperty(cx
, id
, vp
);
4823 sprop
= (JSScopeProperty
*) prop
;
4825 if (getHow
& JSGET_CACHE_RESULT
) {
4826 JS_ASSERT_NOT_ON_TRACE(cx
);
4827 JS_PROPERTY_CACHE(cx
).fill(cx
, aobj
, 0, protoIndex
, obj2
, sprop
);
4830 if (!js_NativeGet(cx
, obj
, obj2
, sprop
, getHow
, vp
))
4833 JS_UNLOCK_OBJ(cx
, obj2
);
4838 js_GetProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
4840 return js_GetPropertyHelper(cx
, obj
, id
, JSGET_METHOD_BARRIER
, vp
);
4844 js_GetMethod(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN getHow
, Value
*vp
)
4846 JSAutoResolveFlags
rf(cx
, JSRESOLVE_QUALIFIED
);
4848 PropertyIdOp op
= obj
->getOps()->getProperty
;
4850 #if JS_HAS_XML_SUPPORT
4851 JS_ASSERT(!obj
->isXML());
4853 return js_GetPropertyHelper(cx
, obj
, id
, getHow
, vp
);
4855 JS_ASSERT_IF(getHow
& JSGET_CACHE_RESULT
, obj
->isDenseArray());
4856 #if JS_HAS_XML_SUPPORT
4858 return js_GetXMLMethod(cx
, obj
, id
, vp
);
4860 return op(cx
, obj
, id
, vp
);
4864 js_CheckUndeclaredVarAssignment(JSContext
*cx
, JSString
*propname
)
4866 JSStackFrame
*const fp
= js_GetTopStackFrame(cx
);
4870 /* If neither cx nor the code is strict, then no check is needed. */
4871 if (!(fp
->script
&& fp
->script
->strictModeCode
) &&
4872 !JS_HAS_STRICT_OPTION(cx
)) {
4876 const char *bytes
= js_GetStringBytes(cx
, propname
);
4878 JS_ReportErrorFlagsAndNumber(cx
,
4879 (JSREPORT_WARNING
| JSREPORT_STRICT
4880 | JSREPORT_STRICT_MODE_ERROR
),
4881 js_GetErrorMessage
, NULL
,
4882 JSMSG_UNDECLARED_VAR
, bytes
);
4888 ReportReadOnly(JSContext
* cx
, jsid id
, uintN flags
)
4890 return js_ReportValueErrorFlags(cx
, flags
, JSMSG_READ_ONLY
,
4891 JSDVG_IGNORE_STACK
, IdToValue(id
), NULL
,
4898 * Note: all non-error exits in this function must notify the tracer using
4899 * SetPropHit when called from the interpreter, which is detected by testing
4900 * (defineHow & JSDNP_CACHE_RESULT).
4903 js_SetPropertyHelper(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN defineHow
,
4909 JSScopeProperty
*sprop
;
4914 PropertyOp getter
, setter
;
4917 JS_ASSERT((defineHow
&
4918 ~(JSDNP_CACHE_RESULT
| JSDNP_SET_METHOD
| JSDNP_UNQUALIFIED
)) == 0);
4919 if (defineHow
& JSDNP_CACHE_RESULT
)
4920 JS_ASSERT_NOT_ON_TRACE(cx
);
4922 /* Convert string indices to integers if appropriate. */
4923 id
= js_CheckForStringIndex(id
);
4926 * We peek at obj->scope() without locking obj. Any race means a failure
4927 * to seal before sharing, which is inherently ambiguous.
4929 if (obj
->scope()->sealed() && obj
->scope()->object
== obj
)
4930 return ReportReadOnly(cx
, id
, JSREPORT_ERROR
);
4932 protoIndex
= js_LookupPropertyWithFlags(cx
, obj
, id
, cx
->resolveFlags
,
4937 if (!pobj
->isNative())
4940 /* We should never add properties to lexical blocks. */
4941 JS_ASSERT(obj
->getClass() != &js_BlockClass
);
4943 if (!obj
->getParent() &&
4944 (defineHow
& JSDNP_UNQUALIFIED
) &&
4945 !js_CheckUndeclaredVarAssignment(cx
, JSID_TO_STRING(id
))) {
4949 sprop
= (JSScopeProperty
*) prop
;
4952 * Now either sprop is null, meaning id was not found in obj or one of its
4953 * prototypes; or sprop is non-null, meaning id was found in pobj's scope.
4954 * If JS_THREADSAFE and sprop is non-null, then scope is locked, and sprop
4955 * is held: we must JSObject::dropProperty or JS_UNLOCK_SCOPE before we
4956 * return (the two are equivalent for native objects, but we use
4957 * JS_UNLOCK_SCOPE because it is cheaper).
4959 attrs
= JSPROP_ENUMERATE
;
4962 clasp
= obj
->getClass();
4963 getter
= clasp
->getProperty
;
4964 setter
= clasp
->setProperty
;
4968 * Set scope for use below. It was locked by js_LookupProperty, and
4969 * we know pobj owns it (i.e., scope->object == pobj). Therefore we
4970 * optimize JS_UNLOCK_OBJ(cx, pobj) into JS_UNLOCK_SCOPE(cx, scope).
4972 scope
= pobj
->scope();
4974 /* ES5 8.12.4 [[Put]] step 2. */
4975 if (sprop
->isAccessorDescriptor()) {
4976 if (sprop
->hasDefaultSetter()) {
4977 JS_UNLOCK_SCOPE(cx
, scope
);
4978 if (defineHow
& JSDNP_CACHE_RESULT
)
4979 TRACE_2(SetPropHit
, JS_NO_PROP_CACHE_FILL
, sprop
);
4980 return js_ReportGetterOnlyAssignment(cx
);
4983 JS_ASSERT(sprop
->isDataDescriptor());
4985 if (!sprop
->writable()) {
4986 JS_UNLOCK_SCOPE(cx
, scope
);
4988 PCMETER((defineHow
& JSDNP_CACHE_RESULT
) && JS_PROPERTY_CACHE(cx
).rofills
++);
4989 if (defineHow
& JSDNP_CACHE_RESULT
) {
4990 JS_ASSERT_NOT_ON_TRACE(cx
);
4991 TRACE_2(SetPropHit
, JS_NO_PROP_CACHE_FILL
, sprop
);
4994 /* Warn in strict mode, otherwise do nothing. */
4995 if (JS_HAS_STRICT_OPTION(cx
))
4996 return ReportReadOnly(cx
, id
, JSREPORT_STRICT
| JSREPORT_WARNING
);
5000 error
: // TRACE_2 jumps here in case of error.
5005 if (scope
->sealed() && !sprop
->hasSlot()) {
5006 JS_UNLOCK_SCOPE(cx
, scope
);
5007 return ReportReadOnly(cx
, id
, JSREPORT_ERROR
);
5010 attrs
= sprop
->attributes();
5013 * We found id in a prototype object: prepare to share or shadow.
5015 * NB: Thanks to the immutable, garbage-collected property tree
5016 * maintained by jsscope.c in cx->runtime, we needn't worry about
5017 * sprop going away behind our back after we've unlocked scope.
5019 JS_UNLOCK_SCOPE(cx
, scope
);
5021 /* Don't clone a prototype property that doesn't have a slot. */
5022 if (!sprop
->hasSlot()) {
5023 if (defineHow
& JSDNP_CACHE_RESULT
) {
5025 JS_ASSERT_NOT_ON_TRACE(cx
);
5026 PropertyCacheEntry
*entry
=
5028 JS_PROPERTY_CACHE(cx
).fill(cx
, obj
, 0, protoIndex
, pobj
, sprop
);
5029 TRACE_2(SetPropHit
, entry
, sprop
);
5032 if (sprop
->hasDefaultSetter() && !sprop
->hasGetterValue())
5035 return sprop
->set(cx
, obj
, vp
);
5038 /* Restore attrs to the ECMA default for new properties. */
5039 attrs
= JSPROP_ENUMERATE
;
5042 * Preserve the shortid, getter, and setter when shadowing any
5043 * property that has a shortid. An old API convention requires
5044 * that the property's getter and setter functions receive the
5045 * shortid, not id, when they are called on the shadow we are
5046 * about to create in obj's scope.
5048 if (sprop
->hasShortID()) {
5049 flags
= JSScopeProperty::HAS_SHORTID
;
5050 shortid
= sprop
->shortid
;
5051 getter
= sprop
->getter();
5052 setter
= sprop
->setter();
5056 * Forget we found the proto-property now that we've copied any
5057 * needed member values.
5061 #ifdef __GNUC__ /* suppress bogus gcc warnings */
5070 * Purge the property cache of now-shadowed id in obj's scope chain.
5071 * Do this early, before locking obj to avoid nesting locks.
5073 js_PurgeScopeChain(cx
, obj
, id
);
5075 /* Find or make a property descriptor with the right heritage. */
5076 JS_LOCK_OBJ(cx
, obj
);
5077 scope
= js_GetMutableScope(cx
, obj
);
5079 JS_UNLOCK_OBJ(cx
, obj
);
5084 * Check for Object class here to avoid defining a method on a class
5085 * with magic resolve, addProperty, getProperty, etc. hooks.
5087 if ((defineHow
& JSDNP_SET_METHOD
) && obj
->canHaveMethodBarrier()) {
5088 JS_ASSERT(IsFunctionObject(*vp
));
5089 JS_ASSERT(!(attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)));
5091 JSObject
*funobj
= &vp
->toObject();
5092 JSFunction
*fun
= GET_FUNCTION_PRIVATE(cx
, funobj
);
5093 if (fun
== funobj
) {
5094 flags
|= JSScopeProperty::METHOD
;
5095 getter
= CastAsPropertyOp(funobj
);
5099 sprop
= scope
->putProperty(cx
, id
, getter
, setter
, SPROP_INVALID_SLOT
,
5100 attrs
, flags
, shortid
);
5102 JS_UNLOCK_SCOPE(cx
, scope
);
5107 * Initialize the new property value (passed to setter) to undefined.
5108 * Note that we store before calling addProperty, to match the order
5109 * in js_DefineNativeProperty.
5111 if (SPROP_HAS_VALID_SLOT(sprop
, scope
))
5112 obj
->lockedSetSlot(sprop
->slot
, UndefinedValue());
5114 /* XXXbe called with obj locked */
5115 if (!AddPropertyHelper(cx
, clasp
, obj
, scope
, sprop
, vp
)) {
5116 scope
->removeProperty(cx
, id
);
5117 JS_UNLOCK_SCOPE(cx
, scope
);
5123 if (defineHow
& JSDNP_CACHE_RESULT
) {
5125 JS_ASSERT_NOT_ON_TRACE(cx
);
5126 PropertyCacheEntry
*entry
=
5128 JS_PROPERTY_CACHE(cx
).fill(cx
, obj
, 0, 0, obj
, sprop
, added
);
5129 TRACE_2(SetPropHit
, entry
, sprop
);
5132 if (!js_NativeSet(cx
, obj
, sprop
, added
, vp
))
5135 JS_UNLOCK_SCOPE(cx
, scope
);
5140 js_SetProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
5142 return js_SetPropertyHelper(cx
, obj
, id
, 0, vp
);
5146 js_GetAttributes(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN
*attrsp
)
5149 if (!js_LookupProperty(cx
, obj
, id
, &obj
, &prop
))
5155 if (!obj
->isNative())
5156 return obj
->getAttributes(cx
, id
, attrsp
);
5158 JSScopeProperty
*sprop
= (JSScopeProperty
*)prop
;
5159 *attrsp
= sprop
->attributes();
5160 JS_UNLOCK_OBJ(cx
, obj
);
5165 js_SetNativeAttributes(JSContext
*cx
, JSObject
*obj
, JSScopeProperty
*sprop
,
5168 JS_ASSERT(obj
->isNative());
5169 sprop
= js_ChangeNativePropertyAttrs(cx
, obj
, sprop
, attrs
, 0,
5170 sprop
->getter(), sprop
->setter());
5171 JS_UNLOCK_OBJ(cx
, obj
);
5172 return (sprop
!= NULL
);
5176 js_SetAttributes(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN
*attrsp
)
5179 if (!js_LookupProperty(cx
, obj
, id
, &obj
, &prop
))
5183 return obj
->isNative()
5184 ? js_SetNativeAttributes(cx
, obj
, (JSScopeProperty
*) prop
, *attrsp
)
5185 : obj
->setAttributes(cx
, id
, attrsp
);
5189 js_DeleteProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*rval
)
5193 JSScopeProperty
*sprop
;
5197 rval
->setBoolean(true);
5199 /* Convert string indices to integers if appropriate. */
5200 id
= js_CheckForStringIndex(id
);
5202 if (!js_LookupProperty(cx
, obj
, id
, &proto
, &prop
))
5204 if (!prop
|| proto
!= obj
) {
5206 * If the property was found in a native prototype, check whether it's
5207 * shared and permanent. Such a property stands for direct properties
5208 * in all delegating objects, matching ECMA semantics without bloating
5209 * each delegating object.
5212 if (proto
->isNative()) {
5213 sprop
= (JSScopeProperty
*)prop
;
5214 if (sprop
->isSharedPermanent())
5215 rval
->setBoolean(false);
5216 JS_UNLOCK_OBJ(cx
, proto
);
5218 if (rval
->isBoolean() && rval
->toBoolean() == false)
5223 * If no property, or the property comes unshared or impermanent from
5224 * a prototype, call the class's delProperty hook, passing rval as the
5227 return callJSPropertyOp(cx
, obj
->getClass()->delProperty
, obj
, id
, rval
);
5230 sprop
= (JSScopeProperty
*)prop
;
5231 if (!sprop
->configurable()) {
5232 JS_UNLOCK_OBJ(cx
, obj
);
5233 rval
->setBoolean(false);
5237 /* XXXbe called with obj locked */
5238 if (!callJSPropertyOp(cx
, obj
->getClass()->delProperty
, obj
, SPROP_USERID(sprop
), rval
)) {
5239 JS_UNLOCK_OBJ(cx
, obj
);
5243 scope
= obj
->scope();
5244 if (SPROP_HAS_VALID_SLOT(sprop
, scope
)) {
5245 const Value
&v
= obj
->lockedGetSlot(sprop
->slot
);
5249 * Delete is rare enough that we can take the hit of checking for an
5250 * active cloned method function object that must be homed to a callee
5251 * slot on the active stack frame before this delete completes, in case
5252 * someone saved the clone and checks it against foo.caller for a foo
5253 * called from the active method.
5255 * We do not check suspended frames. They can't be reached via caller,
5256 * so the only way they could have the method's joined function object
5257 * as callee is through an API abusage. We break any such edge case.
5259 if (scope
->hasMethodBarrier()) {
5262 if (IsFunctionObject(v
, &funobj
)) {
5263 JSFunction
*fun
= GET_FUNCTION_PRIVATE(cx
, funobj
);
5265 if (fun
!= funobj
) {
5266 for (JSStackFrame
*fp
= cx
->fp
; fp
; fp
= fp
->down
) {
5267 if (fp
->callee() == fun
&&
5268 fp
->thisv
.isObject() &&
5269 &fp
->thisv
.toObject() == obj
) {
5270 fp
->setCalleeObject(*funobj
);
5278 ok
= scope
->removeProperty(cx
, id
);
5279 JS_UNLOCK_OBJ(cx
, obj
);
5281 return ok
&& js_SuppressDeletedProperty(cx
, obj
, id
);
5287 DefaultValue(JSContext
*cx
, JSObject
*obj
, JSType hint
, Value
*vp
)
5289 JS_ASSERT(hint
!= JSTYPE_OBJECT
&& hint
!= JSTYPE_FUNCTION
);
5291 Value v
= ObjectValue(*obj
);
5292 if (hint
== JSTYPE_STRING
) {
5294 * Optimize for String objects with standard toString methods. Support
5295 * new String(...) instances whether mutated to have their own scope or
5296 * not, as well as direct String.prototype references.
5298 if (obj
->getClass() == &js_StringClass
) {
5299 jsid toStringId
= ATOM_TO_JSID(cx
->runtime
->atomState
.toStringAtom
);
5301 JS_LOCK_OBJ(cx
, obj
);
5302 JSScope
*scope
= obj
->scope();
5303 JSScopeProperty
*sprop
= scope
->lookup(toStringId
);
5304 JSObject
*pobj
= obj
;
5307 pobj
= obj
->getProto();
5309 if (pobj
&& pobj
->getClass() == &js_StringClass
) {
5310 JS_UNLOCK_SCOPE(cx
, scope
);
5311 JS_LOCK_OBJ(cx
, pobj
);
5312 scope
= pobj
->scope();
5313 sprop
= scope
->lookup(toStringId
);
5317 if (sprop
&& sprop
->hasDefaultGetter() && SPROP_HAS_VALID_SLOT(sprop
, scope
)) {
5318 const Value
&fval
= pobj
->lockedGetSlot(sprop
->slot
);
5321 if (IsFunctionObject(fval
, &funobj
)) {
5322 JSFunction
*fun
= GET_FUNCTION_PRIVATE(cx
, funobj
);
5324 if (FUN_FAST_NATIVE(fun
) == js_str_toString
) {
5325 JS_UNLOCK_SCOPE(cx
, scope
);
5326 *vp
= obj
->getPrimitiveThis();
5331 JS_UNLOCK_SCOPE(cx
, scope
);
5335 * Propagate the exception if js_TryMethod finds an appropriate
5336 * method, and calling that method returned failure.
5338 if (!js_TryMethod(cx
, obj
, cx
->runtime
->atomState
.toStringAtom
,
5343 if (!v
.isPrimitive()) {
5344 if (!obj
->getClass()->convert(cx
, obj
, hint
, &v
))
5348 if (!obj
->getClass()->convert(cx
, obj
, hint
, &v
))
5351 JS_ASSERT(hint
!= TypeOfValue(cx
, v
));
5352 if (!js_TryMethod(cx
, obj
, cx
->runtime
->atomState
.toStringAtom
, 0, NULL
, &v
))
5357 /* Avoid recursive death when decompiling in js_ReportValueError. */
5359 if (hint
== JSTYPE_STRING
) {
5360 str
= JS_InternString(cx
, obj
->getClass()->name
);
5366 vp
->setObject(*obj
);
5367 js_ReportValueError2(cx
, JSMSG_CANT_CONVERT_TO
,
5368 JSDVG_SEARCH_STACK
, *vp
, str
,
5369 (hint
== JSTYPE_VOID
)
5371 : JS_TYPE_STR(hint
));
5378 } /* namespace js */
5380 JS_FRIEND_API(JSBool
)
5381 js_Enumerate(JSContext
*cx
, JSObject
*obj
, JSIterateOp enum_op
, Value
*statep
, jsid
*idp
)
5383 /* If the class has a custom JSCLASS_NEW_ENUMERATE hook, call it. */
5384 Class
*clasp
= obj
->getClass();
5385 JSEnumerateOp enumerate
= clasp
->enumerate
;
5386 if (clasp
->flags
& JSCLASS_NEW_ENUMERATE
) {
5387 JS_ASSERT(enumerate
!= JS_EnumerateStub
);
5388 return ((NewEnumerateOp
) enumerate
)(cx
, obj
, enum_op
, statep
, idp
);
5391 if (!enumerate(cx
, obj
))
5394 /* Tell InitNativeIterator to treat us like a native object. */
5395 JS_ASSERT(enum_op
== JSENUMERATE_INIT
|| enum_op
== JSENUMERATE_INIT_ALL
);
5396 statep
->setMagic(JS_NATIVE_ENUMERATE
);
5403 CheckAccess(JSContext
*cx
, JSObject
*obj
, jsid id
, JSAccessMode mode
,
5404 Value
*vp
, uintN
*attrsp
)
5410 JSScopeProperty
*sprop
;
5411 JSSecurityCallbacks
*callbacks
;
5412 CheckAccessOp check
;
5414 while (JS_UNLIKELY(obj
->getClass() == &js_WithClass
))
5415 obj
= obj
->getProto();
5417 writing
= (mode
& JSACC_WRITE
) != 0;
5418 switch (mode
& JSACC_TYPEMASK
) {
5422 vp
->setObjectOrNull(obj
->getProto());
5423 *attrsp
= JSPROP_PERMANENT
;
5427 JS_ASSERT(!writing
);
5429 vp
->setObject(*obj
->getParent());
5430 *attrsp
= JSPROP_READONLY
| JSPROP_PERMANENT
;
5434 if (!obj
->lookupProperty(cx
, id
, &pobj
, &prop
))
5444 if (!pobj
->isNative()) {
5452 sprop
= (JSScopeProperty
*)prop
;
5453 *attrsp
= sprop
->attributes();
5455 if (SPROP_HAS_VALID_SLOT(sprop
, pobj
->scope()))
5456 *vp
= pobj
->lockedGetSlot(sprop
->slot
);
5460 JS_UNLOCK_OBJ(cx
, pobj
);
5464 * If obj's class has a stub (null) checkAccess hook, use the per-runtime
5465 * checkObjectAccess callback, if configured.
5467 * We don't want to require all classes to supply a checkAccess hook; we
5468 * need that hook only for certain classes used when precompiling scripts
5469 * and functions ("brutal sharing"). But for general safety of built-in
5470 * magic properties like __proto__, we route all access checks, even for
5471 * classes that stub out checkAccess, through the global checkObjectAccess
5472 * hook. This covers precompilation-based sharing and (possibly
5473 * unintended) runtime sharing across trust boundaries.
5475 clasp
= pobj
->getClass();
5476 check
= clasp
->checkAccess
;
5478 callbacks
= JS_GetSecurityCallbacks(cx
);
5479 check
= callbacks
? Valueify(callbacks
->checkObjectAccess
) : NULL
;
5481 return !check
|| check(cx
, pobj
, id
, mode
, vp
);
5487 js_TypeOf(JSContext
*cx
, JSObject
*obj
)
5490 * Unfortunately we have wrappers that are native objects and thus don't
5491 * overwrite js_TypeOf (i.e. XPCCrossOriginWrapper), so we have to
5494 obj
= obj
->wrappedObject(cx
);
5497 * ECMA 262, 11.4.3 says that any native object that implements
5498 * [[Call]] should be of type "function". However, RegExp is of
5499 * type "object", not "function", for Web compatibility.
5501 if (obj
->isCallable()) {
5502 return (obj
->getClass() != &js_RegExpClass
)
5507 return JSTYPE_OBJECT
;
5510 #ifdef JS_THREADSAFE
5512 js_DropProperty(JSContext
*cx
, JSObject
*obj
, JSProperty
*prop
)
5514 JS_UNLOCK_OBJ(cx
, obj
);
5519 js_IsDelegate(JSContext
*cx
, JSObject
*obj
, const Value
&v
)
5521 if (v
.isPrimitive())
5523 JSObject
*obj2
= v
.toObject().wrappedObject(cx
);
5524 while ((obj2
= obj2
->getProto()) != NULL
) {
5532 js::FindClassPrototype(JSContext
*cx
, JSObject
*scope
, JSProtoKey protoKey
, JSObject
**protop
,
5536 if (!js_FindClassObject(cx
, scope
, protoKey
, &v
, clasp
))
5539 if (IsFunctionObject(v
)) {
5540 JSObject
*ctor
= &v
.toObject();
5541 if (!ctor
->getProperty(cx
, ATOM_TO_JSID(cx
->runtime
->atomState
.classPrototypeAtom
), &v
))
5545 *protop
= v
.isObject() ? &v
.toObject() : NULL
;
5550 * The first part of this function has been hand-expanded and optimized into
5551 * NewBuiltinClassInstance in jsobjinlines.h.
5554 js_GetClassPrototype(JSContext
*cx
, JSObject
*scope
, JSProtoKey protoKey
,
5555 JSObject
**protop
, Class
*clasp
)
5557 VOUCH_DOES_NOT_REQUIRE_STACK();
5558 JS_ASSERT(JSProto_Null
<= protoKey
);
5559 JS_ASSERT(protoKey
< JSProto_LIMIT
);
5561 if (protoKey
!= JSProto_Null
) {
5564 scope
= cx
->fp
->maybeScopeChain();
5566 scope
= cx
->globalObject
;
5573 scope
= scope
->getGlobal();
5574 if (scope
->getClass()->flags
& JSCLASS_IS_GLOBAL
) {
5575 const Value
&v
= scope
->getReservedSlot(JSProto_LIMIT
+ protoKey
);
5577 *protop
= &v
.toObject();
5583 return FindClassPrototype(cx
, scope
, protoKey
, protop
, clasp
);
5587 * For shared precompilation of function objects, we support cloning on entry
5588 * to an execution context in which the function declaration or expression
5589 * should be processed as if it were not precompiled, where the precompiled
5590 * function's scope chain does not match the execution context's. The cloned
5591 * function object carries its execution-context scope in its parent slot; it
5592 * links to the precompiled function (the "clone-parent") via its proto slot.
5594 * Note that this prototype-based delegation leaves an unchecked access path
5595 * from the clone to the clone-parent's 'constructor' property. If the clone
5596 * lives in a less privileged or shared scope than the clone-parent, this is
5597 * a security hole, a sharing hazard, or both. Therefore we check all such
5598 * accesses with the following getter/setter pair, which we use when defining
5599 * 'constructor' in f.prototype for all function objects f.
5602 CheckCtorGetAccess(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
5604 JSAtom
*atom
= cx
->runtime
->atomState
.constructorAtom
;
5605 JS_ASSERT(id
== ATOM_TO_JSID(atom
));
5607 return CheckAccess(cx
, obj
, ATOM_TO_JSID(atom
), JSACC_READ
, vp
, &attrs
);
5611 CheckCtorSetAccess(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
5613 JSAtom
*atom
= cx
->runtime
->atomState
.constructorAtom
;
5614 JS_ASSERT(id
== ATOM_TO_JSID(atom
));
5616 return CheckAccess(cx
, obj
, ATOM_TO_JSID(atom
), JSACC_WRITE
, vp
, &attrs
);
5620 js_SetClassPrototype(JSContext
*cx
, JSObject
*ctor
, JSObject
*proto
, uintN attrs
)
5623 * Use the given attributes for the prototype property of the constructor,
5624 * as user-defined constructors have a DontDelete prototype (which may be
5625 * reset), while native or "system" constructors have DontEnum | ReadOnly |
5628 if (!ctor
->defineProperty(cx
, ATOM_TO_JSID(cx
->runtime
->atomState
.classPrototypeAtom
),
5629 ObjectOrNullValue(proto
), PropertyStub
, PropertyStub
, attrs
)) {
5634 * ECMA says that Object.prototype.constructor, or f.prototype.constructor
5635 * for a user-defined function f, is DontEnum.
5637 return proto
->defineProperty(cx
, ATOM_TO_JSID(cx
->runtime
->atomState
.constructorAtom
),
5638 ObjectOrNullValue(ctor
), CheckCtorGetAccess
, CheckCtorSetAccess
, 0);
5642 js_PrimitiveToObject(JSContext
*cx
, Value
*vp
)
5645 JS_ASSERT(v
.isPrimitive());
5649 clasp
= &js_NumberClass
;
5650 else if (v
.isString())
5651 clasp
= &js_StringClass
;
5653 clasp
= &js_BooleanClass
;
5655 JSObject
*obj
= NewBuiltinClassInstance(cx
, clasp
);
5659 obj
->setPrimitiveThis(v
);
5660 vp
->setObject(*obj
);
5665 js_ValueToObjectOrNull(JSContext
*cx
, const Value
&v
, JSObject
**objp
)
5669 if (v
.isObjectOrNull()) {
5670 obj
= v
.toObjectOrNull();
5671 } else if (v
.isUndefined()) {
5675 if (!js_PrimitiveToObject(cx
, &tmp
))
5677 obj
= &tmp
.toObject();
5684 js_ValueToNonNullObject(JSContext
*cx
, const Value
&v
)
5688 if (!js_ValueToObjectOrNull(cx
, v
, &obj
))
5691 js_ReportIsNullOrUndefined(cx
, JSDVG_SEARCH_STACK
, v
, NULL
);
5696 js_TryValueOf(JSContext
*cx
, JSObject
*obj
, JSType type
, Value
*rval
)
5700 argv
[0].setString(ATOM_TO_STRING(cx
->runtime
->atomState
.typeAtoms
[type
]));
5701 return js_TryMethod(cx
, obj
, cx
->runtime
->atomState
.valueOfAtom
,
5706 js_TryMethod(JSContext
*cx
, JSObject
*obj
, JSAtom
*atom
,
5707 uintN argc
, Value
*argv
, Value
*rval
)
5709 JS_CHECK_RECURSION(cx
, return JS_FALSE
);
5712 * Report failure only if an appropriate method was found, and calling it
5713 * returned failure. We propagate failure in this case to make exceptions
5716 JSErrorReporter older
= JS_SetErrorReporter(cx
, NULL
);
5717 jsid id
= ATOM_TO_JSID(atom
);
5719 JSBool ok
= js_GetMethod(cx
, obj
, id
, JSGET_NO_METHOD_BARRIER
, &fval
);
5720 JS_SetErrorReporter(cx
, older
);
5724 if (fval
.isPrimitive())
5726 return InternalCall(cx
, obj
, fval
, argc
, argv
, rval
);
5732 js_XDRObject(JSXDRState
*xdr
, JSObject
**objp
)
5737 uint32 classId
, classDef
;
5738 JSProtoKey protoKey
;
5743 if (xdr
->mode
== JSXDR_ENCODE
) {
5744 clasp
= (*objp
)->getClass();
5745 classId
= JS_XDRFindClassIdByName(xdr
, clasp
->name
);
5746 classDef
= !classId
;
5748 if (!JS_XDRRegisterClass(xdr
, Jsvalify(clasp
), &classId
))
5750 protoKey
= JSCLASS_CACHED_PROTO_KEY(clasp
);
5751 if (protoKey
!= JSProto_Null
) {
5752 classDef
|= (protoKey
<< 1);
5754 atom
= js_Atomize(cx
, clasp
->name
, strlen(clasp
->name
), 0);
5760 clasp
= NULL
; /* quell GCC overwarning */
5765 * XDR a flag word, which could be 0 for a class use, in which case no
5766 * name follows, only the id in xdr's class registry; 1 for a class def,
5767 * in which case the flag word is followed by the class name transferred
5768 * from or to atom; or a value greater than 1, an odd number that when
5769 * divided by two yields the JSProtoKey for class. In the last case, as
5770 * in the 0 classDef case, no name is transferred via atom.
5772 if (!JS_XDRUint32(xdr
, &classDef
))
5774 if (classDef
== 1 && !js_XDRAtom(xdr
, &atom
))
5777 if (!JS_XDRUint32(xdr
, &classId
))
5780 if (xdr
->mode
== JSXDR_DECODE
) {
5782 /* NB: we know that JSProto_Null is 0 here, for backward compat. */
5783 protoKey
= (JSProtoKey
) (classDef
>> 1);
5784 if (!js_GetClassPrototype(cx
, NULL
, protoKey
, &proto
, clasp
))
5786 clasp
= proto
->getClass();
5787 if (!JS_XDRRegisterClass(xdr
, Jsvalify(clasp
), &classId
))
5790 clasp
= Valueify(JS_XDRFindClassById(xdr
, classId
));
5793 JS_snprintf(numBuf
, sizeof numBuf
, "%ld", (long)classId
);
5794 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
5795 JSMSG_CANT_FIND_CLASS
, numBuf
);
5801 if (!clasp
->xdrObject
) {
5802 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
5803 JSMSG_CANT_XDR_CLASS
, clasp
->name
);
5806 return clasp
->xdrObject(xdr
, objp
);
5809 #endif /* JS_HAS_XDR */
5811 #ifdef JS_DUMP_SCOPE_METERS
5815 JSBasicStats js_entry_count_bs
= JS_INIT_STATIC_BASIC_STATS
;
5818 MeterEntryCount(uintN count
)
5820 JS_BASIC_STATS_ACCUM(&js_entry_count_bs
, count
);
5824 js_DumpScopeMeters(JSRuntime
*rt
)
5828 logfp
= fopen("/tmp/scope.stats", "a");
5833 mean
= JS_MeanAndStdDevBS(&js_entry_count_bs
, &sigma
);
5835 fprintf(logfp
, "scopes %u entries %g mean %g sigma %g max %u",
5836 js_entry_count_bs
.num
, js_entry_count_bs
.sum
, mean
, sigma
,
5837 js_entry_count_bs
.max
);
5840 JS_DumpHistogram(&js_entry_count_bs
, logfp
);
5841 JS_BASIC_STATS_INIT(&js_entry_count_bs
);
5848 js_PrintObjectSlotName(JSTracer
*trc
, char *buf
, size_t bufsize
)
5850 JS_ASSERT(trc
->debugPrinter
== js_PrintObjectSlotName
);
5852 JSObject
*obj
= (JSObject
*)trc
->debugPrintArg
;
5853 uint32 slot
= (uint32
)trc
->debugPrintIndex
;
5854 JS_ASSERT(slot
>= JSSLOT_START(obj
->getClass()));
5856 JSScopeProperty
*sprop
;
5857 if (obj
->isNative()) {
5858 JSScope
*scope
= obj
->scope();
5859 sprop
= scope
->lastProperty();
5860 while (sprop
&& sprop
->slot
!= slot
)
5861 sprop
= sprop
->parent
;
5867 const char *slotname
= NULL
;
5868 Class
*clasp
= obj
->getClass();
5869 if (clasp
->flags
& JSCLASS_IS_GLOBAL
) {
5870 uint32 key
= slot
- JSSLOT_START(clasp
);
5871 #define JS_PROTO(name,code,init) \
5872 if ((code) == key) { slotname = js_##name##_str; goto found; }
5873 #include "jsproto.tbl"
5878 JS_snprintf(buf
, bufsize
, "CLASS_OBJECT(%s)", slotname
);
5880 JS_snprintf(buf
, bufsize
, "**UNKNOWN SLOT %ld**", (long)slot
);
5882 jsid id
= sprop
->id
;
5883 if (JSID_IS_INT(id
)) {
5884 JS_snprintf(buf
, bufsize
, "%ld", (long)JSID_TO_INT(id
));
5885 } else if (JSID_IS_ATOM(id
)) {
5886 js_PutEscapedString(buf
, bufsize
, JSID_TO_STRING(id
), 0);
5888 JS_snprintf(buf
, bufsize
, "**FINALIZED ATOM KEY**");
5895 js_TraceObject(JSTracer
*trc
, JSObject
*obj
)
5897 JS_ASSERT(obj
->isNative());
5899 JSContext
*cx
= trc
->context
;
5900 JSScope
*scope
= obj
->scope();
5901 if (!scope
->isSharedEmpty() && IS_GC_MARKING_TRACER(trc
)) {
5903 * Check whether we should shrink the object's slots. Skip this check
5904 * if the scope is shared, since for Block objects and flat closures
5905 * that share their scope, scope->freeslot can be an underestimate.
5907 size_t slots
= scope
->freeslot
;
5908 if (obj
->numSlots() != slots
)
5909 obj
->shrinkSlots(cx
, slots
);
5912 #ifdef JS_DUMP_SCOPE_METERS
5913 MeterEntryCount(scope
->entryCount
);
5918 if (!JS_CLIST_IS_EMPTY(&cx
->runtime
->watchPointList
))
5919 js_TraceWatchPoints(trc
, obj
);
5921 /* No one runs while the GC is running, so we can use LOCKED_... here. */
5922 Class
*clasp
= obj
->getClass();
5924 if (clasp
->flags
& JSCLASS_MARK_IS_TRACE
)
5925 ((JSTraceOp
) clasp
->mark
)(trc
, obj
);
5926 else if (IS_GC_MARKING_TRACER(trc
))
5927 (void) clasp
->mark(cx
, obj
, trc
);
5929 if (clasp
->flags
& JSCLASS_IS_GLOBAL
) {
5930 JSCompartment
*compartment
= obj
->getCompartment(cx
);
5931 compartment
->marked
= true;
5935 * An unmutated object that shares a prototype object's scope. We can't
5936 * tell how many slots are in use in obj by looking at its scope, so we
5937 * use obj->numSlots().
5939 * NB: In case clasp->mark mutates something, leave this code here --
5940 * don't move it up and unify it with the |if (!traceScope)| section
5943 uint32 nslots
= obj
->numSlots();
5944 if (!scope
->isSharedEmpty() && scope
->freeslot
< nslots
)
5945 nslots
= scope
->freeslot
;
5946 JS_ASSERT(nslots
>= JSSLOT_START(clasp
));
5948 for (uint32 i
= JSSLOT_START(clasp
); i
!= nslots
; ++i
) {
5949 const Value
&v
= obj
->getSlot(i
);
5950 JS_SET_TRACING_DETAILS(trc
, js_PrintObjectSlotName
, obj
, i
);
5951 MarkValueRaw(trc
, v
);
5956 js_ClearNative(JSContext
*cx
, JSObject
*obj
)
5959 * Clear our scope and the property cache of all obj's properties only if
5960 * obj owns the scope (i.e., not if obj is sharing another object's scope).
5961 * NB: we do not clear any reserved slots lying below JSSLOT_FREE(clasp).
5963 JS_LOCK_OBJ(cx
, obj
);
5964 JSScope
*scope
= obj
->scope();
5965 if (!scope
->isSharedEmpty()) {
5966 /* Now that we're done using scope->lastProp/table, clear scope. */
5969 /* Clear slot values and reset freeslot so we're consistent. */
5970 uint32 freeslot
= JSSLOT_FREE(obj
->getClass());
5971 uint32 n
= obj
->numSlots();
5972 for (uint32 i
= freeslot
; i
< n
; ++i
)
5973 obj
->setSlot(i
, UndefinedValue());
5974 scope
->freeslot
= freeslot
;
5976 JS_UNLOCK_OBJ(cx
, obj
);
5980 js_GetReservedSlot(JSContext
*cx
, JSObject
*obj
, uint32 index
, Value
*vp
)
5982 if (!obj
->isNative()) {
5987 uint32 slot
= JSSLOT_START(obj
->getClass()) + index
;
5988 JS_LOCK_OBJ(cx
, obj
);
5989 if (slot
< obj
->numSlots())
5990 *vp
= obj
->getSlot(slot
);
5993 JS_UNLOCK_OBJ(cx
, obj
);
5998 js_SetReservedSlot(JSContext
*cx
, JSObject
*obj
, uint32 index
, const Value
&v
)
6000 if (!obj
->isNative())
6003 Class
*clasp
= obj
->getClass();
6004 uint32 slot
= JSSLOT_START(clasp
) + index
;
6006 JS_LOCK_OBJ(cx
, obj
);
6007 if (slot
>= obj
->numSlots()) {
6009 * At this point, obj may or may not own scope, and we may or may not
6010 * need to allocate slots. If scope is shared, scope->freeslot may not
6011 * be accurate for obj (see comment below).
6013 uint32 nslots
= JSSLOT_FREE(clasp
);
6014 JS_ASSERT(slot
< nslots
);
6015 if (!obj
->allocSlots(cx
, nslots
)) {
6016 JS_UNLOCK_OBJ(cx
, obj
);
6022 * Whether or not we grew nslots, we may need to advance freeslot.
6024 * If scope is shared, do not modify scope->freeslot. It is OK for freeslot
6025 * to be an underestimate in objects with shared scopes, as they will get
6026 * their own scopes before mutating, and elsewhere (e.g. js_TraceObject) we
6027 * use obj->numSlots() rather than rely on freeslot.
6029 JSScope
*scope
= obj
->scope();
6030 if (!scope
->isSharedEmpty() && slot
>= scope
->freeslot
)
6031 scope
->freeslot
= slot
+ 1;
6033 obj
->setSlot(slot
, v
);
6034 GC_POKE(cx
, JS_NULL
);
6035 JS_UNLOCK_SCOPE(cx
, scope
);
6040 JSObject::wrappedObject(JSContext
*cx
) const
6042 if (JSObjectOp op
= getClass()->ext
.wrappedObject
) {
6043 if (JSObject
*obj
= op(cx
, const_cast<JSObject
*>(this)))
6046 return const_cast<JSObject
*>(this);
6050 JSObject::getGlobal()
6052 JSObject
*obj
= this;
6053 while (JSObject
*parent
= obj
->getParent())
6059 js_ReportGetterOnlyAssignment(JSContext
*cx
)
6061 return JS_ReportErrorFlagsAndNumber(cx
,
6062 JSREPORT_WARNING
| JSREPORT_STRICT
|
6063 JSREPORT_STRICT_MODE_ERROR
,
6064 js_GetErrorMessage
, NULL
,
6069 JSObject::getCompartment(JSContext
*cx
)
6071 JSObject
*obj
= getGlobal();
6073 Class
*clasp
= obj
->getClass();
6074 if (!(clasp
->flags
& JSCLASS_IS_GLOBAL
)) {
6075 // The magic AnyName object is runtime-wide.
6076 if (clasp
== &js_AnyNameClass
)
6077 return cx
->runtime
->defaultCompartment
;
6079 // The magic function namespace object is runtime-wide.
6080 if (clasp
== &js_NamespaceClass
&&
6081 obj
->getNameURI() == ATOM_TO_JSVAL(cx
->runtime
->atomState
.lazy
.functionNamespaceURIAtom
)) {
6082 return cx
->runtime
->defaultCompartment
;
6085 // Compile-time Function, Block, and RegExp objects are not parented.
6086 if (clasp
== &js_FunctionClass
|| clasp
== &js_BlockClass
|| clasp
== &js_RegExpClass
) {
6087 // This is a bogus answer, but it'll do for now.
6088 return cx
->runtime
->defaultCompartment
;
6090 JS_NOT_REACHED("non-global object at end of scope chain");
6092 const Value
&v
= obj
->getReservedSlot(JSRESERVED_GLOBAL_COMPARTMENT
);
6093 return (JSCompartment
*)v
.toPrivate();
6096 JS_FRIEND_API(JSBool
)
6097 js_GetterOnlyPropertyStub(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
6099 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_GETTER_ONLY
);
6106 * Routines to print out values during debugging. These are FRIEND_API to help
6107 * the debugger find them and to support temporarily hacking js_Dump* calls
6112 dumpChars(const jschar
*s
, size_t n
)
6116 if (n
== (size_t) -1) {
6121 for (i
= 0; i
< n
; i
++) {
6123 fprintf(stderr
, "\\n");
6124 else if (s
[i
] == '\t')
6125 fprintf(stderr
, "\\t");
6126 else if (s
[i
] >= 32 && s
[i
] < 127)
6127 fputc(s
[i
], stderr
);
6128 else if (s
[i
] <= 255)
6129 fprintf(stderr
, "\\x%02x", (unsigned int) s
[i
]);
6131 fprintf(stderr
, "\\u%04x", (unsigned int) s
[i
]);
6137 js_DumpChars(const jschar
*s
, size_t n
)
6139 fprintf(stderr
, "jschar * (%p) = ", (void *) s
);
6141 fputc('\n', stderr
);
6145 dumpString(JSString
*str
)
6147 dumpChars(str
->chars(), str
->length());
6151 js_DumpString(JSString
*str
)
6153 fprintf(stderr
, "JSString* (%p) = jschar * (%p) = ",
6154 (void *) str
, (void *) str
->chars());
6156 fputc('\n', stderr
);
6160 js_DumpAtom(JSAtom
*atom
)
6162 fprintf(stderr
, "JSAtom* (%p) = ", (void *) atom
);
6163 js_DumpString(ATOM_TO_STRING(atom
));
6167 dumpValue(const Value
&v
)
6170 fprintf(stderr
, "null");
6171 else if (v
.isUndefined())
6172 fprintf(stderr
, "undefined");
6173 else if (v
.isInt32())
6174 fprintf(stderr
, "%d", v
.toInt32());
6175 else if (v
.isDouble())
6176 fprintf(stderr
, "%g", v
.toDouble());
6177 else if (v
.isString())
6178 dumpString(v
.toString());
6179 else if (v
.isObject() && v
.toObject().isFunction()) {
6180 JSObject
*funobj
= &v
.toObject();
6181 JSFunction
*fun
= GET_FUNCTION_PRIVATE(cx
, funobj
);
6182 fprintf(stderr
, "<%s %s at %p (JSFunction at %p)>",
6183 fun
->atom
? "function" : "unnamed",
6184 fun
->atom
? JS_GetStringBytes(ATOM_TO_STRING(fun
->atom
)) : "function",
6187 } else if (v
.isObject()) {
6188 JSObject
*obj
= &v
.toObject();
6189 Class
*clasp
= obj
->getClass();
6190 fprintf(stderr
, "<%s%s at %p>",
6192 (clasp
== &js_ObjectClass
) ? "" : " object",
6194 } else if (v
.isBoolean()) {
6196 fprintf(stderr
, "true");
6198 fprintf(stderr
, "false");
6199 } else if (v
.isMagic()) {
6200 fprintf(stderr
, "<invalid");
6202 switch (v
.whyMagic()) {
6203 case JS_ARRAY_HOLE
: fprintf(stderr
, " array hole"); break;
6204 case JS_ARGS_HOLE
: fprintf(stderr
, " args hole"); break;
6205 case JS_NATIVE_ENUMERATE
: fprintf(stderr
, " native enumeration"); break;
6206 case JS_NO_ITER_VALUE
: fprintf(stderr
, " no iter value"); break;
6207 case JS_GENERATOR_CLOSING
: fprintf(stderr
, " generator closing"); break;
6208 default: fprintf(stderr
, " ?!"); break;
6211 fprintf(stderr
, ">");
6213 fprintf(stderr
, "unexpected value");
6218 js_DumpValue(const Value
&val
)
6221 fputc('\n', stderr
);
6227 fprintf(stderr
, "jsid %p = ", (void *) JSID_BITS(id
));
6228 dumpValue(IdToValue(id
));
6229 fputc('\n', stderr
);
6233 dumpScopeProp(JSScopeProperty
*sprop
)
6235 jsid id
= sprop
->id
;
6236 uint8 attrs
= sprop
->attributes();
6238 fprintf(stderr
, " ");
6239 if (attrs
& JSPROP_ENUMERATE
) fprintf(stderr
, "enumerate ");
6240 if (attrs
& JSPROP_READONLY
) fprintf(stderr
, "readonly ");
6241 if (attrs
& JSPROP_PERMANENT
) fprintf(stderr
, "permanent ");
6242 if (attrs
& JSPROP_GETTER
) fprintf(stderr
, "getter ");
6243 if (attrs
& JSPROP_SETTER
) fprintf(stderr
, "setter ");
6244 if (attrs
& JSPROP_SHARED
) fprintf(stderr
, "shared ");
6245 if (sprop
->isAlias()) fprintf(stderr
, "alias ");
6246 if (JSID_IS_ATOM(id
))
6247 dumpString(JSID_TO_STRING(id
));
6248 else if (JSID_IS_INT(id
))
6249 fprintf(stderr
, "%d", (int) JSID_TO_INT(id
));
6251 fprintf(stderr
, "unknown jsid %p", (void *) JSID_BITS(id
));
6252 fprintf(stderr
, ": slot %d", sprop
->slot
);
6253 fprintf(stderr
, "\n");
6257 js_DumpObject(JSObject
*obj
)
6263 fprintf(stderr
, "object %p\n", (void *) obj
);
6264 clasp
= obj
->getClass();
6265 fprintf(stderr
, "class %p %s\n", (void *)clasp
, clasp
->name
);
6267 if (obj
->isDenseArray()) {
6268 slots
= JS_MIN(obj
->getArrayLength(), obj
->getDenseArrayCapacity());
6269 fprintf(stderr
, "elements\n");
6270 for (i
= 0; i
< slots
; i
++) {
6271 fprintf(stderr
, " %3d: ", i
);
6272 dumpValue(obj
->getDenseArrayElement(i
));
6273 fprintf(stderr
, "\n");
6279 if (obj
->isNative()) {
6280 JSScope
*scope
= obj
->scope();
6281 if (scope
->sealed())
6282 fprintf(stderr
, "sealed\n");
6284 fprintf(stderr
, "properties:\n");
6285 for (JSScopeProperty
*sprop
= scope
->lastProperty(); sprop
;
6286 sprop
= sprop
->parent
) {
6287 dumpScopeProp(sprop
);
6290 if (!obj
->isNative())
6291 fprintf(stderr
, "not native\n");
6294 fprintf(stderr
, "proto ");
6295 dumpValue(ObjectOrNullValue(obj
->getProto()));
6296 fputc('\n', stderr
);
6298 fprintf(stderr
, "parent ");
6299 dumpValue(ObjectOrNullValue(obj
->getParent()));
6300 fputc('\n', stderr
);
6303 if (clasp
->flags
& JSCLASS_HAS_PRIVATE
) {
6304 i
= JSSLOT_PRIVATE
+ 1;
6305 fprintf(stderr
, "private %p\n", obj
->getPrivate());
6308 fprintf(stderr
, "slots:\n");
6309 reservedEnd
= i
+ JSCLASS_RESERVED_SLOTS(clasp
);
6310 slots
= (obj
->isNative() && !obj
->scope()->isSharedEmpty())
6311 ? obj
->scope()->freeslot
6313 for (; i
< slots
; i
++) {
6314 fprintf(stderr
, " %3d ", i
);
6315 if (i
< reservedEnd
)
6316 fprintf(stderr
, "(reserved) ");
6317 fprintf(stderr
, "= ");
6318 dumpValue(obj
->getSlot(i
));
6319 fputc('\n', stderr
);
6321 fputc('\n', stderr
);
6325 MaybeDumpObject(const char *name
, JSObject
*obj
)
6328 fprintf(stderr
, " %s: ", name
);
6329 dumpValue(ObjectValue(*obj
));
6330 fputc('\n', stderr
);
6335 MaybeDumpValue(const char *name
, const Value
&v
)
6338 fprintf(stderr
, " %s: ", name
);
6340 fputc('\n', stderr
);
6345 js_DumpStackFrame(JSContext
*cx
, JSStackFrame
*start
)
6347 /* This should only called during live debugging. */
6348 VOUCH_DOES_NOT_REQUIRE_STACK();
6352 FrameRegsIter
i(cx
);
6353 while (!i
.done() && i
.fp() != start
)
6357 fprintf(stderr
, "fp = %p not found in cx = %p\n", (void *)start
, (void *)cx
);
6361 for (; !i
.done(); ++i
) {
6362 JSStackFrame
*const fp
= i
.fp();
6364 fprintf(stderr
, "JSStackFrame at %p\n", (void *) fp
);
6366 fprintf(stderr
, "callee: ");
6367 dumpValue(fp
->argv
[-2]);
6369 fprintf(stderr
, "global frame, no callee");
6371 fputc('\n', stderr
);
6374 fprintf(stderr
, "file %s line %u\n", fp
->script
->filename
, (unsigned) fp
->script
->lineno
);
6376 if (jsbytecode
*pc
= i
.pc()) {
6378 fprintf(stderr
, "*** pc && !script, skipping frame\n\n");
6381 if (fp
->hasIMacroPC()) {
6382 fprintf(stderr
, " pc in imacro at %p\n called from ", pc
);
6383 pc
= fp
->getIMacroPC();
6385 fprintf(stderr
, " ");
6387 fprintf(stderr
, "pc = %p\n", pc
);
6388 fprintf(stderr
, " current op: %s\n", js_CodeName
[*pc
]);
6391 fprintf(stderr
, " slots: %p\n", (void *) fp
->slots());
6392 fprintf(stderr
, " sp: %p = slots + %u\n", (void *) sp
, (unsigned) (sp
- fp
->slots()));
6393 if (sp
- fp
->slots() < 10000) { // sanity
6394 for (Value
*p
= fp
->slots(); p
< sp
; p
++) {
6395 fprintf(stderr
, " %p: ", (void *) p
);
6397 fputc('\n', stderr
);
6400 fprintf(stderr
, " argv: %p (argc: %u)\n", (void *) fp
->argv
, (unsigned) fp
->argc
);
6401 MaybeDumpObject("callobj", fp
->maybeCallObj());
6402 MaybeDumpObject("argsobj", fp
->maybeArgsObj());
6403 MaybeDumpValue("this", fp
->thisv
);
6404 fprintf(stderr
, " rval: ");
6405 dumpValue(fp
->rval
);
6406 fputc('\n', stderr
);
6408 fprintf(stderr
, " flags:");
6410 fprintf(stderr
, " none");
6411 if (fp
->flags
& JSFRAME_CONSTRUCTING
)
6412 fprintf(stderr
, " constructing");
6413 if (fp
->flags
& JSFRAME_COMPUTED_THIS
)
6414 fprintf(stderr
, " computed_this");
6415 if (fp
->flags
& JSFRAME_ASSIGNING
)
6416 fprintf(stderr
, " assigning");
6417 if (fp
->flags
& JSFRAME_DEBUGGER
)
6418 fprintf(stderr
, " debugger");
6419 if (fp
->flags
& JSFRAME_EVAL
)
6420 fprintf(stderr
, " eval");
6421 if (fp
->flags
& JSFRAME_YIELDING
)
6422 fprintf(stderr
, " yielding");
6423 if (fp
->flags
& JSFRAME_GENERATOR
)
6424 fprintf(stderr
, " generator");
6425 if (fp
->flags
& JSFRAME_OVERRIDE_ARGS
)
6426 fprintf(stderr
, " overridden_args");
6427 fputc('\n', stderr
);
6429 if (fp
->hasScopeChain())
6430 fprintf(stderr
, " scopeChain: (JSObject *) %p\n", (void *) fp
->getScopeChain());
6431 if (fp
->hasBlockChain())
6432 fprintf(stderr
, " blockChain: (JSObject *) %p\n", (void *) fp
->getBlockChain());
6434 fputc('\n', stderr
);