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"
81 #include "jsinterpinlines.h"
82 #include "jsscopeinlines.h"
83 #include "jsscriptinlines.h"
84 #include "jsobjinlines.h"
90 #if JS_HAS_XML_SUPPORT
99 #include "jsatominlines.h"
100 #include "jsobjinlines.h"
101 #include "jsscriptinlines.h"
103 #include "jsautooplen.h"
107 JS_FRIEND_DATA(const JSObjectMap
) JSObjectMap::sharedNonNative(JSObjectMap::SHAPELESS
);
109 Class js_ObjectClass
= {
111 JSCLASS_HAS_CACHED_PROTO(JSProto_Object
),
112 PropertyStub
, /* addProperty */
113 PropertyStub
, /* delProperty */
114 PropertyStub
, /* getProperty */
115 PropertyStub
, /* setProperty */
121 JS_FRIEND_API(JSObject
*)
122 js_ObjectToOuterObject(JSContext
*cx
, JSObject
*obj
)
124 OBJ_TO_OUTER_OBJECT(cx
, obj
);
128 #if JS_HAS_OBJ_PROTO_PROP
131 obj_getProto(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
);
134 obj_setProto(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
);
136 static JSPropertySpec object_props
[] = {
137 {js_proto_str
, 0, JSPROP_PERMANENT
|JSPROP_SHARED
, Jsvalify(obj_getProto
), Jsvalify(obj_setProto
)},
142 obj_getProto(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
144 /* Let CheckAccess get the slot's value, based on the access mode. */
146 id
= ATOM_TO_JSID(cx
->runtime
->atomState
.protoAtom
);
147 return CheckAccess(cx
, obj
, id
, JSACC_PROTO
, vp
, &attrs
);
151 obj_setProto(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
153 if (!vp
->isObjectOrNull())
156 JSObject
*pobj
= vp
->toObjectOrNull();
159 * Innerize pobj here to avoid sticking unwanted properties on the
160 * outer object. This ensures that any with statements only grant
161 * access to the inner object.
163 OBJ_TO_INNER_OBJECT(cx
, pobj
);
169 id
= ATOM_TO_JSID(cx
->runtime
->atomState
.protoAtom
);
170 if (!CheckAccess(cx
, obj
, id
, JSAccessMode(JSACC_PROTO
|JSACC_WRITE
), vp
, &attrs
))
173 return SetProto(cx
, obj
, pobj
, JS_TRUE
);
176 #else /* !JS_HAS_OBJ_PROTO_PROP */
178 #define object_props NULL
180 #endif /* !JS_HAS_OBJ_PROTO_PROP */
183 js_hash_object(const void *key
)
185 return JSHashNumber(uintptr_t(key
) >> JS_GCTHING_ALIGN
);
189 MarkSharpObjects(JSContext
*cx
, JSObject
*obj
, JSIdArray
**idap
)
191 JSSharpObjectMap
*map
;
194 JSHashEntry
**hep
, *he
;
203 JS_CHECK_RECURSION(cx
, return NULL
);
205 map
= &cx
->sharpObjectMap
;
206 JS_ASSERT(map
->depth
>= 1);
208 hash
= js_hash_object(obj
);
209 hep
= JS_HashTableRawLookup(table
, hash
, obj
);
213 he
= JS_HashTableRawAdd(table
, hep
, hash
, obj
, (void *) sharpid
);
215 JS_ReportOutOfMemory(cx
);
219 ida
= JS_Enumerate(cx
, obj
);
224 for (i
= 0, length
= ida
->length
; i
< length
; i
++) {
226 ok
= obj
->lookupProperty(cx
, id
, &obj2
, &prop
);
231 bool hasGetter
, hasSetter
;
232 AutoValueRooter
v(cx
);
233 AutoValueRooter
setter(cx
);
234 if (obj2
->isNative()) {
235 const Shape
*shape
= (Shape
*) prop
;
236 hasGetter
= shape
->hasGetterValue();
237 hasSetter
= shape
->hasSetterValue();
239 v
.set(shape
->getterValue());
241 setter
.set(shape
->setterValue());
242 JS_UNLOCK_OBJ(cx
, obj2
);
244 hasGetter
= hasSetter
= false;
247 /* Mark the getter, then set val to setter. */
248 if (hasGetter
&& v
.value().isObject()) {
249 ok
= !!MarkSharpObjects(cx
, &v
.value().toObject(), NULL
);
253 v
.set(setter
.value());
254 } else if (!hasGetter
) {
255 ok
= obj
->getProperty(cx
, id
, v
.addr());
259 if (v
.value().isObject() &&
260 !MarkSharpObjects(cx
, &v
.value().toObject(), NULL
)) {
266 JS_DestroyIdArray(cx
, ida
);
270 sharpid
= uintptr_t(he
->value
);
272 sharpid
= ++map
->sharpgen
<< SHARP_ID_SHIFT
;
273 he
->value
= (void *) sharpid
;
283 js_EnterSharpObject(JSContext
*cx
, JSObject
*obj
, JSIdArray
**idap
,
286 JSSharpObjectMap
*map
;
290 JSHashEntry
*he
, **hep
;
295 if (!JS_CHECK_OPERATION_LIMIT(cx
))
298 /* Set to null in case we return an early error. */
300 map
= &cx
->sharpObjectMap
;
303 table
= JS_NewHashTable(8, js_hash_object
, JS_CompareValues
,
304 JS_CompareValues
, NULL
, NULL
);
306 JS_ReportOutOfMemory(cx
);
310 JS_KEEP_ATOMS(cx
->runtime
);
313 /* From this point the control must flow either through out: or bad:. */
315 if (map
->depth
== 0) {
317 * Although MarkSharpObjects tries to avoid invoking getters,
318 * it ends up doing so anyway under some circumstances; for
319 * example, if a wrapped object has getters, the wrapper will
320 * prevent MarkSharpObjects from recognizing them as such.
321 * This could lead to js_LeaveSharpObject being called while
322 * MarkSharpObjects is still working.
324 * Increment map->depth while we call MarkSharpObjects, to
325 * ensure that such a call doesn't free the hash table we're
329 he
= MarkSharpObjects(cx
, obj
, &ida
);
333 JS_ASSERT((uintptr_t(he
->value
) & SHARP_BIT
) == 0);
335 JS_DestroyIdArray(cx
, ida
);
339 hash
= js_hash_object(obj
);
340 hep
= JS_HashTableRawLookup(table
, hash
, obj
);
344 * It's possible that the value of a property has changed from the
345 * first time the object's properties are traversed (when the property
346 * ids are entered into the hash table) to the second (when they are
347 * converted to strings), i.e., the JSObject::getProperty() call is not
351 he
= JS_HashTableRawAdd(table
, hep
, hash
, obj
, NULL
);
353 JS_ReportOutOfMemory(cx
);
361 sharpid
= uintptr_t(he
->value
);
363 len
= JS_snprintf(buf
, sizeof buf
, "#%u%c",
364 sharpid
>> SHARP_ID_SHIFT
,
365 (sharpid
& SHARP_BIT
) ? '#' : '=');
366 *sp
= js_InflateString(cx
, buf
, &len
);
369 JS_DestroyIdArray(cx
, ida
);
376 if ((sharpid
& SHARP_BIT
) == 0) {
378 ida
= JS_Enumerate(cx
, obj
);
395 /* Clean up the sharpObjectMap table on outermost error. */
396 if (map
->depth
== 0) {
397 JS_UNKEEP_ATOMS(cx
->runtime
);
399 JS_HashTableDestroy(map
->table
);
406 js_LeaveSharpObject(JSContext
*cx
, JSIdArray
**idap
)
408 JSSharpObjectMap
*map
;
411 map
= &cx
->sharpObjectMap
;
412 JS_ASSERT(map
->depth
> 0);
413 if (--map
->depth
== 0) {
414 JS_UNKEEP_ATOMS(cx
->runtime
);
416 JS_HashTableDestroy(map
->table
);
422 JS_DestroyIdArray(cx
, ida
);
429 gc_sharp_table_entry_marker(JSHashEntry
*he
, intN i
, void *arg
)
431 JS_CALL_OBJECT_TRACER((JSTracer
*)arg
, (JSObject
*)he
->key
,
432 "sharp table entry");
433 return JS_DHASH_NEXT
;
437 js_TraceSharpMap(JSTracer
*trc
, JSSharpObjectMap
*map
)
439 JS_ASSERT(map
->depth
> 0);
440 JS_ASSERT(map
->table
);
443 * During recursive calls to MarkSharpObjects a non-native object or
444 * object with a custom getProperty method can potentially return an
445 * unrooted value or even cut from the object graph an argument of one of
446 * MarkSharpObjects recursive invocations. So we must protect map->table
447 * entries against GC.
449 * We can not simply use JSTempValueRooter to mark the obj argument of
450 * MarkSharpObjects during recursion as we have to protect *all* entries
451 * in JSSharpObjectMap including those that contains otherwise unreachable
452 * objects just allocated through custom getProperty. Otherwise newer
453 * allocations can re-use the address of an object stored in the hashtable
454 * confusing js_EnterSharpObject. So to address the problem we simply
455 * mark all objects from map->table.
457 * An alternative "proper" solution is to use JSTempValueRooter in
458 * MarkSharpObjects with code to remove during finalization entries
459 * with otherwise unreachable objects. But this is way too complex
460 * to justify spending efforts.
462 JS_HashTableEnumerateEntries(map
->table
, gc_sharp_table_entry_marker
, trc
);
467 obj_toSource(JSContext
*cx
, uintN argc
, Value
*vp
)
469 JSBool ok
, outermost
;
473 jschar
*chars
, *ochars
, *vsharp
;
474 const jschar
*idstrchars
, *vchars
;
475 size_t nchars
, idstrlength
, gsoplength
, vlength
, vsharplength
, curlen
;
481 JSString
*idstr
, *valstr
, *str
;
483 JS_CHECK_RECURSION(cx
, return JS_FALSE
);
486 PodArrayZero(localroot
);
487 AutoArrayRooter
tvr(cx
, JS_ARRAY_LENGTH(localroot
), localroot
);
489 /* If outermost, we need parentheses to be an expression, not a block. */
490 outermost
= (cx
->sharpObjectMap
.depth
== 0);
491 obj
= ComputeThisFromVp(cx
, vp
);
492 if (!obj
|| !(he
= js_EnterSharpObject(cx
, obj
, &ida
, &chars
))) {
498 * We didn't enter -- obj is already "sharp", meaning we've visited it
499 * already in our depth first search, and therefore chars contains a
500 * string of the form "#n#".
503 #if JS_HAS_SHARP_VARS
504 nchars
= js_strlen(chars
);
517 /* If outermost, allocate 4 + 1 for "({})" and the terminator. */
518 chars
= (jschar
*) cx
->runtime
->malloc(((outermost
? 4 : 2) + 1) * sizeof(jschar
));
523 chars
[nchars
++] = '(';
525 /* js_EnterSharpObject returned a string of the form "#n=" in chars. */
527 nchars
= js_strlen(chars
);
529 js_realloc((ochars
= chars
), (nchars
+ 2 + 1) * sizeof(jschar
));
536 * No need for parentheses around the whole shebang, because #n=
537 * unambiguously begins an object initializer, and never a block
540 outermost
= JS_FALSE
;
544 chars
[nchars
++] = '{';
549 * We have four local roots for cooked and raw value GC safety. Hoist the
550 * "localroot + 2" out of the loop using the val local, which refers to
551 * the raw (unconverted, "uncooked") values.
555 for (jsint i
= 0, length
= ida
->length
; i
< length
; i
++) {
556 /* Get strings for id and value and GC-root them via vp. */
557 jsid id
= ida
->vector
[i
];
559 ok
= obj
->lookupProperty(cx
, id
, &obj2
, &prop
);
564 * Convert id to a value and then to a string. Decide early whether we
565 * prefer get/set or old getter/setter syntax.
567 idstr
= js_ValueToString(cx
, IdToValue(id
));
570 obj2
->dropProperty(cx
, prop
);
573 vp
->setString(idstr
); /* local root */
578 if (obj2
->isNative()) {
579 const Shape
*shape
= (Shape
*) prop
;
580 unsigned attrs
= shape
->attributes();
581 if (attrs
& JSPROP_GETTER
) {
583 val
[valcnt
] = shape
->getterValue();
584 gsop
[valcnt
] = ATOM_TO_STRING(cx
->runtime
->atomState
.getAtom
);
587 if (attrs
& JSPROP_SETTER
) {
589 val
[valcnt
] = shape
->setterValue();
590 gsop
[valcnt
] = ATOM_TO_STRING(cx
->runtime
->atomState
.setAtom
);
593 JS_UNLOCK_OBJ(cx
, obj2
);
598 ok
= obj
->getProperty(cx
, id
, &val
[0]);
605 * If id is a string that's not an identifier, or if it's a negative
606 * integer, then it must be quoted.
608 bool idIsLexicalIdentifier
= !!js_IsIdentifier(idstr
);
610 ? !idIsLexicalIdentifier
611 : (!JSID_IS_INT(id
) || JSID_TO_INT(id
) < 0)) {
612 idstr
= js_QuoteString(cx
, idstr
, jschar('\''));
617 vp
->setString(idstr
); /* local root */
619 idstr
->getCharsAndLength(idstrchars
, idstrlength
);
621 for (jsint j
= 0; j
< valcnt
; j
++) {
623 * Censor an accessor descriptor getter or setter part if it's
626 if (gsop
[j
] && val
[j
].isUndefined())
629 /* Convert val[j] to its canonical source form. */
630 valstr
= js_ValueToSource(cx
, val
[j
]);
635 localroot
[j
].setString(valstr
); /* local root */
636 valstr
->getCharsAndLength(vchars
, vlength
);
639 * If val[j] is a non-sharp object, and we're not serializing an
640 * accessor (ECMA syntax can't accommodate sharpened accessors),
641 * consider sharpening it.
645 #if JS_HAS_SHARP_VARS
646 if (!gsop
[j
] && val
[j
].isObject() && vchars
[0] != '#') {
647 he
= js_EnterSharpObject(cx
, &val
[j
].toObject(), NULL
, &vsharp
);
654 vlength
= js_strlen(vchars
);
657 vsharplength
= js_strlen(vsharp
);
660 js_LeaveSharpObject(cx
, NULL
);
666 * Remove '(function ' from the beginning of valstr and ')' from the
667 * end so that we can put "get" in front of the function definition.
669 if (gsop
[j
] && IsFunctionObject(val
[j
])) {
670 const jschar
*start
= vchars
;
671 const jschar
*end
= vchars
+ vlength
;
673 uint8 parenChomp
= 0;
674 if (vchars
[0] == '(') {
679 /* Try to jump "function" keyword. */
681 vchars
= js_strchr_limit(vchars
, ' ', end
);
684 * Jump over the function's name: it can't be encoded as part
685 * of an ECMA getter or setter.
688 vchars
= js_strchr_limit(vchars
, '(', end
);
693 vlength
= end
- vchars
- parenChomp
;
700 #define SAFE_ADD(n) \
711 SAFE_ADD(idstrlength
+ 1);
713 SAFE_ADD(gsop
[j
]->length() + 1);
714 SAFE_ADD(vsharplength
);
716 /* Account for the trailing null. */
717 SAFE_ADD((outermost
? 2 : 1) + 1);
720 if (curlen
> size_t(-1) / sizeof(jschar
))
723 /* Allocate 1 + 1 at end for closing brace and terminating 0. */
724 chars
= (jschar
*) js_realloc((ochars
= chars
), curlen
* sizeof(jschar
));
726 /* Save code space on error: let JS_free ignore null vsharp. */
733 chars
[nchars
++] = comma
[0];
734 chars
[nchars
++] = comma
[1];
739 gsoplength
= gsop
[j
]->length();
740 js_strncpy(&chars
[nchars
], gsop
[j
]->chars(),
742 nchars
+= gsoplength
;
743 chars
[nchars
++] = ' ';
745 js_strncpy(&chars
[nchars
], idstrchars
, idstrlength
);
746 nchars
+= idstrlength
;
747 /* Extraneous space after id here will be extracted later */
748 chars
[nchars
++] = gsop
[j
] ? ' ' : ':';
751 js_strncpy(&chars
[nchars
], vsharp
, vsharplength
);
752 nchars
+= vsharplength
;
754 js_strncpy(&chars
[nchars
], vchars
, vlength
);
762 chars
[nchars
++] = '}';
764 chars
[nchars
++] = ')';
768 js_LeaveSharpObject(cx
, &ida
);
777 JS_ReportOutOfMemory(cx
);
782 str
= js_NewString(cx
, chars
, nchars
);
799 #endif /* JS_HAS_TOSOURCE */
804 obj_toStringHelper(JSContext
*cx
, JSObject
*obj
)
807 return JSProxy::obj_toString(cx
, obj
);
809 const char *clazz
= obj
->wrappedObject(cx
)->getClass()->name
;
810 size_t nchars
= 9 + strlen(clazz
); /* 9 for "[object ]" */
811 jschar
*chars
= (jschar
*) cx
->malloc((nchars
+ 1) * sizeof(jschar
));
815 const char *prefix
= "[object ";
817 while ((chars
[nchars
] = (jschar
)*prefix
) != 0)
819 while ((chars
[nchars
] = (jschar
)*clazz
) != 0)
821 chars
[nchars
++] = ']';
824 JSString
*str
= js_NewString(cx
, chars
, nchars
);
833 obj_toString(JSContext
*cx
, uintN argc
, Value
*vp
)
835 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
839 JSString
*str
= js::obj_toStringHelper(cx
, obj
);
848 obj_toLocaleString(JSContext
*cx
, uintN argc
, Value
*vp
)
850 if (!ComputeThisFromVp(cx
, vp
))
853 JSString
*str
= js_ValueToString(cx
, vp
[1]);
862 obj_valueOf(JSContext
*cx
, uintN argc
, Value
*vp
)
864 if (!ComputeThisFromVp(cx
, vp
))
871 * Check if CSP allows new Function() or eval() to run in the current
875 js_CheckContentSecurityPolicy(JSContext
*cx
)
877 JSSecurityCallbacks
*callbacks
= JS_GetSecurityCallbacks(cx
);
879 // if there are callbacks, make sure that the CSP callback is installed and
880 // that it permits eval().
881 if (callbacks
&& callbacks
->contentSecurityPolicyAllows
)
882 return callbacks
->contentSecurityPolicyAllows(cx
);
888 * Check whether principals subsumes scopeobj's principals, and return true
889 * if so (or if scopeobj has no principals, for backward compatibility with
890 * the JS API, which does not require principals), and false otherwise.
893 js_CheckPrincipalsAccess(JSContext
*cx
, JSObject
*scopeobj
,
894 JSPrincipals
*principals
, JSAtom
*caller
)
896 JSSecurityCallbacks
*callbacks
;
897 JSPrincipals
*scopePrincipals
;
898 const char *callerstr
;
900 callbacks
= JS_GetSecurityCallbacks(cx
);
901 if (callbacks
&& callbacks
->findObjectPrincipals
) {
902 scopePrincipals
= callbacks
->findObjectPrincipals(cx
, scopeobj
);
903 if (!principals
|| !scopePrincipals
||
904 !principals
->subsume(principals
, scopePrincipals
)) {
905 callerstr
= js_AtomToPrintableString(cx
, caller
);
908 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
909 JSMSG_BAD_INDIRECT_CALL
, callerstr
);
917 CheckScopeChainValidity(JSContext
*cx
, JSObject
*scopeobj
, const char *caller
)
924 OBJ_TO_INNER_OBJECT(cx
, scopeobj
);
928 /* XXX This is an awful gross hack. */
931 JSObjectOp op
= scopeobj
->getClass()->ext
.innerObject
;
932 if (op
&& op(cx
, scopeobj
) != scopeobj
)
934 scopeobj
= scopeobj
->getParent();
940 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
941 JSMSG_BAD_INDIRECT_CALL
, caller
);
946 js_ComputeFilename(JSContext
*cx
, JSStackFrame
*caller
,
947 JSPrincipals
*principals
, uintN
*linenop
)
951 JSSecurityCallbacks
*callbacks
= JS_GetSecurityCallbacks(cx
);
954 JS_ASSERT(principals
|| !(callbacks
&& callbacks
->findObjectPrincipals
));
955 flags
= JS_GetScriptFilenameFlags(caller
->script());
956 if ((flags
& JSFILENAME_PROTECTED
) &&
958 strcmp(principals
->codebase
, "[System Principal]")) {
960 return principals
->codebase
;
963 jsbytecode
*pc
= caller
->pc(cx
);
964 if (pc
&& js_GetOpcode(cx
, caller
->script(), pc
) == JSOP_EVAL
) {
965 JS_ASSERT(js_GetOpcode(cx
, caller
->script(), pc
+ JSOP_EVAL_LENGTH
) == JSOP_LINENO
);
966 *linenop
= GET_UINT16(pc
+ JSOP_EVAL_LENGTH
);
968 *linenop
= js_FramePCToLineNumber(cx
, caller
);
970 return caller
->script()->filename
;
973 #ifndef EVAL_CACHE_CHAIN_LIMIT
974 # define EVAL_CACHE_CHAIN_LIMIT 4
977 static inline JSScript
**
978 EvalCacheHash(JSContext
*cx
, JSString
*str
)
984 str
->getCharsAndLength(s
, n
);
987 for (h
= 0; n
; s
++, n
--)
988 h
= JS_ROTATE_LEFT32(h
, 4) ^ *s
;
990 h
*= JS_GOLDEN_RATIO
;
991 h
>>= 32 - JS_EVAL_CACHE_SHIFT
;
992 return &JS_SCRIPTS_TO_GC(cx
)[h
];
996 obj_eval(JSContext
*cx
, uintN argc
, Value
*vp
)
1003 JSStackFrame
*caller
= js_GetScriptedCaller(cx
, NULL
);
1005 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1006 JSMSG_BAD_INDIRECT_CALL
, js_eval_str
);
1010 jsbytecode
*callerPC
= caller
->pc(cx
);
1011 bool indirectCall
= (callerPC
&& *callerPC
!= JSOP_EVAL
);
1014 * This call to JSObject::wrappedObject is safe because of the security
1015 * checks we do below. However, the control flow below is confusing, so we
1016 * double check. There are two cases:
1017 * - Direct call: This object is never used. So unwrapping can't hurt.
1018 * - Indirect call: If this object isn't already the scope chain (which
1019 * we're guaranteed to be allowed to access) then we do a security
1022 Value
*argv
= JS_ARGV(cx
, vp
);
1023 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1026 obj
= obj
->wrappedObject(cx
);
1028 OBJ_TO_INNER_OBJECT(cx
, obj
);
1033 * Ban indirect uses of eval (nonglobal.eval = eval; nonglobal.eval(....))
1034 * that attempt to use a non-global object as the scope object.
1037 JSObject
*parent
= obj
->getParent();
1038 if (indirectCall
|| parent
) {
1039 uintN flags
= parent
1041 : JSREPORT_STRICT
| JSREPORT_WARNING
;
1042 if (!JS_ReportErrorFlagsAndNumber(cx
, flags
, js_GetErrorMessage
, NULL
,
1043 JSMSG_BAD_INDIRECT_CALL
,
1050 if (!argv
[0].isString()) {
1056 * We once supported a second argument to eval to use as the scope chain
1057 * when evaluating the code string. Warn when such uses are seen so that
1058 * authors will know that support for eval(s, o) has been removed.
1060 if (argc
> 1 && !caller
->script()->warnedAboutTwoArgumentEval
) {
1061 static const char TWO_ARGUMENT_WARNING
[] =
1062 "Support for eval(code, scopeObject) has been removed. "
1063 "Use |with (scopeObject) eval(code);| instead.";
1064 if (!JS_ReportWarning(cx
, TWO_ARGUMENT_WARNING
))
1066 caller
->script()->warnedAboutTwoArgumentEval
= true;
1069 /* From here on, control must exit through label out with ok set. */
1070 MUST_FLOW_THROUGH("out");
1071 uintN staticLevel
= caller
->script()->staticLevel
+ 1;
1074 * Bring fp->scopeChain up to date. We're either going to use
1075 * it (direct call) or save it and restore it (indirect call).
1077 JSObject
*callerScopeChain
= js_GetScopeChain(cx
, caller
);
1078 if (!callerScopeChain
)
1081 JSObject
*scopeobj
= NULL
;
1083 #if JS_HAS_EVAL_THIS_SCOPE
1085 * If we see an indirect call, then run eval in the global scope. We do
1086 * this so the compiler can make assumptions about what bindings may or
1087 * may not exist in the current frame if it doesn't see 'eval'.
1090 /* Pretend that we're top level. */
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
->isFunctionFrame(), 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
;
1134 const jschar
*chars
;
1136 str
->getCharsAndLength(chars
, length
);
1139 * If the eval string starts with '(' and ends with ')', it may be JSON.
1140 * Try the JSON parser first because it's much faster. If the eval string
1141 * isn't JSON, JSON parsing will probably fail quickly, so little time
1144 if (length
> 2 && chars
[0] == '(' && chars
[length
-1] == ')') {
1145 JSONParser
*jp
= js_BeginJSONParse(cx
, vp
, /* suppressErrors = */true);
1146 JSBool ok
= jp
!= NULL
;
1148 /* Run JSON-parser on string inside ( and ). */
1149 ok
= js_ConsumeJSONText(cx
, jp
, chars
+1, length
-2);
1150 ok
&= js_FinishJSONParse(cx
, jp
, NullValue());
1157 * Cache local eval scripts indexed by source qualified by scope.
1159 * An eval cache entry should never be considered a hit unless its
1160 * strictness matches that of the new eval code. The existing code takes
1161 * care of this, because hits are qualified by the function from which
1162 * eval was called, whose strictness doesn't change. Scripts produced by
1163 * calls to eval from global code are not cached.
1165 JSScript
**bucket
= EvalCacheHash(cx
, str
);
1166 if (!indirectCall
&& caller
->isFunctionFrame()) {
1168 JSScript
**scriptp
= bucket
;
1170 EVAL_CACHE_METER(probe
);
1171 JSVersion version
= cx
->findVersion();
1172 while ((script
= *scriptp
) != NULL
) {
1173 if (script
->savedCallerFun
&&
1174 script
->staticLevel
== staticLevel
&&
1175 script
->version
== version
&&
1176 (script
->principals
== principals
||
1177 (principals
->subsume(principals
, script
->principals
) &&
1178 script
->principals
->subsume(script
->principals
, principals
)))) {
1180 * Get the prior (cache-filling) eval's saved caller function.
1181 * See Compiler::compileScript in jsparse.cpp.
1183 JSFunction
*fun
= script
->getFunction(0);
1185 if (fun
== caller
->fun()) {
1187 * Get the source string passed for safekeeping in the
1188 * atom map by the prior eval to Compiler::compileScript.
1190 JSString
*src
= ATOM_TO_STRING(script
->atomMap
.vector
[0]);
1192 if (src
== str
|| js_EqualStrings(src
, str
)) {
1194 * Source matches, qualify by comparing scopeobj to the
1195 * COMPILE_N_GO-memoized parent of the first literal
1196 * function or regexp object if any. If none, then this
1197 * script has no compiled-in dependencies on the prior
1200 JSObjectArray
*objarray
= script
->objects();
1203 if (objarray
->length
== 1) {
1204 if (script
->regexpsOffset
!= 0) {
1205 objarray
= script
->regexps();
1208 EVAL_CACHE_METER(noscope
);
1213 objarray
->vector
[i
]->getParent() == scopeobj
) {
1214 JS_ASSERT(staticLevel
== script
->staticLevel
);
1215 EVAL_CACHE_METER(hit
);
1216 *scriptp
= script
->u
.nextToGC
;
1217 script
->u
.nextToGC
= NULL
;
1224 if (++count
== EVAL_CACHE_CHAIN_LIMIT
) {
1228 EVAL_CACHE_METER(step
);
1229 scriptp
= &script
->u
.nextToGC
;
1234 * We can't have a callerFrame (down in js_Execute's terms) if we're in
1235 * global code. This includes indirect eval and direct eval called with a
1236 * scope object parameter.
1238 JSStackFrame
*callerFrame
= (staticLevel
!= 0) ? caller
: NULL
;
1240 uint32 tcflags
= TCF_COMPILE_N_GO
| TCF_NEED_MUTABLE_SCRIPT
| TCF_COMPILE_FOR_EVAL
;
1241 script
= Compiler::compileScript(cx
, scopeobj
, callerFrame
,
1242 principals
, tcflags
,
1244 NULL
, file
, line
, str
, staticLevel
);
1250 * Belt-and-braces: check that the lesser of eval's principals and the
1251 * caller's principals has access to scopeobj.
1253 JSBool ok
= js_CheckPrincipalsAccess(cx
, scopeobj
, principals
,
1254 cx
->runtime
->atomState
.evalAtom
) &&
1255 Execute(cx
, scopeobj
, script
, callerFrame
, JSFRAME_EVAL
, vp
);
1257 script
->u
.nextToGC
= *bucket
;
1259 #ifdef CHECK_SCRIPT_OWNER
1260 script
->owner
= NULL
;
1266 #if JS_HAS_OBJ_WATCHPOINT
1269 obj_watch_handler(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval old
,
1270 jsval
*nvp
, void *closure
)
1273 JSSecurityCallbacks
*callbacks
;
1274 JSStackFrame
*caller
;
1275 JSPrincipals
*subject
, *watcher
;
1277 JSResolvingEntry
*entry
;
1282 callable
= (JSObject
*) closure
;
1284 callbacks
= JS_GetSecurityCallbacks(cx
);
1285 if (callbacks
&& callbacks
->findObjectPrincipals
) {
1286 /* Skip over any obj_watch_* frames between us and the real subject. */
1287 caller
= js_GetScriptedCaller(cx
, NULL
);
1290 * Only call the watch handler if the watcher is allowed to watch
1291 * the currently executing script.
1293 watcher
= callbacks
->findObjectPrincipals(cx
, callable
);
1294 subject
= js_StackFramePrincipals(cx
, caller
);
1296 if (watcher
&& subject
&& !watcher
->subsume(watcher
, subject
)) {
1297 /* Silently don't call the watch handler. */
1303 /* Avoid recursion on (obj, id) already being watched on cx. */
1306 if (!js_StartResolving(cx
, &key
, JSRESFLAG_WATCH
, &entry
))
1310 generation
= cx
->resolvingTable
->generation
;
1312 argv
[0] = IdToValue(id
);
1313 argv
[1] = Valueify(old
);
1314 argv
[2] = Valueify(*nvp
);
1315 ok
= ExternalInvoke(cx
, obj
, ObjectOrNullValue(callable
), 3, argv
, Valueify(nvp
));
1316 js_StopResolving(cx
, &key
, JSRESFLAG_WATCH
, entry
, generation
);
1321 obj_watch(JSContext
*cx
, uintN argc
, Value
*vp
)
1324 js_ReportMissingArg(cx
, *vp
, 1);
1328 JSObject
*callable
= js_ValueToCallableObject(cx
, &vp
[3], 0);
1332 /* Compute the unique int/atom symbol id needed by js_LookupProperty. */
1334 if (!ValueToId(cx
, vp
[2], &propid
))
1337 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1340 if (!obj
|| !CheckAccess(cx
, obj
, propid
, JSACC_WATCH
, &tmp
, &attrs
))
1345 if (attrs
& JSPROP_READONLY
)
1347 if (obj
->isDenseArray() && !obj
->makeDenseArraySlow(cx
))
1349 return JS_SetWatchPoint(cx
, obj
, propid
, obj_watch_handler
, callable
);
1353 obj_unwatch(JSContext
*cx
, uintN argc
, Value
*vp
)
1355 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1361 if (!ValueToId(cx
, vp
[2], &id
))
1366 return JS_ClearWatchPoint(cx
, obj
, id
, NULL
, NULL
);
1369 #endif /* JS_HAS_OBJ_WATCHPOINT */
1372 * Prototype and property query methods, to complement the 'in' and
1373 * 'instanceof' operators.
1376 /* Proposed ECMA 15.2.4.5. */
1378 obj_hasOwnProperty(JSContext
*cx
, uintN argc
, Value
*vp
)
1380 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1382 js_HasOwnPropertyHelper(cx
, obj
->getOps()->lookupProperty
, argc
, vp
);
1386 js_HasOwnPropertyHelper(JSContext
*cx
, JSLookupPropOp lookup
, uintN argc
,
1390 if (!ValueToId(cx
, argc
!= 0 ? vp
[2] : UndefinedValue(), &id
))
1393 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1398 if (obj
->isProxy()) {
1400 if (!JSProxy::hasOwn(cx
, obj
, id
, &has
))
1402 vp
->setBoolean(has
);
1405 if (!js_HasOwnProperty(cx
, lookup
, obj
, id
, &obj2
, &prop
))
1408 vp
->setBoolean(true);
1409 obj2
->dropProperty(cx
, prop
);
1411 vp
->setBoolean(false);
1417 js_HasOwnProperty(JSContext
*cx
, JSLookupPropOp lookup
, JSObject
*obj
, jsid id
,
1418 JSObject
**objp
, JSProperty
**propp
)
1420 JSAutoResolveFlags
rf(cx
, JSRESOLVE_QUALIFIED
| JSRESOLVE_DETECTING
);
1421 if (!(lookup
? lookup
: js_LookupProperty
)(cx
, obj
, id
, objp
, propp
))
1429 Class
*clasp
= (*objp
)->getClass();
1430 JSObject
*outer
= NULL
;
1431 if (JSObjectOp op
= (*objp
)->getClass()->ext
.outerObject
) {
1432 outer
= op(cx
, *objp
);
1437 if (outer
!= *objp
) {
1438 if ((*objp
)->isNative() && obj
->getClass() == clasp
) {
1440 * The combination of JSPROP_SHARED and JSPROP_PERMANENT in a
1441 * delegated property makes that property appear to be direct in
1442 * all delegating instances of the same native class. This hack
1443 * avoids bloating every function instance with its own 'length'
1444 * (AKA 'arity') property. But it must not extend across class
1445 * boundaries, to avoid making hasOwnProperty lie (bug 320854).
1447 * It's not really a hack, of course: a permanent property can't
1448 * be deleted, and JSPROP_SHARED means "don't allocate a slot in
1449 * any instance, prototype or delegating". Without a slot, and
1450 * without the ability to remove and recreate (with differences)
1451 * the property, there is no way to tell whether it is directly
1452 * owned, or indirectly delegated.
1454 Shape
*shape
= reinterpret_cast<Shape
*>(*propp
);
1455 if (shape
->isSharedPermanent())
1459 (*objp
)->dropProperty(cx
, *propp
);
1465 /* Proposed ECMA 15.2.4.6. */
1467 obj_isPrototypeOf(JSContext
*cx
, uintN argc
, Value
*vp
)
1469 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1472 const Value
&v
= argc
!= 0 ? vp
[2] : UndefinedValue();
1473 vp
->setBoolean(js_IsDelegate(cx
, obj
, v
));
1477 /* Proposed ECMA 15.2.4.7. */
1479 obj_propertyIsEnumerable(JSContext
*cx
, uintN argc
, Value
*vp
)
1482 if (!ValueToId(cx
, argc
!= 0 ? vp
[2] : UndefinedValue(), &id
))
1485 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1486 return obj
&& js_PropertyIsEnumerable(cx
, obj
, id
, vp
);
1490 js_PropertyIsEnumerable(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
1494 if (!obj
->lookupProperty(cx
, id
, &pobj
, &prop
))
1498 vp
->setBoolean(false);
1503 * XXX ECMA spec error compatible: return false unless hasOwnProperty.
1504 * The ECMA spec really should be fixed so propertyIsEnumerable and the
1505 * for..in loop agree on whether prototype properties are enumerable,
1506 * obviously by fixing this method (not by breaking the for..in loop!).
1508 * We check here for shared permanent prototype properties, which should
1509 * be treated as if they are local to obj. They are an implementation
1510 * technique used to satisfy ECMA requirements; users should not be able
1511 * to distinguish a shared permanent proto-property from a local one.
1515 if (pobj
->isNative()) {
1516 Shape
*shape
= (Shape
*) prop
;
1517 shared
= shape
->isSharedPermanent();
1518 attrs
= shape
->attributes();
1519 JS_UNLOCK_OBJ(cx
, pobj
);
1522 if (!pobj
->getAttributes(cx
, id
, &attrs
))
1525 if (pobj
!= obj
&& !shared
) {
1526 vp
->setBoolean(false);
1529 vp
->setBoolean((attrs
& JSPROP_ENUMERATE
) != 0);
1533 #if OLD_GETTER_SETTER_METHODS
1535 const char js_defineGetter_str
[] = "__defineGetter__";
1536 const char js_defineSetter_str
[] = "__defineSetter__";
1537 const char js_lookupGetter_str
[] = "__lookupGetter__";
1538 const char js_lookupSetter_str
[] = "__lookupSetter__";
1540 JS_FRIEND_API(JSBool
)
1541 js_obj_defineGetter(JSContext
*cx
, uintN argc
, Value
*vp
)
1543 if (argc
<= 1 || !js_IsCallable(vp
[3])) {
1544 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1545 JSMSG_BAD_GETTER_OR_SETTER
,
1549 PropertyOp getter
= CastAsPropertyOp(&vp
[3].toObject());
1552 if (!ValueToId(cx
, vp
[2], &id
))
1554 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1555 if (!obj
|| !CheckRedeclaration(cx
, obj
, id
, JSPROP_GETTER
, NULL
, NULL
))
1558 * Getters and setters are just like watchpoints from an access
1559 * control point of view.
1563 if (!CheckAccess(cx
, obj
, id
, JSACC_WATCH
, &junk
, &attrs
))
1566 return obj
->defineProperty(cx
, id
, UndefinedValue(), getter
, PropertyStub
,
1567 JSPROP_ENUMERATE
| JSPROP_GETTER
| JSPROP_SHARED
);
1570 JS_FRIEND_API(JSBool
)
1571 js_obj_defineSetter(JSContext
*cx
, uintN argc
, Value
*vp
)
1573 if (argc
<= 1 || !js_IsCallable(vp
[3])) {
1574 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1575 JSMSG_BAD_GETTER_OR_SETTER
,
1579 PropertyOp setter
= CastAsPropertyOp(&vp
[3].toObject());
1582 if (!ValueToId(cx
, vp
[2], &id
))
1584 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1585 if (!obj
|| !CheckRedeclaration(cx
, obj
, id
, JSPROP_SETTER
, NULL
, NULL
))
1588 * Getters and setters are just like watchpoints from an access
1589 * control point of view.
1593 if (!CheckAccess(cx
, obj
, id
, JSACC_WATCH
, &junk
, &attrs
))
1596 return obj
->defineProperty(cx
, id
, UndefinedValue(), PropertyStub
, setter
,
1597 JSPROP_ENUMERATE
| JSPROP_SETTER
| JSPROP_SHARED
);
1601 obj_lookupGetter(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 Shape
*shape
= (Shape
*) prop
;
1615 if (shape
->hasGetterValue())
1616 *vp
= shape
->getterValue();
1617 JS_UNLOCK_OBJ(cx
, pobj
);
1624 obj_lookupSetter(JSContext
*cx
, uintN argc
, Value
*vp
)
1627 if (!ValueToId(cx
, argc
!= 0 ? vp
[2] : UndefinedValue(), &id
))
1629 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1632 if (!obj
|| !obj
->lookupProperty(cx
, id
, &pobj
, &prop
))
1636 if (pobj
->isNative()) {
1637 Shape
*shape
= (Shape
*) prop
;
1638 if (shape
->hasSetterValue())
1639 *vp
= shape
->setterValue();
1640 JS_UNLOCK_OBJ(cx
, pobj
);
1645 #endif /* OLD_GETTER_SETTER_METHODS */
1648 obj_getPrototypeOf(JSContext
*cx
, uintN argc
, Value
*vp
)
1651 js_ReportMissingArg(cx
, *vp
, 0);
1655 if (vp
[2].isPrimitive()) {
1656 char *bytes
= DecompileValueGenerator(cx
, JSDVG_SEARCH_STACK
, vp
[2], NULL
);
1659 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1660 JSMSG_UNEXPECTED_TYPE
, bytes
, "not an object");
1665 JSObject
*obj
= &vp
[2].toObject();
1667 return CheckAccess(cx
, obj
, ATOM_TO_JSID(cx
->runtime
->atomState
.protoAtom
),
1668 JSACC_PROTO
, vp
, &attrs
);
1672 js_NewPropertyDescriptorObject(JSContext
*cx
, jsid id
, uintN attrs
,
1673 const Value
&getter
, const Value
&setter
,
1674 const Value
&value
, Value
*vp
)
1676 /* We have our own property, so start creating the descriptor. */
1677 JSObject
*desc
= NewBuiltinClassInstance(cx
, &js_ObjectClass
);
1680 vp
->setObject(*desc
); /* Root and return. */
1682 const JSAtomState
&atomState
= cx
->runtime
->atomState
;
1683 if (attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)) {
1684 if (!desc
->defineProperty(cx
, ATOM_TO_JSID(atomState
.getAtom
), getter
,
1685 PropertyStub
, PropertyStub
, JSPROP_ENUMERATE
) ||
1686 !desc
->defineProperty(cx
, ATOM_TO_JSID(atomState
.setAtom
), setter
,
1687 PropertyStub
, PropertyStub
, JSPROP_ENUMERATE
)) {
1691 if (!desc
->defineProperty(cx
, ATOM_TO_JSID(atomState
.valueAtom
), value
,
1692 PropertyStub
, PropertyStub
, JSPROP_ENUMERATE
) ||
1693 !desc
->defineProperty(cx
, ATOM_TO_JSID(atomState
.writableAtom
),
1694 BooleanValue((attrs
& JSPROP_READONLY
) == 0),
1695 PropertyStub
, PropertyStub
, JSPROP_ENUMERATE
)) {
1700 return desc
->defineProperty(cx
, ATOM_TO_JSID(atomState
.enumerableAtom
),
1701 BooleanValue((attrs
& JSPROP_ENUMERATE
) != 0),
1702 PropertyStub
, PropertyStub
, JSPROP_ENUMERATE
) &&
1703 desc
->defineProperty(cx
, ATOM_TO_JSID(atomState
.configurableAtom
),
1704 BooleanValue((attrs
& JSPROP_PERMANENT
) == 0),
1705 PropertyStub
, PropertyStub
, JSPROP_ENUMERATE
);
1709 js_GetOwnPropertyDescriptor(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
1711 if (obj
->isProxy()) {
1712 if (!JSProxy::getOwnPropertyDescriptor(cx
, obj
, id
, vp
))
1718 if (!js_HasOwnProperty(cx
, obj
->getOps()->lookupProperty
, obj
, id
, &pobj
, &prop
))
1725 Value roots
[] = { UndefinedValue(), UndefinedValue(), UndefinedValue() };
1726 AutoArrayRooter
tvr(cx
, JS_ARRAY_LENGTH(roots
), roots
);
1729 if (pobj
->isNative()) {
1730 Shape
*shape
= (Shape
*) prop
;
1731 attrs
= shape
->attributes();
1732 if (attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)) {
1734 if (attrs
& JSPROP_GETTER
)
1735 roots
[0] = shape
->getterValue();
1736 if (attrs
& JSPROP_SETTER
)
1737 roots
[1] = shape
->setterValue();
1739 JS_UNLOCK_OBJ(cx
, pobj
);
1740 } else if (!pobj
->getAttributes(cx
, id
, &attrs
)) {
1744 if (doGet
&& !obj
->getProperty(cx
, id
, &roots
[2]))
1747 return js_NewPropertyDescriptorObject(cx
, id
,
1749 roots
[0], /* getter */
1750 roots
[1], /* setter */
1751 roots
[2], /* value */
1756 GetFirstArgumentAsObject(JSContext
*cx
, uintN argc
, Value
*vp
, const char *method
, JSObject
**objp
)
1759 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_MORE_ARGS_NEEDED
,
1764 const Value
&v
= vp
[2];
1765 if (v
.isPrimitive()) {
1766 char *bytes
= DecompileValueGenerator(cx
, JSDVG_SEARCH_STACK
, v
, NULL
);
1769 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_UNEXPECTED_TYPE
,
1770 bytes
, "not an object");
1775 *objp
= &v
.toObject();
1780 obj_getOwnPropertyDescriptor(JSContext
*cx
, uintN argc
, Value
*vp
)
1783 if (!GetFirstArgumentAsObject(cx
, argc
, vp
, "Object.getOwnPropertyDescriptor", &obj
))
1785 AutoIdRooter
nameidr(cx
);
1786 if (!ValueToId(cx
, argc
>= 2 ? vp
[3] : UndefinedValue(), nameidr
.addr()))
1788 return js_GetOwnPropertyDescriptor(cx
, obj
, nameidr
.id(), vp
);
1792 obj_keys(JSContext
*cx
, uintN argc
, Value
*vp
)
1795 if (!GetFirstArgumentAsObject(cx
, argc
, vp
, "Object.keys", &obj
))
1798 AutoIdVector
props(cx
);
1799 if (!GetPropertyNames(cx
, obj
, JSITER_OWNONLY
, props
))
1802 AutoValueVector
vals(cx
);
1803 vals
.resize(props
.length());
1804 for (size_t i
= 0, len
= props
.length(); i
< len
; i
++) {
1806 if (JSID_IS_STRING(id
)) {
1807 vals
[i
].setString(JSID_TO_STRING(id
));
1809 JS_ASSERT(JSID_IS_INT(id
));
1810 JSString
*str
= js_IntToString(cx
, JSID_TO_INT(id
));
1813 vals
[i
].setString(str
);
1817 JS_ASSERT(props
.length() <= UINT32_MAX
);
1818 JSObject
*aobj
= js_NewArrayObject(cx
, jsuint(vals
.length()), vals
.begin());
1821 vp
->setObject(*aobj
);
1827 HasProperty(JSContext
* cx
, JSObject
* obj
, jsid id
, Value
* vp
, JSBool
* answerp
)
1829 if (!JS_HasPropertyById(cx
, obj
, id
, answerp
))
1835 return JS_GetPropertyById(cx
, obj
, id
, Jsvalify(vp
));
1838 PropDesc::PropDesc()
1839 : pd(UndefinedValue()),
1841 value(UndefinedValue()),
1842 get(UndefinedValue()),
1843 set(UndefinedValue()),
1849 hasEnumerable(false),
1850 hasConfigurable(false)
1855 PropDesc::initialize(JSContext
* cx
, jsid id
, const Value
&origval
)
1861 if (v
.isPrimitive()) {
1862 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_NOT_NONNULL_OBJECT
);
1865 JSObject
* desc
= &v
.toObject();
1867 /* Make a copy of the descriptor. We might need it later. */
1870 /* Start with the proper defaults. */
1871 attrs
= JSPROP_PERMANENT
| JSPROP_READONLY
;
1876 if (!HasProperty(cx
, desc
, ATOM_TO_JSID(cx
->runtime
->atomState
.enumerableAtom
), &v
,
1881 hasEnumerable
= JS_TRUE
;
1882 if (js_ValueToBoolean(v
))
1883 attrs
|= JSPROP_ENUMERATE
;
1887 if (!HasProperty(cx
, desc
, ATOM_TO_JSID(cx
->runtime
->atomState
.configurableAtom
), &v
,
1892 hasConfigurable
= JS_TRUE
;
1893 if (js_ValueToBoolean(v
))
1894 attrs
&= ~JSPROP_PERMANENT
;
1898 if (!HasProperty(cx
, desc
, ATOM_TO_JSID(cx
->runtime
->atomState
.valueAtom
), &v
, &hasProperty
))
1906 if (!HasProperty(cx
, desc
, ATOM_TO_JSID(cx
->runtime
->atomState
.writableAtom
), &v
, &hasProperty
))
1909 hasWritable
= JS_TRUE
;
1910 if (js_ValueToBoolean(v
))
1911 attrs
&= ~JSPROP_READONLY
;
1915 if (!HasProperty(cx
, desc
, ATOM_TO_JSID(cx
->runtime
->atomState
.getAtom
), &v
, &hasProperty
))
1918 if ((v
.isPrimitive() || !js_IsCallable(v
)) && !v
.isUndefined()) {
1919 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_BAD_GET_SET_FIELD
,
1925 attrs
|= JSPROP_GETTER
| JSPROP_SHARED
;
1929 if (!HasProperty(cx
, desc
, ATOM_TO_JSID(cx
->runtime
->atomState
.setAtom
), &v
, &hasProperty
))
1932 if ((v
.isPrimitive() || !js_IsCallable(v
)) && !v
.isUndefined()) {
1933 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_BAD_GET_SET_FIELD
,
1939 attrs
|= JSPROP_SETTER
| JSPROP_SHARED
;
1943 if ((hasGet
|| hasSet
) && (hasValue
|| hasWritable
)) {
1944 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_INVALID_DESCRIPTOR
);
1952 Reject(JSContext
*cx
, uintN errorNumber
, bool throwError
, jsid id
, bool *rval
)
1956 if (!js_ValueToStringId(cx
, IdToValue(id
), &idstr
))
1958 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, errorNumber
,
1959 JS_GetStringBytes(JSID_TO_STRING(idstr
)));
1968 Reject(JSContext
*cx
, uintN errorNumber
, bool throwError
, bool *rval
)
1971 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, errorNumber
);
1980 Reject(JSContext
*cx
, JSObject
*obj
, JSProperty
*prop
, uintN errorNumber
, bool throwError
,
1981 jsid id
, bool *rval
)
1983 obj
->dropProperty(cx
, prop
);
1984 return Reject(cx
, errorNumber
, throwError
, id
, rval
);
1988 DefinePropertyOnObject(JSContext
*cx
, JSObject
*obj
, const PropDesc
&desc
,
1989 bool throwError
, bool *rval
)
1991 /* 8.12.9 step 1. */
1992 JSProperty
*current
;
1994 JS_ASSERT(!obj
->getOps()->lookupProperty
);
1995 if (!js_HasOwnProperty(cx
, NULL
, obj
, desc
.id
, &obj2
, ¤t
))
1998 JS_ASSERT(!obj
->getOps()->defineProperty
);
2000 /* 8.12.9 steps 2-4. */
2003 return Reject(cx
, JSMSG_OBJECT_NOT_EXTENSIBLE
, throwError
, rval
);
2007 if (desc
.isGenericDescriptor() || desc
.isDataDescriptor()) {
2008 JS_ASSERT(!obj
->getOps()->defineProperty
);
2009 return js_DefineProperty(cx
, obj
, desc
.id
, &desc
.value
,
2010 PropertyStub
, PropertyStub
, desc
.attrs
);
2013 JS_ASSERT(desc
.isAccessorDescriptor());
2016 * Getters and setters are just like watchpoints from an access
2017 * control point of view.
2021 if (!CheckAccess(cx
, obj
, desc
.id
, JSACC_WATCH
, &dummy
, &dummyAttrs
))
2024 Value tmp
= UndefinedValue();
2025 return js_DefineProperty(cx
, obj
, desc
.id
, &tmp
,
2026 desc
.getter(), desc
.setter(), desc
.attrs
);
2029 /* 8.12.9 steps 5-6 (note 5 is merely a special case of 6). */
2030 Value v
= UndefinedValue();
2033 * In the special case of shared permanent properties, the "own" property
2034 * can be found on a different object. In that case the returned property
2035 * might not be native, except: the shared permanent property optimization
2036 * is not applied if the objects have different classes (bug 320854), as
2037 * must be enforced by js_HasOwnProperty for the Shape cast below to be
2040 JS_ASSERT(obj
->getClass() == obj2
->getClass());
2042 const Shape
*shape
= reinterpret_cast<Shape
*>(current
);
2044 if (desc
.isAccessorDescriptor()) {
2045 if (!shape
->isAccessorDescriptor())
2049 !SameValue(desc
.getterValue(), shape
->getterOrUndefined(), cx
)) {
2054 !SameValue(desc
.setterValue(), shape
->setterOrUndefined(), cx
)) {
2059 * Determine the current value of the property once, if the current
2060 * value might actually need to be used or preserved later. NB: we
2061 * guard on whether the current property is a data descriptor to
2062 * avoid calling a getter; we won't need the value if it's not a
2065 if (shape
->isDataDescriptor()) {
2067 * Non-standard: if the property is non-configurable and is
2068 * represented by a native getter or setter, don't permit
2069 * redefinition. We expose properties with native getter/setter
2070 * as though they were data properties, for the most part, but
2071 * in this particular case we must worry about integrity
2072 * concerns for JSAPI users who expected that
2073 * permanent+getter/setter means precisely controlled behavior.
2074 * If we permitted such redefinitions, such a property could be
2075 * "fixed" to some specific previous value, no longer varying
2076 * according to the intent of the native getter/setter for the
2079 * Other engines expose properties of this nature using ECMA
2080 * getter/setter pairs, but we can't because we use them even
2081 * for properties which ECMA specifies as being true data
2082 * descriptors ([].length, Function.length, /regex/.lastIndex,
2083 * &c.). Longer-term perhaps we should convert such properties
2084 * to use data descriptors (at which point representing a
2085 * descriptor with native getter/setter as an accessor
2086 * descriptor would be fine) and take a small memory hit, but
2087 * for now we'll simply forbid their redefinition.
2089 if (!shape
->configurable() &&
2090 (!shape
->hasDefaultGetter() || !shape
->hasDefaultSetter())) {
2091 return Reject(cx
, obj2
, current
, JSMSG_CANT_REDEFINE_UNCONFIGURABLE_PROP
,
2092 throwError
, desc
.id
, rval
);
2095 if (!js_NativeGet(cx
, obj
, obj2
, shape
, JSGET_NO_METHOD_BARRIER
, &v
)) {
2096 /* current was dropped when the failure occurred. */
2101 if (desc
.isDataDescriptor()) {
2102 if (!shape
->isDataDescriptor())
2105 if (desc
.hasValue
&& !SameValue(desc
.value
, v
, cx
))
2107 if (desc
.hasWritable
&& desc
.writable() != shape
->writable())
2110 /* The only fields in desc will be handled below. */
2111 JS_ASSERT(desc
.isGenericDescriptor());
2115 if (desc
.hasConfigurable
&& desc
.configurable() != shape
->configurable())
2117 if (desc
.hasEnumerable
&& desc
.enumerable() != shape
->enumerable())
2120 /* The conditions imposed by step 5 or step 6 apply. */
2121 obj2
->dropProperty(cx
, current
);
2126 /* 8.12.9 step 7. */
2127 if (!shape
->configurable()) {
2129 * Since [[Configurable]] defaults to false, we don't need to check
2130 * whether it was specified. We can't do likewise for [[Enumerable]]
2131 * because its putative value is used in a comparison -- a comparison
2132 * whose result must always be false per spec if the [[Enumerable]]
2133 * field is not present. Perfectly pellucid logic, eh?
2135 JS_ASSERT_IF(!desc
.hasConfigurable
, !desc
.configurable());
2136 if (desc
.configurable() ||
2137 (desc
.hasEnumerable
&& desc
.enumerable() != shape
->enumerable())) {
2138 return Reject(cx
, obj2
, current
, JSMSG_CANT_REDEFINE_UNCONFIGURABLE_PROP
, throwError
,
2143 if (desc
.isGenericDescriptor()) {
2144 /* 8.12.9 step 8, no validation required */
2145 } else if (desc
.isDataDescriptor() != shape
->isDataDescriptor()) {
2146 /* 8.12.9 step 9. */
2147 if (!shape
->configurable()) {
2148 return Reject(cx
, obj2
, current
, JSMSG_CANT_REDEFINE_UNCONFIGURABLE_PROP
,
2149 throwError
, desc
.id
, rval
);
2151 } else if (desc
.isDataDescriptor()) {
2152 /* 8.12.9 step 10. */
2153 JS_ASSERT(shape
->isDataDescriptor());
2154 if (!shape
->configurable() && !shape
->writable()) {
2155 if ((desc
.hasWritable
&& desc
.writable()) ||
2156 (desc
.hasValue
&& !SameValue(desc
.value
, v
, cx
))) {
2157 return Reject(cx
, obj2
, current
, JSMSG_CANT_REDEFINE_UNCONFIGURABLE_PROP
,
2158 throwError
, desc
.id
, rval
);
2162 /* 8.12.9 step 11. */
2163 JS_ASSERT(desc
.isAccessorDescriptor() && shape
->isAccessorDescriptor());
2164 if (!shape
->configurable()) {
2166 !SameValue(desc
.setterValue(), shape
->setterOrUndefined(), cx
)) ||
2168 !SameValue(desc
.getterValue(), shape
->getterOrUndefined(), cx
))) {
2169 return Reject(cx
, obj2
, current
, JSMSG_CANT_REDEFINE_UNCONFIGURABLE_PROP
,
2170 throwError
, desc
.id
, rval
);
2175 /* 8.12.9 step 12. */
2177 PropertyOp getter
, setter
;
2178 if (desc
.isGenericDescriptor()) {
2180 if (desc
.hasConfigurable
)
2181 changed
|= JSPROP_PERMANENT
;
2182 if (desc
.hasEnumerable
)
2183 changed
|= JSPROP_ENUMERATE
;
2185 attrs
= (shape
->attributes() & ~changed
) | (desc
.attrs
& changed
);
2186 if (shape
->isMethod()) {
2187 JS_ASSERT(!(attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)));
2188 getter
= setter
= PropertyStub
;
2190 getter
= shape
->getter();
2191 setter
= shape
->setter();
2193 } else if (desc
.isDataDescriptor()) {
2194 uintN unchanged
= 0;
2195 if (!desc
.hasConfigurable
)
2196 unchanged
|= JSPROP_PERMANENT
;
2197 if (!desc
.hasEnumerable
)
2198 unchanged
|= JSPROP_ENUMERATE
;
2199 if (!desc
.hasWritable
)
2200 unchanged
|= JSPROP_READONLY
;
2204 attrs
= (desc
.attrs
& ~unchanged
) | (shape
->attributes() & unchanged
);
2205 getter
= setter
= PropertyStub
;
2207 JS_ASSERT(desc
.isAccessorDescriptor());
2210 * Getters and setters are just like watchpoints from an access
2211 * control point of view.
2214 if (!CheckAccess(cx
, obj2
, desc
.id
, JSACC_WATCH
, &dummy
, &attrs
)) {
2215 obj2
->dropProperty(cx
, current
);
2219 JS_ASSERT_IF(shape
->isMethod(), !(attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)));
2221 /* 8.12.9 step 12. */
2223 if (desc
.hasConfigurable
)
2224 changed
|= JSPROP_PERMANENT
;
2225 if (desc
.hasEnumerable
)
2226 changed
|= JSPROP_ENUMERATE
;
2228 changed
|= JSPROP_GETTER
| JSPROP_SHARED
;
2230 changed
|= JSPROP_SETTER
| JSPROP_SHARED
;
2232 attrs
= (desc
.attrs
& changed
) | (shape
->attributes() & ~changed
);
2234 getter
= desc
.getter();
2236 getter
= (shape
->isMethod() || (shape
->hasDefaultGetter() && !shape
->hasGetterValue()))
2241 setter
= desc
.setter();
2243 setter
= (shape
->hasDefaultSetter() && !shape
->hasSetterValue())
2250 obj2
->dropProperty(cx
, current
);
2251 return js_DefineProperty(cx
, obj
, desc
.id
, &v
, getter
, setter
, attrs
);
2255 DefinePropertyOnArray(JSContext
*cx
, JSObject
*obj
, const PropDesc
&desc
,
2256 bool throwError
, bool *rval
)
2259 * We probably should optimize dense array property definitions where
2260 * the descriptor describes a traditional array property (enumerable,
2261 * configurable, writable, numeric index or length without altering its
2262 * attributes). Such definitions are probably unlikely, so we don't bother
2265 if (obj
->isDenseArray() && !obj
->makeDenseArraySlow(cx
))
2268 jsuint oldLen
= obj
->getArrayLength();
2270 if (JSID_IS_ATOM(desc
.id
, cx
->runtime
->atomState
.lengthAtom
)) {
2272 * Our optimization of storage of the length property of arrays makes
2273 * it very difficult to properly implement defining the property. For
2274 * now simply throw an exception (NB: not merely Reject) on any attempt
2275 * to define the "length" property, rather than attempting to implement
2276 * some difficult-for-authors-to-grasp subset of that functionality.
2278 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_DEFINE_ARRAY_LENGTH_UNSUPPORTED
);
2283 if (js_IdIsIndex(desc
.id
, &index
)) {
2285 // Disabled until we support defining "length":
2286 if (index >= oldLen && lengthPropertyNotWritable())
2287 return ThrowTypeError(cx, JSMSG_CANT_APPEND_PROPERTIES_TO_UNWRITABLE_LENGTH_ARRAY);
2289 if (!DefinePropertyOnObject(cx
, obj
, desc
, false, rval
))
2292 return Reject(cx
, JSMSG_CANT_DEFINE_ARRAY_INDEX
, throwError
, rval
);
2294 if (index
>= oldLen
) {
2295 JS_ASSERT(index
!= UINT32_MAX
);
2296 obj
->setArrayLength(index
+ 1);
2303 return DefinePropertyOnObject(cx
, obj
, desc
, throwError
, rval
);
2307 DefineProperty(JSContext
*cx
, JSObject
*obj
, const PropDesc
&desc
, bool throwError
,
2311 return DefinePropertyOnArray(cx
, obj
, desc
, throwError
, rval
);
2313 if (obj
->getOps()->lookupProperty
) {
2315 return JSProxy::defineProperty(cx
, obj
, desc
.id
, desc
.pd
);
2316 return Reject(cx
, JSMSG_OBJECT_NOT_EXTENSIBLE
, throwError
, rval
);
2319 return DefinePropertyOnObject(cx
, obj
, desc
, throwError
, rval
);
2323 js_DefineOwnProperty(JSContext
*cx
, JSObject
*obj
, jsid id
,
2324 const Value
&descriptor
, JSBool
*bp
)
2326 AutoPropDescArrayRooter
descs(cx
);
2327 PropDesc
*desc
= descs
.append();
2328 if (!desc
|| !desc
->initialize(cx
, id
, descriptor
))
2332 if (!DefineProperty(cx
, obj
, *desc
, true, &rval
))
2338 /* ES5 15.2.3.6: Object.defineProperty(O, P, Attributes) */
2340 obj_defineProperty(JSContext
* cx
, uintN argc
, Value
* vp
)
2342 /* 15.2.3.6 steps 1 and 5. */
2344 if (!GetFirstArgumentAsObject(cx
, argc
, vp
, "Object.defineProperty", &obj
))
2346 vp
->setObject(*obj
);
2348 /* 15.2.3.6 step 2. */
2349 AutoIdRooter
nameidr(cx
);
2350 if (!ValueToId(cx
, argc
>= 2 ? vp
[3] : UndefinedValue(), nameidr
.addr()))
2353 /* 15.2.3.6 step 3. */
2354 const Value
&descval
= argc
>= 3 ? vp
[4] : UndefinedValue();
2356 /* 15.2.3.6 step 4 */
2358 return js_DefineOwnProperty(cx
, obj
, nameidr
.id(), descval
, &junk
);
2362 DefineProperties(JSContext
*cx
, JSObject
*obj
, JSObject
*props
)
2364 AutoIdArray
ida(cx
, JS_Enumerate(cx
, props
));
2368 AutoPropDescArrayRooter
descs(cx
);
2369 size_t len
= ida
.length();
2370 for (size_t i
= 0; i
< len
; i
++) {
2372 PropDesc
* desc
= descs
.append();
2373 AutoValueRooter
tvr(cx
);
2375 !JS_GetPropertyById(cx
, props
, id
, tvr
.jsval_addr()) ||
2376 !desc
->initialize(cx
, id
, tvr
.value())) {
2382 for (size_t i
= 0; i
< len
; i
++) {
2383 if (!DefineProperty(cx
, obj
, descs
[i
], true, &dummy
))
2391 js_PopulateObject(JSContext
*cx
, JSObject
*newborn
, JSObject
*props
)
2393 return DefineProperties(cx
, newborn
, props
);
2396 /* ES5 15.2.3.7: Object.defineProperties(O, Properties) */
2398 obj_defineProperties(JSContext
* cx
, uintN argc
, Value
* vp
)
2400 /* 15.2.3.6 steps 1 and 5. */
2402 if (!GetFirstArgumentAsObject(cx
, argc
, vp
, "Object.defineProperties", &obj
))
2404 vp
->setObject(*obj
);
2407 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_MORE_ARGS_NEEDED
,
2408 "Object.defineProperties", "0", "s");
2412 JSObject
* props
= js_ValueToNonNullObject(cx
, vp
[3]);
2415 vp
[3].setObject(*props
);
2417 return DefineProperties(cx
, obj
, props
);
2420 /* ES5 15.2.3.5: Object.create(O [, Properties]) */
2422 obj_create(JSContext
*cx
, uintN argc
, Value
*vp
)
2425 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_MORE_ARGS_NEEDED
,
2426 "Object.create", "0", "s");
2430 const Value
&v
= vp
[2];
2431 if (!v
.isObjectOrNull()) {
2432 char *bytes
= DecompileValueGenerator(cx
, JSDVG_SEARCH_STACK
, v
, NULL
);
2435 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_UNEXPECTED_TYPE
,
2436 bytes
, "not an object or null");
2442 * Use the callee's global as the parent of the new object to avoid dynamic
2443 * scoping (i.e., using the caller's global).
2445 JSObject
*obj
= NewNonFunction
<WithProto::Given
>(cx
, &js_ObjectClass
, v
.toObjectOrNull(),
2446 vp
->toObject().getGlobal());
2449 vp
->setObject(*obj
); /* Root and prepare for eventual return. */
2451 /* 15.2.3.5 step 4. */
2452 if (argc
> 1 && !vp
[3].isUndefined()) {
2453 if (vp
[3].isPrimitive()) {
2454 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_NOT_NONNULL_OBJECT
);
2458 JSObject
*props
= &vp
[3].toObject();
2459 AutoIdArray
ida(cx
, JS_Enumerate(cx
, props
));
2463 AutoPropDescArrayRooter
descs(cx
);
2464 size_t len
= ida
.length();
2465 for (size_t i
= 0; i
< len
; i
++) {
2467 PropDesc
*desc
= descs
.append();
2468 if (!desc
|| !JS_GetPropertyById(cx
, props
, id
, Jsvalify(&vp
[1])) ||
2469 !desc
->initialize(cx
, id
, vp
[1])) {
2475 for (size_t i
= 0; i
< len
; i
++) {
2476 if (!DefineProperty(cx
, obj
, descs
[i
], true, &dummy
))
2481 /* 5. Return obj. */
2486 obj_getOwnPropertyNames(JSContext
*cx
, uintN argc
, Value
*vp
)
2489 if (!GetFirstArgumentAsObject(cx
, argc
, vp
, "Object.getOwnPropertyNames", &obj
))
2492 AutoIdVector
keys(cx
);
2493 if (!GetPropertyNames(cx
, obj
, JSITER_OWNONLY
| JSITER_HIDDEN
, keys
))
2496 AutoValueVector
vals(cx
);
2497 if (!vals
.resize(keys
.length()))
2500 for (size_t i
= 0, len
= keys
.length(); i
< len
; i
++) {
2502 if (JSID_IS_INT(id
)) {
2503 JSString
*str
= js_ValueToString(cx
, Int32Value(JSID_TO_INT(id
)));
2506 vals
[i
].setString(str
);
2507 } else if (JSID_IS_ATOM(id
)) {
2508 vals
[i
].setString(JSID_TO_STRING(id
));
2510 vals
[i
].setObject(*JSID_TO_OBJECT(id
));
2514 JSObject
*aobj
= js_NewArrayObject(cx
, vals
.length(), vals
.begin());
2518 vp
->setObject(*aobj
);
2523 #if JS_HAS_OBJ_WATCHPOINT
2524 const char js_watch_str
[] = "watch";
2525 const char js_unwatch_str
[] = "unwatch";
2527 const char js_hasOwnProperty_str
[] = "hasOwnProperty";
2528 const char js_isPrototypeOf_str
[] = "isPrototypeOf";
2529 const char js_propertyIsEnumerable_str
[] = "propertyIsEnumerable";
2531 static JSFunctionSpec object_methods
[] = {
2533 JS_FN(js_toSource_str
, obj_toSource
, 0,0),
2535 JS_FN(js_toString_str
, obj_toString
, 0,0),
2536 JS_FN(js_toLocaleString_str
, obj_toLocaleString
, 0,0),
2537 JS_FN(js_valueOf_str
, obj_valueOf
, 0,0),
2538 #if JS_HAS_OBJ_WATCHPOINT
2539 JS_FN(js_watch_str
, obj_watch
, 2,0),
2540 JS_FN(js_unwatch_str
, obj_unwatch
, 1,0),
2542 JS_FN(js_hasOwnProperty_str
, obj_hasOwnProperty
, 1,0),
2543 JS_FN(js_isPrototypeOf_str
, obj_isPrototypeOf
, 1,0),
2544 JS_FN(js_propertyIsEnumerable_str
, obj_propertyIsEnumerable
, 1,0),
2545 #if OLD_GETTER_SETTER_METHODS
2546 JS_FN(js_defineGetter_str
, js_obj_defineGetter
, 2,0),
2547 JS_FN(js_defineSetter_str
, js_obj_defineSetter
, 2,0),
2548 JS_FN(js_lookupGetter_str
, obj_lookupGetter
, 1,0),
2549 JS_FN(js_lookupSetter_str
, obj_lookupSetter
, 1,0),
2554 static JSFunctionSpec object_static_methods
[] = {
2555 JS_FN("getPrototypeOf", obj_getPrototypeOf
, 1,0),
2556 JS_FN("getOwnPropertyDescriptor", obj_getOwnPropertyDescriptor
,2,0),
2557 JS_FN("keys", obj_keys
, 1,0),
2558 JS_FN("defineProperty", obj_defineProperty
, 3,0),
2559 JS_FN("defineProperties", obj_defineProperties
, 2,0),
2560 JS_FN("create", obj_create
, 2,0),
2561 JS_FN("getOwnPropertyNames", obj_getOwnPropertyNames
, 1,0),
2566 js_Object(JSContext
*cx
, uintN argc
, Value
*vp
)
2570 /* Trigger logic below to construct a blank object. */
2573 /* If argv[0] is null or undefined, obj comes back null. */
2574 if (!js_ValueToObjectOrNull(cx
, vp
[2], &obj
))
2578 /* Make an object whether this was called with 'new' or not. */
2579 JS_ASSERT(!argc
|| vp
[2].isNull() || vp
[2].isUndefined());
2580 obj
= NewBuiltinClassInstance(cx
, &js_ObjectClass
);
2584 vp
->setObject(*obj
);
2589 js_NewInstance(JSContext
*cx
, JSObject
*callee
)
2591 Class
*clasp
= callee
->getClass();
2593 Class
*newclasp
= &js_ObjectClass
;
2594 if (clasp
== &js_FunctionClass
) {
2595 JSFunction
*fun
= callee
->getFunctionPrivate();
2596 if (fun
->isNative() && fun
->u
.n
.clasp
)
2597 newclasp
= fun
->u
.n
.clasp
;
2601 if (!callee
->getProperty(cx
, ATOM_TO_JSID(cx
->runtime
->atomState
.classPrototypeAtom
), &protov
))
2604 JSObject
*proto
= protov
.isObjectOrNull() ? protov
.toObjectOrNull() : NULL
;
2605 JSObject
*parent
= callee
->getParent();
2606 return NewObject
<WithProto::Class
>(cx
, newclasp
, proto
, parent
);
2611 static JS_ALWAYS_INLINE JSObject
*
2612 NewObjectWithClassProto(JSContext
*cx
, Class
*clasp
, JSObject
*proto
,
2613 const Value
&privateSlotValue
)
2615 JS_ASSERT(clasp
->isNative());
2617 JSObject
* obj
= js_NewGCObject(cx
);
2621 obj
->initSharingEmptyShape(clasp
, proto
, proto
->getParent(), privateSlotValue
, cx
);
2626 js_Object_tn(JSContext
* cx
, JSObject
* proto
)
2628 JS_ASSERT(!(js_ObjectClass
.flags
& JSCLASS_HAS_PRIVATE
));
2630 return NewObjectWithClassProto(cx
, &js_ObjectClass
, proto
, UndefinedValue());
2633 JS_DEFINE_TRCINFO_1(js_Object
,
2634 (2, (extern, CONSTRUCTOR_RETRY
, js_Object_tn
, CONTEXT
, CALLEE_PROTOTYPE
, 0,
2635 nanojit::ACCSET_STORE_ANY
)))
2638 js_NonEmptyObject(JSContext
* cx
, JSObject
* proto
)
2640 JS_ASSERT(!(js_ObjectClass
.flags
& JSCLASS_HAS_PRIVATE
));
2642 JSObject
*obj
= NewObjectWithClassProto(cx
, &js_ObjectClass
, proto
, UndefinedValue());
2643 return (obj
&& obj
->ensureClassReservedSlotsForEmptyObject(cx
)) ? obj
: NULL
;
2646 JS_DEFINE_CALLINFO_2(extern, CONSTRUCTOR_RETRY
, js_NonEmptyObject
, CONTEXT
, CALLEE_PROTOTYPE
, 0,
2647 nanojit::ACCSET_STORE_ANY
)
2650 js_String_tn(JSContext
* cx
, JSObject
* proto
, JSString
* str
)
2652 JS_ASSERT(JS_ON_TRACE(cx
));
2653 return NewObjectWithClassProto(cx
, &js_StringClass
, proto
, StringValue(str
));
2655 JS_DEFINE_CALLINFO_3(extern, OBJECT
, js_String_tn
, CONTEXT
, CALLEE_PROTOTYPE
, STRING
, 0,
2656 nanojit::ACCSET_STORE_ANY
)
2659 js_NewInstanceFromTrace(JSContext
*cx
, Class
*clasp
, JSObject
*ctor
)
2661 JS_ASSERT(JS_ON_TRACE(cx
));
2662 JS_ASSERT(ctor
->isFunction());
2663 #ifdef JS_THREADSAFE
2664 if (ctor
->title
.ownercx
!= cx
)
2668 if (!ctor
->ensureClassReservedSlots(cx
))
2671 jsid classPrototypeId
= ATOM_TO_JSID(cx
->runtime
->atomState
.classPrototypeAtom
);
2672 const Shape
*shape
= ctor
->nativeLookup(classPrototypeId
);
2673 Value pval
= shape
? ctor
->getSlot(shape
->slot
) : MagicValue(JS_GENERIC_MAGIC
);
2675 JSObject
*parent
= ctor
->getParent();
2677 if (pval
.isObject()) {
2678 /* An object in ctor.prototype, let's use it as the new instance's proto. */
2679 proto
= &pval
.toObject();
2681 /* A hole or a primitive: either way, we need to get Object.prototype. */
2682 if (!js_GetClassPrototype(cx
, parent
, JSProto_Object
, &proto
))
2685 if (pval
.isMagic(JS_GENERIC_MAGIC
)) {
2687 * No ctor.prototype was set, so we inline-expand and optimize
2688 * fun_resolve's prototype creation code.
2690 proto
= NewNativeClassInstance(cx
, clasp
, proto
, parent
);
2693 if (!js_SetClassPrototype(cx
, ctor
, proto
, JSPROP_ENUMERATE
| JSPROP_PERMANENT
))
2697 * A primitive value in .prototype means to use Object.prototype
2698 * for proto. See ES5 13.2.2 step 7.
2704 * FIXME: 561785 at least. Quasi-natives including XML objects prevent us
2705 * from easily or unconditionally calling NewNativeClassInstance here.
2707 return NewNonFunction
<WithProto::Given
>(cx
, clasp
, proto
, parent
);
2710 JS_DEFINE_CALLINFO_3(extern, CONSTRUCTOR_RETRY
, js_NewInstanceFromTrace
, CONTEXT
, CLASS
, OBJECT
, 0,
2711 nanojit::ACCSET_STORE_ANY
)
2713 #else /* !JS_TRACER */
2715 # define js_Object_trcinfo NULL
2717 #endif /* !JS_TRACER */
2720 * Given pc pointing after a property accessing bytecode, return true if the
2721 * access is "object-detecting" in the sense used by web scripts, e.g., when
2722 * checking whether document.all is defined.
2724 JS_REQUIRES_STACK JSBool
2725 Detecting(JSContext
*cx
, jsbytecode
*pc
)
2732 script
= cx
->fp()->script();
2733 endpc
= script
->code
+ script
->length
;
2734 for (;; pc
+= js_CodeSpec
[op
].length
) {
2735 JS_ASSERT_IF(!cx
->fp()->hasImacropc(), script
->code
<= pc
&& pc
< endpc
);
2737 /* General case: a branch or equality op follows the access. */
2738 op
= js_GetOpcode(cx
, script
, pc
);
2739 if (js_CodeSpec
[op
].format
& JOF_DETECTING
)
2745 * Special case #1: handle (document.all == null). Don't sweat
2746 * about JS1.2's revision of the equality operators here.
2749 op
= js_GetOpcode(cx
, script
, pc
);
2750 return *pc
== JSOP_EQ
|| *pc
== JSOP_NE
;
2757 * Special case #2: handle (document.all == undefined). Don't
2758 * worry about someone redefining undefined, which was added by
2759 * Edition 3, so is read/write for backward compatibility.
2761 GET_ATOM_FROM_BYTECODE(script
, pc
, 0, atom
);
2762 if (atom
== cx
->runtime
->atomState
.typeAtoms
[JSTYPE_VOID
] &&
2763 (pc
+= js_CodeSpec
[op
].length
) < endpc
) {
2764 op
= js_GetOpcode(cx
, script
, pc
);
2765 return op
== JSOP_EQ
|| op
== JSOP_NE
||
2766 op
== JSOP_STRICTEQ
|| op
== JSOP_STRICTNE
;
2772 * At this point, anything but an extended atom index prefix means
2773 * we're not detecting.
2775 if (!(js_CodeSpec
[op
].format
& JOF_INDEXBASE
))
2783 * Infer lookup flags from the currently executing bytecode. This does
2784 * not attempt to infer JSRESOLVE_WITH, because the current bytecode
2785 * does not indicate whether we are in a with statement. Return defaultFlags
2786 * if a currently executing bytecode cannot be determined.
2789 js_InferFlags(JSContext
*cx
, uintN defaultFlags
)
2792 if (JS_ON_TRACE(cx
))
2793 return cx
->bailExit
->lookupFlags
;
2796 JS_ASSERT_NOT_ON_TRACE(cx
);
2799 const JSCodeSpec
*cs
;
2803 JSStackFrame
*const fp
= js_GetTopStackFrame(cx
);
2804 if (!fp
|| !(pc
= cx
->regs
->pc
))
2805 return defaultFlags
;
2806 cs
= &js_CodeSpec
[js_GetOpcode(cx
, fp
->script(), pc
)];
2807 format
= cs
->format
;
2808 if (JOF_MODE(format
) != JOF_NAME
)
2809 flags
|= JSRESOLVE_QUALIFIED
;
2810 if ((format
& (JOF_SET
| JOF_FOR
)) || fp
->isAssigning()) {
2811 flags
|= JSRESOLVE_ASSIGNING
;
2812 } else if (cs
->length
>= 0) {
2814 JSScript
*script
= cx
->fp()->script();
2815 if (pc
< script
->code
+ script
->length
&& Detecting(cx
, pc
))
2816 flags
|= JSRESOLVE_DETECTING
;
2818 if (format
& JOF_DECLARING
)
2819 flags
|= JSRESOLVE_DECLARING
;
2824 * ObjectOps and Class for with-statement stack objects.
2827 with_LookupProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, JSObject
**objp
,
2830 /* Fixes bug 463997 */
2831 uintN flags
= cx
->resolveFlags
;
2832 if (flags
== JSRESOLVE_INFER
)
2833 flags
= js_InferFlags(cx
, flags
);
2834 flags
|= JSRESOLVE_WITH
;
2835 JSAutoResolveFlags
rf(cx
, flags
);
2836 return obj
->getProto()->lookupProperty(cx
, id
, objp
, propp
);
2840 with_GetProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
2842 return obj
->getProto()->getProperty(cx
, id
, vp
);
2846 with_SetProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
, JSBool strict
)
2848 return obj
->getProto()->setProperty(cx
, id
, vp
, strict
);
2852 with_GetAttributes(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN
*attrsp
)
2854 return obj
->getProto()->getAttributes(cx
, id
, attrsp
);
2858 with_SetAttributes(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN
*attrsp
)
2860 return obj
->getProto()->setAttributes(cx
, id
, attrsp
);
2864 with_DeleteProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*rval
, JSBool strict
)
2866 return obj
->getProto()->deleteProperty(cx
, id
, rval
, strict
);
2870 with_Enumerate(JSContext
*cx
, JSObject
*obj
, JSIterateOp enum_op
,
2871 Value
*statep
, jsid
*idp
)
2873 return obj
->getProto()->enumerate(cx
, enum_op
, statep
, idp
);
2877 with_TypeOf(JSContext
*cx
, JSObject
*obj
)
2879 return JSTYPE_OBJECT
;
2883 with_ThisObject(JSContext
*cx
, JSObject
*obj
)
2885 return obj
->getWithThis();
2888 Class js_WithClass
= {
2890 JSCLASS_HAS_PRIVATE
| JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS
,
2891 PropertyStub
, /* addProperty */
2892 PropertyStub
, /* delProperty */
2893 PropertyStub
, /* getProperty */
2894 PropertyStub
, /* setProperty */
2898 NULL
, /* finalize */
2899 NULL
, /* reserved */
2900 NULL
, /* checkAccess */
2902 NULL
, /* construct */
2903 NULL
, /* xdrObject */
2904 NULL
, /* hasInstance */
2908 with_LookupProperty
,
2909 NULL
, /* defineProperty */
2914 with_DeleteProperty
,
2923 JS_REQUIRES_STACK JSObject
*
2924 js_NewWithObject(JSContext
*cx
, JSObject
*proto
, JSObject
*parent
, jsint depth
)
2928 obj
= js_NewGCObject(cx
);
2932 obj
->init(&js_WithClass
, proto
, parent
, js_FloatingFrameIfGenerator(cx
, cx
->fp()), cx
);
2933 obj
->setMap(cx
->runtime
->emptyWithShape
);
2934 OBJ_SET_BLOCK_DEPTH(cx
, obj
, depth
);
2936 AutoObjectRooter
tvr(cx
, obj
);
2937 JSObject
*thisp
= proto
->thisObject(cx
);
2941 obj
->setWithThis(thisp
);
2946 js_NewBlockObject(JSContext
*cx
)
2949 * Null obj's proto slot so that Object.prototype.* does not pollute block
2950 * scopes and to give the block object its own scope.
2952 JSObject
*blockObj
= js_NewGCObject(cx
);
2956 blockObj
->init(&js_BlockClass
, NULL
, NULL
, NullValue(), cx
);
2957 blockObj
->setMap(cx
->runtime
->emptyBlockShape
);
2962 js_CloneBlockObject(JSContext
*cx
, JSObject
*proto
, JSStackFrame
*fp
)
2964 JS_ASSERT(proto
->isStaticBlock());
2966 JSObject
*clone
= js_NewGCObject(cx
);
2970 JSStackFrame
*priv
= js_FloatingFrameIfGenerator(cx
, fp
);
2972 /* The caller sets parent on its own. */
2973 clone
->init(&js_BlockClass
, proto
, NULL
, priv
, cx
);
2974 clone
->fslots
[JSSLOT_BLOCK_DEPTH
] = proto
->fslots
[JSSLOT_BLOCK_DEPTH
];
2976 clone
->setMap(proto
->map
);
2977 if (!clone
->ensureInstanceReservedSlots(cx
, OBJ_BLOCK_COUNT(cx
, proto
)))
2980 JS_ASSERT(clone
->isClonedBlock());
2984 JS_REQUIRES_STACK JSBool
2985 js_PutBlockObject(JSContext
*cx
, JSBool normalUnwind
)
2987 /* Blocks have one fixed slot available for the first local.*/
2988 JS_STATIC_ASSERT(JS_INITIAL_NSLOTS
== JSSLOT_BLOCK_DEPTH
+ 2);
2990 JSStackFrame
*const fp
= cx
->fp();
2991 JSObject
*obj
= &fp
->scopeChain();
2992 JS_ASSERT(obj
->isClonedBlock());
2993 JS_ASSERT(obj
->getPrivate() == js_FloatingFrameIfGenerator(cx
, cx
->fp()));
2995 /* Block objects should have all reserved slots allocated early. */
2996 uintN count
= OBJ_BLOCK_COUNT(cx
, obj
);
2997 JS_ASSERT(obj
->numSlots() == JSSLOT_BLOCK_DEPTH
+ 1 + count
);
2999 /* The block and its locals must be on the current stack for GC safety. */
3000 uintN depth
= OBJ_BLOCK_DEPTH(cx
, obj
);
3001 JS_ASSERT(depth
<= size_t(cx
->regs
->sp
- fp
->base()));
3002 JS_ASSERT(count
<= size_t(cx
->regs
->sp
- fp
->base() - depth
));
3004 /* See comments in CheckDestructuring from jsparse.cpp. */
3005 JS_ASSERT(count
>= 1);
3008 uintN slot
= JSSLOT_BLOCK_DEPTH
+ 1;
3009 uintN flen
= JS_MIN(count
, JS_INITIAL_NSLOTS
- slot
);
3010 uintN stop
= slot
+ flen
;
3012 depth
+= fp
->numFixed();
3014 obj
->fslots
[slot
++] = fp
->slots()[depth
++];
3017 memcpy(obj
->dslots
, fp
->slots() + depth
, count
* sizeof(Value
));
3020 /* We must clear the private slot even with errors. */
3021 obj
->setPrivate(NULL
);
3022 fp
->setScopeChainNoCallObj(*obj
->getParent());
3023 return normalUnwind
;
3027 block_getProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
3030 * Block objects are never exposed to script, and the engine handles them
3031 * with care. So unlike other getters, this one can assert (rather than
3032 * check) certain invariants about obj.
3034 JS_ASSERT(obj
->isClonedBlock());
3035 uintN index
= (uintN
) JSID_TO_INT(id
);
3036 JS_ASSERT(index
< OBJ_BLOCK_COUNT(cx
, obj
));
3038 JSStackFrame
*fp
= (JSStackFrame
*) obj
->getPrivate();
3040 fp
= js_LiveFrameIfGenerator(fp
);
3041 index
+= fp
->numFixed() + OBJ_BLOCK_DEPTH(cx
, obj
);
3042 JS_ASSERT(index
< fp
->numSlots());
3043 *vp
= fp
->slots()[index
];
3047 /* Values are in slots immediately following the class-reserved ones. */
3048 JS_ASSERT(obj
->getSlot(JSSLOT_FREE(&js_BlockClass
) + index
) == *vp
);
3053 block_setProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
3055 JS_ASSERT(obj
->isClonedBlock());
3056 uintN index
= (uintN
) JSID_TO_INT(id
);
3057 JS_ASSERT(index
< OBJ_BLOCK_COUNT(cx
, obj
));
3059 JSStackFrame
*fp
= (JSStackFrame
*) obj
->getPrivate();
3061 fp
= js_LiveFrameIfGenerator(fp
);
3062 index
+= fp
->numFixed() + OBJ_BLOCK_DEPTH(cx
, obj
);
3063 JS_ASSERT(index
< fp
->numSlots());
3064 fp
->slots()[index
] = *vp
;
3069 * The value in *vp will be written back to the slot in obj that was
3070 * allocated when this let binding was defined.
3076 JSObject::defineBlockVariable(JSContext
*cx
, jsid id
, intN index
)
3078 JS_ASSERT(isStaticBlock());
3080 /* Use JSPROP_ENUMERATE to aid the disassembler. */
3081 uint32 slot
= JSSLOT_FREE(&js_BlockClass
) + index
;
3082 const Shape
*shape
= addProperty(cx
, id
,
3083 block_getProperty
, block_setProperty
,
3084 slot
, JSPROP_ENUMERATE
| JSPROP_PERMANENT
,
3085 Shape::HAS_SHORTID
, index
);
3088 if (slot
>= numSlots() && !growSlots(cx
, slot
+ 1))
3094 GetObjectSize(JSObject
*obj
)
3096 return (obj
->isFunction() && !obj
->getPrivate())
3097 ? sizeof(JSFunction
)
3102 * Use this method with extreme caution. It trades the guts of two objects and updates
3103 * scope ownership. This operation is not thread-safe, just as fast array to slow array
3104 * transitions are inherently not thread-safe. Don't perform a swap operation on objects
3105 * shared across threads or, or bad things will happen. You have been warned.
3108 JSObject::swap(JSObject
*other
)
3110 size_t size
= GetObjectSize(this);
3111 JS_ASSERT(size
== GetObjectSize(other
));
3113 /* Trade the guts of the objects. */
3114 char tmp
[tl::Max
<sizeof(JSFunction
), sizeof(JSObject
)>::result
];
3115 memcpy(tmp
, this, size
);
3116 memcpy(this, other
, size
);
3117 memcpy(other
, tmp
, size
);
3122 #define NO_PARENT_INDEX ((uint32)-1)
3125 FindObjectIndex(JSObjectArray
*array
, JSObject
*obj
)
3133 if (array
->vector
[--i
] == obj
)
3138 return NO_PARENT_INDEX
;
3142 js_XDRBlockObject(JSXDRState
*xdr
, JSObject
**objp
)
3146 JSObject
*obj
, *parent
;
3148 uint32 depthAndCount
;
3153 obj
= NULL
; /* quell GCC overwarning */
3156 if (xdr
->mode
== JSXDR_ENCODE
) {
3158 parent
= obj
->getParent();
3159 parentId
= (xdr
->script
->objectsOffset
== 0)
3161 : FindObjectIndex(xdr
->script
->objects(), parent
);
3162 depth
= (uint16
)OBJ_BLOCK_DEPTH(cx
, obj
);
3163 count
= (uint16
)OBJ_BLOCK_COUNT(cx
, obj
);
3164 depthAndCount
= (uint32
)(depth
<< 16) | count
;
3166 #ifdef __GNUC__ /* suppress bogus gcc warnings */
3170 /* First, XDR the parent atomid. */
3171 if (!JS_XDRUint32(xdr
, &parentId
))
3174 if (xdr
->mode
== JSXDR_DECODE
) {
3175 obj
= js_NewBlockObject(cx
);
3181 * If there's a parent id, then get the parent out of our script's
3182 * object array. We know that we XDR block object in outer-to-inner
3183 * order, which means that getting the parent now will work.
3185 if (parentId
== NO_PARENT_INDEX
)
3188 parent
= xdr
->script
->getObject(parentId
);
3189 obj
->setParent(parent
);
3192 AutoObjectRooter
tvr(cx
, obj
);
3194 if (!JS_XDRUint32(xdr
, &depthAndCount
))
3197 if (xdr
->mode
== JSXDR_DECODE
) {
3198 depth
= (uint16
)(depthAndCount
>> 16);
3199 count
= (uint16
)depthAndCount
;
3200 obj
->setSlot(JSSLOT_BLOCK_DEPTH
, Value(Int32Value(depth
)));
3203 * XDR the block object's properties. We know that there are 'count'
3204 * properties to XDR, stored as id/shortid pairs.
3206 for (uintN i
= 0; i
< count
; i
++) {
3210 /* XDR the real id, then the shortid. */
3211 if (!js_XDRAtom(xdr
, &atom
) || !JS_XDRUint16(xdr
, &shortid
))
3214 if (!obj
->defineBlockVariable(cx
, ATOM_TO_JSID(atom
), shortid
))
3218 Vector
<const Shape
*, 8> shapes(cx
);
3219 shapes
.growByUninitialized(count
);
3221 for (Shape::Range
r(obj
->lastProperty()); !r
.empty(); r
.popFront()) {
3223 shapes
[shape
->shortid
] = shape
;
3227 * XDR the block object's properties. We know that there are 'count'
3228 * properties to XDR, stored as id/shortid pairs.
3230 for (uintN i
= 0; i
< count
; i
++) {
3232 JS_ASSERT(shape
->getter() == block_getProperty
);
3234 jsid propid
= shape
->id
;
3235 JS_ASSERT(JSID_IS_ATOM(propid
));
3236 JSAtom
*atom
= JSID_TO_ATOM(propid
);
3238 uint16 shortid
= uint16(shape
->shortid
);
3239 JS_ASSERT(shortid
== i
);
3241 /* XDR the real id, then the shortid. */
3242 if (!js_XDRAtom(xdr
, &atom
) || !JS_XDRUint16(xdr
, &shortid
))
3251 Class js_BlockClass
= {
3253 JSCLASS_HAS_PRIVATE
| JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_IS_ANONYMOUS
,
3254 PropertyStub
, /* addProperty */
3255 PropertyStub
, /* delProperty */
3256 PropertyStub
, /* getProperty */
3257 PropertyStub
, /* setProperty */
3264 js_InitObjectClass(JSContext
*cx
, JSObject
*obj
)
3266 JSObject
*proto
= js_InitClass(cx
, obj
, NULL
, &js_ObjectClass
, js_Object
, 1,
3267 object_props
, object_methods
, NULL
, object_static_methods
);
3271 /* ECMA (15.1.2.1) says 'eval' is a property of the global object. */
3272 if (!js_DefineFunction(cx
, obj
, cx
->runtime
->atomState
.evalAtom
, obj_eval
, 1,
3273 JSFUN_STUB_GSOPS
)) {
3281 DefineStandardSlot(JSContext
*cx
, JSObject
*obj
, JSProtoKey key
, JSAtom
*atom
,
3282 const Value
&v
, uint32 attrs
, bool &named
)
3284 jsid id
= ATOM_TO_JSID(atom
);
3286 if (key
!= JSProto_Null
) {
3288 * Initializing an actual standard class on a global object. If the
3289 * property is not yet present, force it into a new one bound to a
3290 * reserved slot. Otherwise, go through the normal property path.
3292 JS_ASSERT(obj
->getClass()->flags
& JSCLASS_IS_GLOBAL
);
3293 JS_ASSERT(obj
->isNative());
3295 JS_LOCK_OBJ(cx
, obj
);
3296 if (!obj
->ensureClassReservedSlots(cx
)) {
3297 JS_UNLOCK_OBJ(cx
, obj
);
3301 const Shape
*shape
= obj
->nativeLookup(id
);
3303 uint32 index
= 2 * JSProto_LIMIT
+ key
;
3304 if (!js_SetReservedSlot(cx
, obj
, index
, v
)) {
3305 JS_UNLOCK_OBJ(cx
, obj
);
3309 uint32 slot
= JSSLOT_START(obj
->getClass()) + index
;
3310 shape
= obj
->addProperty(cx
, id
, PropertyStub
, PropertyStub
, slot
, attrs
, 0, 0);
3312 JS_UNLOCK_OBJ(cx
, obj
);
3319 JS_UNLOCK_OBJ(cx
, obj
);
3322 named
= obj
->defineProperty(cx
, id
, v
, PropertyStub
, PropertyStub
, attrs
);
3327 js_InitClass(JSContext
*cx
, JSObject
*obj
, JSObject
*parent_proto
,
3328 Class
*clasp
, Native constructor
, uintN nargs
,
3329 JSPropertySpec
*ps
, JSFunctionSpec
*fs
,
3330 JSPropertySpec
*static_ps
, JSFunctionSpec
*static_fs
)
3337 atom
= js_Atomize(cx
, clasp
->name
, strlen(clasp
->name
), 0);
3342 * When initializing a standard class, if no parent_proto (grand-proto of
3343 * instances of the class, parent-proto of the class's prototype object)
3344 * is given, we must use Object.prototype if it is available. Otherwise,
3345 * we could look up the wrong binding for a class name in obj. Example:
3348 * print("hi there".join);
3350 * should print undefined, not Array.prototype.join. This is required by
3351 * ECMA-262, alas. It might have been better to make String readonly and
3352 * permanent in the global object, instead -- but that's too big a change
3353 * to swallow at this point.
3355 key
= JSCLASS_CACHED_PROTO_KEY(clasp
);
3356 if (key
!= JSProto_Null
&&
3358 !js_GetClassPrototype(cx
, obj
, JSProto_Object
, &parent_proto
)) {
3363 * Create a prototype object for this class.
3365 * FIXME: lazy standard (built-in) class initialization and even older
3366 * eager boostrapping code rely on all of these properties:
3368 * 1. NewObject attempting to compute a default prototype object when
3369 * passed null for proto; and
3371 * 2. NewObject tolerating no default prototype (null proto slot value)
3372 * due to this js_InitClass call coming from js_InitFunctionClass on an
3373 * otherwise-uninitialized global.
3375 * 3. NewObject allocating a JSFunction-sized GC-thing when clasp is
3376 * &js_FunctionClass, not a JSObject-sized (smaller) GC-thing.
3378 * The JS_NewObjectForGivenProto and JS_NewObject APIs also allow clasp to
3379 * be &js_FunctionClass (we could break compatibility easily). But fixing
3380 * (3) is not enough without addressing the bootstrapping dependency on (1)
3383 JSObject
*proto
= NewObject
<WithProto::Class
>(cx
, clasp
, parent_proto
, obj
);
3387 /* After this point, control must exit via label bad or out. */
3388 AutoObjectRooter
tvr(cx
, proto
);
3393 * Lacking a constructor, name the prototype (e.g., Math) unless this
3394 * class (a) is anonymous, i.e. for internal use only; (b) the class
3395 * of obj (the global object) is has a reserved slot indexed by key;
3396 * and (c) key is not the null key.
3398 if (!(clasp
->flags
& JSCLASS_IS_ANONYMOUS
) ||
3399 !(obj
->getClass()->flags
& JSCLASS_IS_GLOBAL
) ||
3400 key
== JSProto_Null
)
3402 uint32 attrs
= (clasp
->flags
& JSCLASS_IS_ANONYMOUS
)
3403 ? JSPROP_READONLY
| JSPROP_PERMANENT
3405 if (!DefineStandardSlot(cx
, obj
, key
, atom
, ObjectValue(*proto
), attrs
, named
))
3411 fun
= js_NewFunction(cx
, NULL
, constructor
, nargs
, JSFUN_CONSTRUCTOR
, obj
, atom
);
3415 AutoValueRooter
tvr2(cx
, ObjectValue(*fun
));
3416 if (!DefineStandardSlot(cx
, obj
, key
, atom
, tvr2
.value(), 0, named
))
3420 * Remember the class this function is a constructor for so that
3421 * we know to create an object of this class when we call the
3424 FUN_CLASP(fun
) = clasp
;
3427 * Optionally construct the prototype object, before the class has
3428 * been fully initialized. Allow the ctor to replace proto with a
3429 * different object, as is done for operator new -- and as at least
3430 * XML support requires.
3432 ctor
= FUN_OBJECT(fun
);
3433 if (clasp
->flags
& JSCLASS_CONSTRUCT_PROTOTYPE
) {
3435 if (!InvokeConstructorWithGivenThis(cx
, proto
, ObjectOrNullValue(ctor
),
3439 if (rval
.isObject() && &rval
.toObject() != proto
)
3440 proto
= &rval
.toObject();
3443 /* Connect constructor and prototype by named properties. */
3444 if (!js_SetClassPrototype(cx
, ctor
, proto
,
3445 JSPROP_READONLY
| JSPROP_PERMANENT
)) {
3449 /* Bootstrap Function.prototype (see also JS_InitStandardClasses). */
3450 if (ctor
->getClass() == clasp
)
3451 ctor
->setProto(proto
);
3454 /* Add properties and methods to the prototype and the constructor. */
3455 if ((ps
&& !JS_DefineProperties(cx
, proto
, ps
)) ||
3456 (fs
&& !JS_DefineFunctions(cx
, proto
, fs
)) ||
3457 (static_ps
&& !JS_DefineProperties(cx
, ctor
, static_ps
)) ||
3458 (static_fs
&& !JS_DefineFunctions(cx
, ctor
, static_fs
))) {
3463 * Make sure proto's emptyShape is available to be shared by objects of
3464 * this class. JSObject::emptyShape is a one-slot cache. If we omit this,
3465 * some other class could snap it up. (The risk is particularly great for
3466 * Object.prototype.)
3468 * All callers of JSObject::initSharingEmptyShape depend on this.
3470 * FIXME: bug 592296 -- js_InitArrayClass should pass &js_SlowArrayClass
3471 * and make the Array.prototype slow from the start.
3473 JS_ASSERT_IF(proto
->clasp
!= clasp
,
3474 clasp
== &js_ArrayClass
&& proto
->clasp
== &js_SlowArrayClass
);
3475 if (!proto
->getEmptyShape(cx
, proto
->clasp
))
3478 /* If this is a standard class, cache its prototype. */
3479 if (key
!= JSProto_Null
&& !js_SetClassObject(cx
, obj
, key
, ctor
, proto
))
3487 obj
->deleteProperty(cx
, ATOM_TO_JSID(atom
), &rval
, false);
3493 JSObject::allocSlots(JSContext
*cx
, size_t nslots
)
3496 JS_ASSERT(nslots
> JS_INITIAL_NSLOTS
);
3498 size_t nwords
= slotsToDynamicWords(nslots
);
3499 dslots
= (Value
*) cx
->malloc(nwords
* sizeof(Value
));
3504 dslots
[-1].setPrivateUint32(nslots
);
3505 SetValueRangeToUndefined(dslots
, nslots
- JS_INITIAL_NSLOTS
);
3510 JSObject::growSlots(JSContext
*cx
, size_t nslots
)
3513 * Minimal number of dynamic slots to allocate.
3515 const size_t MIN_DYNAMIC_WORDS
= 4;
3518 * The limit to switch to linear allocation strategy from the power of 2
3519 * growth no to waste too much memory.
3521 const size_t LINEAR_GROWTH_STEP
= JS_BIT(16);
3523 /* If we are allocating fslots, there is nothing to do. */
3524 if (nslots
<= JS_INITIAL_NSLOTS
)
3527 /* Don't let nslots get close to wrapping around uint32. */
3528 if (nslots
>= NSLOTS_LIMIT
) {
3529 JS_ReportOutOfMemory(cx
);
3533 size_t nwords
= slotsToDynamicWords(nslots
);
3536 * Round up nslots so the number of bytes in dslots array is power
3537 * of 2 to ensure exponential grouth.
3540 if (nwords
<= MIN_DYNAMIC_WORDS
) {
3541 nwords
= MIN_DYNAMIC_WORDS
;
3542 } else if (nwords
< LINEAR_GROWTH_STEP
) {
3543 JS_CEILING_LOG2(log
, nwords
);
3544 nwords
= JS_BIT(log
);
3546 nwords
= JS_ROUNDUP(nwords
, LINEAR_GROWTH_STEP
);
3548 nslots
= dynamicWordsToSlots(nwords
);
3551 * If nothing was allocated yet, treat it as initial allocation (but with
3552 * the exponential growth algorithm applied).
3555 return allocSlots(cx
, nslots
);
3557 size_t oldnslots
= dslots
[-1].toPrivateUint32();
3559 Value
*tmpdslots
= (Value
*) cx
->realloc(dslots
- 1, nwords
* sizeof(Value
));
3561 return false; /* leave dslots at its old size */
3565 dslots
[-1].setPrivateUint32(nslots
);
3567 /* Initialize the additional slots we added. */
3568 JS_ASSERT(nslots
> oldnslots
);
3569 Value
*beg
= dslots
+ (oldnslots
- JS_INITIAL_NSLOTS
);
3570 Value
*end
= dslots
+ (nslots
- JS_INITIAL_NSLOTS
);
3571 SetValueRangeToUndefined(beg
, end
);
3577 JSObject::shrinkSlots(JSContext
*cx
, size_t nslots
)
3579 /* Nothing to shrink? */
3583 JS_ASSERT(dslots
[-1].toPrivateUint32() > JS_INITIAL_NSLOTS
);
3584 JS_ASSERT(nslots
<= dslots
[-1].toPrivateUint32());
3586 if (nslots
<= JS_INITIAL_NSLOTS
) {
3590 size_t nwords
= slotsToDynamicWords(nslots
);
3591 Value
*tmpdslots
= (Value
*) cx
->realloc(dslots
- 1, nwords
* sizeof(Value
));
3593 return; /* leave dslots at its old size */
3597 dslots
[-1].setPrivateUint32(nslots
);
3602 JSObject::ensureInstanceReservedSlots(JSContext
*cx
, size_t nreserved
)
3604 JS_ASSERT_IF(isNative(),
3605 isBlock() || isCall() || (isFunction() && getFunctionPrivate()->isBound()));
3607 uintN nslots
= JSSLOT_FREE(clasp
) + nreserved
;
3608 return nslots
<= numSlots() || allocSlots(cx
, nslots
);
3612 js_InitNullClass(JSContext
*cx
, JSObject
*obj
)
3618 #define JS_PROTO(name,code,init) extern JSObject *init(JSContext *, JSObject *);
3619 #include "jsproto.tbl"
3622 static JSObjectOp lazy_prototype_init
[JSProto_LIMIT
] = {
3623 #define JS_PROTO(name,code,init) init,
3624 #include "jsproto.tbl"
3631 SetProto(JSContext
*cx
, JSObject
*obj
, JSObject
*proto
, bool checkForCycles
)
3633 JS_ASSERT_IF(!checkForCycles
, obj
!= proto
);
3635 if (obj
->isNative()) {
3636 JS_LOCK_OBJ(cx
, obj
);
3637 bool ok
= obj
->ensureClassReservedSlots(cx
);
3638 JS_UNLOCK_OBJ(cx
, obj
);
3644 * Regenerate property cache shape ids for all of the scopes along the
3645 * old prototype chain to invalidate their property cache entries, in
3646 * case any entries were filled by looking up through obj.
3648 JSObject
*oldproto
= obj
;
3649 while (oldproto
&& oldproto
->isNative()) {
3650 JS_LOCK_OBJ(cx
, oldproto
);
3651 oldproto
->protoShapeChange(cx
);
3652 JSObject
*tmp
= oldproto
->getProto();
3653 JS_UNLOCK_OBJ(cx
, oldproto
);
3657 if (!proto
|| !checkForCycles
) {
3658 obj
->setProto(proto
);
3659 } else if (!SetProtoCheckingForCycles(cx
, obj
, proto
)) {
3660 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_CYCLIC_VALUE
, js_proto_str
);
3669 js_GetClassObject(JSContext
*cx
, JSObject
*obj
, JSProtoKey key
,
3672 JSObject
*tmp
, *cobj
;
3673 JSResolvingKey rkey
;
3674 JSResolvingEntry
*rentry
;
3679 while ((tmp
= obj
->getParent()) != NULL
)
3681 if (!(obj
->getClass()->flags
& JSCLASS_IS_GLOBAL
)) {
3686 v
= obj
->getReservedSlot(key
);
3688 *objp
= &v
.toObject();
3693 rkey
.id
= ATOM_TO_JSID(cx
->runtime
->atomState
.classAtoms
[key
]);
3694 if (!js_StartResolving(cx
, &rkey
, JSRESFLAG_LOOKUP
, &rentry
))
3697 /* Already caching key in obj -- suppress recursion. */
3701 generation
= cx
->resolvingTable
->generation
;
3705 init
= lazy_prototype_init
[key
];
3707 if (!init(cx
, obj
)) {
3710 v
= obj
->getReservedSlot(key
);
3712 cobj
= &v
.toObject();
3716 js_StopResolving(cx
, &rkey
, JSRESFLAG_LOOKUP
, rentry
, generation
);
3722 js_SetClassObject(JSContext
*cx
, JSObject
*obj
, JSProtoKey key
, JSObject
*cobj
, JSObject
*proto
)
3724 JS_ASSERT(!obj
->getParent());
3725 if (!(obj
->getClass()->flags
& JSCLASS_IS_GLOBAL
))
3728 return js_SetReservedSlot(cx
, obj
, key
, ObjectOrNullValue(cobj
)) &&
3729 js_SetReservedSlot(cx
, obj
, JSProto_LIMIT
+ key
, ObjectOrNullValue(proto
));
3733 js_FindClassObject(JSContext
*cx
, JSObject
*start
, JSProtoKey protoKey
,
3734 Value
*vp
, Class
*clasp
)
3737 JSObject
*obj
, *cobj
, *pobj
;
3743 * Find the global object. Use cx->fp() directly to avoid falling off
3744 * trace; all JIT-elided stack frames have the same global object as
3747 VOUCH_DOES_NOT_REQUIRE_STACK();
3748 if (!start
&& (fp
= cx
->maybefp()) != NULL
)
3749 start
= &fp
->scopeChain();
3752 /* Find the topmost object in the scope chain. */
3755 start
= obj
->getParent();
3758 obj
= cx
->globalObject
;
3765 OBJ_TO_INNER_OBJECT(cx
, obj
);
3769 if (protoKey
!= JSProto_Null
) {
3770 JS_ASSERT(JSProto_Null
< protoKey
);
3771 JS_ASSERT(protoKey
< JSProto_LIMIT
);
3772 if (!js_GetClassObject(cx
, obj
, protoKey
, &cobj
))
3775 vp
->setObject(*cobj
);
3778 id
= ATOM_TO_JSID(cx
->runtime
->atomState
.classAtoms
[protoKey
]);
3780 JSAtom
*atom
= js_Atomize(cx
, clasp
->name
, strlen(clasp
->name
), 0);
3783 id
= ATOM_TO_JSID(atom
);
3786 JS_ASSERT(obj
->isNative());
3787 if (js_LookupPropertyWithFlags(cx
, obj
, id
, JSRESOLVE_CLASSNAME
,
3788 &pobj
, &prop
) < 0) {
3791 Value v
= UndefinedValue();
3792 if (prop
&& pobj
->isNative()) {
3793 shape
= (Shape
*) prop
;
3794 if (pobj
->containsSlot(shape
->slot
)) {
3795 v
= pobj
->lockedGetSlot(shape
->slot
);
3796 if (v
.isPrimitive())
3799 JS_UNLOCK_OBJ(cx
, pobj
);
3806 js_ConstructObject(JSContext
*cx
, Class
*clasp
, JSObject
*proto
, JSObject
*parent
,
3807 uintN argc
, Value
*argv
)
3809 AutoArrayRooter
argtvr(cx
, argc
, argv
);
3811 JSProtoKey protoKey
= GetClassProtoKey(clasp
);
3813 /* Protect constructor in case a crazy getter for .prototype uproots it. */
3814 AutoValueRooter
tvr(cx
);
3815 if (!js_FindClassObject(cx
, parent
, protoKey
, tvr
.addr(), clasp
))
3818 const Value
&cval
= tvr
.value();
3819 if (tvr
.value().isPrimitive()) {
3820 js_ReportIsNotFunction(cx
, tvr
.addr(), JSV2F_CONSTRUCT
| JSV2F_SEARCH_STACK
);
3825 * If proto is NULL, set it to Constructor.prototype, just like JSOP_NEW
3826 * does, likewise for the new object's parent.
3828 JSObject
*ctor
= &cval
.toObject();
3830 parent
= ctor
->getParent();
3833 if (!ctor
->getProperty(cx
, ATOM_TO_JSID(cx
->runtime
->atomState
.classPrototypeAtom
),
3837 if (rval
.isObjectOrNull())
3838 proto
= rval
.toObjectOrNull();
3841 JSObject
*obj
= NewObject
<WithProto::Class
>(cx
, clasp
, proto
, parent
);
3846 if (!InvokeConstructorWithGivenThis(cx
, obj
, cval
, argc
, argv
, &rval
))
3849 if (rval
.isPrimitive())
3853 * If the instance's class differs from what was requested, throw a type
3854 * error. If the given class has both the JSCLASS_HAS_PRIVATE and the
3855 * JSCLASS_CONSTRUCT_PROTOTYPE flags, and the instance does not have its
3856 * private data set at this point, then the constructor was replaced and
3857 * we should throw a type error.
3859 obj
= &rval
.toObject();
3860 if (obj
->getClass() != clasp
||
3861 (!(~clasp
->flags
& (JSCLASS_HAS_PRIVATE
|
3862 JSCLASS_CONSTRUCT_PROTOTYPE
)) &&
3863 !obj
->getPrivate())) {
3864 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
3865 JSMSG_WRONG_CONSTRUCTOR
, clasp
->name
);
3872 JSObject::allocSlot(JSContext
*cx
, uint32
*slotp
)
3874 uint32 slot
= slotSpan();
3875 JS_ASSERT(slot
>= JSSLOT_FREE(clasp
));
3878 * If this object is in dictionary mode and it has a property table, try to
3879 * pull a free slot from the property table's slot-number freelist.
3881 if (inDictionaryMode() && lastProp
->table
) {
3882 uint32
&last
= lastProp
->table
->freelist
;
3883 if (last
!= SHAPE_INVALID_SLOT
) {
3885 JS_ASSERT(last
< slot
);
3886 uint32 next
= getSlot(last
).toPrivateUint32();
3887 JS_ASSERT_IF(next
!= SHAPE_INVALID_SLOT
, next
< slot
);
3892 Value
&vref
= getSlotRef(last
);
3893 last
= vref
.toPrivateUint32();
3894 vref
.setUndefined();
3899 if (slot
>= numSlots() && !growSlots(cx
, slot
+ 1))
3902 /* JSObject::growSlots or JSObject::freeSlot should set the free slots to void. */
3903 JS_ASSERT(getSlot(slot
).isUndefined());
3909 JSObject::freeSlot(JSContext
*cx
, uint32 slot
)
3911 uint32 limit
= slotSpan();
3912 JS_ASSERT(slot
< limit
);
3914 Value
&vref
= getSlotRef(slot
);
3915 if (inDictionaryMode() && lastProp
->table
) {
3916 uint32
&last
= lastProp
->table
->freelist
;
3918 /* Can't afford to check the whole freelist, but let's check the head. */
3919 JS_ASSERT_IF(last
!= SHAPE_INVALID_SLOT
, last
< limit
&& last
!= slot
);
3922 * Freeing a slot other than the last one mapped by this object's
3923 * shape: push the slot onto the dictionary table's freelist. We want
3924 * to let the last slot be freed by shrinking the dslots vector; see
3927 if (slot
+ 1 < limit
) {
3928 JS_ASSERT_IF(last
!= SHAPE_INVALID_SLOT
, last
< slotSpan());
3929 vref
.setPrivateUint32(last
);
3934 vref
.setUndefined();
3937 /* JSBOXEDWORD_INT_MAX as a string */
3938 #define JSBOXEDWORD_INT_MAX_STRING "1073741823"
3941 * Convert string indexes that convert to int jsvals as ints to save memory.
3942 * Care must be taken to use this macro every time a property name is used, or
3943 * else double-sets, incorrect property cache misses, or other mistakes could
3947 js_CheckForStringIndex(jsid id
)
3949 if (!JSID_IS_ATOM(id
))
3952 JSAtom
*atom
= JSID_TO_ATOM(id
);
3953 JSString
*str
= ATOM_TO_STRING(atom
);
3954 const jschar
*s
= str
->flatChars();
3957 JSBool negative
= (ch
== '-');
3964 size_t n
= str
->flatLength() - negative
;
3965 if (n
> sizeof(JSBOXEDWORD_INT_MAX_STRING
) - 1)
3968 const jschar
*cp
= s
;
3969 const jschar
*end
= s
+ n
;
3971 jsuint index
= JS7_UNDEC(*cp
++);
3972 jsuint oldIndex
= 0;
3976 while (JS7_ISDEC(*cp
)) {
3979 index
= 10 * index
+ c
;
3985 * Non-integer indexes can't be represented as integers. Also, distinguish
3986 * index "-0" from "0", because JSBOXEDWORD_INT cannot.
3988 if (cp
!= end
|| (negative
&& index
== 0))
3991 if (oldIndex
< JSID_INT_MAX
/ 10 ||
3992 (oldIndex
== JSID_INT_MAX
/ 10 && c
<= (JSID_INT_MAX
% 10))) {
3995 id
= INT_TO_JSID((jsint
)index
);
4002 PurgeProtoChain(JSContext
*cx
, JSObject
*obj
, jsid id
)
4007 if (!obj
->isNative()) {
4008 obj
= obj
->getProto();
4011 JS_LOCK_OBJ(cx
, obj
);
4012 shape
= obj
->nativeLookup(id
);
4014 PCMETER(JS_PROPERTY_CACHE(cx
).pcpurges
++);
4015 obj
->shadowingShapeChange(cx
, *shape
);
4016 JS_UNLOCK_OBJ(cx
, obj
);
4018 if (!obj
->getParent()) {
4020 * All scope chains end in a global object, so this will change
4021 * the global shape. jstracer.cpp assumes that the global shape
4022 * never changes on trace, so we must deep-bail here.
4028 #ifdef JS_THREADSAFE
4029 JSObject
*pobj
= obj
;
4031 obj
= obj
->getProto();
4032 JS_UNLOCK_OBJ(cx
, pobj
);
4038 js_PurgeScopeChainHelper(JSContext
*cx
, JSObject
*obj
, jsid id
)
4040 JS_ASSERT(obj
->isDelegate());
4041 PurgeProtoChain(cx
, obj
->getProto(), id
);
4044 * We must purge the scope chain only for Call objects as they are the only
4045 * kind of cacheable non-global object that can gain properties after outer
4046 * properties with the same names have been cached or traced. Call objects
4047 * may gain such properties via eval introducing new vars; see bug 490364.
4049 if (obj
->isCall()) {
4050 while ((obj
= obj
->getParent()) != NULL
) {
4051 if (PurgeProtoChain(cx
, obj
, id
))
4058 js_AddNativeProperty(JSContext
*cx
, JSObject
*obj
, jsid id
,
4059 PropertyOp getter
, PropertyOp setter
, uint32 slot
,
4060 uintN attrs
, uintN flags
, intN shortid
)
4064 JS_ASSERT(!(flags
& Shape::METHOD
));
4067 * Purge the property cache of now-shadowed id in obj's scope chain. Do
4068 * this optimistically (assuming no failure below) before locking obj, so
4069 * we can lock the shadowed scope.
4071 js_PurgeScopeChain(cx
, obj
, id
);
4073 JS_LOCK_OBJ(cx
, obj
);
4074 if (!obj
->ensureClassReservedSlots(cx
)) {
4077 /* Convert string indices to integers if appropriate. */
4078 id
= js_CheckForStringIndex(id
);
4079 shape
= obj
->putProperty(cx
, id
, getter
, setter
, slot
, attrs
, flags
, shortid
);
4081 JS_UNLOCK_OBJ(cx
, obj
);
4086 js_ChangeNativePropertyAttrs(JSContext
*cx
, JSObject
*obj
,
4087 const Shape
*shape
, uintN attrs
, uintN mask
,
4088 PropertyOp getter
, PropertyOp setter
)
4090 JS_LOCK_OBJ(cx
, obj
);
4091 shape
= obj
->ensureClassReservedSlots(cx
)
4092 ? obj
->changeProperty(cx
, shape
, attrs
, mask
, getter
, setter
)
4094 JS_UNLOCK_OBJ(cx
, obj
);
4099 js_DefineProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, const Value
*value
,
4100 PropertyOp getter
, PropertyOp setter
, uintN attrs
)
4102 return js_DefineNativeProperty(cx
, obj
, id
, *value
, getter
, setter
, attrs
,
4107 * Backward compatibility requires allowing addProperty hooks to mutate the
4108 * nominal initial value of a slotful property, while GC safety wants that
4109 * value to be stored before the call-out through the hook. Optimize to do
4110 * both while saving cycles for classes that stub their addProperty hook.
4113 CallAddPropertyHook(JSContext
*cx
, Class
*clasp
, JSObject
*obj
, const Shape
*shape
, Value
*vp
)
4115 if (clasp
->addProperty
!= PropertyStub
) {
4116 Value nominal
= *vp
;
4118 if (!CallJSPropertyOp(cx
, clasp
->addProperty
, obj
, SHAPE_USERID(shape
), vp
))
4120 if (*vp
!= nominal
) {
4121 if (obj
->containsSlot(shape
->slot
))
4122 obj
->lockedSetSlot(shape
->slot
, *vp
);
4129 js_DefineNativeProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, const Value
&value
,
4130 PropertyOp getter
, PropertyOp setter
, uintN attrs
,
4131 uintN flags
, intN shortid
, JSProperty
**propp
,
4132 uintN defineHow
/* = 0 */)
4139 JS_ASSERT((defineHow
& ~(JSDNP_CACHE_RESULT
| JSDNP_DONT_PURGE
| JSDNP_SET_METHOD
)) == 0);
4140 LeaveTraceIfGlobalObject(cx
, obj
);
4142 /* Convert string indices to integers if appropriate. */
4143 id
= js_CheckForStringIndex(id
);
4146 * If defining a getter or setter, we must check for its counterpart and
4147 * update the attributes and property ops. A getter or setter is really
4148 * only half of a property.
4151 if (attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)) {
4156 * If JS_THREADSAFE and id is found, js_LookupProperty returns with
4157 * shape non-null and pobj locked. If pobj == obj, the property is
4158 * already in obj and obj has its own (mutable) scope. So if we are
4159 * defining a getter whose setter was already defined, or vice versa,
4160 * finish the job via obj->changeProperty, and refresh the property
4161 * cache line for (obj, id) to map shape.
4163 if (!js_LookupProperty(cx
, obj
, id
, &pobj
, &prop
))
4165 shape
= (Shape
*) prop
;
4166 if (shape
&& pobj
== obj
&& shape
->isAccessorDescriptor()) {
4167 shape
= obj
->changeProperty(cx
, shape
, attrs
,
4168 JSPROP_GETTER
| JSPROP_SETTER
,
4169 (attrs
& JSPROP_GETTER
)
4172 (attrs
& JSPROP_SETTER
)
4176 /* NB: obj == pobj, so we can share unlock code at the bottom. */
4180 pobj
->dropProperty(cx
, prop
);
4187 * Purge the property cache of any properties named by id that are about
4188 * to be shadowed in obj's scope chain unless it is known a priori that it
4189 * is not possible. We do this before locking obj to avoid nesting locks.
4191 if (!(defineHow
& JSDNP_DONT_PURGE
))
4192 js_PurgeScopeChain(cx
, obj
, id
);
4195 * Check whether a readonly property or setter is being defined on a known
4196 * prototype object. See the comment in jscntxt.h before protoHazardShape's
4197 * member declaration.
4199 if (obj
->isDelegate() && (attrs
& (JSPROP_READONLY
| JSPROP_SETTER
)))
4200 cx
->runtime
->protoHazardShape
= js_GenerateShape(cx
, false);
4202 /* Lock if object locking is required by this implementation. */
4203 JS_LOCK_OBJ(cx
, obj
);
4205 /* Use the object's class getter and setter by default. */
4206 clasp
= obj
->getClass();
4207 if (!(defineHow
& JSDNP_SET_METHOD
)) {
4208 if (!getter
&& !(attrs
& JSPROP_GETTER
))
4209 getter
= clasp
->getProperty
;
4210 if (!setter
&& !(attrs
& JSPROP_SETTER
))
4211 setter
= clasp
->setProperty
;
4214 /* Get obj's own scope if it has one, or create a new one for obj. */
4215 if (!obj
->ensureClassReservedSlots(cx
))
4220 /* Add a new property, or replace an existing one of the same id. */
4221 if (defineHow
& JSDNP_SET_METHOD
) {
4222 JS_ASSERT(clasp
== &js_ObjectClass
);
4223 JS_ASSERT(IsFunctionObject(value
));
4224 JS_ASSERT(!(attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)));
4225 JS_ASSERT(!getter
&& !setter
);
4227 JSObject
*funobj
= &value
.toObject();
4228 if (FUN_OBJECT(GET_FUNCTION_PRIVATE(cx
, funobj
)) == funobj
) {
4229 flags
|= Shape::METHOD
;
4230 getter
= CastAsPropertyOp(funobj
);
4234 added
= !obj
->nativeContains(id
);
4235 uint32 oldShape
= obj
->shape();
4236 shape
= obj
->putProperty(cx
, id
, getter
, setter
, SHAPE_INVALID_SLOT
,
4237 attrs
, flags
, shortid
);
4242 * If shape is a method, the above call to putProperty suffices to
4243 * update the shape if necessary. But if scope->branded(), the shape
4244 * may not have changed and we may be overwriting a function-valued
4245 * property. See bug 560998.
4247 if (obj
->shape() == oldShape
&& obj
->branded() && shape
->slot
!= SHAPE_INVALID_SLOT
)
4248 obj
->methodWriteBarrier(cx
, shape
->slot
, value
);
4251 /* Store value before calling addProperty, in case the latter GC's. */
4252 if (obj
->containsSlot(shape
->slot
))
4253 obj
->lockedSetSlot(shape
->slot
, value
);
4255 /* XXXbe called with lock held */
4257 if (!CallAddPropertyHook(cx
, clasp
, obj
, shape
, &valueCopy
)) {
4258 obj
->removeProperty(cx
, id
);
4262 if (defineHow
& JSDNP_CACHE_RESULT
) {
4264 JS_ASSERT_NOT_ON_TRACE(cx
);
4265 PropertyCacheEntry
*entry
=
4267 JS_PROPERTY_CACHE(cx
).fill(cx
, obj
, 0, 0, obj
, shape
, added
);
4268 TRACE_2(SetPropHit
, entry
, shape
);
4271 *propp
= (JSProperty
*) shape
;
4273 JS_UNLOCK_OBJ(cx
, obj
);
4276 error
: // TRACE_2 jumps here on error, as well.
4277 JS_UNLOCK_OBJ(cx
, obj
);
4281 JS_FRIEND_API(JSBool
)
4282 js_LookupProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, JSObject
**objp
,
4285 return js_LookupPropertyWithFlags(cx
, obj
, id
, cx
->resolveFlags
,
4289 #define SCOPE_DEPTH_ACCUM(bs,val) \
4290 JS_SCOPE_DEPTH_METERING(JS_BASIC_STATS_ACCUM(bs, val))
4293 * Call obj's resolve hook. obj is a native object and the caller holds its
4296 * cx, start, id, and flags are the parameters initially passed to the ongoing
4297 * lookup; objp and propp are its out parameters. obj is an object along
4298 * start's prototype chain.
4300 * There are four possible outcomes:
4302 * - On failure, report an error or exception, unlock obj, and return false.
4304 * - If we are alrady resolving a property of *curobjp, set *recursedp = true,
4305 * unlock obj, and return true.
4307 * - If the resolve hook finds or defines the sought property, set *objp and
4308 * *propp appropriately, set *recursedp = false, and return true with *objp's
4311 * - Otherwise no property was resolved. Set *propp = NULL and *recursedp = false
4315 CallResolveOp(JSContext
*cx
, JSObject
*start
, JSObject
*obj
, jsid id
, uintN flags
,
4316 JSObject
**objp
, JSProperty
**propp
, bool *recursedp
)
4318 Class
*clasp
= obj
->getClass();
4319 JSResolveOp resolve
= clasp
->resolve
;
4322 * Avoid recursion on (obj, id) already being resolved on cx.
4324 * Once we have successfully added an entry for (obj, key) to
4325 * cx->resolvingTable, control must go through cleanup: before
4326 * returning. But note that JS_DHASH_ADD may find an existing
4327 * entry, in which case we bail to suppress runaway recursion.
4329 JSResolvingKey key
= {obj
, id
};
4330 JSResolvingEntry
*entry
;
4331 if (!js_StartResolving(cx
, &key
, JSRESFLAG_LOOKUP
, &entry
)) {
4332 JS_UNLOCK_OBJ(cx
, obj
);
4336 /* Already resolving id in obj -- suppress recursion. */
4337 JS_UNLOCK_OBJ(cx
, obj
);
4341 uint32 generation
= cx
->resolvingTable
->generation
;
4347 const Shape
*shape
= NULL
;
4348 if (clasp
->flags
& JSCLASS_NEW_RESOLVE
) {
4349 JSNewResolveOp newresolve
= (JSNewResolveOp
)resolve
;
4350 if (flags
== JSRESOLVE_INFER
)
4351 flags
= js_InferFlags(cx
, 0);
4352 JSObject
*obj2
= (clasp
->flags
& JSCLASS_NEW_RESOLVE_GETS_START
) ? start
: NULL
;
4353 JS_UNLOCK_OBJ(cx
, obj
);
4356 /* Protect id and all atoms from a GC nested in resolve. */
4357 AutoKeepAtoms
keep(cx
->runtime
);
4358 ok
= newresolve(cx
, obj
, id
, flags
, &obj2
);
4363 JS_LOCK_OBJ(cx
, obj
);
4365 /* Resolved: juggle locks and lookup id again. */
4367 JS_UNLOCK_OBJ(cx
, obj
);
4368 if (obj2
->isNative())
4369 JS_LOCK_OBJ(cx
, obj2
);
4371 if (!obj2
->isNative()) {
4372 /* Whoops, newresolve handed back a foreign obj2. */
4373 JS_ASSERT(obj2
!= obj
);
4374 ok
= obj2
->lookupProperty(cx
, id
, objp
, propp
);
4377 JS_LOCK_OBJ(cx
, obj2
);
4380 * Require that obj2 not be empty now, as we do for old-style
4381 * resolve. If it doesn't, then id was not truly resolved, and
4382 * we'll find it in the proto chain, or miss it if obj2's proto
4383 * is not on obj's proto chain. That last case is a "too bad!"
4386 if (!obj2
->nativeEmpty())
4387 shape
= obj2
->nativeLookup(id
);
4390 JS_ASSERT(!obj2
->nativeEmpty());
4392 } else if (obj2
!= obj
) {
4393 if (obj2
->isNative())
4394 JS_UNLOCK_OBJ(cx
, obj2
);
4395 JS_LOCK_OBJ(cx
, obj
);
4400 * Old resolve always requires id re-lookup if obj is not empty after
4403 JS_UNLOCK_OBJ(cx
, obj
);
4404 ok
= resolve(cx
, obj
, id
);
4407 JS_LOCK_OBJ(cx
, obj
);
4408 JS_ASSERT(obj
->isNative());
4409 if (!obj
->nativeEmpty())
4410 shape
= obj
->nativeLookup(id
);
4416 *propp
= (JSProperty
*) shape
;
4418 js_StopResolving(cx
, &key
, JSRESFLAG_LOOKUP
, entry
, generation
);
4423 js_LookupPropertyWithFlags(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN flags
,
4424 JSObject
**objp
, JSProperty
**propp
)
4426 /* Convert string indices to integers if appropriate. */
4427 id
= js_CheckForStringIndex(id
);
4429 /* Search scopes starting with obj and following the prototype link. */
4430 JSObject
*start
= obj
;
4432 for (protoIndex
= 0; ; protoIndex
++) {
4433 JS_LOCK_OBJ(cx
, obj
);
4434 const Shape
*shape
= obj
->nativeLookup(id
);
4436 SCOPE_DEPTH_ACCUM(&cx
->runtime
->protoLookupDepthStats
, protoIndex
);
4438 *propp
= (JSProperty
*) shape
;
4442 /* Try obj's class resolve hook if id was not found in obj's scope. */
4443 if (!shape
&& obj
->getClass()->resolve
!= JS_ResolveStub
) {
4445 if (!CallResolveOp(cx
, start
, obj
, id
, flags
, objp
, propp
, &recursed
))
4450 /* Recalculate protoIndex in case it was resolved on some other object. */
4452 for (JSObject
*proto
= start
; proto
&& proto
!= *objp
; proto
= proto
->getProto())
4454 SCOPE_DEPTH_ACCUM(&cx
->runtime
->protoLookupDepthStats
, protoIndex
);
4459 JSObject
*proto
= obj
->getProto();
4460 JS_UNLOCK_OBJ(cx
, obj
);
4463 if (!proto
->isNative()) {
4464 if (!proto
->lookupProperty(cx
, id
, objp
, propp
))
4466 return protoIndex
+ 1;
4477 PropertyCacheEntry
*
4478 js_FindPropertyHelper(JSContext
*cx
, jsid id
, JSBool cacheResult
,
4479 JSObject
**objp
, JSObject
**pobjp
, JSProperty
**propp
)
4481 JSObject
*scopeChain
, *obj
, *parent
, *pobj
;
4482 PropertyCacheEntry
*entry
;
4483 int scopeIndex
, protoIndex
;
4486 JS_ASSERT_IF(cacheResult
, !JS_ON_TRACE(cx
));
4487 scopeChain
= &js_GetTopStackFrame(cx
)->scopeChain();
4489 /* Scan entries on the scope chain that we can cache across. */
4490 entry
= JS_NO_PROP_CACHE_FILL
;
4492 parent
= obj
->getParent();
4493 for (scopeIndex
= 0;
4495 ? js_IsCacheableNonGlobalScope(obj
)
4496 : !obj
->getOps()->lookupProperty
;
4499 js_LookupPropertyWithFlags(cx
, obj
, id
, cx
->resolveFlags
,
4507 Class
*clasp
= obj
->getClass();
4508 JS_ASSERT(pobj
->isNative());
4509 JS_ASSERT(pobj
->getClass() == clasp
);
4510 if (clasp
== &js_BlockClass
) {
4512 * A block instance on the scope chain is immutable and it
4513 * shares its shapes with its compile-time prototype.
4515 JS_ASSERT(pobj
== obj
);
4516 JS_ASSERT(pobj
->isClonedBlock());
4517 JS_ASSERT(protoIndex
== 0);
4519 /* Call and DeclEnvClass objects have no prototypes. */
4520 JS_ASSERT(!obj
->getProto());
4521 JS_ASSERT(protoIndex
== 0);
4524 JS_ASSERT(obj
->isNative());
4528 * We must check if pobj is native as a global object can have
4529 * non-native prototype.
4531 if (cacheResult
&& pobj
->isNative()) {
4532 entry
= JS_PROPERTY_CACHE(cx
).fill(cx
, scopeChain
, scopeIndex
,
4536 SCOPE_DEPTH_ACCUM(&cx
->runtime
->scopeSearchDepthStats
, scopeIndex
);
4545 parent
= obj
->getParent();
4549 if (!obj
->lookupProperty(cx
, id
, &pobj
, &prop
))
4552 PCMETER(JS_PROPERTY_CACHE(cx
).nofills
++);
4557 * We conservatively assume that a resolve hook could mutate the scope
4558 * chain during JSObject::lookupProperty. So we read parent here again.
4560 parent
= obj
->getParent();
4569 JS_ASSERT(!!pobj
== !!prop
);
4576 JS_FRIEND_API(JSBool
)
4577 js_FindProperty(JSContext
*cx
, jsid id
, JSObject
**objp
, JSObject
**pobjp
,
4580 return !!js_FindPropertyHelper(cx
, id
, false, objp
, pobjp
, propp
);
4584 js_FindIdentifierBase(JSContext
*cx
, JSObject
*scopeChain
, jsid id
)
4587 * This function should not be called for a global object or from the
4588 * trace and should have a valid cache entry for native scopeChain.
4590 JS_ASSERT(scopeChain
->getParent());
4591 JS_ASSERT(!JS_ON_TRACE(cx
));
4593 JSObject
*obj
= scopeChain
;
4596 * Loop over cacheable objects on the scope chain until we find a
4597 * property. We also stop when we reach the global object skipping any
4598 * farther checks or lookups. For details see the JSOP_BINDNAME case of
4601 * The test order here matters because js_IsCacheableNonGlobalScope
4602 * must not be passed a global object (i.e. one with null parent).
4604 for (int scopeIndex
= 0;
4605 !obj
->getParent() || js_IsCacheableNonGlobalScope(obj
);
4609 int protoIndex
= js_LookupPropertyWithFlags(cx
, obj
, id
,
4615 if (!pobj
->isNative()) {
4616 JS_ASSERT(!obj
->getParent());
4619 JS_ASSERT_IF(obj
->getParent(), pobj
->getClass() == obj
->getClass());
4621 PropertyCacheEntry
*entry
=
4623 JS_PROPERTY_CACHE(cx
).fill(cx
, scopeChain
, scopeIndex
, protoIndex
, pobj
,
4626 JS_UNLOCK_OBJ(cx
, pobj
);
4630 JSObject
*parent
= obj
->getParent();
4636 /* Loop until we find a property or reach the global object. */
4640 if (!obj
->lookupProperty(cx
, id
, &pobj
, &prop
))
4643 pobj
->dropProperty(cx
, prop
);
4648 * We conservatively assume that a resolve hook could mutate the scope
4649 * chain during JSObject::lookupProperty. So we must check if parent is
4650 * not null here even if it wasn't before the lookup.
4652 JSObject
*parent
= obj
->getParent();
4656 } while (obj
->getParent());
4661 js_NativeGet(JSContext
*cx
, JSObject
*obj
, JSObject
*pobj
, const Shape
*shape
, uintN getHow
,
4664 LeaveTraceIfGlobalObject(cx
, pobj
);
4669 JS_ASSERT(pobj
->isNative());
4670 JS_ASSERT(JS_IS_OBJ_LOCKED(cx
, pobj
));
4673 if (slot
!= SHAPE_INVALID_SLOT
)
4674 *vp
= pobj
->lockedGetSlot(slot
);
4677 if (shape
->hasDefaultGetter())
4680 if (JS_UNLIKELY(shape
->isMethod()) && (getHow
& JSGET_NO_METHOD_BARRIER
)) {
4681 JS_ASSERT(&shape
->methodObject() == &vp
->toObject());
4685 sample
= cx
->runtime
->propertyRemovals
;
4686 JS_UNLOCK_OBJ(cx
, pobj
);
4688 AutoShapeRooter
tvr(cx
, shape
);
4689 AutoObjectRooter
tvr2(cx
, pobj
);
4690 if (!shape
->get(cx
, obj
, pobj
, vp
))
4693 JS_LOCK_OBJ(cx
, pobj
);
4695 if (pobj
->containsSlot(slot
) &&
4696 (JS_LIKELY(cx
->runtime
->propertyRemovals
== sample
) ||
4697 pobj
->nativeContains(*shape
))) {
4698 if (!pobj
->methodWriteBarrier(cx
, *shape
, *vp
)) {
4699 JS_UNLOCK_OBJ(cx
, pobj
);
4702 pobj
->lockedSetSlot(slot
, *vp
);
4709 js_NativeSet(JSContext
*cx
, JSObject
*obj
, const Shape
*shape
, bool added
, Value
*vp
)
4711 LeaveTraceIfGlobalObject(cx
, obj
);
4716 JS_ASSERT(obj
->isNative());
4717 JS_ASSERT(JS_IS_OBJ_LOCKED(cx
, obj
));
4720 if (slot
!= SHAPE_INVALID_SLOT
) {
4721 OBJ_CHECK_SLOT(obj
, slot
);
4723 /* If shape has a stub setter, keep obj locked and just store *vp. */
4724 if (shape
->hasDefaultSetter()) {
4725 if (!added
&& !obj
->methodWriteBarrier(cx
, *shape
, *vp
)) {
4726 JS_UNLOCK_OBJ(cx
, obj
);
4729 obj
->lockedSetSlot(slot
, *vp
);
4734 * Allow API consumers to create shared properties with stub setters.
4735 * Such properties effectively function as data descriptors which are
4736 * not writable, so attempting to set such a property should do nothing
4737 * or throw if we're in strict mode.
4739 if (!shape
->hasGetterValue() && shape
->hasDefaultSetter())
4740 return js_ReportGetterOnlyAssignment(cx
);
4743 sample
= cx
->runtime
->propertyRemovals
;
4744 JS_UNLOCK_OBJ(cx
, obj
);
4746 AutoShapeRooter
tvr(cx
, shape
);
4747 if (!shape
->set(cx
, obj
, vp
))
4751 JS_LOCK_OBJ(cx
, obj
);
4752 if (obj
->containsSlot(slot
) &&
4753 (JS_LIKELY(cx
->runtime
->propertyRemovals
== sample
) ||
4754 obj
->nativeContains(*shape
))) {
4755 if (!added
&& !obj
->methodWriteBarrier(cx
, *shape
, *vp
)) {
4756 JS_UNLOCK_OBJ(cx
, obj
);
4759 obj
->lockedSetSlot(slot
, *vp
);
4765 static JS_ALWAYS_INLINE
bool
4766 js_GetPropertyHelperWithShapeInline(JSContext
*cx
, JSObject
*obj
, jsid id
,
4767 uintN getHow
, Value
*vp
,
4768 const Shape
**shapeOut
, JSObject
**holderOut
)
4770 JSObject
*aobj
, *obj2
;
4775 JS_ASSERT_IF(getHow
& JSGET_CACHE_RESULT
, !JS_ON_TRACE(cx
));
4779 /* Convert string indices to integers if appropriate. */
4780 id
= js_CheckForStringIndex(id
);
4782 aobj
= js_GetProtoIfDenseArray(obj
);
4783 protoIndex
= js_LookupPropertyWithFlags(cx
, aobj
, id
, cx
->resolveFlags
,
4793 if (!CallJSPropertyOp(cx
, obj
->getClass()->getProperty
, obj
, id
, vp
))
4796 PCMETER(getHow
& JSGET_CACHE_RESULT
&& JS_PROPERTY_CACHE(cx
).nofills
++);
4799 * Give a strict warning if foo.bar is evaluated by a script for an
4800 * object foo with no property named 'bar'.
4803 if (vp
->isUndefined() && ((pc
= js_GetCurrentBytecodePC(cx
)) != NULL
)) {
4808 if (op
== JSOP_TRAP
) {
4809 JS_ASSERT_NOT_ON_TRACE(cx
);
4810 op
= JS_GetTrapOpcode(cx
, cx
->fp()->script(), pc
);
4812 if (op
== JSOP_GETXPROP
) {
4813 flags
= JSREPORT_ERROR
;
4815 if (!JS_HAS_STRICT_OPTION(cx
) ||
4816 (op
!= JSOP_GETPROP
&& op
!= JSOP_GETELEM
) ||
4817 js_CurrentPCIsInImacro(cx
)) {
4822 * XXX do not warn about missing __iterator__ as the function
4823 * may be called from JS_GetMethodById. See bug 355145.
4825 if (JSID_IS_ATOM(id
, cx
->runtime
->atomState
.iteratorAtom
))
4828 /* Do not warn about tests like (obj[prop] == undefined). */
4829 if (cx
->resolveFlags
== JSRESOLVE_INFER
) {
4831 pc
+= js_CodeSpec
[op
].length
;
4832 if (Detecting(cx
, pc
))
4834 } else if (cx
->resolveFlags
& JSRESOLVE_DETECTING
) {
4838 flags
= JSREPORT_WARNING
| JSREPORT_STRICT
;
4841 /* Ok, bad undefined property reference: whine about it. */
4842 if (!js_ReportValueErrorFlags(cx
, flags
, JSMSG_UNDEFINED_PROP
,
4843 JSDVG_IGNORE_STACK
, IdToValue(id
),
4844 NULL
, NULL
, NULL
)) {
4851 if (!obj2
->isNative())
4852 return obj2
->getProperty(cx
, id
, vp
);
4854 shape
= (Shape
*) prop
;
4857 if (getHow
& JSGET_CACHE_RESULT
) {
4858 JS_ASSERT_NOT_ON_TRACE(cx
);
4859 JS_PROPERTY_CACHE(cx
).fill(cx
, aobj
, 0, protoIndex
, obj2
, shape
);
4862 if (!js_NativeGet(cx
, obj
, obj2
, shape
, getHow
, vp
))
4865 JS_UNLOCK_OBJ(cx
, obj2
);
4870 js_GetPropertyHelperWithShape(JSContext
*cx
, JSObject
*obj
, jsid id
,
4871 uint32 getHow
, Value
*vp
,
4872 const Shape
**shapeOut
, JSObject
**holderOut
)
4874 return js_GetPropertyHelperWithShapeInline(cx
, obj
, id
, getHow
, vp
, shapeOut
, holderOut
);
4878 js_GetPropertyHelper(JSContext
*cx
, JSObject
*obj
, jsid id
, uint32 getHow
, Value
*vp
)
4882 return js_GetPropertyHelperWithShapeInline(cx
, obj
, id
, getHow
, vp
, &shape
, &holder
);
4886 js_GetProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
4888 return js_GetPropertyHelper(cx
, obj
, id
, JSGET_METHOD_BARRIER
, vp
);
4892 js_GetMethod(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN getHow
, Value
*vp
)
4894 JSAutoResolveFlags
rf(cx
, JSRESOLVE_QUALIFIED
);
4896 PropertyIdOp op
= obj
->getOps()->getProperty
;
4898 #if JS_HAS_XML_SUPPORT
4899 JS_ASSERT(!obj
->isXML());
4901 return js_GetPropertyHelper(cx
, obj
, id
, getHow
, vp
);
4903 JS_ASSERT_IF(getHow
& JSGET_CACHE_RESULT
, obj
->isDenseArray());
4904 #if JS_HAS_XML_SUPPORT
4906 return js_GetXMLMethod(cx
, obj
, id
, vp
);
4908 return op(cx
, obj
, id
, vp
);
4912 js_CheckUndeclaredVarAssignment(JSContext
*cx
, JSString
*propname
)
4914 JSStackFrame
*const fp
= js_GetTopStackFrame(cx
);
4918 /* If neither cx nor the code is strict, then no check is needed. */
4919 if (!(fp
->isScriptFrame() && fp
->script()->strictModeCode
) &&
4920 !JS_HAS_STRICT_OPTION(cx
)) {
4924 const char *bytes
= js_GetStringBytes(cx
, propname
);
4926 JS_ReportErrorFlagsAndNumber(cx
,
4927 (JSREPORT_WARNING
| JSREPORT_STRICT
4928 | JSREPORT_STRICT_MODE_ERROR
),
4929 js_GetErrorMessage
, NULL
,
4930 JSMSG_UNDECLARED_VAR
, bytes
);
4936 ReportReadOnly(JSContext
* cx
, jsid id
, uintN flags
)
4938 return js_ReportValueErrorFlags(cx
, flags
, JSMSG_READ_ONLY
,
4939 JSDVG_IGNORE_STACK
, IdToValue(id
), NULL
,
4944 ReportNotConfigurable(JSContext
* cx
, jsid id
, uintN flags
)
4946 return js_ReportValueErrorFlags(cx
, flags
, JSMSG_CANT_DELETE
,
4947 JSDVG_IGNORE_STACK
, IdToValue(id
), NULL
,
4954 * Note: all non-error exits in this function must notify the tracer using
4955 * SetPropHit when called from the interpreter, which is detected by testing
4956 * (defineHow & JSDNP_CACHE_RESULT).
4959 js_SetPropertyHelper(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN defineHow
,
4960 Value
*vp
, JSBool strict
)
4969 PropertyOp getter
, setter
;
4972 JS_ASSERT((defineHow
&
4973 ~(JSDNP_CACHE_RESULT
| JSDNP_SET_METHOD
| JSDNP_UNQUALIFIED
)) == 0);
4974 if (defineHow
& JSDNP_CACHE_RESULT
)
4975 JS_ASSERT_NOT_ON_TRACE(cx
);
4977 /* Convert string indices to integers if appropriate. */
4978 id
= js_CheckForStringIndex(id
);
4980 /* Check for a sealed object first (now that id has been normalized). */
4982 return ReportReadOnly(cx
, id
, JSREPORT_ERROR
);
4984 protoIndex
= js_LookupPropertyWithFlags(cx
, obj
, id
, cx
->resolveFlags
,
4989 if (!pobj
->isNative())
4992 /* We should never add properties to lexical blocks. */
4993 JS_ASSERT(!obj
->isBlock());
4995 if (!obj
->getParent() &&
4996 (defineHow
& JSDNP_UNQUALIFIED
) &&
4997 !js_CheckUndeclaredVarAssignment(cx
, JSID_TO_STRING(id
))) {
5001 shape
= (Shape
*) prop
;
5004 * Now either shape is null, meaning id was not found in obj or one of its
5005 * prototypes; or shape is non-null, meaning id was found directly in pobj.
5006 * If JS_THREADSAFE and shape is non-null, then pobj is locked, and shape
5007 * is held: we must JSObject::dropProperty or else JS_UNLOCK_OBJ before we
5008 * return (the two are equivalent for native objects; we use JS_UNLOCK_OBJ
5009 * because it is cheaper).
5011 attrs
= JSPROP_ENUMERATE
;
5014 clasp
= obj
->getClass();
5015 getter
= clasp
->getProperty
;
5016 setter
= clasp
->setProperty
;
5019 /* ES5 8.12.4 [[Put]] step 2. */
5020 if (shape
->isAccessorDescriptor()) {
5021 if (shape
->hasDefaultSetter()) {
5022 JS_UNLOCK_OBJ(cx
, pobj
);
5023 if (defineHow
& JSDNP_CACHE_RESULT
)
5024 TRACE_2(SetPropHit
, JS_NO_PROP_CACHE_FILL
, shape
);
5025 return js_ReportGetterOnlyAssignment(cx
);
5028 JS_ASSERT(shape
->isDataDescriptor());
5030 if (!shape
->writable()) {
5031 JS_UNLOCK_OBJ(cx
, pobj
);
5033 PCMETER((defineHow
& JSDNP_CACHE_RESULT
) && JS_PROPERTY_CACHE(cx
).rofills
++);
5034 if (defineHow
& JSDNP_CACHE_RESULT
) {
5035 JS_ASSERT_NOT_ON_TRACE(cx
);
5036 TRACE_2(SetPropHit
, JS_NO_PROP_CACHE_FILL
, shape
);
5039 /* Error in strict mode code, warn with strict option, otherwise do nothing. */
5041 return ReportReadOnly(cx
, id
, 0);
5042 if (JS_HAS_STRICT_OPTION(cx
))
5043 return ReportReadOnly(cx
, id
, JSREPORT_STRICT
| JSREPORT_WARNING
);
5047 error
: // TRACE_2 jumps here in case of error.
5052 if (pobj
->sealed() && !shape
->hasSlot()) {
5053 JS_UNLOCK_OBJ(cx
, pobj
);
5054 return ReportReadOnly(cx
, id
, JSREPORT_ERROR
);
5057 attrs
= shape
->attributes();
5060 * We found id in a prototype object: prepare to share or shadow.
5062 * NB: Thanks to the immutable, garbage-collected property tree
5063 * maintained by jsscope.c in cx->runtime, we needn't worry about
5064 * shape going away behind our back after we've unlocked pobj.
5066 JS_UNLOCK_OBJ(cx
, pobj
);
5068 /* Don't clone a prototype property that doesn't have a slot. */
5069 if (!shape
->hasSlot()) {
5070 if (defineHow
& JSDNP_CACHE_RESULT
) {
5072 JS_ASSERT_NOT_ON_TRACE(cx
);
5073 PropertyCacheEntry
*entry
=
5075 JS_PROPERTY_CACHE(cx
).fill(cx
, obj
, 0, protoIndex
, pobj
, shape
);
5076 TRACE_2(SetPropHit
, entry
, shape
);
5079 if (shape
->hasDefaultSetter() && !shape
->hasGetterValue())
5082 return shape
->set(cx
, obj
, vp
);
5085 /* Restore attrs to the ECMA default for new properties. */
5086 attrs
= JSPROP_ENUMERATE
;
5089 * Preserve the shortid, getter, and setter when shadowing any
5090 * property that has a shortid. An old API convention requires
5091 * that the property's getter and setter functions receive the
5092 * shortid, not id, when they are called on the shadow we are
5093 * about to create in obj.
5095 if (shape
->hasShortID()) {
5096 flags
= Shape::HAS_SHORTID
;
5097 shortid
= shape
->shortid
;
5098 getter
= shape
->getter();
5099 setter
= shape
->setter();
5103 * Forget we found the proto-property now that we've copied any
5104 * needed member values.
5110 if (shape
->isMethod()) {
5111 JS_ASSERT(pobj
->hasMethodBarrier());
5112 } else if ((defineHow
& JSDNP_SET_METHOD
) && obj
->canHaveMethodBarrier()) {
5113 JS_ASSERT(IsFunctionObject(*vp
));
5114 JS_ASSERT(!(attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)));
5116 JSObject
*funobj
= &vp
->toObject();
5117 JSFunction
*fun
= GET_FUNCTION_PRIVATE(cx
, funobj
);
5118 if (fun
== funobj
) {
5119 funobj
= CloneFunctionObject(cx
, fun
, fun
->parent
);
5122 vp
->setObject(*funobj
);
5131 * Purge the property cache of now-shadowed id in obj's scope chain.
5132 * Do this early, before locking obj to avoid nesting locks.
5134 js_PurgeScopeChain(cx
, obj
, id
);
5136 /* Find or make a property descriptor with the right heritage. */
5137 JS_LOCK_OBJ(cx
, obj
);
5138 if (!obj
->ensureClassReservedSlots(cx
)) {
5139 JS_UNLOCK_OBJ(cx
, obj
);
5144 * Check for Object class here to avoid defining a method on a class
5145 * with magic resolve, addProperty, getProperty, etc. hooks.
5147 if ((defineHow
& JSDNP_SET_METHOD
) && obj
->canHaveMethodBarrier()) {
5148 JS_ASSERT(IsFunctionObject(*vp
));
5149 JS_ASSERT(!(attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)));
5151 JSObject
*funobj
= &vp
->toObject();
5152 JSFunction
*fun
= GET_FUNCTION_PRIVATE(cx
, funobj
);
5153 if (fun
== funobj
) {
5154 flags
|= Shape::METHOD
;
5155 getter
= CastAsPropertyOp(funobj
);
5159 shape
= obj
->putProperty(cx
, id
, getter
, setter
, SHAPE_INVALID_SLOT
,
5160 attrs
, flags
, shortid
);
5162 JS_UNLOCK_OBJ(cx
, obj
);
5167 * Initialize the new property value (passed to setter) to undefined.
5168 * Note that we store before calling addProperty, to match the order
5169 * in js_DefineNativeProperty.
5171 if (obj
->containsSlot(shape
->slot
))
5172 obj
->lockedSetSlot(shape
->slot
, UndefinedValue());
5174 /* XXXbe called with obj locked */
5175 if (!CallAddPropertyHook(cx
, clasp
, obj
, shape
, vp
)) {
5176 obj
->removeProperty(cx
, id
);
5177 JS_UNLOCK_OBJ(cx
, obj
);
5183 if (defineHow
& JSDNP_CACHE_RESULT
) {
5185 JS_ASSERT_NOT_ON_TRACE(cx
);
5186 PropertyCacheEntry
*entry
=
5188 JS_PROPERTY_CACHE(cx
).fill(cx
, obj
, 0, 0, obj
, shape
, added
);
5189 TRACE_2(SetPropHit
, entry
, shape
);
5192 if (!js_NativeSet(cx
, obj
, shape
, added
, vp
))
5195 JS_UNLOCK_OBJ(cx
, obj
);
5200 js_SetProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
, JSBool strict
)
5202 return js_SetPropertyHelper(cx
, obj
, id
, 0, vp
, strict
);
5206 js_GetAttributes(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN
*attrsp
)
5209 if (!js_LookupProperty(cx
, obj
, id
, &obj
, &prop
))
5215 if (!obj
->isNative())
5216 return obj
->getAttributes(cx
, id
, attrsp
);
5218 const Shape
*shape
= (Shape
*)prop
;
5219 *attrsp
= shape
->attributes();
5220 JS_UNLOCK_OBJ(cx
, obj
);
5225 js_SetNativeAttributes(JSContext
*cx
, JSObject
*obj
, Shape
*shape
, uintN attrs
)
5227 JS_ASSERT(obj
->isNative());
5228 bool ok
= !!js_ChangeNativePropertyAttrs(cx
, obj
, shape
, attrs
, 0,
5229 shape
->getter(), shape
->setter());
5230 JS_UNLOCK_OBJ(cx
, obj
);
5235 js_SetAttributes(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN
*attrsp
)
5238 if (!js_LookupProperty(cx
, obj
, id
, &obj
, &prop
))
5242 return obj
->isNative()
5243 ? js_SetNativeAttributes(cx
, obj
, (Shape
*) prop
, *attrsp
)
5244 : obj
->setAttributes(cx
, id
, attrsp
);
5248 js_DeleteProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*rval
, JSBool strict
)
5255 rval
->setBoolean(true);
5257 /* Convert string indices to integers if appropriate. */
5258 id
= js_CheckForStringIndex(id
);
5260 if (!js_LookupProperty(cx
, obj
, id
, &proto
, &prop
))
5262 if (!prop
|| proto
!= obj
) {
5264 * If the property was found in a native prototype, check whether it's
5265 * shared and permanent. Such a property stands for direct properties
5266 * in all delegating objects, matching ECMA semantics without bloating
5267 * each delegating object.
5269 if (prop
&& proto
->isNative()) {
5270 shape
= (Shape
*)prop
;
5271 if (shape
->isSharedPermanent()) {
5272 JS_UNLOCK_OBJ(cx
, proto
);
5274 return ReportNotConfigurable(cx
, id
, 0);
5275 rval
->setBoolean(false);
5278 JS_UNLOCK_OBJ(cx
, proto
);
5282 * If no property, or the property comes unshared or impermanent from
5283 * a prototype, call the class's delProperty hook, passing rval as the
5286 return CallJSPropertyOp(cx
, obj
->getClass()->delProperty
, obj
, id
, rval
);
5289 shape
= (Shape
*)prop
;
5290 if (!shape
->configurable()) {
5291 JS_UNLOCK_OBJ(cx
, obj
);
5293 return ReportNotConfigurable(cx
, id
, 0);
5294 rval
->setBoolean(false);
5298 /* XXXbe called with obj locked */
5299 if (!CallJSPropertyOp(cx
, obj
->getClass()->delProperty
, obj
, SHAPE_USERID(shape
), rval
)) {
5300 JS_UNLOCK_OBJ(cx
, obj
);
5304 if (obj
->containsSlot(shape
->slot
)) {
5305 const Value
&v
= obj
->lockedGetSlot(shape
->slot
);
5309 * Delete is rare enough that we can take the hit of checking for an
5310 * active cloned method function object that must be homed to a callee
5311 * slot on the active stack frame before this delete completes, in case
5312 * someone saved the clone and checks it against foo.caller for a foo
5313 * called from the active method.
5315 * We do not check suspended frames. They can't be reached via caller,
5316 * so the only way they could have the method's joined function object
5317 * as callee is through an API abusage. We break any such edge case.
5319 if (obj
->hasMethodBarrier()) {
5322 if (IsFunctionObject(v
, &funobj
)) {
5323 JSFunction
*fun
= GET_FUNCTION_PRIVATE(cx
, funobj
);
5325 if (fun
!= funobj
) {
5326 for (JSStackFrame
*fp
= cx
->maybefp(); fp
; fp
= fp
->prev()) {
5327 if (fp
->isFunctionFrame() &&
5328 &fp
->callee() == &fun
->compiledFunObj() &&
5329 fp
->thisValue().isObject() &&
5330 &fp
->thisValue().toObject() == obj
) {
5331 fp
->calleeValue().setObject(*funobj
);
5339 ok
= obj
->removeProperty(cx
, id
);
5340 JS_UNLOCK_OBJ(cx
, obj
);
5342 return ok
&& js_SuppressDeletedProperty(cx
, obj
, id
);
5348 DefaultValue(JSContext
*cx
, JSObject
*obj
, JSType hint
, Value
*vp
)
5350 JS_ASSERT(hint
!= JSTYPE_OBJECT
&& hint
!= JSTYPE_FUNCTION
);
5352 Value v
= ObjectValue(*obj
);
5353 if (hint
== JSTYPE_STRING
) {
5355 * Optimize for String objects with standard toString methods. Support
5356 * new String(...) instances whether mutated to have their own scope or
5357 * not, as well as direct String.prototype references.
5359 if (obj
->getClass() == &js_StringClass
) {
5360 jsid toStringId
= ATOM_TO_JSID(cx
->runtime
->atomState
.toStringAtom
);
5362 JS_LOCK_OBJ(cx
, obj
);
5363 JSObject
*lockedobj
= obj
;
5364 const Shape
*shape
= obj
->nativeLookup(toStringId
);
5365 JSObject
*pobj
= obj
;
5368 pobj
= obj
->getProto();
5370 if (pobj
&& pobj
->getClass() == &js_StringClass
) {
5371 JS_UNLOCK_OBJ(cx
, obj
);
5372 JS_LOCK_OBJ(cx
, pobj
);
5374 shape
= pobj
->nativeLookup(toStringId
);
5378 if (shape
&& shape
->hasDefaultGetter() && pobj
->containsSlot(shape
->slot
)) {
5379 const Value
&fval
= pobj
->lockedGetSlot(shape
->slot
);
5382 if (IsFunctionObject(fval
, &funobj
)) {
5383 JSFunction
*fun
= funobj
->getFunctionPrivate();
5384 if (fun
->maybeNative() == js_str_toString
) {
5385 JS_UNLOCK_OBJ(cx
, lockedobj
);
5386 *vp
= obj
->getPrimitiveThis();
5391 JS_UNLOCK_OBJ(cx
, lockedobj
);
5395 * Propagate the exception if js_TryMethod finds an appropriate
5396 * method, and calling that method returned failure.
5398 if (!js_TryMethod(cx
, obj
, cx
->runtime
->atomState
.toStringAtom
,
5403 if (!v
.isPrimitive()) {
5404 if (!obj
->getClass()->convert(cx
, obj
, hint
, &v
))
5408 if (!obj
->getClass()->convert(cx
, obj
, hint
, &v
))
5411 JS_ASSERT(hint
!= TypeOfValue(cx
, v
));
5412 if (!js_TryMethod(cx
, obj
, cx
->runtime
->atomState
.toStringAtom
, 0, NULL
, &v
))
5417 /* Avoid recursive death when decompiling in js_ReportValueError. */
5419 if (hint
== JSTYPE_STRING
) {
5420 str
= JS_InternString(cx
, obj
->getClass()->name
);
5426 vp
->setObject(*obj
);
5427 js_ReportValueError2(cx
, JSMSG_CANT_CONVERT_TO
,
5428 JSDVG_SEARCH_STACK
, *vp
, str
,
5429 (hint
== JSTYPE_VOID
)
5431 : JS_TYPE_STR(hint
));
5438 } /* namespace js */
5440 JS_FRIEND_API(JSBool
)
5441 js_Enumerate(JSContext
*cx
, JSObject
*obj
, JSIterateOp enum_op
, Value
*statep
, jsid
*idp
)
5443 /* If the class has a custom JSCLASS_NEW_ENUMERATE hook, call it. */
5444 Class
*clasp
= obj
->getClass();
5445 JSEnumerateOp enumerate
= clasp
->enumerate
;
5446 if (clasp
->flags
& JSCLASS_NEW_ENUMERATE
) {
5447 JS_ASSERT(enumerate
!= JS_EnumerateStub
);
5448 return ((NewEnumerateOp
) enumerate
)(cx
, obj
, enum_op
, statep
, idp
);
5451 if (!enumerate(cx
, obj
))
5454 /* Tell InitNativeIterator to treat us like a native object. */
5455 JS_ASSERT(enum_op
== JSENUMERATE_INIT
|| enum_op
== JSENUMERATE_INIT_ALL
);
5456 statep
->setMagic(JS_NATIVE_ENUMERATE
);
5463 CheckAccess(JSContext
*cx
, JSObject
*obj
, jsid id
, JSAccessMode mode
,
5464 Value
*vp
, uintN
*attrsp
)
5471 JSSecurityCallbacks
*callbacks
;
5472 CheckAccessOp check
;
5474 while (JS_UNLIKELY(obj
->getClass() == &js_WithClass
))
5475 obj
= obj
->getProto();
5477 writing
= (mode
& JSACC_WRITE
) != 0;
5478 switch (mode
& JSACC_TYPEMASK
) {
5482 vp
->setObjectOrNull(obj
->getProto());
5483 *attrsp
= JSPROP_PERMANENT
;
5487 JS_ASSERT(!writing
);
5489 vp
->setObject(*obj
->getParent());
5490 *attrsp
= JSPROP_READONLY
| JSPROP_PERMANENT
;
5494 if (!obj
->lookupProperty(cx
, id
, &pobj
, &prop
))
5504 if (!pobj
->isNative()) {
5512 shape
= (Shape
*)prop
;
5513 *attrsp
= shape
->attributes();
5515 if (pobj
->containsSlot(shape
->slot
))
5516 *vp
= pobj
->lockedGetSlot(shape
->slot
);
5520 JS_UNLOCK_OBJ(cx
, pobj
);
5524 * If obj's class has a stub (null) checkAccess hook, use the per-runtime
5525 * checkObjectAccess callback, if configured.
5527 * We don't want to require all classes to supply a checkAccess hook; we
5528 * need that hook only for certain classes used when precompiling scripts
5529 * and functions ("brutal sharing"). But for general safety of built-in
5530 * magic properties like __proto__, we route all access checks, even for
5531 * classes that stub out checkAccess, through the global checkObjectAccess
5532 * hook. This covers precompilation-based sharing and (possibly
5533 * unintended) runtime sharing across trust boundaries.
5535 clasp
= pobj
->getClass();
5536 check
= clasp
->checkAccess
;
5538 callbacks
= JS_GetSecurityCallbacks(cx
);
5539 check
= callbacks
? Valueify(callbacks
->checkObjectAccess
) : NULL
;
5541 return !check
|| check(cx
, pobj
, id
, mode
, vp
);
5547 js_TypeOf(JSContext
*cx
, JSObject
*obj
)
5550 * Unfortunately we have wrappers that are native objects and thus don't
5551 * overwrite js_TypeOf (i.e. XPCCrossOriginWrapper), so we have to
5554 obj
= obj
->wrappedObject(cx
);
5557 * ECMA 262, 11.4.3 says that any native object that implements
5558 * [[Call]] should be of type "function". However, RegExp is of
5559 * type "object", not "function", for Web compatibility.
5561 if (obj
->isCallable()) {
5562 return (obj
->getClass() != &js_RegExpClass
)
5567 return JSTYPE_OBJECT
;
5570 #ifdef JS_THREADSAFE
5572 js_DropProperty(JSContext
*cx
, JSObject
*obj
, JSProperty
*prop
)
5574 JS_UNLOCK_OBJ(cx
, obj
);
5579 js_IsDelegate(JSContext
*cx
, JSObject
*obj
, const Value
&v
)
5581 if (v
.isPrimitive())
5583 JSObject
*obj2
= v
.toObject().wrappedObject(cx
);
5584 while ((obj2
= obj2
->getProto()) != NULL
) {
5592 js::FindClassPrototype(JSContext
*cx
, JSObject
*scopeobj
, JSProtoKey protoKey
,
5593 JSObject
**protop
, Class
*clasp
)
5596 if (!js_FindClassObject(cx
, scopeobj
, protoKey
, &v
, clasp
))
5599 if (IsFunctionObject(v
)) {
5600 JSObject
*ctor
= &v
.toObject();
5601 if (!ctor
->getProperty(cx
, ATOM_TO_JSID(cx
->runtime
->atomState
.classPrototypeAtom
), &v
))
5605 *protop
= v
.isObject() ? &v
.toObject() : NULL
;
5610 * The first part of this function has been hand-expanded and optimized into
5611 * NewBuiltinClassInstance in jsobjinlines.h.
5614 js_GetClassPrototype(JSContext
*cx
, JSObject
*scopeobj
, JSProtoKey protoKey
,
5615 JSObject
**protop
, Class
*clasp
)
5617 VOUCH_DOES_NOT_REQUIRE_STACK();
5618 JS_ASSERT(JSProto_Null
<= protoKey
);
5619 JS_ASSERT(protoKey
< JSProto_LIMIT
);
5621 if (protoKey
!= JSProto_Null
) {
5624 scopeobj
= &cx
->fp()->scopeChain();
5626 scopeobj
= cx
->globalObject
;
5633 scopeobj
= scopeobj
->getGlobal();
5634 if (scopeobj
->getClass()->flags
& JSCLASS_IS_GLOBAL
) {
5635 const Value
&v
= scopeobj
->getReservedSlot(JSProto_LIMIT
+ protoKey
);
5637 *protop
= &v
.toObject();
5643 return FindClassPrototype(cx
, scopeobj
, protoKey
, protop
, clasp
);
5647 * For shared precompilation of function objects, we support cloning on entry
5648 * to an execution context in which the function declaration or expression
5649 * should be processed as if it were not precompiled, where the precompiled
5650 * function's scope chain does not match the execution context's. The cloned
5651 * function object carries its execution-context scope in its parent slot; it
5652 * links to the precompiled function (the "clone-parent") via its proto slot.
5654 * Note that this prototype-based delegation leaves an unchecked access path
5655 * from the clone to the clone-parent's 'constructor' property. If the clone
5656 * lives in a less privileged or shared scope than the clone-parent, this is
5657 * a security hole, a sharing hazard, or both. Therefore we check all such
5658 * accesses with the following getter/setter pair, which we use when defining
5659 * 'constructor' in f.prototype for all function objects f.
5662 CheckCtorGetAccess(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
5664 JSAtom
*atom
= cx
->runtime
->atomState
.constructorAtom
;
5665 JS_ASSERT(id
== ATOM_TO_JSID(atom
));
5667 return CheckAccess(cx
, obj
, ATOM_TO_JSID(atom
), JSACC_READ
, vp
, &attrs
);
5671 CheckCtorSetAccess(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
5673 JSAtom
*atom
= cx
->runtime
->atomState
.constructorAtom
;
5674 JS_ASSERT(id
== ATOM_TO_JSID(atom
));
5676 return CheckAccess(cx
, obj
, ATOM_TO_JSID(atom
), JSACC_WRITE
, vp
, &attrs
);
5680 js_SetClassPrototype(JSContext
*cx
, JSObject
*ctor
, JSObject
*proto
, uintN attrs
)
5683 * Use the given attributes for the prototype property of the constructor,
5684 * as user-defined constructors have a DontDelete prototype (which may be
5685 * reset), while native or "system" constructors have DontEnum | ReadOnly |
5688 if (!ctor
->defineProperty(cx
, ATOM_TO_JSID(cx
->runtime
->atomState
.classPrototypeAtom
),
5689 ObjectOrNullValue(proto
), PropertyStub
, PropertyStub
, attrs
)) {
5694 * ECMA says that Object.prototype.constructor, or f.prototype.constructor
5695 * for a user-defined function f, is DontEnum.
5697 return proto
->defineProperty(cx
, ATOM_TO_JSID(cx
->runtime
->atomState
.constructorAtom
),
5698 ObjectOrNullValue(ctor
), CheckCtorGetAccess
, CheckCtorSetAccess
, 0);
5702 js_PrimitiveToObject(JSContext
*cx
, Value
*vp
)
5705 JS_ASSERT(v
.isPrimitive());
5709 clasp
= &js_NumberClass
;
5710 else if (v
.isString())
5711 clasp
= &js_StringClass
;
5713 clasp
= &js_BooleanClass
;
5715 JSObject
*obj
= NewBuiltinClassInstance(cx
, clasp
);
5719 obj
->setPrimitiveThis(v
);
5720 vp
->setObject(*obj
);
5725 js_ValueToObjectOrNull(JSContext
*cx
, const Value
&v
, JSObject
**objp
)
5729 if (v
.isObjectOrNull()) {
5730 obj
= v
.toObjectOrNull();
5731 } else if (v
.isUndefined()) {
5735 if (!js_PrimitiveToObject(cx
, &tmp
))
5737 obj
= &tmp
.toObject();
5744 js_ValueToNonNullObject(JSContext
*cx
, const Value
&v
)
5748 if (!js_ValueToObjectOrNull(cx
, v
, &obj
))
5751 js_ReportIsNullOrUndefined(cx
, JSDVG_SEARCH_STACK
, v
, NULL
);
5756 js_TryValueOf(JSContext
*cx
, JSObject
*obj
, JSType type
, Value
*rval
)
5760 argv
[0].setString(ATOM_TO_STRING(cx
->runtime
->atomState
.typeAtoms
[type
]));
5761 return js_TryMethod(cx
, obj
, cx
->runtime
->atomState
.valueOfAtom
,
5766 js_TryMethod(JSContext
*cx
, JSObject
*obj
, JSAtom
*atom
,
5767 uintN argc
, Value
*argv
, Value
*rval
)
5769 JS_CHECK_RECURSION(cx
, return JS_FALSE
);
5772 * Report failure only if an appropriate method was found, and calling it
5773 * returned failure. We propagate failure in this case to make exceptions
5776 JSErrorReporter older
= JS_SetErrorReporter(cx
, NULL
);
5777 jsid id
= ATOM_TO_JSID(atom
);
5779 JSBool ok
= js_GetMethod(cx
, obj
, id
, JSGET_NO_METHOD_BARRIER
, &fval
);
5780 JS_SetErrorReporter(cx
, older
);
5784 if (fval
.isPrimitive())
5786 return ExternalInvoke(cx
, obj
, fval
, argc
, argv
, rval
);
5792 js_XDRObject(JSXDRState
*xdr
, JSObject
**objp
)
5797 uint32 classId
, classDef
;
5798 JSProtoKey protoKey
;
5803 if (xdr
->mode
== JSXDR_ENCODE
) {
5804 clasp
= (*objp
)->getClass();
5805 classId
= JS_XDRFindClassIdByName(xdr
, clasp
->name
);
5806 classDef
= !classId
;
5808 if (!JS_XDRRegisterClass(xdr
, Jsvalify(clasp
), &classId
))
5810 protoKey
= JSCLASS_CACHED_PROTO_KEY(clasp
);
5811 if (protoKey
!= JSProto_Null
) {
5812 classDef
|= (protoKey
<< 1);
5814 atom
= js_Atomize(cx
, clasp
->name
, strlen(clasp
->name
), 0);
5820 clasp
= NULL
; /* quell GCC overwarning */
5825 * XDR a flag word, which could be 0 for a class use, in which case no
5826 * name follows, only the id in xdr's class registry; 1 for a class def,
5827 * in which case the flag word is followed by the class name transferred
5828 * from or to atom; or a value greater than 1, an odd number that when
5829 * divided by two yields the JSProtoKey for class. In the last case, as
5830 * in the 0 classDef case, no name is transferred via atom.
5832 if (!JS_XDRUint32(xdr
, &classDef
))
5834 if (classDef
== 1 && !js_XDRAtom(xdr
, &atom
))
5837 if (!JS_XDRUint32(xdr
, &classId
))
5840 if (xdr
->mode
== JSXDR_DECODE
) {
5842 /* NB: we know that JSProto_Null is 0 here, for backward compat. */
5843 protoKey
= (JSProtoKey
) (classDef
>> 1);
5844 if (!js_GetClassPrototype(cx
, NULL
, protoKey
, &proto
, clasp
))
5846 clasp
= proto
->getClass();
5847 if (!JS_XDRRegisterClass(xdr
, Jsvalify(clasp
), &classId
))
5850 clasp
= Valueify(JS_XDRFindClassById(xdr
, classId
));
5853 JS_snprintf(numBuf
, sizeof numBuf
, "%ld", (long)classId
);
5854 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
5855 JSMSG_CANT_FIND_CLASS
, numBuf
);
5861 if (!clasp
->xdrObject
) {
5862 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
5863 JSMSG_CANT_XDR_CLASS
, clasp
->name
);
5866 return clasp
->xdrObject(xdr
, objp
);
5869 #endif /* JS_HAS_XDR */
5871 #ifdef JS_DUMP_SCOPE_METERS
5875 JSBasicStats js_entry_count_bs
= JS_INIT_STATIC_BASIC_STATS
;
5878 MeterEntryCount(uintN count
)
5880 JS_BASIC_STATS_ACCUM(&js_entry_count_bs
, count
);
5884 js_DumpScopeMeters(JSRuntime
*rt
)
5888 logfp
= fopen("/tmp/scope.stats", "a");
5893 mean
= JS_MeanAndStdDevBS(&js_entry_count_bs
, &sigma
);
5895 fprintf(logfp
, "scopes %u entries %g mean %g sigma %g max %u",
5896 js_entry_count_bs
.num
, js_entry_count_bs
.sum
, mean
, sigma
,
5897 js_entry_count_bs
.max
);
5900 JS_DumpHistogram(&js_entry_count_bs
, logfp
);
5901 JS_BASIC_STATS_INIT(&js_entry_count_bs
);
5908 js_PrintObjectSlotName(JSTracer
*trc
, char *buf
, size_t bufsize
)
5910 JS_ASSERT(trc
->debugPrinter
== js_PrintObjectSlotName
);
5912 JSObject
*obj
= (JSObject
*)trc
->debugPrintArg
;
5913 uint32 slot
= (uint32
)trc
->debugPrintIndex
;
5914 JS_ASSERT(slot
>= JSSLOT_START(obj
->getClass()));
5917 if (obj
->isNative()) {
5918 shape
= obj
->lastProperty();
5919 while (shape
->previous() && shape
->slot
!= slot
)
5920 shape
= shape
->previous();
5926 const char *slotname
= NULL
;
5927 Class
*clasp
= obj
->getClass();
5928 if (clasp
->flags
& JSCLASS_IS_GLOBAL
) {
5929 uint32 key
= slot
- JSSLOT_START(clasp
);
5930 #define JS_PROTO(name,code,init) \
5931 if ((code) == key) { slotname = js_##name##_str; goto found; }
5932 #include "jsproto.tbl"
5937 JS_snprintf(buf
, bufsize
, "CLASS_OBJECT(%s)", slotname
);
5939 JS_snprintf(buf
, bufsize
, "**UNKNOWN SLOT %ld**", (long)slot
);
5941 jsid id
= shape
->id
;
5942 if (JSID_IS_INT(id
)) {
5943 JS_snprintf(buf
, bufsize
, "%ld", (long)JSID_TO_INT(id
));
5944 } else if (JSID_IS_ATOM(id
)) {
5945 js_PutEscapedString(buf
, bufsize
, JSID_TO_STRING(id
), 0);
5947 JS_snprintf(buf
, bufsize
, "**FINALIZED ATOM KEY**");
5954 js_TraceObject(JSTracer
*trc
, JSObject
*obj
)
5956 JS_ASSERT(obj
->isNative());
5958 JSContext
*cx
= trc
->context
;
5959 if (!obj
->nativeEmpty() && IS_GC_MARKING_TRACER(trc
)) {
5961 * Trim overlong dslots allocations from the GC, to avoid thrashing in
5962 * case of delete-happy code that settles down at a given population.
5963 * The !obj->nativeEmpty() guard above is due to the bug described by
5964 * the FIXME comment below.
5966 size_t slots
= obj
->slotSpan();
5967 if (obj
->numSlots() != slots
)
5968 obj
->shrinkSlots(cx
, slots
);
5971 #ifdef JS_DUMP_SCOPE_METERS
5972 MeterEntryCount(obj
->propertyCount
);
5977 if (!JS_CLIST_IS_EMPTY(&cx
->runtime
->watchPointList
))
5978 js_TraceWatchPoints(trc
, obj
);
5980 /* No one runs while the GC is running, so we can use LOCKED_... here. */
5981 Class
*clasp
= obj
->getClass();
5983 if (clasp
->flags
& JSCLASS_MARK_IS_TRACE
)
5984 ((JSTraceOp
) clasp
->mark
)(trc
, obj
);
5985 else if (IS_GC_MARKING_TRACER(trc
))
5986 (void) clasp
->mark(cx
, obj
, trc
);
5988 if (clasp
->flags
& JSCLASS_IS_GLOBAL
) {
5989 JSCompartment
*compartment
= obj
->getCompartment(cx
);
5990 compartment
->marked
= true;
5994 * NB: In case clasp->mark mutates something (which would be a bug, but we
5995 * want to be defensive), leave this code here -- don't move it up and
5996 * unify it with the |if (!traceScope)| section above.
5998 * FIXME: We minimize nslots against obj->slotSpan because native objects
5999 * such as Date instances may have failed to advance slotSpan to cover all
6000 * reserved slots (this Date issue may be a bug in JSObject::growSlots, but
6001 * the general problem occurs in other built-in class implementations).
6003 uint32 nslots
= obj
->numSlots();
6004 if (!obj
->nativeEmpty() && obj
->slotSpan() < nslots
)
6005 nslots
= obj
->slotSpan();
6006 JS_ASSERT(nslots
>= JSSLOT_START(clasp
));
6008 for (uint32 i
= JSSLOT_START(clasp
); i
!= nslots
; ++i
) {
6009 const Value
&v
= obj
->getSlot(i
);
6010 JS_SET_TRACING_DETAILS(trc
, js_PrintObjectSlotName
, obj
, i
);
6011 MarkValueRaw(trc
, v
);
6016 js_ClearNative(JSContext
*cx
, JSObject
*obj
)
6019 * Clear obj of all obj's properties. FIXME: we do not clear reserved slots
6020 * lying below JSSLOT_FREE(clasp). JS_ClearScope does that.
6022 JS_LOCK_OBJ(cx
, obj
);
6023 if (!obj
->nativeEmpty()) {
6024 /* Now that we're done using real properties, clear obj. */
6027 /* Clear slot values since obj->clear reset our shape to empty. */
6028 uint32 freeslot
= JSSLOT_FREE(obj
->getClass());
6029 uint32 n
= obj
->numSlots();
6030 for (uint32 i
= freeslot
; i
< n
; ++i
)
6031 obj
->setSlot(i
, UndefinedValue());
6033 JS_UNLOCK_OBJ(cx
, obj
);
6037 js_GetReservedSlot(JSContext
*cx
, JSObject
*obj
, uint32 index
, Value
*vp
)
6039 if (!obj
->isNative()) {
6044 uint32 slot
= JSSLOT_START(obj
->getClass()) + index
;
6045 JS_LOCK_OBJ(cx
, obj
);
6046 if (slot
< obj
->numSlots())
6047 *vp
= obj
->getSlot(slot
);
6050 JS_UNLOCK_OBJ(cx
, obj
);
6055 js_SetReservedSlot(JSContext
*cx
, JSObject
*obj
, uint32 index
, const Value
&v
)
6057 if (!obj
->isNative())
6060 Class
*clasp
= obj
->getClass();
6061 uint32 slot
= JSSLOT_START(clasp
) + index
;
6063 JS_LOCK_OBJ(cx
, obj
);
6064 if (slot
>= obj
->numSlots()) {
6065 uint32 nslots
= JSSLOT_FREE(clasp
);
6066 JS_ASSERT(slot
< nslots
);
6067 if (!obj
->allocSlots(cx
, nslots
)) {
6068 JS_UNLOCK_OBJ(cx
, obj
);
6073 obj
->setSlot(slot
, v
);
6074 GC_POKE(cx
, JS_NULL
);
6075 JS_UNLOCK_OBJ(cx
, obj
);
6080 JSObject::wrappedObject(JSContext
*cx
) const
6082 if (JSObjectOp op
= getClass()->ext
.wrappedObject
) {
6083 if (JSObject
*obj
= op(cx
, const_cast<JSObject
*>(this)))
6086 return const_cast<JSObject
*>(this);
6090 JSObject::getGlobal() const
6092 JSObject
*obj
= const_cast<JSObject
*>(this);
6093 while (JSObject
*parent
= obj
->getParent())
6099 js_ReportGetterOnlyAssignment(JSContext
*cx
)
6101 return JS_ReportErrorFlagsAndNumber(cx
,
6102 JSREPORT_WARNING
| JSREPORT_STRICT
|
6103 JSREPORT_STRICT_MODE_ERROR
,
6104 js_GetErrorMessage
, NULL
,
6109 JSObject::getCompartment(JSContext
*cx
)
6111 JSObject
*obj
= getGlobal();
6113 Class
*clasp
= obj
->getClass();
6114 if (!(clasp
->flags
& JSCLASS_IS_GLOBAL
)) {
6115 #if JS_HAS_XML_SUPPORT
6116 // The magic AnyName object is runtime-wide.
6117 if (clasp
== &js_AnyNameClass
)
6118 return cx
->runtime
->defaultCompartment
;
6120 // The magic function namespace object is runtime-wide.
6121 if (clasp
== &js_NamespaceClass
&&
6122 obj
->getNameURI() == ATOM_TO_JSVAL(cx
->runtime
->
6123 atomState
.functionNamespaceURIAtom
)) {
6124 return cx
->runtime
->defaultCompartment
;
6129 * Script objects and compile-time Function, Block, RegExp objects
6132 if (clasp
== &js_FunctionClass
|| clasp
== &js_BlockClass
|| clasp
== &js_RegExpClass
||
6133 clasp
== &js_ScriptClass
) {
6134 // This is a bogus answer, but it'll do for now.
6135 return cx
->runtime
->defaultCompartment
;
6137 JS_NOT_REACHED("non-global object at end of scope chain");
6139 const Value
&v
= obj
->getReservedSlot(JSRESERVED_GLOBAL_COMPARTMENT
);
6140 return (JSCompartment
*)v
.toPrivate();
6143 JS_FRIEND_API(JSBool
)
6144 js_GetterOnlyPropertyStub(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
6146 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_GETTER_ONLY
);
6153 * Routines to print out values during debugging. These are FRIEND_API to help
6154 * the debugger find them and to support temporarily hacking js_Dump* calls
6159 dumpChars(const jschar
*s
, size_t n
)
6163 if (n
== (size_t) -1) {
6168 for (i
= 0; i
< n
; i
++) {
6170 fprintf(stderr
, "\\n");
6171 else if (s
[i
] == '\t')
6172 fprintf(stderr
, "\\t");
6173 else if (s
[i
] >= 32 && s
[i
] < 127)
6174 fputc(s
[i
], stderr
);
6175 else if (s
[i
] <= 255)
6176 fprintf(stderr
, "\\x%02x", (unsigned int) s
[i
]);
6178 fprintf(stderr
, "\\u%04x", (unsigned int) s
[i
]);
6184 js_DumpChars(const jschar
*s
, size_t n
)
6186 fprintf(stderr
, "jschar * (%p) = ", (void *) s
);
6188 fputc('\n', stderr
);
6192 dumpString(JSString
*str
)
6194 dumpChars(str
->chars(), str
->length());
6198 js_DumpString(JSString
*str
)
6200 fprintf(stderr
, "JSString* (%p) = jschar * (%p) = ",
6201 (void *) str
, (void *) str
->chars());
6203 fputc('\n', stderr
);
6207 js_DumpAtom(JSAtom
*atom
)
6209 fprintf(stderr
, "JSAtom* (%p) = ", (void *) atom
);
6210 js_DumpString(ATOM_TO_STRING(atom
));
6214 dumpValue(const Value
&v
)
6217 fprintf(stderr
, "null");
6218 else if (v
.isUndefined())
6219 fprintf(stderr
, "undefined");
6220 else if (v
.isInt32())
6221 fprintf(stderr
, "%d", v
.toInt32());
6222 else if (v
.isDouble())
6223 fprintf(stderr
, "%g", v
.toDouble());
6224 else if (v
.isString())
6225 dumpString(v
.toString());
6226 else if (v
.isObject() && v
.toObject().isFunction()) {
6227 JSObject
*funobj
= &v
.toObject();
6228 JSFunction
*fun
= GET_FUNCTION_PRIVATE(cx
, funobj
);
6229 fprintf(stderr
, "<%s %s at %p (JSFunction at %p)>",
6230 fun
->atom
? "function" : "unnamed",
6231 fun
->atom
? JS_GetStringBytes(ATOM_TO_STRING(fun
->atom
)) : "function",
6234 } else if (v
.isObject()) {
6235 JSObject
*obj
= &v
.toObject();
6236 Class
*clasp
= obj
->getClass();
6237 fprintf(stderr
, "<%s%s at %p>",
6239 (clasp
== &js_ObjectClass
) ? "" : " object",
6241 } else if (v
.isBoolean()) {
6243 fprintf(stderr
, "true");
6245 fprintf(stderr
, "false");
6246 } else if (v
.isMagic()) {
6247 fprintf(stderr
, "<invalid");
6249 switch (v
.whyMagic()) {
6250 case JS_ARRAY_HOLE
: fprintf(stderr
, " array hole"); break;
6251 case JS_ARGS_HOLE
: fprintf(stderr
, " args hole"); break;
6252 case JS_NATIVE_ENUMERATE
: fprintf(stderr
, " native enumeration"); break;
6253 case JS_NO_ITER_VALUE
: fprintf(stderr
, " no iter value"); break;
6254 case JS_GENERATOR_CLOSING
: fprintf(stderr
, " generator closing"); break;
6255 default: fprintf(stderr
, " ?!"); break;
6258 fprintf(stderr
, ">");
6260 fprintf(stderr
, "unexpected value");
6265 js_DumpValue(const Value
&val
)
6268 fputc('\n', stderr
);
6274 fprintf(stderr
, "jsid %p = ", (void *) JSID_BITS(id
));
6275 dumpValue(IdToValue(id
));
6276 fputc('\n', stderr
);
6280 DumpShape(const Shape
&shape
)
6283 uint8 attrs
= shape
.attributes();
6285 fprintf(stderr
, " ");
6286 if (attrs
& JSPROP_ENUMERATE
) fprintf(stderr
, "enumerate ");
6287 if (attrs
& JSPROP_READONLY
) fprintf(stderr
, "readonly ");
6288 if (attrs
& JSPROP_PERMANENT
) fprintf(stderr
, "permanent ");
6289 if (attrs
& JSPROP_GETTER
) fprintf(stderr
, "getter ");
6290 if (attrs
& JSPROP_SETTER
) fprintf(stderr
, "setter ");
6291 if (attrs
& JSPROP_SHARED
) fprintf(stderr
, "shared ");
6292 if (shape
.isAlias()) fprintf(stderr
, "alias ");
6293 if (JSID_IS_ATOM(id
))
6294 dumpString(JSID_TO_STRING(id
));
6295 else if (JSID_IS_INT(id
))
6296 fprintf(stderr
, "%d", (int) JSID_TO_INT(id
));
6298 fprintf(stderr
, "unknown jsid %p", (void *) JSID_BITS(id
));
6299 fprintf(stderr
, ": slot %d", shape
.slot
);
6300 fprintf(stderr
, "\n");
6304 js_DumpObject(JSObject
*obj
)
6310 fprintf(stderr
, "object %p\n", (void *) obj
);
6311 clasp
= obj
->getClass();
6312 fprintf(stderr
, "class %p %s\n", (void *)clasp
, clasp
->name
);
6314 fprintf(stderr
, "flags:");
6315 uint32 flags
= obj
->flags
;
6316 if (flags
& JSObject::DELEGATE
) fprintf(stderr
, " delegate");
6317 if (flags
& JSObject::SYSTEM
) fprintf(stderr
, " system");
6318 if (flags
& JSObject::SEALED
) fprintf(stderr
, " sealed");
6319 if (flags
& JSObject::BRANDED
) fprintf(stderr
, " branded");
6320 if (flags
& JSObject::GENERIC
) fprintf(stderr
, " generic");
6321 if (flags
& JSObject::METHOD_BARRIER
) fprintf(stderr
, " method_barrier");
6322 if (flags
& JSObject::INDEXED
) fprintf(stderr
, " indexed");
6323 if (flags
& JSObject::OWN_SHAPE
) fprintf(stderr
, " own_shape");
6324 bool anyFlags
= flags
!= 0;
6325 if (obj
->isNative()) {
6326 if (obj
->inDictionaryMode()) {
6327 fprintf(stderr
, " inDictionaryMode");
6330 if (obj
->hasPropertyTable()) {
6331 fprintf(stderr
, " hasPropertyTable");
6336 fprintf(stderr
, " none");
6337 fprintf(stderr
, "\n");
6339 if (obj
->isDenseArray()) {
6340 slots
= JS_MIN(obj
->getArrayLength(), obj
->getDenseArrayCapacity());
6341 fprintf(stderr
, "elements\n");
6342 for (i
= 0; i
< slots
; i
++) {
6343 fprintf(stderr
, " %3d: ", i
);
6344 dumpValue(obj
->getDenseArrayElement(i
));
6345 fprintf(stderr
, "\n");
6351 if (obj
->isNative()) {
6353 fprintf(stderr
, "sealed\n");
6355 fprintf(stderr
, "properties:\n");
6356 for (Shape::Range r
= obj
->lastProperty()->all(); !r
.empty(); r
.popFront())
6357 DumpShape(r
.front());
6359 if (!obj
->isNative())
6360 fprintf(stderr
, "not native\n");
6363 fprintf(stderr
, "proto ");
6364 dumpValue(ObjectOrNullValue(obj
->getProto()));
6365 fputc('\n', stderr
);
6367 fprintf(stderr
, "parent ");
6368 dumpValue(ObjectOrNullValue(obj
->getParent()));
6369 fputc('\n', stderr
);
6372 if (clasp
->flags
& JSCLASS_HAS_PRIVATE
) {
6373 i
= JSSLOT_PRIVATE
+ 1;
6374 fprintf(stderr
, "private %p\n", obj
->getPrivate());
6377 fprintf(stderr
, "slots:\n");
6378 reservedEnd
= i
+ JSCLASS_RESERVED_SLOTS(clasp
);
6379 slots
= obj
->slotSpan();
6380 for (; i
< slots
; i
++) {
6381 fprintf(stderr
, " %3d ", i
);
6382 if (i
< reservedEnd
)
6383 fprintf(stderr
, "(reserved) ");
6384 fprintf(stderr
, "= ");
6385 dumpValue(obj
->getSlot(i
));
6386 fputc('\n', stderr
);
6388 fputc('\n', stderr
);
6392 MaybeDumpObject(const char *name
, JSObject
*obj
)
6395 fprintf(stderr
, " %s: ", name
);
6396 dumpValue(ObjectValue(*obj
));
6397 fputc('\n', stderr
);
6402 MaybeDumpValue(const char *name
, const Value
&v
)
6405 fprintf(stderr
, " %s: ", name
);
6407 fputc('\n', stderr
);
6412 js_DumpStackFrame(JSContext
*cx
, JSStackFrame
*start
)
6414 /* This should only called during live debugging. */
6415 VOUCH_DOES_NOT_REQUIRE_STACK();
6418 start
= cx
->maybefp();
6419 FrameRegsIter
i(cx
);
6420 while (!i
.done() && i
.fp() != start
)
6424 fprintf(stderr
, "fp = %p not found in cx = %p\n", (void *)start
, (void *)cx
);
6428 for (; !i
.done(); ++i
) {
6429 JSStackFrame
*const fp
= i
.fp();
6431 fprintf(stderr
, "JSStackFrame at %p\n", (void *) fp
);
6432 if (fp
->isFunctionFrame()) {
6433 fprintf(stderr
, "callee fun: ");
6434 dumpValue(ObjectValue(fp
->callee()));
6436 fprintf(stderr
, "global frame, no callee");
6438 fputc('\n', stderr
);
6440 if (fp
->isScriptFrame()) {
6441 fprintf(stderr
, "file %s line %u\n",
6442 fp
->script()->filename
, (unsigned) fp
->script()->lineno
);
6445 if (jsbytecode
*pc
= i
.pc()) {
6446 if (!fp
->isScriptFrame()) {
6447 fprintf(stderr
, "*** pc && !script, skipping frame\n\n");
6450 if (fp
->hasImacropc()) {
6451 fprintf(stderr
, " pc in imacro at %p\n called from ", pc
);
6452 pc
= fp
->imacropc();
6454 fprintf(stderr
, " ");
6456 fprintf(stderr
, "pc = %p\n", pc
);
6457 fprintf(stderr
, " current op: %s\n", js_CodeName
[*pc
]);
6460 fprintf(stderr
, " slots: %p\n", (void *) fp
->slots());
6461 fprintf(stderr
, " sp: %p = slots + %u\n", (void *) sp
, (unsigned) (sp
- fp
->slots()));
6462 if (sp
- fp
->slots() < 10000) { // sanity
6463 for (Value
*p
= fp
->slots(); p
< sp
; p
++) {
6464 fprintf(stderr
, " %p: ", (void *) p
);
6466 fputc('\n', stderr
);
6469 if (fp
->isFunctionFrame() && !fp
->isEvalFrame()) {
6470 fprintf(stderr
, " actuals: %p (%u) ", (void *) fp
->actualArgs(), (unsigned) fp
->numActualArgs());
6471 fprintf(stderr
, " formals: %p (%u)\n", (void *) fp
->formalArgs(), (unsigned) fp
->numFormalArgs());
6473 MaybeDumpObject("callobj", fp
->maybeCallObj());
6474 MaybeDumpObject("argsobj", fp
->maybeArgsObj());
6475 MaybeDumpValue("this", fp
->thisValue());
6476 fprintf(stderr
, " rval: ");
6477 dumpValue(fp
->returnValue());
6478 fputc('\n', stderr
);
6480 fprintf(stderr
, " flags:");
6481 if (fp
->isConstructing())
6482 fprintf(stderr
, " constructing");
6483 if (fp
->hasOverriddenArgs())
6484 fprintf(stderr
, " overridden_args");
6485 if (fp
->isAssigning())
6486 fprintf(stderr
, " assigning");
6487 if (fp
->isDebuggerFrame())
6488 fprintf(stderr
, " debugger");
6489 if (fp
->isEvalFrame())
6490 fprintf(stderr
, " eval");
6491 if (fp
->isYielding())
6492 fprintf(stderr
, " yielding");
6493 if (fp
->isGeneratorFrame())
6494 fprintf(stderr
, " generator");
6495 fputc('\n', stderr
);
6497 fprintf(stderr
, " scopeChain: (JSObject *) %p\n", (void *) &fp
->scopeChain());
6498 if (fp
->hasBlockChain())
6499 fprintf(stderr
, " blockChain: (JSObject *) %p\n", (void *) fp
->blockChain());
6501 fputc('\n', stderr
);
6507 IsSaneThisObject(JSObject
&obj
)
6509 Class
*clasp
= obj
.getClass();
6510 return clasp
!= &js_CallClass
&&
6511 clasp
!= &js_BlockClass
&&
6512 clasp
!= &js_DeclEnvClass
&&
6513 clasp
!= &js_WithClass
;