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.
58 #include "jsbuiltins.h"
60 #include "jsversion.h"
74 #include "jsstaticcheck.h"
80 #include "jswrapper.h"
82 #include "jsinterpinlines.h"
83 #include "jsscopeinlines.h"
84 #include "jsscriptinlines.h"
85 #include "jsobjinlines.h"
91 #if JS_HAS_XML_SUPPORT
100 #include "jsatominlines.h"
101 #include "jsobjinlines.h"
102 #include "jsscriptinlines.h"
104 #include "jsautooplen.h"
107 using namespace js::gc
;
109 JS_FRIEND_DATA(const JSObjectMap
) JSObjectMap::sharedNonNative(JSObjectMap::SHAPELESS
);
111 Class js_ObjectClass
= {
113 JSCLASS_HAS_CACHED_PROTO(JSProto_Object
),
114 PropertyStub
, /* addProperty */
115 PropertyStub
, /* delProperty */
116 PropertyStub
, /* getProperty */
117 PropertyStub
, /* setProperty */
123 JS_FRIEND_API(JSObject
*)
124 js_ObjectToOuterObject(JSContext
*cx
, JSObject
*obj
)
126 OBJ_TO_OUTER_OBJECT(cx
, obj
);
130 #if JS_HAS_OBJ_PROTO_PROP
133 obj_getProto(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
);
136 obj_setProto(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
);
138 static JSPropertySpec object_props
[] = {
139 {js_proto_str
, 0, JSPROP_PERMANENT
|JSPROP_SHARED
, Jsvalify(obj_getProto
), Jsvalify(obj_setProto
)},
144 obj_getProto(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
146 /* Let CheckAccess get the slot's value, based on the access mode. */
148 id
= ATOM_TO_JSID(cx
->runtime
->atomState
.protoAtom
);
149 return CheckAccess(cx
, obj
, id
, JSACC_PROTO
, vp
, &attrs
);
153 obj_setProto(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
155 /* ECMAScript 5 8.6.2 forbids changing [[Prototype]] if not [[Extensible]]. */
156 if (!obj
->isExtensible()) {
157 obj
->reportNotExtensible(cx
);
161 if (!vp
->isObjectOrNull())
164 JSObject
*pobj
= vp
->toObjectOrNull();
167 * Innerize pobj here to avoid sticking unwanted properties on the
168 * outer object. This ensures that any with statements only grant
169 * access to the inner object.
171 OBJ_TO_INNER_OBJECT(cx
, pobj
);
177 id
= ATOM_TO_JSID(cx
->runtime
->atomState
.protoAtom
);
178 if (!CheckAccess(cx
, obj
, id
, JSAccessMode(JSACC_PROTO
|JSACC_WRITE
), vp
, &attrs
))
181 return SetProto(cx
, obj
, pobj
, JS_TRUE
);
184 #else /* !JS_HAS_OBJ_PROTO_PROP */
186 #define object_props NULL
188 #endif /* !JS_HAS_OBJ_PROTO_PROP */
191 js_hash_object(const void *key
)
193 return JSHashNumber(uintptr_t(key
) >> JS_GCTHING_ALIGN
);
197 MarkSharpObjects(JSContext
*cx
, JSObject
*obj
, JSIdArray
**idap
)
199 JSSharpObjectMap
*map
;
202 JSHashEntry
**hep
, *he
;
211 JS_CHECK_RECURSION(cx
, return NULL
);
213 map
= &cx
->sharpObjectMap
;
214 JS_ASSERT(map
->depth
>= 1);
216 hash
= js_hash_object(obj
);
217 hep
= JS_HashTableRawLookup(table
, hash
, obj
);
221 he
= JS_HashTableRawAdd(table
, hep
, hash
, obj
, (void *) sharpid
);
223 JS_ReportOutOfMemory(cx
);
227 ida
= JS_Enumerate(cx
, obj
);
232 for (i
= 0, length
= ida
->length
; i
< length
; i
++) {
234 ok
= obj
->lookupProperty(cx
, id
, &obj2
, &prop
);
239 bool hasGetter
, hasSetter
;
240 AutoValueRooter
v(cx
);
241 AutoValueRooter
setter(cx
);
242 if (obj2
->isNative()) {
243 const Shape
*shape
= (Shape
*) prop
;
244 hasGetter
= shape
->hasGetterValue();
245 hasSetter
= shape
->hasSetterValue();
247 v
.set(shape
->getterValue());
249 setter
.set(shape
->setterValue());
251 hasGetter
= hasSetter
= false;
254 /* Mark the getter, then set val to setter. */
255 if (hasGetter
&& v
.value().isObject()) {
256 ok
= !!MarkSharpObjects(cx
, &v
.value().toObject(), NULL
);
260 v
.set(setter
.value());
261 } else if (!hasGetter
) {
262 ok
= obj
->getProperty(cx
, id
, v
.addr());
266 if (v
.value().isObject() &&
267 !MarkSharpObjects(cx
, &v
.value().toObject(), NULL
)) {
273 JS_DestroyIdArray(cx
, ida
);
277 sharpid
= uintptr_t(he
->value
);
279 sharpid
= ++map
->sharpgen
<< SHARP_ID_SHIFT
;
280 he
->value
= (void *) sharpid
;
290 js_EnterSharpObject(JSContext
*cx
, JSObject
*obj
, JSIdArray
**idap
,
293 JSSharpObjectMap
*map
;
297 JSHashEntry
*he
, **hep
;
302 if (!JS_CHECK_OPERATION_LIMIT(cx
))
305 /* Set to null in case we return an early error. */
307 map
= &cx
->sharpObjectMap
;
310 table
= JS_NewHashTable(8, js_hash_object
, JS_CompareValues
,
311 JS_CompareValues
, NULL
, NULL
);
313 JS_ReportOutOfMemory(cx
);
317 JS_KEEP_ATOMS(cx
->runtime
);
320 /* From this point the control must flow either through out: or bad:. */
322 if (map
->depth
== 0) {
324 * Although MarkSharpObjects tries to avoid invoking getters,
325 * it ends up doing so anyway under some circumstances; for
326 * example, if a wrapped object has getters, the wrapper will
327 * prevent MarkSharpObjects from recognizing them as such.
328 * This could lead to js_LeaveSharpObject being called while
329 * MarkSharpObjects is still working.
331 * Increment map->depth while we call MarkSharpObjects, to
332 * ensure that such a call doesn't free the hash table we're
336 he
= MarkSharpObjects(cx
, obj
, &ida
);
340 JS_ASSERT((uintptr_t(he
->value
) & SHARP_BIT
) == 0);
342 JS_DestroyIdArray(cx
, ida
);
346 hash
= js_hash_object(obj
);
347 hep
= JS_HashTableRawLookup(table
, hash
, obj
);
351 * It's possible that the value of a property has changed from the
352 * first time the object's properties are traversed (when the property
353 * ids are entered into the hash table) to the second (when they are
354 * converted to strings), i.e., the JSObject::getProperty() call is not
358 he
= JS_HashTableRawAdd(table
, hep
, hash
, obj
, NULL
);
360 JS_ReportOutOfMemory(cx
);
368 sharpid
= uintptr_t(he
->value
);
370 len
= JS_snprintf(buf
, sizeof buf
, "#%u%c",
371 sharpid
>> SHARP_ID_SHIFT
,
372 (sharpid
& SHARP_BIT
) ? '#' : '=');
373 *sp
= js_InflateString(cx
, buf
, &len
);
376 JS_DestroyIdArray(cx
, ida
);
383 if ((sharpid
& SHARP_BIT
) == 0) {
385 ida
= JS_Enumerate(cx
, obj
);
402 /* Clean up the sharpObjectMap table on outermost error. */
403 if (map
->depth
== 0) {
404 JS_UNKEEP_ATOMS(cx
->runtime
);
406 JS_HashTableDestroy(map
->table
);
413 js_LeaveSharpObject(JSContext
*cx
, JSIdArray
**idap
)
415 JSSharpObjectMap
*map
;
418 map
= &cx
->sharpObjectMap
;
419 JS_ASSERT(map
->depth
> 0);
420 if (--map
->depth
== 0) {
421 JS_UNKEEP_ATOMS(cx
->runtime
);
423 JS_HashTableDestroy(map
->table
);
429 JS_DestroyIdArray(cx
, ida
);
436 gc_sharp_table_entry_marker(JSHashEntry
*he
, intN i
, void *arg
)
438 MarkObject((JSTracer
*)arg
, *(JSObject
*)he
->key
, "sharp table entry");
439 return JS_DHASH_NEXT
;
443 js_TraceSharpMap(JSTracer
*trc
, JSSharpObjectMap
*map
)
445 JS_ASSERT(map
->depth
> 0);
446 JS_ASSERT(map
->table
);
449 * During recursive calls to MarkSharpObjects a non-native object or
450 * object with a custom getProperty method can potentially return an
451 * unrooted value or even cut from the object graph an argument of one of
452 * MarkSharpObjects recursive invocations. So we must protect map->table
453 * entries against GC.
455 * We can not simply use JSTempValueRooter to mark the obj argument of
456 * MarkSharpObjects during recursion as we have to protect *all* entries
457 * in JSSharpObjectMap including those that contains otherwise unreachable
458 * objects just allocated through custom getProperty. Otherwise newer
459 * allocations can re-use the address of an object stored in the hashtable
460 * confusing js_EnterSharpObject. So to address the problem we simply
461 * mark all objects from map->table.
463 * An alternative "proper" solution is to use JSTempValueRooter in
464 * MarkSharpObjects with code to remove during finalization entries
465 * with otherwise unreachable objects. But this is way too complex
466 * to justify spending efforts.
468 JS_HashTableEnumerateEntries(map
->table
, gc_sharp_table_entry_marker
, trc
);
473 obj_toSource(JSContext
*cx
, uintN argc
, Value
*vp
)
475 JSBool ok
, outermost
;
479 jschar
*chars
, *ochars
, *vsharp
;
480 const jschar
*idstrchars
, *vchars
;
481 size_t nchars
, idstrlength
, gsoplength
, vlength
, vsharplength
, curlen
;
487 JSString
*valstr
, *str
;
488 JSLinearString
*idstr
;
490 JS_CHECK_RECURSION(cx
, return JS_FALSE
);
493 PodArrayZero(localroot
);
494 AutoArrayRooter
tvr(cx
, JS_ARRAY_LENGTH(localroot
), localroot
);
496 /* If outermost, we need parentheses to be an expression, not a block. */
497 outermost
= (cx
->sharpObjectMap
.depth
== 0);
498 obj
= ComputeThisFromVp(cx
, vp
);
499 if (!obj
|| !(he
= js_EnterSharpObject(cx
, obj
, &ida
, &chars
))) {
505 * We didn't enter -- obj is already "sharp", meaning we've visited it
506 * already in our depth first search, and therefore chars contains a
507 * string of the form "#n#".
510 #if JS_HAS_SHARP_VARS
511 nchars
= js_strlen(chars
);
524 /* If outermost, allocate 4 + 1 for "({})" and the terminator. */
525 chars
= (jschar
*) cx
->runtime
->malloc(((outermost
? 4 : 2) + 1) * sizeof(jschar
));
530 chars
[nchars
++] = '(';
532 /* js_EnterSharpObject returned a string of the form "#n=" in chars. */
534 nchars
= js_strlen(chars
);
536 js_realloc((ochars
= chars
), (nchars
+ 2 + 1) * sizeof(jschar
));
543 * No need for parentheses around the whole shebang, because #n=
544 * unambiguously begins an object initializer, and never a block
547 outermost
= JS_FALSE
;
551 chars
[nchars
++] = '{';
556 * We have four local roots for cooked and raw value GC safety. Hoist the
557 * "localroot + 2" out of the loop using the val local, which refers to
558 * the raw (unconverted, "uncooked") values.
562 for (jsint i
= 0, length
= ida
->length
; i
< length
; i
++) {
563 /* Get strings for id and value and GC-root them via vp. */
564 jsid id
= ida
->vector
[i
];
566 ok
= obj
->lookupProperty(cx
, id
, &obj2
, &prop
);
571 * Convert id to a value and then to a string. Decide early whether we
572 * prefer get/set or old getter/setter syntax.
574 JSString
*s
= js_ValueToString(cx
, IdToValue(id
));
575 if (!s
|| !(idstr
= s
->ensureLinear(cx
))) {
579 vp
->setString(idstr
); /* local root */
584 if (obj2
->isNative()) {
585 const Shape
*shape
= (Shape
*) prop
;
586 unsigned attrs
= shape
->attributes();
587 if (attrs
& JSPROP_GETTER
) {
589 val
[valcnt
] = shape
->getterValue();
590 gsop
[valcnt
] = ATOM_TO_STRING(cx
->runtime
->atomState
.getAtom
);
593 if (attrs
& JSPROP_SETTER
) {
595 val
[valcnt
] = shape
->setterValue();
596 gsop
[valcnt
] = ATOM_TO_STRING(cx
->runtime
->atomState
.setAtom
);
603 ok
= obj
->getProperty(cx
, id
, &val
[0]);
610 * If id is a string that's not an identifier, or if it's a negative
611 * integer, then it must be quoted.
613 bool idIsLexicalIdentifier
= js_IsIdentifier(idstr
);
615 ? !idIsLexicalIdentifier
616 : (!JSID_IS_INT(id
) || JSID_TO_INT(id
) < 0)) {
617 s
= js_QuoteString(cx
, idstr
, jschar('\''));
618 if (!s
|| !(idstr
= s
->ensureLinear(cx
))) {
622 vp
->setString(idstr
); /* local root */
624 idstrlength
= idstr
->length();
625 idstrchars
= idstr
->getChars(cx
);
631 for (jsint j
= 0; j
< valcnt
; j
++) {
633 * Censor an accessor descriptor getter or setter part if it's
636 if (gsop
[j
] && val
[j
].isUndefined())
639 /* Convert val[j] to its canonical source form. */
640 valstr
= js_ValueToSource(cx
, val
[j
]);
645 localroot
[j
].setString(valstr
); /* local root */
646 vchars
= valstr
->getChars(cx
);
651 vlength
= valstr
->length();
654 * If val[j] is a non-sharp object, and we're not serializing an
655 * accessor (ECMA syntax can't accommodate sharpened accessors),
656 * consider sharpening it.
660 #if JS_HAS_SHARP_VARS
661 if (!gsop
[j
] && val
[j
].isObject() && vchars
[0] != '#') {
662 he
= js_EnterSharpObject(cx
, &val
[j
].toObject(), NULL
, &vsharp
);
669 vlength
= js_strlen(vchars
);
672 vsharplength
= js_strlen(vsharp
);
675 js_LeaveSharpObject(cx
, NULL
);
681 * Remove '(function ' from the beginning of valstr and ')' from the
682 * end so that we can put "get" in front of the function definition.
684 if (gsop
[j
] && IsFunctionObject(val
[j
])) {
685 const jschar
*start
= vchars
;
686 const jschar
*end
= vchars
+ vlength
;
688 uint8 parenChomp
= 0;
689 if (vchars
[0] == '(') {
694 /* Try to jump "function" keyword. */
696 vchars
= js_strchr_limit(vchars
, ' ', end
);
699 * Jump over the function's name: it can't be encoded as part
700 * of an ECMA getter or setter.
703 vchars
= js_strchr_limit(vchars
, '(', end
);
708 vlength
= end
- vchars
- parenChomp
;
715 #define SAFE_ADD(n) \
726 SAFE_ADD(idstrlength
+ 1);
728 SAFE_ADD(gsop
[j
]->length() + 1);
729 SAFE_ADD(vsharplength
);
731 /* Account for the trailing null. */
732 SAFE_ADD((outermost
? 2 : 1) + 1);
735 if (curlen
> size_t(-1) / sizeof(jschar
))
738 /* Allocate 1 + 1 at end for closing brace and terminating 0. */
739 chars
= (jschar
*) js_realloc((ochars
= chars
), curlen
* sizeof(jschar
));
746 chars
[nchars
++] = comma
[0];
747 chars
[nchars
++] = comma
[1];
752 gsoplength
= gsop
[j
]->length();
753 const jschar
*gsopchars
= gsop
[j
]->getChars(cx
);
756 js_strncpy(&chars
[nchars
], gsopchars
, gsoplength
);
757 nchars
+= gsoplength
;
758 chars
[nchars
++] = ' ';
760 js_strncpy(&chars
[nchars
], idstrchars
, idstrlength
);
761 nchars
+= idstrlength
;
762 /* Extraneous space after id here will be extracted later */
763 chars
[nchars
++] = gsop
[j
] ? ' ' : ':';
766 js_strncpy(&chars
[nchars
], vsharp
, vsharplength
);
767 nchars
+= vsharplength
;
769 js_strncpy(&chars
[nchars
], vchars
, vlength
);
777 chars
[nchars
++] = '}';
779 chars
[nchars
++] = ')';
783 js_LeaveSharpObject(cx
, &ida
);
792 JS_ReportOutOfMemory(cx
);
797 str
= js_NewString(cx
, chars
, nchars
);
814 #endif /* JS_HAS_TOSOURCE */
819 obj_toStringHelper(JSContext
*cx
, JSObject
*obj
)
822 return JSProxy::obj_toString(cx
, obj
);
824 const char *clazz
= obj
->getClass()->name
;
825 size_t nchars
= 9 + strlen(clazz
); /* 9 for "[object ]" */
826 jschar
*chars
= (jschar
*) cx
->malloc((nchars
+ 1) * sizeof(jschar
));
830 const char *prefix
= "[object ";
832 while ((chars
[nchars
] = (jschar
)*prefix
) != 0)
834 while ((chars
[nchars
] = (jschar
)*clazz
) != 0)
836 chars
[nchars
++] = ']';
839 JSString
*str
= js_NewString(cx
, chars
, nchars
);
847 /* ES5 15.2.4.2. Note steps 1 and 2 are errata. */
849 obj_toString(JSContext
*cx
, uintN argc
, Value
*vp
)
851 Value
&thisv
= vp
[1];
853 /* ES5 15.2.4.2 step 1. */
854 if (thisv
.isUndefined()) {
855 vp
->setString(ATOM_TO_STRING(cx
->runtime
->atomState
.objectUndefinedAtom
));
859 /* ES5 15.2.4.2 step 2. */
860 if (thisv
.isNull()) {
861 vp
->setString(ATOM_TO_STRING(cx
->runtime
->atomState
.objectNullAtom
));
865 /* ES5 15.2.4.2 step 3. */
866 if (!thisv
.isObject() && !js_PrimitiveToObject(cx
, &thisv
))
869 /* ES5 15.2.4.2 steps 4-5. */
870 JSString
*str
= js::obj_toStringHelper(cx
, &thisv
.toObject());
878 obj_toLocaleString(JSContext
*cx
, uintN argc
, Value
*vp
)
880 if (!ComputeThisFromVp(cx
, vp
))
883 JSString
*str
= js_ValueToString(cx
, vp
[1]);
892 obj_valueOf(JSContext
*cx
, uintN argc
, Value
*vp
)
894 if (!ComputeThisFromVp(cx
, vp
))
901 * Check if CSP allows new Function() or eval() to run in the current
905 js_CheckContentSecurityPolicy(JSContext
*cx
, JSObject
*scopeobj
)
907 // CSP is static per document, so if our check said yes before, that
908 // answer is still valid.
909 JSObject
*global
= scopeobj
->getGlobal();
910 Value v
= global
->getReservedSlot(JSRESERVED_GLOBAL_EVAL_ALLOWED
);
911 if (v
.isUndefined()) {
912 JSSecurityCallbacks
*callbacks
= JS_GetSecurityCallbacks(cx
);
914 // if there are callbacks, make sure that the CSP callback is installed and
915 // that it permits eval().
916 v
.setBoolean((!callbacks
|| !callbacks
->contentSecurityPolicyAllows
) ||
917 callbacks
->contentSecurityPolicyAllows(cx
));
919 // update the cache in the global object for the result of the security check
920 js_SetReservedSlot(cx
, global
, JSRESERVED_GLOBAL_EVAL_ALLOWED
, v
);
926 * Check whether principals subsumes scopeobj's principals, and return true
927 * if so (or if scopeobj has no principals, for backward compatibility with
928 * the JS API, which does not require principals), and false otherwise.
931 js_CheckPrincipalsAccess(JSContext
*cx
, JSObject
*scopeobj
,
932 JSPrincipals
*principals
, JSAtom
*caller
)
934 JSSecurityCallbacks
*callbacks
;
935 JSPrincipals
*scopePrincipals
;
937 callbacks
= JS_GetSecurityCallbacks(cx
);
938 if (callbacks
&& callbacks
->findObjectPrincipals
) {
939 scopePrincipals
= callbacks
->findObjectPrincipals(cx
, scopeobj
);
940 if (!principals
|| !scopePrincipals
||
941 !principals
->subsume(principals
, scopePrincipals
)) {
942 JSAutoByteString callerstr
;
943 if (!js_AtomToPrintableString(cx
, caller
, &callerstr
))
945 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
946 JSMSG_BAD_INDIRECT_CALL
, callerstr
.ptr());
954 CheckScopeChainValidity(JSContext
*cx
, JSObject
*scopeobj
)
956 JSObject
*inner
= scopeobj
;
957 OBJ_TO_INNER_OBJECT(cx
, inner
);
960 JS_ASSERT(inner
== scopeobj
);
962 /* XXX This is an awful gross hack. */
964 JSObjectOp op
= scopeobj
->getClass()->ext
.innerObject
;
965 if (op
&& op(cx
, scopeobj
) != scopeobj
) {
966 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_BAD_INDIRECT_CALL
,
970 scopeobj
= scopeobj
->getParent();
977 js_ComputeFilename(JSContext
*cx
, JSStackFrame
*caller
,
978 JSPrincipals
*principals
, uintN
*linenop
)
982 JSSecurityCallbacks
*callbacks
= JS_GetSecurityCallbacks(cx
);
985 JS_ASSERT(principals
|| !(callbacks
&& callbacks
->findObjectPrincipals
));
986 flags
= JS_GetScriptFilenameFlags(caller
->script());
987 if ((flags
& JSFILENAME_PROTECTED
) &&
989 strcmp(principals
->codebase
, "[System Principal]")) {
991 return principals
->codebase
;
994 jsbytecode
*pc
= caller
->pc(cx
);
995 if (pc
&& js_GetOpcode(cx
, caller
->script(), pc
) == JSOP_EVAL
) {
996 JS_ASSERT(js_GetOpcode(cx
, caller
->script(), pc
+ JSOP_EVAL_LENGTH
) == JSOP_LINENO
);
997 *linenop
= GET_UINT16(pc
+ JSOP_EVAL_LENGTH
);
999 *linenop
= js_FramePCToLineNumber(cx
, caller
);
1001 return caller
->script()->filename
;
1004 #ifndef EVAL_CACHE_CHAIN_LIMIT
1005 # define EVAL_CACHE_CHAIN_LIMIT 4
1008 static inline JSScript
**
1009 EvalCacheHash(JSContext
*cx
, JSLinearString
*str
)
1011 const jschar
*s
= str
->chars();
1012 size_t n
= str
->length();
1017 for (h
= 0; n
; s
++, n
--)
1018 h
= JS_ROTATE_LEFT32(h
, 4) ^ *s
;
1020 h
*= JS_GOLDEN_RATIO
;
1021 h
>>= 32 - JS_EVAL_CACHE_SHIFT
;
1022 return &JS_SCRIPTS_TO_GC(cx
)[h
];
1025 static JS_ALWAYS_INLINE JSScript
*
1026 EvalCacheLookup(JSContext
*cx
, JSLinearString
*str
, JSStackFrame
*caller
, uintN staticLevel
,
1027 JSPrincipals
*principals
, JSObject
*scopeobj
, JSScript
**bucket
)
1030 * Cache local eval scripts indexed by source qualified by scope.
1032 * An eval cache entry should never be considered a hit unless its
1033 * strictness matches that of the new eval code. The existing code takes
1034 * care of this, because hits are qualified by the function from which
1035 * eval was called, whose strictness doesn't change. (We don't cache evals
1036 * in eval code, so the calling function corresponds to the calling script,
1037 * and its strictness never varies.) Scripts produced by calls to eval from
1038 * global code aren't cached.
1040 * FIXME bug 620141: Qualify hits by calling script rather than function.
1041 * Then we wouldn't need the unintuitive !isEvalFrame() hack in EvalKernel
1042 * to avoid caching nested evals in functions (thus potentially mismatching
1043 * on strict mode), and we could cache evals in global code if desired.
1046 JSScript
**scriptp
= bucket
;
1048 EVAL_CACHE_METER(probe
);
1049 JSVersion version
= cx
->findVersion();
1051 while ((script
= *scriptp
) != NULL
) {
1052 if (script
->savedCallerFun
&&
1053 script
->staticLevel
== staticLevel
&&
1054 script
->version
== version
&&
1055 !script
->hasSingletons
&&
1056 (script
->principals
== principals
||
1057 (principals
->subsume(principals
, script
->principals
) &&
1058 script
->principals
->subsume(script
->principals
, principals
)))) {
1060 * Get the prior (cache-filling) eval's saved caller function.
1061 * See Compiler::compileScript in jsparse.cpp.
1063 JSFunction
*fun
= script
->getFunction(0);
1065 if (fun
== caller
->fun()) {
1067 * Get the source string passed for safekeeping in the
1068 * atom map by the prior eval to Compiler::compileScript.
1070 JSAtom
*src
= script
->atomMap
.vector
[0];
1072 if (src
== str
|| EqualStrings(src
, str
)) {
1074 * Source matches, qualify by comparing scopeobj to the
1075 * COMPILE_N_GO-memoized parent of the first literal
1076 * function or regexp object if any. If none, then this
1077 * script has no compiled-in dependencies on the prior
1080 JSObjectArray
*objarray
= script
->objects();
1083 if (objarray
->length
== 1) {
1084 if (JSScript::isValidOffset(script
->regexpsOffset
)) {
1085 objarray
= script
->regexps();
1088 EVAL_CACHE_METER(noscope
);
1093 objarray
->vector
[i
]->getParent() == scopeobj
) {
1094 JS_ASSERT(staticLevel
== script
->staticLevel
);
1095 EVAL_CACHE_METER(hit
);
1096 *scriptp
= script
->u
.nextToGC
;
1097 script
->u
.nextToGC
= NULL
;
1104 if (++count
== EVAL_CACHE_CHAIN_LIMIT
)
1106 EVAL_CACHE_METER(step
);
1107 scriptp
= &script
->u
.nextToGC
;
1114 eval(JSContext
*cx
, uintN argc
, Value
*vp
)
1117 * NB: This method handles only indirect eval: direct eval is handled by
1121 JSStackFrame
*caller
= js_GetScriptedCaller(cx
, NULL
);
1123 /* FIXME Bug 602994: This really should be perfectly cromulent. */
1125 /* Eval code needs to inherit principals from the caller. */
1126 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1127 JSMSG_BAD_INDIRECT_CALL
, js_eval_str
);
1131 return EvalKernel(cx
, argc
, vp
, INDIRECT_EVAL
, caller
, vp
[0].toObject().getGlobal());
1137 EvalKernel(JSContext
*cx
, uintN argc
, Value
*vp
, EvalType evalType
, JSStackFrame
*caller
,
1141 * FIXME Bug 602994: Calls with no scripted caller should be permitted and
1142 * should be implemented as indirect calls.
1145 JS_ASSERT(scopeobj
);
1148 * We once supported a second argument to eval to use as the scope chain
1149 * when evaluating the code string. Warn when such uses are seen so that
1150 * authors will know that support for eval(s, o) has been removed.
1152 JSScript
*callerScript
= caller
->script();
1153 if (argc
> 1 && !callerScript
->warnedAboutTwoArgumentEval
) {
1154 static const char TWO_ARGUMENT_WARNING
[] =
1155 "Support for eval(code, scopeObject) has been removed. "
1156 "Use |with (scopeObject) eval(code);| instead.";
1157 if (!JS_ReportWarning(cx
, TWO_ARGUMENT_WARNING
))
1159 callerScript
->warnedAboutTwoArgumentEval
= true;
1163 * CSP check: Is eval() allowed at all?
1164 * Report errors via CSP is done in the script security mgr.
1166 if (!js_CheckContentSecurityPolicy(cx
, scopeobj
)) {
1167 JS_ReportError(cx
, "call to eval() blocked by CSP");
1171 /* ES5 15.1.2.1 step 1. */
1176 if (!vp
[2].isString()) {
1180 JSString
*str
= vp
[2].toString();
1182 /* ES5 15.1.2.1 steps 2-8. */
1183 JSObject
*callee
= JSVAL_TO_OBJECT(JS_CALLEE(cx
, Jsvalify(vp
)));
1184 JS_ASSERT(IsBuiltinEvalFunction(callee
->getFunctionPrivate()));
1185 JSPrincipals
*principals
= js_EvalFramePrincipals(cx
, callee
, caller
);
1188 * Per ES5, indirect eval runs in the global scope. (eval is specified this
1189 * way so that the compiler can make assumptions about what bindings may or
1190 * may not exist in the current frame if it doesn't see 'eval'.)
1193 if (evalType
== DIRECT_EVAL
) {
1194 staticLevel
= caller
->script()->staticLevel
+ 1;
1197 jsbytecode
*callerPC
= caller
->pc(cx
);
1198 JS_ASSERT_IF(caller
->isFunctionFrame(), caller
->hasCallObj());
1199 JS_ASSERT(callerPC
&& js_GetOpcode(cx
, caller
->script(), callerPC
) == JSOP_EVAL
);
1202 /* Pretend that we're top level. */
1205 JS_ASSERT(scopeobj
== scopeobj
->getGlobal());
1206 JS_ASSERT(scopeobj
->isGlobal());
1209 /* Ensure we compile this eval with the right object in the scope chain. */
1210 if (!CheckScopeChainValidity(cx
, scopeobj
))
1213 JSLinearString
*linearStr
= str
->ensureLinear(cx
);
1216 const jschar
*chars
= linearStr
->chars();
1217 size_t length
= linearStr
->length();
1220 * If the eval string starts with '(' and ends with ')', it may be JSON.
1221 * Try the JSON parser first because it's much faster. If the eval string
1222 * isn't JSON, JSON parsing will probably fail quickly, so little time
1225 if (length
> 2 && chars
[0] == '(' && chars
[length
- 1] == ')') {
1226 JSONParser
*jp
= js_BeginJSONParse(cx
, vp
, /* suppressErrors = */true);
1228 /* Run JSON-parser on string inside ( and ). */
1229 bool ok
= js_ConsumeJSONText(cx
, jp
, chars
+ 1, length
- 2);
1230 ok
&= js_FinishJSONParse(cx
, jp
, NullValue());
1237 * Direct calls to eval are supposed to see the caller's |this|. If we
1238 * haven't wrapped that yet, do so now, before we make a copy of it for
1239 * the eval code to use.
1241 if (evalType
== DIRECT_EVAL
&& !caller
->computeThis(cx
))
1244 JSScript
*script
= NULL
;
1245 JSScript
**bucket
= EvalCacheHash(cx
, linearStr
);
1246 if (evalType
== DIRECT_EVAL
&& caller
->isFunctionFrame() && !caller
->isEvalFrame())
1247 script
= EvalCacheLookup(cx
, linearStr
, caller
, staticLevel
, principals
, scopeobj
, bucket
);
1250 * We can't have a callerFrame (down in js::Execute's terms) if we're in
1251 * global code (or if we're an indirect eval).
1253 JSStackFrame
*callerFrame
= (staticLevel
!= 0) ? caller
: NULL
;
1256 const char *filename
= js_ComputeFilename(cx
, caller
, principals
, &lineno
);
1258 uint32 tcflags
= TCF_COMPILE_N_GO
| TCF_NEED_MUTABLE_SCRIPT
| TCF_COMPILE_FOR_EVAL
;
1259 script
= Compiler::compileScript(cx
, scopeobj
, callerFrame
,
1260 principals
, tcflags
, chars
, length
,
1261 filename
, lineno
, linearStr
, staticLevel
);
1266 assertSameCompartment(cx
, scopeobj
, script
);
1269 * Belt-and-braces: check that the lesser of eval's principals and the
1270 * caller's principals has access to scopeobj.
1272 JSBool ok
= js_CheckPrincipalsAccess(cx
, scopeobj
, principals
,
1273 cx
->runtime
->atomState
.evalAtom
) &&
1274 Execute(cx
, scopeobj
, script
, callerFrame
, JSFRAME_EVAL
, vp
);
1276 script
->u
.nextToGC
= *bucket
;
1278 #ifdef CHECK_SCRIPT_OWNER
1279 script
->owner
= NULL
;
1286 IsBuiltinEvalFunction(JSFunction
*fun
)
1288 return fun
->maybeNative() == eval
;
1293 #if JS_HAS_OBJ_WATCHPOINT
1296 obj_watch_handler(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval old
,
1297 jsval
*nvp
, void *closure
)
1300 JSSecurityCallbacks
*callbacks
;
1301 JSStackFrame
*caller
;
1302 JSPrincipals
*subject
, *watcher
;
1304 JSResolvingEntry
*entry
;
1309 callable
= (JSObject
*) closure
;
1311 callbacks
= JS_GetSecurityCallbacks(cx
);
1312 if (callbacks
&& callbacks
->findObjectPrincipals
) {
1313 /* Skip over any obj_watch_* frames between us and the real subject. */
1314 caller
= js_GetScriptedCaller(cx
, NULL
);
1317 * Only call the watch handler if the watcher is allowed to watch
1318 * the currently executing script.
1320 watcher
= callbacks
->findObjectPrincipals(cx
, callable
);
1321 subject
= js_StackFramePrincipals(cx
, caller
);
1323 if (watcher
&& subject
&& !watcher
->subsume(watcher
, subject
)) {
1324 /* Silently don't call the watch handler. */
1330 /* Avoid recursion on (obj, id) already being watched on cx. */
1333 if (!js_StartResolving(cx
, &key
, JSRESFLAG_WATCH
, &entry
))
1337 generation
= cx
->resolvingTable
->generation
;
1339 argv
[0] = IdToValue(id
);
1340 argv
[1] = Valueify(old
);
1341 argv
[2] = Valueify(*nvp
);
1342 ok
= ExternalInvoke(cx
, obj
, ObjectOrNullValue(callable
), 3, argv
, Valueify(nvp
));
1343 js_StopResolving(cx
, &key
, JSRESFLAG_WATCH
, entry
, generation
);
1348 obj_watch(JSContext
*cx
, uintN argc
, Value
*vp
)
1351 js_ReportMissingArg(cx
, *vp
, 1);
1355 JSObject
*callable
= js_ValueToCallableObject(cx
, &vp
[3], 0);
1359 /* Compute the unique int/atom symbol id needed by js_LookupProperty. */
1361 if (!ValueToId(cx
, vp
[2], &propid
))
1364 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1367 if (!obj
|| !CheckAccess(cx
, obj
, propid
, JSACC_WATCH
, &tmp
, &attrs
))
1372 if (attrs
& JSPROP_READONLY
)
1374 if (obj
->isDenseArray() && !obj
->makeDenseArraySlow(cx
))
1376 return JS_SetWatchPoint(cx
, obj
, propid
, obj_watch_handler
, callable
);
1380 obj_unwatch(JSContext
*cx
, uintN argc
, Value
*vp
)
1382 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1388 if (!ValueToId(cx
, vp
[2], &id
))
1393 return JS_ClearWatchPoint(cx
, obj
, id
, NULL
, NULL
);
1396 #endif /* JS_HAS_OBJ_WATCHPOINT */
1399 * Prototype and property query methods, to complement the 'in' and
1400 * 'instanceof' operators.
1403 /* Proposed ECMA 15.2.4.5. */
1405 obj_hasOwnProperty(JSContext
*cx
, uintN argc
, Value
*vp
)
1407 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1409 js_HasOwnPropertyHelper(cx
, obj
->getOps()->lookupProperty
, argc
, vp
);
1413 js_HasOwnPropertyHelper(JSContext
*cx
, LookupPropOp lookup
, uintN argc
,
1417 if (!ValueToId(cx
, argc
!= 0 ? vp
[2] : UndefinedValue(), &id
))
1420 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1425 if (obj
->isProxy()) {
1427 if (!JSProxy::hasOwn(cx
, obj
, id
, &has
))
1429 vp
->setBoolean(has
);
1432 if (!js_HasOwnProperty(cx
, lookup
, obj
, id
, &obj2
, &prop
))
1434 vp
->setBoolean(!!prop
);
1439 js_HasOwnProperty(JSContext
*cx
, LookupPropOp lookup
, JSObject
*obj
, jsid id
,
1440 JSObject
**objp
, JSProperty
**propp
)
1442 JSAutoResolveFlags
rf(cx
, JSRESOLVE_QUALIFIED
| JSRESOLVE_DETECTING
);
1443 if (!(lookup
? lookup
: js_LookupProperty
)(cx
, obj
, id
, objp
, propp
))
1451 Class
*clasp
= (*objp
)->getClass();
1452 JSObject
*outer
= NULL
;
1453 if (JSObjectOp op
= (*objp
)->getClass()->ext
.outerObject
) {
1454 outer
= op(cx
, *objp
);
1459 if (outer
!= *objp
) {
1460 if ((*objp
)->isNative() && obj
->getClass() == clasp
) {
1462 * The combination of JSPROP_SHARED and JSPROP_PERMANENT in a
1463 * delegated property makes that property appear to be direct in
1464 * all delegating instances of the same native class. This hack
1465 * avoids bloating every function instance with its own 'length'
1466 * (AKA 'arity') property. But it must not extend across class
1467 * boundaries, to avoid making hasOwnProperty lie (bug 320854).
1469 * It's not really a hack, of course: a permanent property can't
1470 * be deleted, and JSPROP_SHARED means "don't allocate a slot in
1471 * any instance, prototype or delegating". Without a slot, and
1472 * without the ability to remove and recreate (with differences)
1473 * the property, there is no way to tell whether it is directly
1474 * owned, or indirectly delegated.
1476 Shape
*shape
= reinterpret_cast<Shape
*>(*propp
);
1477 if (shape
->isSharedPermanent())
1486 /* Proposed ECMA 15.2.4.6. */
1488 obj_isPrototypeOf(JSContext
*cx
, uintN argc
, Value
*vp
)
1490 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1493 const Value
&v
= argc
!= 0 ? vp
[2] : UndefinedValue();
1494 vp
->setBoolean(js_IsDelegate(cx
, obj
, v
));
1498 /* Proposed ECMA 15.2.4.7. */
1500 obj_propertyIsEnumerable(JSContext
*cx
, uintN argc
, Value
*vp
)
1503 if (!ValueToId(cx
, argc
!= 0 ? vp
[2] : UndefinedValue(), &id
))
1506 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1507 return obj
&& js_PropertyIsEnumerable(cx
, obj
, id
, vp
);
1511 js_PropertyIsEnumerable(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
1515 if (!obj
->lookupProperty(cx
, id
, &pobj
, &prop
))
1519 vp
->setBoolean(false);
1524 * XXX ECMA spec error compatible: return false unless hasOwnProperty.
1525 * The ECMA spec really should be fixed so propertyIsEnumerable and the
1526 * for..in loop agree on whether prototype properties are enumerable,
1527 * obviously by fixing this method (not by breaking the for..in loop!).
1529 * We check here for shared permanent prototype properties, which should
1530 * be treated as if they are local to obj. They are an implementation
1531 * technique used to satisfy ECMA requirements; users should not be able
1532 * to distinguish a shared permanent proto-property from a local one.
1536 if (pobj
->isNative()) {
1537 Shape
*shape
= (Shape
*) prop
;
1538 shared
= shape
->isSharedPermanent();
1539 attrs
= shape
->attributes();
1542 if (!pobj
->getAttributes(cx
, id
, &attrs
))
1545 if (pobj
!= obj
&& !shared
) {
1546 vp
->setBoolean(false);
1549 vp
->setBoolean((attrs
& JSPROP_ENUMERATE
) != 0);
1553 #if OLD_GETTER_SETTER_METHODS
1555 const char js_defineGetter_str
[] = "__defineGetter__";
1556 const char js_defineSetter_str
[] = "__defineSetter__";
1557 const char js_lookupGetter_str
[] = "__lookupGetter__";
1558 const char js_lookupSetter_str
[] = "__lookupSetter__";
1560 JS_FRIEND_API(JSBool
)
1561 js_obj_defineGetter(JSContext
*cx
, uintN argc
, Value
*vp
)
1563 if (argc
<= 1 || !js_IsCallable(vp
[3])) {
1564 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1565 JSMSG_BAD_GETTER_OR_SETTER
,
1569 PropertyOp getter
= CastAsPropertyOp(&vp
[3].toObject());
1572 if (!ValueToId(cx
, vp
[2], &id
))
1574 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1575 if (!obj
|| !CheckRedeclaration(cx
, obj
, id
, JSPROP_GETTER
, NULL
, NULL
))
1578 * Getters and setters are just like watchpoints from an access
1579 * control point of view.
1583 if (!CheckAccess(cx
, obj
, id
, JSACC_WATCH
, &junk
, &attrs
))
1586 return obj
->defineProperty(cx
, id
, UndefinedValue(), getter
, PropertyStub
,
1587 JSPROP_ENUMERATE
| JSPROP_GETTER
| JSPROP_SHARED
);
1590 JS_FRIEND_API(JSBool
)
1591 js_obj_defineSetter(JSContext
*cx
, uintN argc
, Value
*vp
)
1593 if (argc
<= 1 || !js_IsCallable(vp
[3])) {
1594 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1595 JSMSG_BAD_GETTER_OR_SETTER
,
1599 PropertyOp setter
= CastAsPropertyOp(&vp
[3].toObject());
1602 if (!ValueToId(cx
, vp
[2], &id
))
1604 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1605 if (!obj
|| !CheckRedeclaration(cx
, obj
, id
, JSPROP_SETTER
, NULL
, NULL
))
1608 * Getters and setters are just like watchpoints from an access
1609 * control point of view.
1613 if (!CheckAccess(cx
, obj
, id
, JSACC_WATCH
, &junk
, &attrs
))
1616 return obj
->defineProperty(cx
, id
, UndefinedValue(), PropertyStub
, setter
,
1617 JSPROP_ENUMERATE
| JSPROP_SETTER
| JSPROP_SHARED
);
1621 obj_lookupGetter(JSContext
*cx
, uintN argc
, Value
*vp
)
1624 if (!ValueToId(cx
, argc
!= 0 ? vp
[2] : UndefinedValue(), &id
))
1626 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1629 if (!obj
|| !obj
->lookupProperty(cx
, id
, &pobj
, &prop
))
1633 if (pobj
->isNative()) {
1634 Shape
*shape
= (Shape
*) prop
;
1635 if (shape
->hasGetterValue())
1636 *vp
= shape
->getterValue();
1643 obj_lookupSetter(JSContext
*cx
, uintN argc
, Value
*vp
)
1646 if (!ValueToId(cx
, argc
!= 0 ? vp
[2] : UndefinedValue(), &id
))
1648 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1651 if (!obj
|| !obj
->lookupProperty(cx
, id
, &pobj
, &prop
))
1655 if (pobj
->isNative()) {
1656 Shape
*shape
= (Shape
*) prop
;
1657 if (shape
->hasSetterValue())
1658 *vp
= shape
->setterValue();
1663 #endif /* OLD_GETTER_SETTER_METHODS */
1666 obj_getPrototypeOf(JSContext
*cx
, uintN argc
, Value
*vp
)
1669 js_ReportMissingArg(cx
, *vp
, 0);
1673 if (vp
[2].isPrimitive()) {
1674 char *bytes
= DecompileValueGenerator(cx
, JSDVG_SEARCH_STACK
, vp
[2], NULL
);
1677 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1678 JSMSG_UNEXPECTED_TYPE
, bytes
, "not an object");
1683 JSObject
*obj
= &vp
[2].toObject();
1685 return CheckAccess(cx
, obj
, ATOM_TO_JSID(cx
->runtime
->atomState
.protoAtom
),
1686 JSACC_PROTO
, vp
, &attrs
);
1690 js_NewPropertyDescriptorObject(JSContext
*cx
, jsid id
, uintN attrs
,
1691 const Value
&getter
, const Value
&setter
,
1692 const Value
&value
, Value
*vp
)
1694 /* We have our own property, so start creating the descriptor. */
1695 JSObject
*desc
= NewBuiltinClassInstance(cx
, &js_ObjectClass
);
1698 vp
->setObject(*desc
); /* Root and return. */
1700 const JSAtomState
&atomState
= cx
->runtime
->atomState
;
1701 if (attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)) {
1702 if (!desc
->defineProperty(cx
, ATOM_TO_JSID(atomState
.getAtom
), getter
,
1703 PropertyStub
, PropertyStub
, JSPROP_ENUMERATE
) ||
1704 !desc
->defineProperty(cx
, ATOM_TO_JSID(atomState
.setAtom
), setter
,
1705 PropertyStub
, PropertyStub
, JSPROP_ENUMERATE
)) {
1709 if (!desc
->defineProperty(cx
, ATOM_TO_JSID(atomState
.valueAtom
), value
,
1710 PropertyStub
, PropertyStub
, JSPROP_ENUMERATE
) ||
1711 !desc
->defineProperty(cx
, ATOM_TO_JSID(atomState
.writableAtom
),
1712 BooleanValue((attrs
& JSPROP_READONLY
) == 0),
1713 PropertyStub
, PropertyStub
, JSPROP_ENUMERATE
)) {
1718 return desc
->defineProperty(cx
, ATOM_TO_JSID(atomState
.enumerableAtom
),
1719 BooleanValue((attrs
& JSPROP_ENUMERATE
) != 0),
1720 PropertyStub
, PropertyStub
, JSPROP_ENUMERATE
) &&
1721 desc
->defineProperty(cx
, ATOM_TO_JSID(atomState
.configurableAtom
),
1722 BooleanValue((attrs
& JSPROP_PERMANENT
) == 0),
1723 PropertyStub
, PropertyStub
, JSPROP_ENUMERATE
);
1727 js_GetOwnPropertyDescriptor(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
1730 return JSProxy::getOwnPropertyDescriptor(cx
, obj
, id
, false, vp
);
1734 if (!js_HasOwnProperty(cx
, obj
->getOps()->lookupProperty
, obj
, id
, &pobj
, &prop
))
1741 Value roots
[] = { UndefinedValue(), UndefinedValue(), UndefinedValue() };
1742 AutoArrayRooter
tvr(cx
, JS_ARRAY_LENGTH(roots
), roots
);
1745 if (pobj
->isNative()) {
1746 Shape
*shape
= (Shape
*) prop
;
1747 attrs
= shape
->attributes();
1748 if (attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)) {
1750 if (attrs
& JSPROP_GETTER
)
1751 roots
[0] = shape
->getterValue();
1752 if (attrs
& JSPROP_SETTER
)
1753 roots
[1] = shape
->setterValue();
1756 if (!pobj
->getAttributes(cx
, id
, &attrs
))
1760 if (doGet
&& !obj
->getProperty(cx
, id
, &roots
[2]))
1763 return js_NewPropertyDescriptorObject(cx
, id
,
1765 roots
[0], /* getter */
1766 roots
[1], /* setter */
1767 roots
[2], /* value */
1772 GetFirstArgumentAsObject(JSContext
*cx
, uintN argc
, Value
*vp
, const char *method
, JSObject
**objp
)
1775 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_MORE_ARGS_NEEDED
,
1780 const Value
&v
= vp
[2];
1781 if (!v
.isObject()) {
1782 char *bytes
= DecompileValueGenerator(cx
, JSDVG_SEARCH_STACK
, v
, NULL
);
1785 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_UNEXPECTED_TYPE
,
1786 bytes
, "not an object");
1791 *objp
= &v
.toObject();
1796 obj_getOwnPropertyDescriptor(JSContext
*cx
, uintN argc
, Value
*vp
)
1799 if (!GetFirstArgumentAsObject(cx
, argc
, vp
, "Object.getOwnPropertyDescriptor", &obj
))
1801 AutoIdRooter
nameidr(cx
);
1802 if (!ValueToId(cx
, argc
>= 2 ? vp
[3] : UndefinedValue(), nameidr
.addr()))
1804 return js_GetOwnPropertyDescriptor(cx
, obj
, nameidr
.id(), vp
);
1808 obj_keys(JSContext
*cx
, uintN argc
, Value
*vp
)
1811 if (!GetFirstArgumentAsObject(cx
, argc
, vp
, "Object.keys", &obj
))
1814 AutoIdVector
props(cx
);
1815 if (!GetPropertyNames(cx
, obj
, JSITER_OWNONLY
, &props
))
1818 AutoValueVector
vals(cx
);
1819 if (!vals
.reserve(props
.length()))
1821 for (size_t i
= 0, len
= props
.length(); i
< len
; i
++) {
1823 if (JSID_IS_STRING(id
)) {
1824 JS_ALWAYS_TRUE(vals
.append(StringValue(JSID_TO_STRING(id
))));
1825 } else if (JSID_IS_INT(id
)) {
1826 JSString
*str
= js_IntToString(cx
, JSID_TO_INT(id
));
1829 JS_ALWAYS_TRUE(vals
.append(StringValue(str
)));
1831 JS_ASSERT(JSID_IS_OBJECT(id
));
1835 JS_ASSERT(props
.length() <= UINT32_MAX
);
1836 JSObject
*aobj
= NewDenseCopiedArray(cx
, jsuint(vals
.length()), vals
.begin());
1839 vp
->setObject(*aobj
);
1845 HasProperty(JSContext
* cx
, JSObject
* obj
, jsid id
, Value
* vp
, bool *foundp
)
1847 if (!obj
->hasProperty(cx
, id
, foundp
, JSRESOLVE_QUALIFIED
| JSRESOLVE_DETECTING
))
1855 * We must go through the method read barrier in case id is 'get' or 'set'.
1856 * There is no obvious way to defer cloning a joined function object whose
1857 * identity will be used by DefinePropertyOnObject, e.g., or reflected via
1858 * js_GetOwnPropertyDescriptor, as the getter or setter callable object.
1860 return !!obj
->getProperty(cx
, id
, vp
);
1863 PropDesc::PropDesc()
1864 : pd(UndefinedValue()),
1866 value(UndefinedValue()),
1867 get(UndefinedValue()),
1868 set(UndefinedValue()),
1874 hasEnumerable(false),
1875 hasConfigurable(false)
1880 PropDesc::initialize(JSContext
* cx
, jsid id
, const Value
&origval
)
1886 if (v
.isPrimitive()) {
1887 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_NOT_NONNULL_OBJECT
);
1890 JSObject
* desc
= &v
.toObject();
1892 /* Make a copy of the descriptor. We might need it later. */
1895 /* Start with the proper defaults. */
1896 attrs
= JSPROP_PERMANENT
| JSPROP_READONLY
;
1901 #ifdef __GNUC__ /* quell GCC overwarning */
1904 if (!HasProperty(cx
, desc
, ATOM_TO_JSID(cx
->runtime
->atomState
.enumerableAtom
), &v
, &found
))
1907 hasEnumerable
= JS_TRUE
;
1908 if (js_ValueToBoolean(v
))
1909 attrs
|= JSPROP_ENUMERATE
;
1913 if (!HasProperty(cx
, desc
, ATOM_TO_JSID(cx
->runtime
->atomState
.configurableAtom
), &v
, &found
))
1916 hasConfigurable
= JS_TRUE
;
1917 if (js_ValueToBoolean(v
))
1918 attrs
&= ~JSPROP_PERMANENT
;
1922 if (!HasProperty(cx
, desc
, ATOM_TO_JSID(cx
->runtime
->atomState
.valueAtom
), &v
, &found
))
1930 if (!HasProperty(cx
, desc
, ATOM_TO_JSID(cx
->runtime
->atomState
.writableAtom
), &v
, &found
))
1933 hasWritable
= JS_TRUE
;
1934 if (js_ValueToBoolean(v
))
1935 attrs
&= ~JSPROP_READONLY
;
1939 if (!HasProperty(cx
, desc
, ATOM_TO_JSID(cx
->runtime
->atomState
.getAtom
), &v
, &found
))
1942 if ((v
.isPrimitive() || !js_IsCallable(v
)) && !v
.isUndefined()) {
1943 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_BAD_GET_SET_FIELD
,
1949 attrs
|= JSPROP_GETTER
| JSPROP_SHARED
;
1953 if (!HasProperty(cx
, desc
, ATOM_TO_JSID(cx
->runtime
->atomState
.setAtom
), &v
, &found
))
1956 if ((v
.isPrimitive() || !js_IsCallable(v
)) && !v
.isUndefined()) {
1957 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_BAD_GET_SET_FIELD
,
1963 attrs
|= JSPROP_SETTER
| JSPROP_SHARED
;
1967 if ((hasGet
|| hasSet
) && (hasValue
|| hasWritable
)) {
1968 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_INVALID_DESCRIPTOR
);
1976 Reject(JSContext
*cx
, uintN errorNumber
, bool throwError
, jsid id
, bool *rval
)
1980 if (!js_ValueToStringId(cx
, IdToValue(id
), &idstr
))
1982 JSAutoByteString
bytes(cx
, JSID_TO_STRING(idstr
));
1985 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, errorNumber
, bytes
.ptr());
1994 Reject(JSContext
*cx
, JSObject
*obj
, uintN errorNumber
, bool throwError
, bool *rval
)
1997 if (js_ErrorFormatString
[errorNumber
].argCount
== 1) {
1998 js_ReportValueErrorFlags(cx
, JSREPORT_ERROR
, errorNumber
,
1999 JSDVG_IGNORE_STACK
, ObjectValue(*obj
),
2002 JS_ASSERT(js_ErrorFormatString
[errorNumber
].argCount
== 0);
2003 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, errorNumber
);
2013 DefinePropertyOnObject(JSContext
*cx
, JSObject
*obj
, const PropDesc
&desc
,
2014 bool throwError
, bool *rval
)
2016 /* 8.12.9 step 1. */
2017 JSProperty
*current
;
2019 JS_ASSERT(!obj
->getOps()->lookupProperty
);
2020 if (!js_HasOwnProperty(cx
, NULL
, obj
, desc
.id
, &obj2
, ¤t
))
2023 JS_ASSERT(!obj
->getOps()->defineProperty
);
2026 * If we find a shared permanent property in a different object obj2 from
2027 * obj, then if the property is shared permanent (an old hack to optimize
2028 * per-object properties into one prototype property), ignore that lookup
2029 * result (null current).
2031 * FIXME: bug 575997 (see also bug 607863).
2033 if (current
&& obj2
!= obj
&& obj2
->isNative()) {
2034 /* See same assertion with comment further below. */
2035 JS_ASSERT(obj2
->getClass() == obj
->getClass());
2037 Shape
*shape
= (Shape
*) current
;
2038 if (shape
->isSharedPermanent())
2042 /* 8.12.9 steps 2-4. */
2044 if (!obj
->isExtensible())
2045 return Reject(cx
, obj
, JSMSG_OBJECT_NOT_EXTENSIBLE
, throwError
, rval
);
2049 if (desc
.isGenericDescriptor() || desc
.isDataDescriptor()) {
2050 JS_ASSERT(!obj
->getOps()->defineProperty
);
2051 return js_DefineProperty(cx
, obj
, desc
.id
, &desc
.value
,
2052 PropertyStub
, PropertyStub
, desc
.attrs
);
2055 JS_ASSERT(desc
.isAccessorDescriptor());
2058 * Getters and setters are just like watchpoints from an access
2059 * control point of view.
2063 if (!CheckAccess(cx
, obj
, desc
.id
, JSACC_WATCH
, &dummy
, &dummyAttrs
))
2066 Value tmp
= UndefinedValue();
2067 return js_DefineProperty(cx
, obj
, desc
.id
, &tmp
,
2068 desc
.getter(), desc
.setter(), desc
.attrs
);
2071 /* 8.12.9 steps 5-6 (note 5 is merely a special case of 6). */
2072 Value v
= UndefinedValue();
2075 * In the special case of shared permanent properties, the "own" property
2076 * can be found on a different object. In that case the returned property
2077 * might not be native, except: the shared permanent property optimization
2078 * is not applied if the objects have different classes (bug 320854), as
2079 * must be enforced by js_HasOwnProperty for the Shape cast below to be
2082 JS_ASSERT(obj
->getClass() == obj2
->getClass());
2084 const Shape
*shape
= reinterpret_cast<Shape
*>(current
);
2086 if (desc
.isAccessorDescriptor()) {
2087 if (!shape
->isAccessorDescriptor())
2092 if (!SameValue(cx
, desc
.getterValue(), shape
->getterOrUndefined(), &same
))
2100 if (!SameValue(cx
, desc
.setterValue(), shape
->setterOrUndefined(), &same
))
2107 * Determine the current value of the property once, if the current
2108 * value might actually need to be used or preserved later. NB: we
2109 * guard on whether the current property is a data descriptor to
2110 * avoid calling a getter; we won't need the value if it's not a
2113 if (shape
->isDataDescriptor()) {
2115 * Non-standard: if the property is non-configurable and is
2116 * represented by a native getter or setter, don't permit
2117 * redefinition. We expose properties with native getter/setter
2118 * as though they were data properties, for the most part, but
2119 * in this particular case we must worry about integrity
2120 * concerns for JSAPI users who expected that
2121 * permanent+getter/setter means precisely controlled behavior.
2122 * If we permitted such redefinitions, such a property could be
2123 * "fixed" to some specific previous value, no longer varying
2124 * according to the intent of the native getter/setter for the
2127 * Other engines expose properties of this nature using ECMA
2128 * getter/setter pairs, but we can't because we use them even
2129 * for properties which ECMA specifies as being true data
2130 * descriptors ([].length, Function.length, /regex/.lastIndex,
2131 * &c.). Longer-term perhaps we should convert such properties
2132 * to use data descriptors (at which point representing a
2133 * descriptor with native getter/setter as an accessor
2134 * descriptor would be fine) and take a small memory hit, but
2135 * for now we'll simply forbid their redefinition.
2137 if (!shape
->configurable() &&
2138 (!shape
->hasDefaultGetter() || !shape
->hasDefaultSetter())) {
2139 return Reject(cx
, JSMSG_CANT_REDEFINE_PROP
, throwError
, desc
.id
, rval
);
2142 if (!js_NativeGet(cx
, obj
, obj2
, shape
, JSGET_NO_METHOD_BARRIER
, &v
)) {
2143 /* current was dropped when the failure occurred. */
2148 if (desc
.isDataDescriptor()) {
2149 if (!shape
->isDataDescriptor())
2153 if (desc
.hasValue
) {
2154 if (!SameValue(cx
, desc
.value
, v
, &same
))
2159 if (desc
.hasWritable
&& desc
.writable() != shape
->writable())
2162 /* The only fields in desc will be handled below. */
2163 JS_ASSERT(desc
.isGenericDescriptor());
2167 if (desc
.hasConfigurable
&& desc
.configurable() != shape
->configurable())
2169 if (desc
.hasEnumerable
&& desc
.enumerable() != shape
->enumerable())
2172 /* The conditions imposed by step 5 or step 6 apply. */
2177 /* 8.12.9 step 7. */
2178 if (!shape
->configurable()) {
2180 * Since [[Configurable]] defaults to false, we don't need to check
2181 * whether it was specified. We can't do likewise for [[Enumerable]]
2182 * because its putative value is used in a comparison -- a comparison
2183 * whose result must always be false per spec if the [[Enumerable]]
2184 * field is not present. Perfectly pellucid logic, eh?
2186 JS_ASSERT_IF(!desc
.hasConfigurable
, !desc
.configurable());
2187 if (desc
.configurable() ||
2188 (desc
.hasEnumerable
&& desc
.enumerable() != shape
->enumerable())) {
2189 return Reject(cx
, JSMSG_CANT_REDEFINE_PROP
, throwError
, desc
.id
, rval
);
2193 bool callDelProperty
= false;
2195 if (desc
.isGenericDescriptor()) {
2196 /* 8.12.9 step 8, no validation required */
2197 } else if (desc
.isDataDescriptor() != shape
->isDataDescriptor()) {
2198 /* 8.12.9 step 9. */
2199 if (!shape
->configurable())
2200 return Reject(cx
, JSMSG_CANT_REDEFINE_PROP
, throwError
, desc
.id
, rval
);
2201 } else if (desc
.isDataDescriptor()) {
2202 /* 8.12.9 step 10. */
2203 JS_ASSERT(shape
->isDataDescriptor());
2204 if (!shape
->configurable() && !shape
->writable()) {
2205 if (desc
.hasWritable
&& desc
.writable())
2206 return Reject(cx
, JSMSG_CANT_REDEFINE_PROP
, throwError
, desc
.id
, rval
);
2207 if (desc
.hasValue
) {
2209 if (!SameValue(cx
, desc
.value
, v
, &same
))
2212 return Reject(cx
, JSMSG_CANT_REDEFINE_PROP
, throwError
, desc
.id
, rval
);
2216 callDelProperty
= !shape
->hasDefaultGetter() || !shape
->hasDefaultSetter();
2218 /* 8.12.9 step 11. */
2219 JS_ASSERT(desc
.isAccessorDescriptor() && shape
->isAccessorDescriptor());
2220 if (!shape
->configurable()) {
2223 if (!SameValue(cx
, desc
.setterValue(), shape
->setterOrUndefined(), &same
))
2226 return Reject(cx
, JSMSG_CANT_REDEFINE_PROP
, throwError
, desc
.id
, rval
);
2231 if (!SameValue(cx
, desc
.getterValue(), shape
->getterOrUndefined(), &same
))
2234 return Reject(cx
, JSMSG_CANT_REDEFINE_PROP
, throwError
, desc
.id
, rval
);
2239 /* 8.12.9 step 12. */
2241 PropertyOp getter
, setter
;
2242 if (desc
.isGenericDescriptor()) {
2244 if (desc
.hasConfigurable
)
2245 changed
|= JSPROP_PERMANENT
;
2246 if (desc
.hasEnumerable
)
2247 changed
|= JSPROP_ENUMERATE
;
2249 attrs
= (shape
->attributes() & ~changed
) | (desc
.attrs
& changed
);
2250 if (shape
->isMethod()) {
2251 JS_ASSERT(!(attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)));
2252 getter
= setter
= PropertyStub
;
2254 getter
= shape
->getter();
2255 setter
= shape
->setter();
2257 } else if (desc
.isDataDescriptor()) {
2258 uintN unchanged
= 0;
2259 if (!desc
.hasConfigurable
)
2260 unchanged
|= JSPROP_PERMANENT
;
2261 if (!desc
.hasEnumerable
)
2262 unchanged
|= JSPROP_ENUMERATE
;
2263 if (!desc
.hasWritable
)
2264 unchanged
|= JSPROP_READONLY
;
2268 attrs
= (desc
.attrs
& ~unchanged
) | (shape
->attributes() & unchanged
);
2269 getter
= setter
= PropertyStub
;
2271 JS_ASSERT(desc
.isAccessorDescriptor());
2274 * Getters and setters are just like watchpoints from an access
2275 * control point of view.
2278 if (!CheckAccess(cx
, obj2
, desc
.id
, JSACC_WATCH
, &dummy
, &attrs
))
2281 JS_ASSERT_IF(shape
->isMethod(), !(attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)));
2283 /* 8.12.9 step 12. */
2285 if (desc
.hasConfigurable
)
2286 changed
|= JSPROP_PERMANENT
;
2287 if (desc
.hasEnumerable
)
2288 changed
|= JSPROP_ENUMERATE
;
2290 changed
|= JSPROP_GETTER
| JSPROP_SHARED
;
2292 changed
|= JSPROP_SETTER
| JSPROP_SHARED
;
2294 attrs
= (desc
.attrs
& changed
) | (shape
->attributes() & ~changed
);
2296 getter
= desc
.getter();
2298 getter
= (shape
->isMethod() || (shape
->hasDefaultGetter() && !shape
->hasGetterValue()))
2303 setter
= desc
.setter();
2305 setter
= (shape
->hasDefaultSetter() && !shape
->hasSetterValue())
2314 * Since "data" properties implemented using native C functions may rely on
2315 * side effects during setting, we must make them aware that they have been
2316 * "assigned"; deleting the property before redefining it does the trick.
2317 * See bug 539766, where we ran into problems when we redefined
2318 * arguments.length without making the property aware that its value had
2319 * been changed (which would have happened if we had deleted it before
2320 * redefining it or we had invoked its setter to change its value).
2322 if (callDelProperty
) {
2324 if (!CallJSPropertyOp(cx
, obj2
->getClass()->delProperty
, obj2
, desc
.id
, &dummy
))
2328 return js_DefineProperty(cx
, obj
, desc
.id
, &v
, getter
, setter
, attrs
);
2332 DefinePropertyOnArray(JSContext
*cx
, JSObject
*obj
, const PropDesc
&desc
,
2333 bool throwError
, bool *rval
)
2336 * We probably should optimize dense array property definitions where
2337 * the descriptor describes a traditional array property (enumerable,
2338 * configurable, writable, numeric index or length without altering its
2339 * attributes). Such definitions are probably unlikely, so we don't bother
2342 if (obj
->isDenseArray() && !obj
->makeDenseArraySlow(cx
))
2345 jsuint oldLen
= obj
->getArrayLength();
2347 if (JSID_IS_ATOM(desc
.id
, cx
->runtime
->atomState
.lengthAtom
)) {
2349 * Our optimization of storage of the length property of arrays makes
2350 * it very difficult to properly implement defining the property. For
2351 * now simply throw an exception (NB: not merely Reject) on any attempt
2352 * to define the "length" property, rather than attempting to implement
2353 * some difficult-for-authors-to-grasp subset of that functionality.
2355 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_CANT_DEFINE_ARRAY_LENGTH
);
2360 if (js_IdIsIndex(desc
.id
, &index
)) {
2362 // Disabled until we support defining "length":
2363 if (index >= oldLen && lengthPropertyNotWritable())
2364 return ThrowTypeError(cx, JSMSG_CANT_APPEND_TO_ARRAY);
2366 if (!DefinePropertyOnObject(cx
, obj
, desc
, false, rval
))
2369 return Reject(cx
, obj
, JSMSG_CANT_DEFINE_ARRAY_INDEX
, throwError
, rval
);
2371 if (index
>= oldLen
) {
2372 JS_ASSERT(index
!= UINT32_MAX
);
2373 obj
->setArrayLength(index
+ 1);
2380 return DefinePropertyOnObject(cx
, obj
, desc
, throwError
, rval
);
2384 DefineProperty(JSContext
*cx
, JSObject
*obj
, const PropDesc
&desc
, bool throwError
,
2388 return DefinePropertyOnArray(cx
, obj
, desc
, throwError
, rval
);
2390 if (obj
->getOps()->lookupProperty
) {
2392 return JSProxy::defineProperty(cx
, obj
, desc
.id
, desc
.pd
);
2393 return Reject(cx
, obj
, JSMSG_OBJECT_NOT_EXTENSIBLE
, throwError
, rval
);
2396 return DefinePropertyOnObject(cx
, obj
, desc
, throwError
, rval
);
2400 js_DefineOwnProperty(JSContext
*cx
, JSObject
*obj
, jsid id
,
2401 const Value
&descriptor
, JSBool
*bp
)
2403 AutoPropDescArrayRooter
descs(cx
);
2404 PropDesc
*desc
= descs
.append();
2405 if (!desc
|| !desc
->initialize(cx
, id
, descriptor
))
2409 if (!DefineProperty(cx
, obj
, *desc
, true, &rval
))
2415 /* ES5 15.2.3.6: Object.defineProperty(O, P, Attributes) */
2417 obj_defineProperty(JSContext
* cx
, uintN argc
, Value
* vp
)
2419 /* 15.2.3.6 steps 1 and 5. */
2421 if (!GetFirstArgumentAsObject(cx
, argc
, vp
, "Object.defineProperty", &obj
))
2423 vp
->setObject(*obj
);
2425 /* 15.2.3.6 step 2. */
2426 AutoIdRooter
nameidr(cx
);
2427 if (!ValueToId(cx
, argc
>= 2 ? vp
[3] : UndefinedValue(), nameidr
.addr()))
2430 /* 15.2.3.6 step 3. */
2431 const Value
&descval
= argc
>= 3 ? vp
[4] : UndefinedValue();
2433 /* 15.2.3.6 step 4 */
2435 return js_DefineOwnProperty(cx
, obj
, nameidr
.id(), descval
, &junk
);
2439 DefineProperties(JSContext
*cx
, JSObject
*obj
, JSObject
*props
)
2441 AutoIdArray
ida(cx
, JS_Enumerate(cx
, props
));
2445 AutoPropDescArrayRooter
descs(cx
);
2446 size_t len
= ida
.length();
2447 for (size_t i
= 0; i
< len
; i
++) {
2449 PropDesc
* desc
= descs
.append();
2450 AutoValueRooter
tvr(cx
);
2452 !JS_GetPropertyById(cx
, props
, id
, tvr
.jsval_addr()) ||
2453 !desc
->initialize(cx
, id
, tvr
.value())) {
2459 for (size_t i
= 0; i
< len
; i
++) {
2460 if (!DefineProperty(cx
, obj
, descs
[i
], true, &dummy
))
2468 js_PopulateObject(JSContext
*cx
, JSObject
*newborn
, JSObject
*props
)
2470 return DefineProperties(cx
, newborn
, props
);
2473 /* ES5 15.2.3.7: Object.defineProperties(O, Properties) */
2475 obj_defineProperties(JSContext
* cx
, uintN argc
, Value
* vp
)
2477 /* 15.2.3.6 steps 1 and 5. */
2479 if (!GetFirstArgumentAsObject(cx
, argc
, vp
, "Object.defineProperties", &obj
))
2481 vp
->setObject(*obj
);
2484 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_MORE_ARGS_NEEDED
,
2485 "Object.defineProperties", "0", "s");
2489 JSObject
* props
= js_ValueToNonNullObject(cx
, vp
[3]);
2492 vp
[3].setObject(*props
);
2494 return DefineProperties(cx
, obj
, props
);
2497 /* ES5 15.2.3.5: Object.create(O [, Properties]) */
2499 obj_create(JSContext
*cx
, uintN argc
, Value
*vp
)
2502 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_MORE_ARGS_NEEDED
,
2503 "Object.create", "0", "s");
2507 const Value
&v
= vp
[2];
2508 if (!v
.isObjectOrNull()) {
2509 char *bytes
= DecompileValueGenerator(cx
, JSDVG_SEARCH_STACK
, v
, NULL
);
2512 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_UNEXPECTED_TYPE
,
2513 bytes
, "not an object or null");
2519 * Use the callee's global as the parent of the new object to avoid dynamic
2520 * scoping (i.e., using the caller's global).
2522 JSObject
*obj
= NewNonFunction
<WithProto::Given
>(cx
, &js_ObjectClass
, v
.toObjectOrNull(),
2523 vp
->toObject().getGlobal());
2526 vp
->setObject(*obj
); /* Root and prepare for eventual return. */
2528 /* 15.2.3.5 step 4. */
2529 if (argc
> 1 && !vp
[3].isUndefined()) {
2530 if (vp
[3].isPrimitive()) {
2531 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_NOT_NONNULL_OBJECT
);
2535 JSObject
*props
= &vp
[3].toObject();
2536 AutoIdArray
ida(cx
, JS_Enumerate(cx
, props
));
2540 AutoPropDescArrayRooter
descs(cx
);
2541 size_t len
= ida
.length();
2542 for (size_t i
= 0; i
< len
; i
++) {
2544 PropDesc
*desc
= descs
.append();
2545 if (!desc
|| !JS_GetPropertyById(cx
, props
, id
, Jsvalify(&vp
[1])) ||
2546 !desc
->initialize(cx
, id
, vp
[1])) {
2552 for (size_t i
= 0; i
< len
; i
++) {
2553 if (!DefineProperty(cx
, obj
, descs
[i
], true, &dummy
))
2558 /* 5. Return obj. */
2563 obj_getOwnPropertyNames(JSContext
*cx
, uintN argc
, Value
*vp
)
2566 if (!GetFirstArgumentAsObject(cx
, argc
, vp
, "Object.getOwnPropertyNames", &obj
))
2569 AutoIdVector
keys(cx
);
2570 if (!GetPropertyNames(cx
, obj
, JSITER_OWNONLY
| JSITER_HIDDEN
, &keys
))
2573 AutoValueVector
vals(cx
);
2574 if (!vals
.resize(keys
.length()))
2577 for (size_t i
= 0, len
= keys
.length(); i
< len
; i
++) {
2579 if (JSID_IS_INT(id
)) {
2580 JSString
*str
= js_ValueToString(cx
, Int32Value(JSID_TO_INT(id
)));
2583 vals
[i
].setString(str
);
2584 } else if (JSID_IS_ATOM(id
)) {
2585 vals
[i
].setString(JSID_TO_STRING(id
));
2587 vals
[i
].setObject(*JSID_TO_OBJECT(id
));
2591 JSObject
*aobj
= NewDenseCopiedArray(cx
, vals
.length(), vals
.begin());
2595 vp
->setObject(*aobj
);
2600 obj_isExtensible(JSContext
*cx
, uintN argc
, Value
*vp
)
2603 if (!GetFirstArgumentAsObject(cx
, argc
, vp
, "Object.isExtensible", &obj
))
2606 vp
->setBoolean(obj
->isExtensible());
2611 obj_preventExtensions(JSContext
*cx
, uintN argc
, Value
*vp
)
2614 if (!GetFirstArgumentAsObject(cx
, argc
, vp
, "Object.preventExtensions", &obj
))
2617 vp
->setObject(*obj
);
2618 if (!obj
->isExtensible())
2621 AutoIdVector
props(cx
);
2622 return obj
->preventExtensions(cx
, &props
);
2626 JSObject::sealOrFreeze(JSContext
*cx
, ImmutabilityType it
)
2628 assertSameCompartment(cx
, this);
2629 JS_ASSERT(it
== SEAL
|| it
== FREEZE
);
2631 AutoIdVector
props(cx
);
2632 if (isExtensible()) {
2633 if (!preventExtensions(cx
, &props
))
2636 if (!GetPropertyNames(cx
, this, JSITER_HIDDEN
| JSITER_OWNONLY
, &props
))
2640 /* preventExtensions must slowify dense arrays, so we can assign to holes without checks. */
2641 JS_ASSERT(!isDenseArray());
2643 for (size_t i
= 0, len
= props
.length(); i
< len
; i
++) {
2647 if (!getAttributes(cx
, id
, &attrs
))
2650 /* Make all attributes permanent; if freezing, make data attributes read-only. */
2652 if (it
== FREEZE
&& !(attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)))
2653 new_attrs
= JSPROP_PERMANENT
| JSPROP_READONLY
;
2655 new_attrs
= JSPROP_PERMANENT
;
2657 /* If we already have the attributes we need, skip the setAttributes call. */
2658 if ((attrs
| new_attrs
) == attrs
)
2662 if (!setAttributes(cx
, id
, &attrs
))
2670 obj_freeze(JSContext
*cx
, uintN argc
, Value
*vp
)
2673 if (!GetFirstArgumentAsObject(cx
, argc
, vp
, "Object.freeze", &obj
))
2676 vp
->setObject(*obj
);
2678 return obj
->freeze(cx
);
2682 obj_isFrozen(JSContext
*cx
, uintN argc
, Value
*vp
)
2685 if (!GetFirstArgumentAsObject(cx
, argc
, vp
, "Object.preventExtensions", &obj
))
2688 vp
->setBoolean(false);
2690 if (obj
->isExtensible())
2691 return true; /* The JavaScript value returned is false. */
2693 AutoIdVector
props(cx
);
2694 if (!GetPropertyNames(cx
, obj
, JSITER_HIDDEN
| JSITER_OWNONLY
, &props
))
2697 for (size_t i
= 0, len
= props
.length(); i
< len
; i
++) {
2701 if (!obj
->getAttributes(cx
, id
, &attrs
))
2704 /* The property must be non-configurable and either read-only or an accessor. */
2705 if (!(attrs
& JSPROP_PERMANENT
) ||
2706 !(attrs
& (JSPROP_READONLY
| JSPROP_GETTER
| JSPROP_SETTER
)))
2707 return true; /* The JavaScript value returned is false. */
2710 /* It really was sealed, so return true. */
2711 vp
->setBoolean(true);
2716 obj_seal(JSContext
*cx
, uintN argc
, Value
*vp
)
2719 if (!GetFirstArgumentAsObject(cx
, argc
, vp
, "Object.seal", &obj
))
2722 vp
->setObject(*obj
);
2724 return obj
->seal(cx
);
2728 obj_isSealed(JSContext
*cx
, uintN argc
, Value
*vp
)
2731 if (!GetFirstArgumentAsObject(cx
, argc
, vp
, "Object.isSealed", &obj
))
2734 /* Assume not sealed until proven otherwise. */
2735 vp
->setBoolean(false);
2737 if (obj
->isExtensible())
2738 return true; /* The JavaScript value returned is false. */
2740 AutoIdVector
props(cx
);
2741 if (!GetPropertyNames(cx
, obj
, JSITER_HIDDEN
| JSITER_OWNONLY
, &props
))
2744 for (size_t i
= 0, len
= props
.length(); i
< len
; i
++) {
2748 if (!obj
->getAttributes(cx
, id
, &attrs
))
2751 if (!(attrs
& JSPROP_PERMANENT
))
2752 return true; /* The JavaScript value returned is false. */
2755 /* It really was sealed, so return true. */
2756 vp
->setBoolean(true);
2760 #if JS_HAS_OBJ_WATCHPOINT
2761 const char js_watch_str
[] = "watch";
2762 const char js_unwatch_str
[] = "unwatch";
2764 const char js_hasOwnProperty_str
[] = "hasOwnProperty";
2765 const char js_isPrototypeOf_str
[] = "isPrototypeOf";
2766 const char js_propertyIsEnumerable_str
[] = "propertyIsEnumerable";
2768 static JSFunctionSpec object_methods
[] = {
2770 JS_FN(js_toSource_str
, obj_toSource
, 0,0),
2772 JS_FN(js_toString_str
, obj_toString
, 0,0),
2773 JS_FN(js_toLocaleString_str
, obj_toLocaleString
, 0,0),
2774 JS_FN(js_valueOf_str
, obj_valueOf
, 0,0),
2775 #if JS_HAS_OBJ_WATCHPOINT
2776 JS_FN(js_watch_str
, obj_watch
, 2,0),
2777 JS_FN(js_unwatch_str
, obj_unwatch
, 1,0),
2779 JS_FN(js_hasOwnProperty_str
, obj_hasOwnProperty
, 1,0),
2780 JS_FN(js_isPrototypeOf_str
, obj_isPrototypeOf
, 1,0),
2781 JS_FN(js_propertyIsEnumerable_str
, obj_propertyIsEnumerable
, 1,0),
2782 #if OLD_GETTER_SETTER_METHODS
2783 JS_FN(js_defineGetter_str
, js_obj_defineGetter
, 2,0),
2784 JS_FN(js_defineSetter_str
, js_obj_defineSetter
, 2,0),
2785 JS_FN(js_lookupGetter_str
, obj_lookupGetter
, 1,0),
2786 JS_FN(js_lookupSetter_str
, obj_lookupSetter
, 1,0),
2791 static JSFunctionSpec object_static_methods
[] = {
2792 JS_FN("getPrototypeOf", obj_getPrototypeOf
, 1,0),
2793 JS_FN("getOwnPropertyDescriptor", obj_getOwnPropertyDescriptor
,2,0),
2794 JS_FN("keys", obj_keys
, 1,0),
2795 JS_FN("defineProperty", obj_defineProperty
, 3,0),
2796 JS_FN("defineProperties", obj_defineProperties
, 2,0),
2797 JS_FN("create", obj_create
, 2,0),
2798 JS_FN("getOwnPropertyNames", obj_getOwnPropertyNames
, 1,0),
2799 JS_FN("isExtensible", obj_isExtensible
, 1,0),
2800 JS_FN("preventExtensions", obj_preventExtensions
, 1,0),
2801 JS_FN("freeze", obj_freeze
, 1,0),
2802 JS_FN("isFrozen", obj_isFrozen
, 1,0),
2803 JS_FN("seal", obj_seal
, 1,0),
2804 JS_FN("isSealed", obj_isSealed
, 1,0),
2809 js_Object(JSContext
*cx
, uintN argc
, Value
*vp
)
2813 /* Trigger logic below to construct a blank object. */
2816 /* If argv[0] is null or undefined, obj comes back null. */
2817 if (!js_ValueToObjectOrNull(cx
, vp
[2], &obj
))
2821 /* Make an object whether this was called with 'new' or not. */
2822 JS_ASSERT(!argc
|| vp
[2].isNull() || vp
[2].isUndefined());
2823 gc::FinalizeKind kind
= NewObjectGCKind(cx
, &js_ObjectClass
);
2824 obj
= NewBuiltinClassInstance(cx
, &js_ObjectClass
, kind
);
2828 vp
->setObject(*obj
);
2833 js_CreateThis(JSContext
*cx
, JSObject
*callee
)
2835 Class
*clasp
= callee
->getClass();
2837 Class
*newclasp
= &js_ObjectClass
;
2838 if (clasp
== &js_FunctionClass
) {
2839 JSFunction
*fun
= callee
->getFunctionPrivate();
2840 if (fun
->isNative() && fun
->u
.n
.clasp
)
2841 newclasp
= fun
->u
.n
.clasp
;
2845 if (!callee
->getProperty(cx
, ATOM_TO_JSID(cx
->runtime
->atomState
.classPrototypeAtom
), &protov
))
2848 JSObject
*proto
= protov
.isObjectOrNull() ? protov
.toObjectOrNull() : NULL
;
2849 JSObject
*parent
= callee
->getParent();
2850 gc::FinalizeKind kind
= NewObjectGCKind(cx
, newclasp
);
2851 JSObject
*obj
= NewObject
<WithProto::Class
>(cx
, newclasp
, proto
, parent
, kind
);
2853 obj
->syncSpecialEquality();
2858 js_CreateThisForFunctionWithProto(JSContext
*cx
, JSObject
*callee
, JSObject
*proto
)
2860 gc::FinalizeKind kind
= NewObjectGCKind(cx
, &js_ObjectClass
);
2861 return NewNonFunction
<WithProto::Class
>(cx
, &js_ObjectClass
, proto
, callee
->getParent(), kind
);
2865 js_CreateThisForFunction(JSContext
*cx
, JSObject
*callee
)
2868 if (!callee
->getProperty(cx
,
2869 ATOM_TO_JSID(cx
->runtime
->atomState
.classPrototypeAtom
),
2873 JSObject
*proto
= protov
.isObject() ? &protov
.toObject() : NULL
;
2874 return js_CreateThisForFunctionWithProto(cx
, callee
, proto
);
2879 static JS_ALWAYS_INLINE JSObject
*
2880 NewObjectWithClassProto(JSContext
*cx
, Class
*clasp
, JSObject
*proto
,
2881 /*gc::FinalizeKind*/ unsigned _kind
)
2883 JS_ASSERT(clasp
->isNative());
2884 gc::FinalizeKind kind
= gc::FinalizeKind(_kind
);
2886 JSObject
* obj
= js_NewGCObject(cx
, kind
);
2890 if (!obj
->initSharingEmptyShape(cx
, clasp
, proto
, proto
->getParent(), NULL
, kind
))
2896 js_Object_tn(JSContext
* cx
, JSObject
* proto
)
2898 JS_ASSERT(!(js_ObjectClass
.flags
& JSCLASS_HAS_PRIVATE
));
2899 return NewObjectWithClassProto(cx
, &js_ObjectClass
, proto
, FINALIZE_OBJECT8
);
2902 JS_DEFINE_TRCINFO_1(js_Object
,
2903 (2, (extern, CONSTRUCTOR_RETRY
, js_Object_tn
, CONTEXT
, CALLEE_PROTOTYPE
, 0,
2904 nanojit::ACCSET_STORE_ANY
)))
2907 js_InitializerObject(JSContext
* cx
, JSObject
*proto
, JSObject
*baseobj
)
2910 gc::FinalizeKind kind
= GuessObjectGCKind(0, false);
2911 return NewObjectWithClassProto(cx
, &js_ObjectClass
, proto
, kind
);
2914 return CopyInitializerObject(cx
, baseobj
);
2917 JS_DEFINE_CALLINFO_3(extern, OBJECT
, js_InitializerObject
, CONTEXT
, OBJECT
, OBJECT
,
2918 0, nanojit::ACCSET_STORE_ANY
)
2921 js_String_tn(JSContext
* cx
, JSObject
* proto
, JSString
* str
)
2923 JS_ASSERT(JS_ON_TRACE(cx
));
2924 JSObject
*obj
= NewObjectWithClassProto(cx
, &js_StringClass
, proto
, FINALIZE_OBJECT2
);
2927 obj
->setPrimitiveThis(StringValue(str
));
2930 JS_DEFINE_CALLINFO_3(extern, OBJECT
, js_String_tn
, CONTEXT
, CALLEE_PROTOTYPE
, STRING
, 0,
2931 nanojit::ACCSET_STORE_ANY
)
2934 js_CreateThisFromTrace(JSContext
*cx
, Class
*clasp
, JSObject
*ctor
)
2936 JS_ASSERT(JS_ON_TRACE(cx
));
2937 JS_ASSERT(ctor
->isFunction());
2939 if (!ctor
->ensureClassReservedSlots(cx
))
2942 jsid classPrototypeId
= ATOM_TO_JSID(cx
->runtime
->atomState
.classPrototypeAtom
);
2943 const Shape
*shape
= ctor
->nativeLookup(classPrototypeId
);
2944 Value pval
= shape
? ctor
->getSlot(shape
->slot
) : MagicValue(JS_GENERIC_MAGIC
);
2946 JSObject
*parent
= ctor
->getParent();
2948 if (pval
.isObject()) {
2949 /* An object in ctor.prototype, let's use it as the new instance's proto. */
2950 proto
= &pval
.toObject();
2952 /* A hole or a primitive: either way, we need to get Object.prototype. */
2953 if (!js_GetClassPrototype(cx
, parent
, JSProto_Object
, &proto
))
2956 if (pval
.isMagic(JS_GENERIC_MAGIC
)) {
2958 * No ctor.prototype was set, so we inline-expand and optimize
2959 * fun_resolve's prototype creation code.
2961 proto
= NewNativeClassInstance(cx
, clasp
, proto
, parent
);
2964 JSFunction
*fun
= ctor
->getFunctionPrivate();
2965 if (!fun
->isNative() && !fun
->isFunctionPrototype()) {
2966 if (!js_SetClassPrototype(cx
, ctor
, proto
, JSPROP_ENUMERATE
| JSPROP_PERMANENT
))
2971 * A primitive value in .prototype means to use Object.prototype
2972 * for proto. See ES5 13.2.2 step 7.
2978 * FIXME: 561785 at least. Quasi-natives including XML objects prevent us
2979 * from easily or unconditionally calling NewNativeClassInstance here.
2981 gc::FinalizeKind kind
= NewObjectGCKind(cx
, clasp
);
2982 return NewNonFunction
<WithProto::Given
>(cx
, clasp
, proto
, parent
, kind
);
2985 JS_DEFINE_CALLINFO_3(extern, CONSTRUCTOR_RETRY
, js_CreateThisFromTrace
, CONTEXT
, CLASS
, OBJECT
, 0,
2986 nanojit::ACCSET_STORE_ANY
)
2988 #else /* !JS_TRACER */
2990 # define js_Object_trcinfo NULL
2992 #endif /* !JS_TRACER */
2995 * Given pc pointing after a property accessing bytecode, return true if the
2996 * access is "object-detecting" in the sense used by web scripts, e.g., when
2997 * checking whether document.all is defined.
2999 JS_REQUIRES_STACK JSBool
3000 Detecting(JSContext
*cx
, jsbytecode
*pc
)
3007 script
= cx
->fp()->script();
3008 endpc
= script
->code
+ script
->length
;
3009 for (;; pc
+= js_CodeSpec
[op
].length
) {
3010 JS_ASSERT_IF(!cx
->fp()->hasImacropc(), script
->code
<= pc
&& pc
< endpc
);
3012 /* General case: a branch or equality op follows the access. */
3013 op
= js_GetOpcode(cx
, script
, pc
);
3014 if (js_CodeSpec
[op
].format
& JOF_DETECTING
)
3020 * Special case #1: handle (document.all == null). Don't sweat
3021 * about JS1.2's revision of the equality operators here.
3024 op
= js_GetOpcode(cx
, script
, pc
);
3025 return *pc
== JSOP_EQ
|| *pc
== JSOP_NE
;
3032 * Special case #2: handle (document.all == undefined). Don't
3033 * worry about someone redefining undefined, which was added by
3034 * Edition 3, so is read/write for backward compatibility.
3036 GET_ATOM_FROM_BYTECODE(script
, pc
, 0, atom
);
3037 if (atom
== cx
->runtime
->atomState
.typeAtoms
[JSTYPE_VOID
] &&
3038 (pc
+= js_CodeSpec
[op
].length
) < endpc
) {
3039 op
= js_GetOpcode(cx
, script
, pc
);
3040 return op
== JSOP_EQ
|| op
== JSOP_NE
||
3041 op
== JSOP_STRICTEQ
|| op
== JSOP_STRICTNE
;
3047 * At this point, anything but an extended atom index prefix means
3048 * we're not detecting.
3050 if (!(js_CodeSpec
[op
].format
& JOF_INDEXBASE
))
3058 * Infer lookup flags from the currently executing bytecode. This does
3059 * not attempt to infer JSRESOLVE_WITH, because the current bytecode
3060 * does not indicate whether we are in a with statement. Return defaultFlags
3061 * if a currently executing bytecode cannot be determined.
3064 js_InferFlags(JSContext
*cx
, uintN defaultFlags
)
3067 if (JS_ON_TRACE(cx
))
3068 return JS_TRACE_MONITOR(cx
).bailExit
->lookupFlags
;
3071 JS_ASSERT_NOT_ON_TRACE(cx
);
3074 const JSCodeSpec
*cs
;
3078 JSStackFrame
*const fp
= js_GetTopStackFrame(cx
);
3079 if (!fp
|| !(pc
= cx
->regs
->pc
))
3080 return defaultFlags
;
3081 cs
= &js_CodeSpec
[js_GetOpcode(cx
, fp
->script(), pc
)];
3082 format
= cs
->format
;
3083 if (JOF_MODE(format
) != JOF_NAME
)
3084 flags
|= JSRESOLVE_QUALIFIED
;
3085 if ((format
& (JOF_SET
| JOF_FOR
)) || fp
->isAssigning()) {
3086 flags
|= JSRESOLVE_ASSIGNING
;
3087 } else if (cs
->length
>= 0) {
3089 JSScript
*script
= cx
->fp()->script();
3090 if (pc
< script
->code
+ script
->length
&& Detecting(cx
, pc
))
3091 flags
|= JSRESOLVE_DETECTING
;
3093 if (format
& JOF_DECLARING
)
3094 flags
|= JSRESOLVE_DECLARING
;
3099 * ObjectOps and Class for with-statement stack objects.
3102 with_LookupProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, JSObject
**objp
,
3105 /* Fixes bug 463997 */
3106 uintN flags
= cx
->resolveFlags
;
3107 if (flags
== JSRESOLVE_INFER
)
3108 flags
= js_InferFlags(cx
, flags
);
3109 flags
|= JSRESOLVE_WITH
;
3110 JSAutoResolveFlags
rf(cx
, flags
);
3111 return obj
->getProto()->lookupProperty(cx
, id
, objp
, propp
);
3115 with_GetProperty(JSContext
*cx
, JSObject
*obj
, JSObject
*receiver
, jsid id
, Value
*vp
)
3117 return obj
->getProto()->getProperty(cx
, id
, vp
);
3121 with_SetProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
, JSBool strict
)
3123 return obj
->getProto()->setProperty(cx
, id
, vp
, strict
);
3127 with_GetAttributes(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN
*attrsp
)
3129 return obj
->getProto()->getAttributes(cx
, id
, attrsp
);
3133 with_SetAttributes(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN
*attrsp
)
3135 return obj
->getProto()->setAttributes(cx
, id
, attrsp
);
3139 with_DeleteProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*rval
, JSBool strict
)
3141 return obj
->getProto()->deleteProperty(cx
, id
, rval
, strict
);
3145 with_Enumerate(JSContext
*cx
, JSObject
*obj
, JSIterateOp enum_op
,
3146 Value
*statep
, jsid
*idp
)
3148 return obj
->getProto()->enumerate(cx
, enum_op
, statep
, idp
);
3152 with_TypeOf(JSContext
*cx
, JSObject
*obj
)
3154 return JSTYPE_OBJECT
;
3158 with_ThisObject(JSContext
*cx
, JSObject
*obj
)
3160 return obj
->getWithThis();
3163 Class js_WithClass
= {
3165 JSCLASS_HAS_PRIVATE
| JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS
,
3166 PropertyStub
, /* addProperty */
3167 PropertyStub
, /* delProperty */
3168 PropertyStub
, /* getProperty */
3169 PropertyStub
, /* setProperty */
3173 NULL
, /* finalize */
3174 NULL
, /* reserved */
3175 NULL
, /* checkAccess */
3177 NULL
, /* construct */
3178 NULL
, /* xdrObject */
3179 NULL
, /* hasInstance */
3183 with_LookupProperty
,
3184 NULL
, /* defineProperty */
3189 with_DeleteProperty
,
3199 JS_REQUIRES_STACK JSObject
*
3200 js_NewWithObject(JSContext
*cx
, JSObject
*proto
, JSObject
*parent
, jsint depth
)
3204 obj
= js_NewGCObject(cx
, FINALIZE_OBJECT2
);
3208 JSStackFrame
*priv
= js_FloatingFrameIfGenerator(cx
, cx
->fp());
3210 obj
->init(cx
, &js_WithClass
, proto
, parent
, priv
, false);
3211 obj
->setMap(cx
->runtime
->emptyWithShape
);
3212 OBJ_SET_BLOCK_DEPTH(cx
, obj
, depth
);
3214 AutoObjectRooter
tvr(cx
, obj
);
3215 JSObject
*thisp
= proto
->thisObject(cx
);
3219 assertSameCompartment(cx
, obj
, thisp
);
3221 obj
->setWithThis(thisp
);
3226 js_NewBlockObject(JSContext
*cx
)
3229 * Null obj's proto slot so that Object.prototype.* does not pollute block
3230 * scopes and to give the block object its own scope.
3232 JSObject
*blockObj
= js_NewGCObject(cx
, FINALIZE_OBJECT2
);
3236 blockObj
->init(cx
, &js_BlockClass
, NULL
, NULL
, NULL
, false);
3237 blockObj
->setMap(cx
->runtime
->emptyBlockShape
);
3242 js_CloneBlockObject(JSContext
*cx
, JSObject
*proto
, JSStackFrame
*fp
)
3244 JS_ASSERT(proto
->isStaticBlock());
3246 size_t count
= OBJ_BLOCK_COUNT(cx
, proto
);
3247 gc::FinalizeKind kind
= gc::GetGCObjectKind(count
+ 1);
3249 JSObject
*clone
= js_NewGCObject(cx
, kind
);
3253 JSStackFrame
*priv
= js_FloatingFrameIfGenerator(cx
, fp
);
3255 /* The caller sets parent on its own. */
3256 clone
->init(cx
, &js_BlockClass
, proto
, NULL
, priv
, false);
3258 clone
->setMap(proto
->map
);
3259 if (!clone
->ensureInstanceReservedSlots(cx
, count
+ 1))
3262 clone
->setSlot(JSSLOT_BLOCK_DEPTH
, proto
->getSlot(JSSLOT_BLOCK_DEPTH
));
3264 JS_ASSERT(clone
->isClonedBlock());
3268 JS_REQUIRES_STACK JSBool
3269 js_PutBlockObject(JSContext
*cx
, JSBool normalUnwind
)
3271 JSStackFrame
*const fp
= cx
->fp();
3272 JSObject
*obj
= &fp
->scopeChain();
3273 JS_ASSERT(obj
->isClonedBlock());
3274 JS_ASSERT(obj
->getPrivate() == js_FloatingFrameIfGenerator(cx
, cx
->fp()));
3276 /* Block objects should have all reserved slots allocated early. */
3277 uintN count
= OBJ_BLOCK_COUNT(cx
, obj
);
3278 JS_ASSERT(obj
->numSlots() >= JSSLOT_BLOCK_DEPTH
+ 1 + count
);
3280 /* The block and its locals must be on the current stack for GC safety. */
3281 uintN depth
= OBJ_BLOCK_DEPTH(cx
, obj
);
3282 JS_ASSERT(depth
<= size_t(cx
->regs
->sp
- fp
->base()));
3283 JS_ASSERT(count
<= size_t(cx
->regs
->sp
- fp
->base() - depth
));
3285 /* See comments in CheckDestructuring from jsparse.cpp. */
3286 JS_ASSERT(count
>= 1);
3289 uintN slot
= JSSLOT_BLOCK_FIRST_FREE_SLOT
;
3290 depth
+= fp
->numFixed();
3291 memcpy(obj
->getSlots() + slot
, fp
->slots() + depth
, count
* sizeof(Value
));
3294 /* We must clear the private slot even with errors. */
3295 obj
->setPrivate(NULL
);
3296 fp
->setScopeChainNoCallObj(*obj
->getParent());
3297 return normalUnwind
;
3301 block_getProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
3304 * Block objects are never exposed to script, and the engine handles them
3305 * with care. So unlike other getters, this one can assert (rather than
3306 * check) certain invariants about obj.
3308 JS_ASSERT(obj
->isClonedBlock());
3309 uintN index
= (uintN
) JSID_TO_INT(id
);
3310 JS_ASSERT(index
< OBJ_BLOCK_COUNT(cx
, obj
));
3312 JSStackFrame
*fp
= (JSStackFrame
*) obj
->getPrivate();
3314 fp
= js_LiveFrameIfGenerator(fp
);
3315 index
+= fp
->numFixed() + OBJ_BLOCK_DEPTH(cx
, obj
);
3316 JS_ASSERT(index
< fp
->numSlots());
3317 *vp
= fp
->slots()[index
];
3321 /* Values are in slots immediately following the class-reserved ones. */
3322 JS_ASSERT(obj
->getSlot(JSSLOT_FREE(&js_BlockClass
) + index
) == *vp
);
3327 block_setProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
3329 JS_ASSERT(obj
->isClonedBlock());
3330 uintN index
= (uintN
) JSID_TO_INT(id
);
3331 JS_ASSERT(index
< OBJ_BLOCK_COUNT(cx
, obj
));
3333 JSStackFrame
*fp
= (JSStackFrame
*) obj
->getPrivate();
3335 fp
= js_LiveFrameIfGenerator(fp
);
3336 index
+= fp
->numFixed() + OBJ_BLOCK_DEPTH(cx
, obj
);
3337 JS_ASSERT(index
< fp
->numSlots());
3338 fp
->slots()[index
] = *vp
;
3343 * The value in *vp will be written back to the slot in obj that was
3344 * allocated when this let binding was defined.
3350 JSObject::defineBlockVariable(JSContext
*cx
, jsid id
, intN index
)
3352 JS_ASSERT(isStaticBlock());
3354 /* Use JSPROP_ENUMERATE to aid the disassembler. */
3355 uint32 slot
= JSSLOT_FREE(&js_BlockClass
) + index
;
3356 const Shape
*shape
= addProperty(cx
, id
,
3357 block_getProperty
, block_setProperty
,
3358 slot
, JSPROP_ENUMERATE
| JSPROP_PERMANENT
,
3359 Shape::HAS_SHORTID
, index
);
3362 if (slot
>= numSlots() && !growSlots(cx
, slot
+ 1))
3368 GetObjectSize(JSObject
*obj
)
3370 return (obj
->isFunction() && !obj
->getPrivate())
3371 ? sizeof(JSFunction
)
3372 : sizeof(JSObject
) + sizeof(js::Value
) * obj
->numFixedSlots();
3376 JSObject::copyPropertiesFrom(JSContext
*cx
, JSObject
*obj
)
3378 // If we're not native, then we cannot copy properties.
3379 JS_ASSERT(isNative() == obj
->isNative());
3383 Vector
<const Shape
*> shapes(cx
);
3384 for (Shape::Range
r(obj
->lastProperty()); !r
.empty(); r
.popFront()) {
3385 if (!shapes
.append(&r
.front()))
3389 size_t n
= shapes
.length();
3391 const Shape
*shape
= shapes
[--n
];
3392 uintN attrs
= shape
->attributes();
3393 PropertyOp getter
= shape
->getter();
3394 if ((attrs
& JSPROP_GETTER
) && !cx
->compartment
->wrap(cx
, &getter
))
3396 PropertyOp setter
= shape
->setter();
3397 if ((attrs
& JSPROP_SETTER
) && !cx
->compartment
->wrap(cx
, &setter
))
3399 Value v
= shape
->hasSlot() ? obj
->getSlot(shape
->slot
) : UndefinedValue();
3400 if (!cx
->compartment
->wrap(cx
, &v
))
3402 if (!defineProperty(cx
, shape
->id
, v
, getter
, setter
, attrs
))
3409 CopySlots(JSContext
*cx
, JSObject
*from
, JSObject
*to
)
3411 JS_ASSERT(!from
->isNative() && !to
->isNative());
3412 size_t nslots
= from
->numSlots();
3413 if (to
->ensureSlots(cx
, nslots
))
3417 if (to
->isWrapper() &&
3418 (JSWrapper::wrapperHandler(to
)->flags() & JSWrapper::CROSS_COMPARTMENT
)) {
3419 to
->slots
[0] = from
->slots
[0];
3420 to
->slots
[1] = from
->slots
[1];
3424 for (; n
< nslots
; ++n
) {
3425 Value v
= from
->slots
[n
];
3426 if (!cx
->compartment
->wrap(cx
, &v
))
3434 JSObject::clone(JSContext
*cx
, JSObject
*proto
, JSObject
*parent
)
3437 * We can only clone native objects and proxies. Dense arrays are slowified if
3438 * we try to clone them.
3441 if (isDenseArray()) {
3442 if (!makeDenseArraySlow(cx
))
3444 } else if (!isProxy()) {
3445 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
3446 JSMSG_CANT_CLONE_OBJECT
);
3450 JSObject
*clone
= NewObject
<WithProto::Given
>(cx
, getClass(),
3452 gc::FinalizeKind(finalizeKind()));
3456 if (clone
->isFunction() && (compartment() != clone
->compartment())) {
3457 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
3458 JSMSG_CANT_CLONE_OBJECT
);
3462 if (getClass()->flags
& JSCLASS_HAS_PRIVATE
)
3463 clone
->setPrivate(getPrivate());
3465 JS_ASSERT(isProxy());
3466 if (!CopySlots(cx
, this, clone
))
3473 TradeGuts(JSObject
*a
, JSObject
*b
)
3475 JS_ASSERT(a
->compartment() == b
->compartment());
3476 JS_ASSERT(a
->isFunction() == b
->isFunction());
3479 * Regexp guts are more complicated -- we would need to migrate the
3480 * refcounted JIT code blob for them across compartments instead of just
3483 JS_ASSERT(!a
->isRegExp() && !b
->isRegExp());
3485 bool aInline
= !a
->hasSlotsArray();
3486 bool bInline
= !b
->hasSlotsArray();
3488 /* Trade the guts of the objects. */
3489 const size_t size
= GetObjectSize(a
);
3490 if (size
== GetObjectSize(b
)) {
3492 * If the objects are the same size, then we make no assumptions about
3493 * whether they have dynamically allocated slots and instead just copy
3494 * them over wholesale.
3496 char tmp
[tl::Max
<sizeof(JSFunction
), sizeof(JSObject_Slots16
)>::result
];
3497 JS_ASSERT(size
<= sizeof(tmp
));
3499 memcpy(tmp
, a
, size
);
3501 memcpy(b
, tmp
, size
);
3503 /* Fixup pointers for inline slots on the objects. */
3505 b
->slots
= b
->fixedSlots();
3507 a
->slots
= a
->fixedSlots();
3510 * If the objects are of differing sizes, then we only copy over the
3511 * JSObject portion (things like class, etc.) and leave it to
3512 * JSObject::clone to copy over the dynamic slots for us.
3514 if (a
->isFunction()) {
3516 memcpy(&tmp
, a
, sizeof tmp
);
3517 memcpy(a
, b
, sizeof tmp
);
3518 memcpy(b
, &tmp
, sizeof tmp
);
3521 memcpy(&tmp
, a
, sizeof tmp
);
3522 memcpy(a
, b
, sizeof tmp
);
3523 memcpy(b
, &tmp
, sizeof tmp
);
3526 JS_ASSERT(!aInline
);
3527 JS_ASSERT(!bInline
);
3532 * Use this method with extreme caution. It trades the guts of two objects and updates
3533 * scope ownership. This operation is not thread-safe, just as fast array to slow array
3534 * transitions are inherently not thread-safe. Don't perform a swap operation on objects
3535 * shared across threads or, or bad things will happen. You have been warned.
3538 JSObject::swap(JSContext
*cx
, JSObject
*other
)
3541 * If we are swapping objects with a different number of builtin slots, force
3542 * both to not use their inline slots.
3544 if (GetObjectSize(this) != GetObjectSize(other
)) {
3545 if (!hasSlotsArray()) {
3546 if (!allocSlots(cx
, numSlots()))
3549 if (!other
->hasSlotsArray()) {
3550 if (!other
->allocSlots(cx
, other
->numSlots()))
3555 if (this->compartment() == other
->compartment()) {
3556 TradeGuts(this, other
);
3560 JSObject
*thisClone
;
3561 JSObject
*otherClone
;
3563 AutoCompartment
ac(cx
, other
);
3566 thisClone
= this->clone(cx
, other
->getProto(), other
->getParent());
3567 if (!thisClone
|| !thisClone
->copyPropertiesFrom(cx
, this))
3571 AutoCompartment
ac(cx
, this);
3574 otherClone
= other
->clone(cx
, other
->getProto(), other
->getParent());
3575 if (!otherClone
|| !otherClone
->copyPropertiesFrom(cx
, other
))
3578 TradeGuts(this, otherClone
);
3579 TradeGuts(other
, thisClone
);
3586 #define NO_PARENT_INDEX ((uint32)-1)
3589 FindObjectIndex(JSObjectArray
*array
, JSObject
*obj
)
3597 if (array
->vector
[--i
] == obj
)
3602 return NO_PARENT_INDEX
;
3606 js_XDRBlockObject(JSXDRState
*xdr
, JSObject
**objp
)
3610 JSObject
*obj
, *parent
;
3612 uint32 depthAndCount
;
3617 obj
= NULL
; /* quell GCC overwarning */
3620 if (xdr
->mode
== JSXDR_ENCODE
) {
3622 parent
= obj
->getParent();
3623 parentId
= JSScript::isValidOffset(xdr
->script
->objectsOffset
)
3624 ? FindObjectIndex(xdr
->script
->objects(), parent
)
3626 depth
= (uint16
)OBJ_BLOCK_DEPTH(cx
, obj
);
3627 count
= (uint16
)OBJ_BLOCK_COUNT(cx
, obj
);
3628 depthAndCount
= (uint32
)(depth
<< 16) | count
;
3630 #ifdef __GNUC__ /* suppress bogus gcc warnings */
3634 /* First, XDR the parent atomid. */
3635 if (!JS_XDRUint32(xdr
, &parentId
))
3638 if (xdr
->mode
== JSXDR_DECODE
) {
3639 obj
= js_NewBlockObject(cx
);
3645 * If there's a parent id, then get the parent out of our script's
3646 * object array. We know that we XDR block object in outer-to-inner
3647 * order, which means that getting the parent now will work.
3649 if (parentId
== NO_PARENT_INDEX
)
3652 parent
= xdr
->script
->getObject(parentId
);
3653 obj
->setParent(parent
);
3656 AutoObjectRooter
tvr(cx
, obj
);
3658 if (!JS_XDRUint32(xdr
, &depthAndCount
))
3661 if (xdr
->mode
== JSXDR_DECODE
) {
3662 depth
= (uint16
)(depthAndCount
>> 16);
3663 count
= (uint16
)depthAndCount
;
3664 obj
->setSlot(JSSLOT_BLOCK_DEPTH
, Value(Int32Value(depth
)));
3667 * XDR the block object's properties. We know that there are 'count'
3668 * properties to XDR, stored as id/shortid pairs.
3670 for (uintN i
= 0; i
< count
; i
++) {
3674 /* XDR the real id, then the shortid. */
3675 if (!js_XDRAtom(xdr
, &atom
) || !JS_XDRUint16(xdr
, &shortid
))
3678 if (!obj
->defineBlockVariable(cx
, ATOM_TO_JSID(atom
), shortid
))
3682 Vector
<const Shape
*, 8> shapes(cx
);
3683 shapes
.growByUninitialized(count
);
3685 for (Shape::Range
r(obj
->lastProperty()); !r
.empty(); r
.popFront()) {
3687 shapes
[shape
->shortid
] = shape
;
3691 * XDR the block object's properties. We know that there are 'count'
3692 * properties to XDR, stored as id/shortid pairs.
3694 for (uintN i
= 0; i
< count
; i
++) {
3696 JS_ASSERT(shape
->getter() == block_getProperty
);
3698 jsid propid
= shape
->id
;
3699 JS_ASSERT(JSID_IS_ATOM(propid
));
3700 JSAtom
*atom
= JSID_TO_ATOM(propid
);
3702 uint16 shortid
= uint16(shape
->shortid
);
3703 JS_ASSERT(shortid
== i
);
3705 /* XDR the real id, then the shortid. */
3706 if (!js_XDRAtom(xdr
, &atom
) || !JS_XDRUint16(xdr
, &shortid
))
3715 Class js_BlockClass
= {
3717 JSCLASS_HAS_PRIVATE
| JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_IS_ANONYMOUS
,
3718 PropertyStub
, /* addProperty */
3719 PropertyStub
, /* delProperty */
3720 PropertyStub
, /* getProperty */
3721 PropertyStub
, /* setProperty */
3728 js_InitObjectClass(JSContext
*cx
, JSObject
*obj
)
3730 JSObject
*proto
= js_InitClass(cx
, obj
, NULL
, &js_ObjectClass
, js_Object
, 1,
3731 object_props
, object_methods
, NULL
, object_static_methods
);
3735 /* ECMA (15.1.2.1) says 'eval' is a property of the global object. */
3736 jsid id
= ATOM_TO_JSID(cx
->runtime
->atomState
.evalAtom
);
3737 if (!js_DefineFunction(cx
, obj
, id
, eval
, 1, JSFUN_STUB_GSOPS
))
3744 DefineStandardSlot(JSContext
*cx
, JSObject
*obj
, JSProtoKey key
, JSAtom
*atom
,
3745 const Value
&v
, uint32 attrs
, bool &named
)
3747 jsid id
= ATOM_TO_JSID(atom
);
3749 if (key
!= JSProto_Null
) {
3751 * Initializing an actual standard class on a global object. If the
3752 * property is not yet present, force it into a new one bound to a
3753 * reserved slot. Otherwise, go through the normal property path.
3755 JS_ASSERT(obj
->isGlobal());
3756 JS_ASSERT(obj
->isNative());
3758 if (!obj
->ensureClassReservedSlots(cx
))
3761 const Shape
*shape
= obj
->nativeLookup(id
);
3763 uint32 slot
= 2 * JSProto_LIMIT
+ key
;
3764 if (!js_SetReservedSlot(cx
, obj
, slot
, v
))
3766 if (!obj
->addProperty(cx
, id
, PropertyStub
, PropertyStub
, slot
, attrs
, 0, 0))
3774 named
= obj
->defineProperty(cx
, id
, v
, PropertyStub
, PropertyStub
, attrs
);
3779 js_InitClass(JSContext
*cx
, JSObject
*obj
, JSObject
*parent_proto
,
3780 Class
*clasp
, Native constructor
, uintN nargs
,
3781 JSPropertySpec
*ps
, JSFunctionSpec
*fs
,
3782 JSPropertySpec
*static_ps
, JSFunctionSpec
*static_fs
)
3789 atom
= js_Atomize(cx
, clasp
->name
, strlen(clasp
->name
), 0);
3794 * When initializing a standard class, if no parent_proto (grand-proto of
3795 * instances of the class, parent-proto of the class's prototype object)
3796 * is given, we must use Object.prototype if it is available. Otherwise,
3797 * we could look up the wrong binding for a class name in obj. Example:
3800 * print("hi there".join);
3802 * should print undefined, not Array.prototype.join. This is required by
3803 * ECMA-262, alas. It might have been better to make String readonly and
3804 * permanent in the global object, instead -- but that's too big a change
3805 * to swallow at this point.
3807 key
= JSCLASS_CACHED_PROTO_KEY(clasp
);
3808 if (key
!= JSProto_Null
&&
3810 !js_GetClassPrototype(cx
, obj
, JSProto_Object
, &parent_proto
)) {
3815 * Create a prototype object for this class.
3817 * FIXME: lazy standard (built-in) class initialization and even older
3818 * eager boostrapping code rely on all of these properties:
3820 * 1. NewObject attempting to compute a default prototype object when
3821 * passed null for proto; and
3823 * 2. NewObject tolerating no default prototype (null proto slot value)
3824 * due to this js_InitClass call coming from js_InitFunctionClass on an
3825 * otherwise-uninitialized global.
3827 * 3. NewObject allocating a JSFunction-sized GC-thing when clasp is
3828 * &js_FunctionClass, not a JSObject-sized (smaller) GC-thing.
3830 * The JS_NewObjectForGivenProto and JS_NewObject APIs also allow clasp to
3831 * be &js_FunctionClass (we could break compatibility easily). But fixing
3832 * (3) is not enough without addressing the bootstrapping dependency on (1)
3835 JSObject
*proto
= NewObject
<WithProto::Class
>(cx
, clasp
, parent_proto
, obj
);
3839 proto
->syncSpecialEquality();
3841 /* After this point, control must exit via label bad or out. */
3842 AutoObjectRooter
tvr(cx
, proto
);
3847 * Lacking a constructor, name the prototype (e.g., Math) unless this
3848 * class (a) is anonymous, i.e. for internal use only; (b) the class
3849 * of obj (the global object) is has a reserved slot indexed by key;
3850 * and (c) key is not the null key.
3852 if (!(clasp
->flags
& JSCLASS_IS_ANONYMOUS
) || !obj
->isGlobal() || key
== JSProto_Null
) {
3853 uint32 attrs
= (clasp
->flags
& JSCLASS_IS_ANONYMOUS
)
3854 ? JSPROP_READONLY
| JSPROP_PERMANENT
3856 if (!DefineStandardSlot(cx
, obj
, key
, atom
, ObjectValue(*proto
), attrs
, named
))
3862 fun
= js_NewFunction(cx
, NULL
, constructor
, nargs
, JSFUN_CONSTRUCTOR
, obj
, atom
);
3866 AutoValueRooter
tvr2(cx
, ObjectValue(*fun
));
3867 if (!DefineStandardSlot(cx
, obj
, key
, atom
, tvr2
.value(), 0, named
))
3871 * Remember the class this function is a constructor for so that
3872 * we know to create an object of this class when we call the
3875 FUN_CLASP(fun
) = clasp
;
3878 * Optionally construct the prototype object, before the class has
3879 * been fully initialized. Allow the ctor to replace proto with a
3880 * different object, as is done for operator new -- and as at least
3881 * XML support requires.
3883 ctor
= FUN_OBJECT(fun
);
3884 if (clasp
->flags
& JSCLASS_CONSTRUCT_PROTOTYPE
) {
3886 if (!InvokeConstructorWithGivenThis(cx
, proto
, ObjectOrNullValue(ctor
),
3890 if (rval
.isObject() && &rval
.toObject() != proto
)
3891 proto
= &rval
.toObject();
3894 /* Connect constructor and prototype by named properties. */
3895 if (!js_SetClassPrototype(cx
, ctor
, proto
,
3896 JSPROP_READONLY
| JSPROP_PERMANENT
)) {
3900 /* Bootstrap Function.prototype (see also JS_InitStandardClasses). */
3901 if (ctor
->getClass() == clasp
)
3902 ctor
->setProto(proto
);
3905 /* Add properties and methods to the prototype and the constructor. */
3906 if ((ps
&& !JS_DefineProperties(cx
, proto
, ps
)) ||
3907 (fs
&& !JS_DefineFunctions(cx
, proto
, fs
)) ||
3908 (static_ps
&& !JS_DefineProperties(cx
, ctor
, static_ps
)) ||
3909 (static_fs
&& !JS_DefineFunctions(cx
, ctor
, static_fs
))) {
3914 * Pre-brand the prototype and constructor if they have built-in methods.
3915 * This avoids extra shape guard branch exits in the tracejitted code.
3919 if (ctor
!= proto
&& static_fs
)
3923 * Make sure proto's emptyShape is available to be shared by objects of
3924 * this class. JSObject::emptyShape is a one-slot cache. If we omit this,
3925 * some other class could snap it up. (The risk is particularly great for
3926 * Object.prototype.)
3928 * All callers of JSObject::initSharingEmptyShape depend on this.
3930 * FIXME: bug 592296 -- js_InitArrayClass should pass &js_SlowArrayClass
3931 * and make the Array.prototype slow from the start.
3933 JS_ASSERT_IF(proto
->clasp
!= clasp
,
3934 clasp
== &js_ArrayClass
&& proto
->clasp
== &js_SlowArrayClass
);
3935 if (!proto
->getEmptyShape(cx
, proto
->clasp
, FINALIZE_OBJECT0
))
3938 if (clasp
->flags
& (JSCLASS_FREEZE_PROTO
|JSCLASS_FREEZE_CTOR
)) {
3939 JS_ASSERT_IF(ctor
== proto
, !(clasp
->flags
& JSCLASS_FREEZE_CTOR
));
3940 if (proto
&& (clasp
->flags
& JSCLASS_FREEZE_PROTO
) && !proto
->freeze(cx
))
3942 if (ctor
&& (clasp
->flags
& JSCLASS_FREEZE_CTOR
) && !ctor
->freeze(cx
))
3946 /* If this is a standard class, cache its prototype. */
3947 if (key
!= JSProto_Null
&& !js_SetClassObject(cx
, obj
, key
, ctor
, proto
))
3955 obj
->deleteProperty(cx
, ATOM_TO_JSID(atom
), &rval
, false);
3961 JSObject::allocSlots(JSContext
*cx
, size_t newcap
)
3963 uint32 oldcap
= numSlots();
3965 JS_ASSERT(newcap
>= oldcap
&& !hasSlotsArray());
3967 if (newcap
> NSLOTS_LIMIT
) {
3968 if (!JS_ON_TRACE(cx
))
3969 js_ReportAllocationOverflow(cx
);
3973 Value
*tmpslots
= (Value
*) cx
->malloc(newcap
* sizeof(Value
));
3975 return false; /* Leave slots at inline buffer. */
3979 /* Copy over anything from the inline buffer. */
3980 memcpy(slots
, fixedSlots(), oldcap
* sizeof(Value
));
3981 ClearValueRange(slots
+ oldcap
, newcap
- oldcap
, isDenseArray());
3986 JSObject::growSlots(JSContext
*cx
, size_t newcap
)
3989 * When an object with CAPACITY_DOUBLING_MAX or fewer slots needs to
3990 * grow, double its capacity, to add N elements in amortized O(N) time.
3992 * Above this limit, grow by 12.5% each time. Speed is still amortized
3993 * O(N), with a higher constant factor, and we waste less space.
3995 static const size_t CAPACITY_DOUBLING_MAX
= 1024 * 1024;
3996 static const size_t CAPACITY_CHUNK
= CAPACITY_DOUBLING_MAX
/ sizeof(Value
);
3998 uint32 oldcap
= numSlots();
3999 JS_ASSERT(oldcap
< newcap
);
4001 uint32 nextsize
= (oldcap
<= CAPACITY_DOUBLING_MAX
)
4003 : oldcap
+ (oldcap
>> 3);
4005 uint32 actualCapacity
= JS_MAX(newcap
, nextsize
);
4006 if (actualCapacity
>= CAPACITY_CHUNK
)
4007 actualCapacity
= JS_ROUNDUP(actualCapacity
, CAPACITY_CHUNK
);
4008 else if (actualCapacity
< SLOT_CAPACITY_MIN
)
4009 actualCapacity
= SLOT_CAPACITY_MIN
;
4011 /* Don't let nslots get close to wrapping around uint32. */
4012 if (actualCapacity
>= NSLOTS_LIMIT
) {
4013 JS_ReportOutOfMemory(cx
);
4017 /* If nothing was allocated yet, treat it as initial allocation. */
4018 if (!hasSlotsArray())
4019 return allocSlots(cx
, actualCapacity
);
4021 Value
*tmpslots
= (Value
*) cx
->realloc(slots
, oldcap
* sizeof(Value
), actualCapacity
* sizeof(Value
));
4023 return false; /* Leave dslots as its old size. */
4025 capacity
= actualCapacity
;
4027 /* Initialize the additional slots we added. */
4028 ClearValueRange(slots
+ oldcap
, actualCapacity
- oldcap
, isDenseArray());
4033 JSObject::shrinkSlots(JSContext
*cx
, size_t newcap
)
4035 uint32 oldcap
= numSlots();
4036 JS_ASSERT(newcap
<= oldcap
);
4037 JS_ASSERT(newcap
>= slotSpan());
4039 if (oldcap
<= SLOT_CAPACITY_MIN
|| !hasSlotsArray()) {
4040 /* We won't shrink the slots any more. Clear excess holes. */
4041 ClearValueRange(slots
+ newcap
, oldcap
- newcap
, isDenseArray());
4045 uint32 fill
= newcap
;
4046 if (newcap
< SLOT_CAPACITY_MIN
)
4047 newcap
= SLOT_CAPACITY_MIN
;
4048 if (newcap
< numFixedSlots())
4049 newcap
= numFixedSlots();
4051 Value
*tmpslots
= (Value
*) cx
->realloc(slots
, newcap
* sizeof(Value
));
4053 return; /* Leave slots at its old size. */
4057 if (fill
< newcap
) {
4058 /* Clear any excess holes if we tried to shrink below SLOT_CAPACITY_MIN. */
4059 ClearValueRange(slots
+ fill
, newcap
- fill
, isDenseArray());
4064 JSObject::ensureInstanceReservedSlots(JSContext
*cx
, size_t nreserved
)
4066 JS_ASSERT_IF(isNative(),
4067 isBlock() || isCall() || (isFunction() && isBoundFunction()));
4069 uintN nslots
= JSSLOT_FREE(clasp
) + nreserved
;
4070 return nslots
<= numSlots() || allocSlots(cx
, nslots
);
4074 js_InitNullClass(JSContext
*cx
, JSObject
*obj
)
4080 #define JS_PROTO(name,code,init) extern JSObject *init(JSContext *, JSObject *);
4081 #include "jsproto.tbl"
4084 static JSObjectOp lazy_prototype_init
[JSProto_LIMIT
] = {
4085 #define JS_PROTO(name,code,init) init,
4086 #include "jsproto.tbl"
4093 SetProto(JSContext
*cx
, JSObject
*obj
, JSObject
*proto
, bool checkForCycles
)
4095 JS_ASSERT_IF(!checkForCycles
, obj
!= proto
);
4096 JS_ASSERT(obj
->isExtensible());
4098 if (obj
->isNative()) {
4099 if (!obj
->ensureClassReservedSlots(cx
))
4104 * Regenerate property cache shape ids for all of the scopes along the
4105 * old prototype chain to invalidate their property cache entries, in
4106 * case any entries were filled by looking up through obj.
4108 JSObject
*oldproto
= obj
;
4109 while (oldproto
&& oldproto
->isNative()) {
4110 oldproto
->protoShapeChange(cx
);
4111 oldproto
= oldproto
->getProto();
4114 if (!proto
|| !checkForCycles
) {
4115 obj
->setProto(proto
);
4116 } else if (!SetProtoCheckingForCycles(cx
, obj
, proto
)) {
4117 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_CYCLIC_VALUE
, js_proto_str
);
4126 js_GetClassObject(JSContext
*cx
, JSObject
*obj
, JSProtoKey key
,
4130 JSResolvingKey rkey
;
4131 JSResolvingEntry
*rentry
;
4136 obj
= obj
->getGlobal();
4137 if (!obj
->isGlobal()) {
4142 v
= obj
->getReservedSlot(key
);
4144 *objp
= &v
.toObject();
4149 rkey
.id
= ATOM_TO_JSID(cx
->runtime
->atomState
.classAtoms
[key
]);
4150 if (!js_StartResolving(cx
, &rkey
, JSRESFLAG_LOOKUP
, &rentry
))
4153 /* Already caching key in obj -- suppress recursion. */
4157 generation
= cx
->resolvingTable
->generation
;
4161 init
= lazy_prototype_init
[key
];
4163 if (!init(cx
, obj
)) {
4166 v
= obj
->getReservedSlot(key
);
4168 cobj
= &v
.toObject();
4172 js_StopResolving(cx
, &rkey
, JSRESFLAG_LOOKUP
, rentry
, generation
);
4178 js_SetClassObject(JSContext
*cx
, JSObject
*obj
, JSProtoKey key
, JSObject
*cobj
, JSObject
*proto
)
4180 JS_ASSERT(!obj
->getParent());
4181 if (!obj
->isGlobal())
4184 return js_SetReservedSlot(cx
, obj
, key
, ObjectOrNullValue(cobj
)) &&
4185 js_SetReservedSlot(cx
, obj
, JSProto_LIMIT
+ key
, ObjectOrNullValue(proto
));
4189 js_FindClassObject(JSContext
*cx
, JSObject
*start
, JSProtoKey protoKey
,
4190 Value
*vp
, Class
*clasp
)
4193 JSObject
*obj
, *cobj
, *pobj
;
4199 * Find the global object. Use cx->fp() directly to avoid falling off
4200 * trace; all JIT-elided stack frames have the same global object as
4203 VOUCH_DOES_NOT_REQUIRE_STACK();
4204 if (!start
&& (fp
= cx
->maybefp()) != NULL
)
4205 start
= &fp
->scopeChain();
4208 /* Find the topmost object in the scope chain. */
4211 start
= obj
->getParent();
4214 obj
= cx
->globalObject
;
4221 OBJ_TO_INNER_OBJECT(cx
, obj
);
4225 if (protoKey
!= JSProto_Null
) {
4226 JS_ASSERT(JSProto_Null
< protoKey
);
4227 JS_ASSERT(protoKey
< JSProto_LIMIT
);
4228 if (!js_GetClassObject(cx
, obj
, protoKey
, &cobj
))
4231 vp
->setObject(*cobj
);
4234 id
= ATOM_TO_JSID(cx
->runtime
->atomState
.classAtoms
[protoKey
]);
4236 JSAtom
*atom
= js_Atomize(cx
, clasp
->name
, strlen(clasp
->name
), 0);
4239 id
= ATOM_TO_JSID(atom
);
4242 JS_ASSERT(obj
->isNative());
4243 if (js_LookupPropertyWithFlags(cx
, obj
, id
, JSRESOLVE_CLASSNAME
,
4244 &pobj
, &prop
) < 0) {
4247 Value v
= UndefinedValue();
4248 if (prop
&& pobj
->isNative()) {
4249 shape
= (Shape
*) prop
;
4250 if (pobj
->containsSlot(shape
->slot
)) {
4251 v
= pobj
->nativeGetSlot(shape
->slot
);
4252 if (v
.isPrimitive())
4261 js_ConstructObject(JSContext
*cx
, Class
*clasp
, JSObject
*proto
, JSObject
*parent
,
4262 uintN argc
, Value
*argv
)
4264 AutoArrayRooter
argtvr(cx
, argc
, argv
);
4266 JSProtoKey protoKey
= GetClassProtoKey(clasp
);
4268 /* Protect constructor in case a crazy getter for .prototype uproots it. */
4269 AutoValueRooter
tvr(cx
);
4270 if (!js_FindClassObject(cx
, parent
, protoKey
, tvr
.addr(), clasp
))
4273 const Value
&cval
= tvr
.value();
4274 if (tvr
.value().isPrimitive()) {
4275 js_ReportIsNotFunction(cx
, tvr
.addr(), JSV2F_CONSTRUCT
| JSV2F_SEARCH_STACK
);
4280 * If proto is NULL, set it to Constructor.prototype, just like JSOP_NEW
4281 * does, likewise for the new object's parent.
4283 JSObject
*ctor
= &cval
.toObject();
4285 parent
= ctor
->getParent();
4288 if (!ctor
->getProperty(cx
, ATOM_TO_JSID(cx
->runtime
->atomState
.classPrototypeAtom
),
4292 if (rval
.isObjectOrNull())
4293 proto
= rval
.toObjectOrNull();
4296 JSObject
*obj
= NewObject
<WithProto::Class
>(cx
, clasp
, proto
, parent
);
4300 obj
->syncSpecialEquality();
4303 if (!InvokeConstructorWithGivenThis(cx
, obj
, cval
, argc
, argv
, &rval
))
4306 if (rval
.isPrimitive())
4310 * If the instance's class differs from what was requested, throw a type
4311 * error. If the given class has both the JSCLASS_HAS_PRIVATE and the
4312 * JSCLASS_CONSTRUCT_PROTOTYPE flags, and the instance does not have its
4313 * private data set at this point, then the constructor was replaced and
4314 * we should throw a type error.
4316 obj
= &rval
.toObject();
4317 if (obj
->getClass() != clasp
||
4318 (!(~clasp
->flags
& (JSCLASS_HAS_PRIVATE
|
4319 JSCLASS_CONSTRUCT_PROTOTYPE
)) &&
4320 !obj
->getPrivate())) {
4321 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
4322 JSMSG_WRONG_CONSTRUCTOR
, clasp
->name
);
4329 JSObject::allocSlot(JSContext
*cx
, uint32
*slotp
)
4331 uint32 slot
= slotSpan();
4332 JS_ASSERT(slot
>= JSSLOT_FREE(clasp
));
4335 * If this object is in dictionary mode and it has a property table, try to
4336 * pull a free slot from the property table's slot-number freelist.
4338 if (inDictionaryMode() && lastProp
->table
) {
4339 uint32
&last
= lastProp
->table
->freelist
;
4340 if (last
!= SHAPE_INVALID_SLOT
) {
4342 JS_ASSERT(last
< slot
);
4343 uint32 next
= getSlot(last
).toPrivateUint32();
4344 JS_ASSERT_IF(next
!= SHAPE_INVALID_SLOT
, next
< slot
);
4349 Value
&vref
= getSlotRef(last
);
4350 last
= vref
.toPrivateUint32();
4351 vref
.setUndefined();
4356 if (slot
>= numSlots() && !growSlots(cx
, slot
+ 1))
4359 /* JSObject::growSlots or JSObject::freeSlot should set the free slots to void. */
4360 JS_ASSERT(getSlot(slot
).isUndefined());
4366 JSObject::freeSlot(JSContext
*cx
, uint32 slot
)
4368 uint32 limit
= slotSpan();
4369 JS_ASSERT(slot
< limit
);
4371 Value
&vref
= getSlotRef(slot
);
4372 if (inDictionaryMode() && lastProp
->table
) {
4373 uint32
&last
= lastProp
->table
->freelist
;
4375 /* Can't afford to check the whole freelist, but let's check the head. */
4376 JS_ASSERT_IF(last
!= SHAPE_INVALID_SLOT
, last
< limit
&& last
!= slot
);
4379 * Freeing a slot other than the last one mapped by this object's
4380 * shape (and not a reserved slot; see bug 595230): push the slot onto
4381 * the dictionary property table's freelist. We want to let the last
4382 * slot be freed by shrinking the dslots vector; see js_TraceObject.
4384 if (JSSLOT_FREE(clasp
) <= slot
&& slot
+ 1 < limit
) {
4385 JS_ASSERT_IF(last
!= SHAPE_INVALID_SLOT
, last
< slotSpan());
4386 vref
.setPrivateUint32(last
);
4391 vref
.setUndefined();
4395 /* JSBOXEDWORD_INT_MAX as a string */
4396 #define JSBOXEDWORD_INT_MAX_STRING "1073741823"
4399 * Convert string indexes that convert to int jsvals as ints to save memory.
4400 * Care must be taken to use this macro every time a property name is used, or
4401 * else double-sets, incorrect property cache misses, or other mistakes could
4405 js_CheckForStringIndex(jsid id
)
4407 if (!JSID_IS_ATOM(id
))
4410 JSAtom
*atom
= JSID_TO_ATOM(id
);
4411 JSString
*str
= ATOM_TO_STRING(atom
);
4412 const jschar
*s
= str
->flatChars();
4415 JSBool negative
= (ch
== '-');
4422 size_t n
= str
->flatLength() - negative
;
4423 if (n
> sizeof(JSBOXEDWORD_INT_MAX_STRING
) - 1)
4426 const jschar
*cp
= s
;
4427 const jschar
*end
= s
+ n
;
4429 jsuint index
= JS7_UNDEC(*cp
++);
4430 jsuint oldIndex
= 0;
4434 while (JS7_ISDEC(*cp
)) {
4437 index
= 10 * index
+ c
;
4443 * Non-integer indexes can't be represented as integers. Also, distinguish
4444 * index "-0" from "0", because JSBOXEDWORD_INT cannot.
4446 if (cp
!= end
|| (negative
&& index
== 0))
4450 if (oldIndex
< -(JSID_INT_MIN
/ 10) ||
4451 (oldIndex
== -(JSID_INT_MIN
/ 10) && c
<= (-JSID_INT_MIN
% 10)))
4453 id
= INT_TO_JSID(-jsint(index
));
4456 if (oldIndex
< JSID_INT_MAX
/ 10 ||
4457 (oldIndex
== JSID_INT_MAX
/ 10 && c
<= (JSID_INT_MAX
% 10)))
4459 id
= INT_TO_JSID(jsint(index
));
4467 PurgeProtoChain(JSContext
*cx
, JSObject
*obj
, jsid id
)
4472 if (!obj
->isNative()) {
4473 obj
= obj
->getProto();
4476 shape
= obj
->nativeLookup(id
);
4478 PCMETER(JS_PROPERTY_CACHE(cx
).pcpurges
++);
4479 obj
->shadowingShapeChange(cx
, *shape
);
4481 if (!obj
->getParent()) {
4483 * All scope chains end in a global object, so this will change
4484 * the global shape. jstracer.cpp assumes that the global shape
4485 * never changes on trace, so we must deep-bail here.
4491 obj
= obj
->getProto();
4497 js_PurgeScopeChainHelper(JSContext
*cx
, JSObject
*obj
, jsid id
)
4499 JS_ASSERT(obj
->isDelegate());
4500 PurgeProtoChain(cx
, obj
->getProto(), id
);
4503 * We must purge the scope chain only for Call objects as they are the only
4504 * kind of cacheable non-global object that can gain properties after outer
4505 * properties with the same names have been cached or traced. Call objects
4506 * may gain such properties via eval introducing new vars; see bug 490364.
4508 if (obj
->isCall()) {
4509 while ((obj
= obj
->getParent()) != NULL
) {
4510 if (PurgeProtoChain(cx
, obj
, id
))
4517 js_AddNativeProperty(JSContext
*cx
, JSObject
*obj
, jsid id
,
4518 PropertyOp getter
, PropertyOp setter
, uint32 slot
,
4519 uintN attrs
, uintN flags
, intN shortid
)
4521 JS_ASSERT(!(flags
& Shape::METHOD
));
4524 * Purge the property cache of now-shadowed id in obj's scope chain. Do
4525 * this optimistically (assuming no failure below) before locking obj, so
4526 * we can lock the shadowed scope.
4528 js_PurgeScopeChain(cx
, obj
, id
);
4530 if (!obj
->ensureClassReservedSlots(cx
))
4533 /* Convert string indices to integers if appropriate. */
4534 id
= js_CheckForStringIndex(id
);
4535 return obj
->putProperty(cx
, id
, getter
, setter
, slot
, attrs
, flags
, shortid
);
4539 js_ChangeNativePropertyAttrs(JSContext
*cx
, JSObject
*obj
,
4540 const Shape
*shape
, uintN attrs
, uintN mask
,
4541 PropertyOp getter
, PropertyOp setter
)
4543 if (!obj
->ensureClassReservedSlots(cx
))
4545 return obj
->changeProperty(cx
, shape
, attrs
, mask
, getter
, setter
);
4549 js_DefineProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, const Value
*value
,
4550 PropertyOp getter
, PropertyOp setter
, uintN attrs
)
4552 return js_DefineNativeProperty(cx
, obj
, id
, *value
, getter
, setter
, attrs
,
4557 * Backward compatibility requires allowing addProperty hooks to mutate the
4558 * nominal initial value of a slotful property, while GC safety wants that
4559 * value to be stored before the call-out through the hook. Optimize to do
4560 * both while saving cycles for classes that stub their addProperty hook.
4563 CallAddPropertyHook(JSContext
*cx
, Class
*clasp
, JSObject
*obj
, const Shape
*shape
, Value
*vp
)
4565 if (clasp
->addProperty
!= PropertyStub
) {
4566 Value nominal
= *vp
;
4568 if (!CallJSPropertyOp(cx
, clasp
->addProperty
, obj
, shape
->id
, vp
))
4570 if (*vp
!= nominal
) {
4571 if (obj
->containsSlot(shape
->slot
))
4572 obj
->nativeSetSlot(shape
->slot
, *vp
);
4579 js_DefineNativeProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, const Value
&value
,
4580 PropertyOp getter
, PropertyOp setter
, uintN attrs
,
4581 uintN flags
, intN shortid
, JSProperty
**propp
,
4582 uintN defineHow
/* = 0 */)
4589 JS_ASSERT((defineHow
& ~(JSDNP_CACHE_RESULT
| JSDNP_DONT_PURGE
| JSDNP_SET_METHOD
)) == 0);
4590 LeaveTraceIfGlobalObject(cx
, obj
);
4592 /* Convert string indices to integers if appropriate. */
4593 id
= js_CheckForStringIndex(id
);
4596 * If defining a getter or setter, we must check for its counterpart and
4597 * update the attributes and property ops. A getter or setter is really
4598 * only half of a property.
4601 if (attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)) {
4606 * If JS_THREADSAFE and id is found, js_LookupProperty returns with
4607 * shape non-null and pobj locked. If pobj == obj, the property is
4608 * already in obj and obj has its own (mutable) scope. So if we are
4609 * defining a getter whose setter was already defined, or vice versa,
4610 * finish the job via obj->changeProperty, and refresh the property
4611 * cache line for (obj, id) to map shape.
4613 if (!js_LookupProperty(cx
, obj
, id
, &pobj
, &prop
))
4615 shape
= (Shape
*) prop
;
4616 if (shape
&& pobj
== obj
&& shape
->isAccessorDescriptor()) {
4617 shape
= obj
->changeProperty(cx
, shape
, attrs
,
4618 JSPROP_GETTER
| JSPROP_SETTER
,
4619 (attrs
& JSPROP_GETTER
)
4622 (attrs
& JSPROP_SETTER
)
4635 * Purge the property cache of any properties named by id that are about
4636 * to be shadowed in obj's scope chain unless it is known a priori that it
4637 * is not possible. We do this before locking obj to avoid nesting locks.
4639 if (!(defineHow
& JSDNP_DONT_PURGE
))
4640 js_PurgeScopeChain(cx
, obj
, id
);
4643 * Check whether a readonly property or setter is being defined on a known
4644 * prototype object. See the comment in jscntxt.h before protoHazardShape's
4645 * member declaration.
4647 if (obj
->isDelegate() && (attrs
& (JSPROP_READONLY
| JSPROP_SETTER
)))
4648 cx
->runtime
->protoHazardShape
= js_GenerateShape(cx
, false);
4650 /* Use the object's class getter and setter by default. */
4651 clasp
= obj
->getClass();
4652 if (!(defineHow
& JSDNP_SET_METHOD
)) {
4653 if (!getter
&& !(attrs
& JSPROP_GETTER
))
4654 getter
= clasp
->getProperty
;
4655 if (!setter
&& !(attrs
& JSPROP_SETTER
))
4656 setter
= clasp
->setProperty
;
4659 /* Get obj's own scope if it has one, or create a new one for obj. */
4660 if (!obj
->ensureClassReservedSlots(cx
))
4665 /* Add a new property, or replace an existing one of the same id. */
4666 if (defineHow
& JSDNP_SET_METHOD
) {
4667 JS_ASSERT(clasp
== &js_ObjectClass
);
4668 JS_ASSERT(IsFunctionObject(value
));
4669 JS_ASSERT(!(attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)));
4670 JS_ASSERT(!getter
&& !setter
);
4672 JSObject
*funobj
= &value
.toObject();
4673 if (FUN_OBJECT(GET_FUNCTION_PRIVATE(cx
, funobj
)) == funobj
) {
4674 flags
|= Shape::METHOD
;
4675 getter
= CastAsPropertyOp(funobj
);
4679 added
= !obj
->nativeContains(id
);
4680 uint32 oldShape
= obj
->shape();
4681 shape
= obj
->putProperty(cx
, id
, getter
, setter
, SHAPE_INVALID_SLOT
,
4682 attrs
, flags
, shortid
);
4687 * If shape is a method, the above call to putProperty suffices to
4688 * update the shape if necessary. But if scope->branded(), the shape
4689 * may not have changed and we may be overwriting a function-valued
4690 * property. See bug 560998.
4692 if (obj
->shape() == oldShape
&& obj
->branded() && shape
->slot
!= SHAPE_INVALID_SLOT
) {
4694 const Shape
*newshape
=
4696 obj
->methodWriteBarrier(cx
, *shape
, value
);
4697 JS_ASSERT(newshape
== shape
);
4701 /* Store value before calling addProperty, in case the latter GC's. */
4702 if (obj
->containsSlot(shape
->slot
)) {
4703 AbortRecordingIfUnexpectedGlobalWrite(cx
, obj
, shape
->slot
);
4704 obj
->nativeSetSlot(shape
->slot
, value
);
4707 /* XXXbe called with lock held */
4709 if (!CallAddPropertyHook(cx
, clasp
, obj
, shape
, &valueCopy
)) {
4710 obj
->removeProperty(cx
, id
);
4714 if (defineHow
& JSDNP_CACHE_RESULT
) {
4715 JS_ASSERT_NOT_ON_TRACE(cx
);
4717 JS_PROPERTY_CACHE(cx
).fill(cx
, obj
, 0, 0, obj
, shape
, true);
4718 TRACE_1(AddProperty
, obj
);
4722 *propp
= (JSProperty
*) shape
;
4726 error
: // TRACE_1 jumps here on error.
4731 #define SCOPE_DEPTH_ACCUM(bs,val) \
4732 JS_SCOPE_DEPTH_METERING(JS_BASIC_STATS_ACCUM(bs, val))
4735 * Call obj's resolve hook. obj is a native object and the caller holds its
4738 * cx, start, id, and flags are the parameters initially passed to the ongoing
4739 * lookup; objp and propp are its out parameters. obj is an object along
4740 * start's prototype chain.
4742 * There are four possible outcomes:
4744 * - On failure, report an error or exception, unlock obj, and return false.
4746 * - If we are alrady resolving a property of *curobjp, set *recursedp = true,
4747 * unlock obj, and return true.
4749 * - If the resolve hook finds or defines the sought property, set *objp and
4750 * *propp appropriately, set *recursedp = false, and return true with *objp's
4753 * - Otherwise no property was resolved. Set *propp = NULL and *recursedp = false
4757 CallResolveOp(JSContext
*cx
, JSObject
*start
, JSObject
*obj
, jsid id
, uintN flags
,
4758 JSObject
**objp
, JSProperty
**propp
, bool *recursedp
)
4760 Class
*clasp
= obj
->getClass();
4761 JSResolveOp resolve
= clasp
->resolve
;
4764 * Avoid recursion on (obj, id) already being resolved on cx.
4766 * Once we have successfully added an entry for (obj, key) to
4767 * cx->resolvingTable, control must go through cleanup: before
4768 * returning. But note that JS_DHASH_ADD may find an existing
4769 * entry, in which case we bail to suppress runaway recursion.
4771 JSResolvingKey key
= {obj
, id
};
4772 JSResolvingEntry
*entry
;
4773 if (!js_StartResolving(cx
, &key
, JSRESFLAG_LOOKUP
, &entry
))
4776 /* Already resolving id in obj -- suppress recursion. */
4780 uint32 generation
= cx
->resolvingTable
->generation
;
4786 const Shape
*shape
= NULL
;
4787 if (clasp
->flags
& JSCLASS_NEW_RESOLVE
) {
4788 JSNewResolveOp newresolve
= (JSNewResolveOp
)resolve
;
4789 if (flags
== JSRESOLVE_INFER
)
4790 flags
= js_InferFlags(cx
, 0);
4791 JSObject
*obj2
= (clasp
->flags
& JSCLASS_NEW_RESOLVE_GETS_START
) ? start
: NULL
;
4794 /* Protect id and all atoms from a GC nested in resolve. */
4795 AutoKeepAtoms
keep(cx
->runtime
);
4796 ok
= newresolve(cx
, obj
, id
, flags
, &obj2
);
4802 /* Resolved: lookup id again for backward compatibility. */
4803 if (!obj2
->isNative()) {
4804 /* Whoops, newresolve handed back a foreign obj2. */
4805 JS_ASSERT(obj2
!= obj
);
4806 ok
= obj2
->lookupProperty(cx
, id
, objp
, propp
);
4811 * Require that obj2 not be empty now, as we do for old-style
4812 * resolve. If it doesn't, then id was not truly resolved, and
4813 * we'll find it in the proto chain, or miss it if obj2's proto
4814 * is not on obj's proto chain. That last case is a "too bad!"
4817 if (!obj2
->nativeEmpty())
4818 shape
= obj2
->nativeLookup(id
);
4821 JS_ASSERT(!obj2
->nativeEmpty());
4827 * Old resolve always requires id re-lookup if obj is not empty after
4830 ok
= resolve(cx
, obj
, id
);
4833 JS_ASSERT(obj
->isNative());
4834 if (!obj
->nativeEmpty())
4835 shape
= obj
->nativeLookup(id
);
4841 *propp
= (JSProperty
*) shape
;
4843 js_StopResolving(cx
, &key
, JSRESFLAG_LOOKUP
, entry
, generation
);
4847 static JS_ALWAYS_INLINE
int
4848 js_LookupPropertyWithFlagsInline(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN flags
,
4849 JSObject
**objp
, JSProperty
**propp
)
4851 /* We should not get string indices which aren't already integers here. */
4852 JS_ASSERT(id
== js_CheckForStringIndex(id
));
4854 /* Search scopes starting with obj and following the prototype link. */
4855 JSObject
*start
= obj
;
4857 for (protoIndex
= 0; ; protoIndex
++) {
4858 const Shape
*shape
= obj
->nativeLookup(id
);
4860 SCOPE_DEPTH_ACCUM(&cx
->runtime
->protoLookupDepthStats
, protoIndex
);
4862 *propp
= (JSProperty
*) shape
;
4866 /* Try obj's class resolve hook if id was not found in obj's scope. */
4867 if (!shape
&& obj
->getClass()->resolve
!= JS_ResolveStub
) {
4869 if (!CallResolveOp(cx
, start
, obj
, id
, flags
, objp
, propp
, &recursed
))
4874 /* Recalculate protoIndex in case it was resolved on some other object. */
4876 for (JSObject
*proto
= start
; proto
&& proto
!= *objp
; proto
= proto
->getProto())
4878 SCOPE_DEPTH_ACCUM(&cx
->runtime
->protoLookupDepthStats
, protoIndex
);
4883 JSObject
*proto
= obj
->getProto();
4886 if (!proto
->isNative()) {
4887 if (!proto
->lookupProperty(cx
, id
, objp
, propp
))
4891 * Non-native objects must have either non-native lookup results,
4892 * or else native results from the non-native's prototype chain.
4894 * See JSStackFrame::getValidCalleeObject, where we depend on this
4895 * fact to force a prototype-delegated joined method accessed via
4896 * arguments.callee through the delegating |this| object's method
4899 if (*propp
&& (*objp
)->isNative()) {
4900 while ((proto
= proto
->getProto()) != *objp
)
4904 return protoIndex
+ 1;
4915 JS_FRIEND_API(JSBool
)
4916 js_LookupProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, JSObject
**objp
,
4919 /* Convert string indices to integers if appropriate. */
4920 id
= js_CheckForStringIndex(id
);
4922 return js_LookupPropertyWithFlagsInline(cx
, obj
, id
, cx
->resolveFlags
, objp
, propp
) >= 0;
4926 js_LookupPropertyWithFlags(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN flags
,
4927 JSObject
**objp
, JSProperty
**propp
)
4929 /* Convert string indices to integers if appropriate. */
4930 id
= js_CheckForStringIndex(id
);
4932 return js_LookupPropertyWithFlagsInline(cx
, obj
, id
, flags
, objp
, propp
);
4935 PropertyCacheEntry
*
4936 js_FindPropertyHelper(JSContext
*cx
, jsid id
, JSBool cacheResult
,
4937 JSObject
**objp
, JSObject
**pobjp
, JSProperty
**propp
)
4939 JSObject
*scopeChain
, *obj
, *parent
, *pobj
;
4940 PropertyCacheEntry
*entry
;
4941 int scopeIndex
, protoIndex
;
4944 JS_ASSERT_IF(cacheResult
, !JS_ON_TRACE(cx
));
4945 scopeChain
= &js_GetTopStackFrame(cx
)->scopeChain();
4947 /* Scan entries on the scope chain that we can cache across. */
4948 entry
= JS_NO_PROP_CACHE_FILL
;
4950 parent
= obj
->getParent();
4951 for (scopeIndex
= 0;
4953 ? js_IsCacheableNonGlobalScope(obj
)
4954 : !obj
->getOps()->lookupProperty
;
4957 js_LookupPropertyWithFlags(cx
, obj
, id
, cx
->resolveFlags
,
4965 Class
*clasp
= obj
->getClass();
4966 JS_ASSERT(pobj
->isNative());
4967 JS_ASSERT(pobj
->getClass() == clasp
);
4968 if (clasp
== &js_BlockClass
) {
4970 * A block instance on the scope chain is immutable and it
4971 * shares its shapes with its compile-time prototype.
4973 JS_ASSERT(pobj
== obj
);
4974 JS_ASSERT(pobj
->isClonedBlock());
4975 JS_ASSERT(protoIndex
== 0);
4977 /* Call and DeclEnvClass objects have no prototypes. */
4978 JS_ASSERT(!obj
->getProto());
4979 JS_ASSERT(protoIndex
== 0);
4982 JS_ASSERT(obj
->isNative());
4986 * We must check if pobj is native as a global object can have
4987 * non-native prototype.
4989 if (cacheResult
&& pobj
->isNative()) {
4990 entry
= JS_PROPERTY_CACHE(cx
).fill(cx
, scopeChain
, scopeIndex
,
4994 SCOPE_DEPTH_ACCUM(&cx
->runtime
->scopeSearchDepthStats
, scopeIndex
);
5003 parent
= obj
->getParent();
5007 if (!obj
->lookupProperty(cx
, id
, &pobj
, &prop
))
5010 PCMETER(JS_PROPERTY_CACHE(cx
).nofills
++);
5015 * We conservatively assume that a resolve hook could mutate the scope
5016 * chain during JSObject::lookupProperty. So we read parent here again.
5018 parent
= obj
->getParent();
5027 JS_ASSERT(!!pobj
== !!prop
);
5035 * On return, if |*pobjp| is a native object, then |*propp| is a |Shape *|.
5036 * Otherwise, its type and meaning depends on the host object's implementation.
5038 JS_FRIEND_API(JSBool
)
5039 js_FindProperty(JSContext
*cx
, jsid id
, JSObject
**objp
, JSObject
**pobjp
,
5042 return !!js_FindPropertyHelper(cx
, id
, false, objp
, pobjp
, propp
);
5046 js_FindIdentifierBase(JSContext
*cx
, JSObject
*scopeChain
, jsid id
)
5049 * This function should not be called for a global object or from the
5050 * trace and should have a valid cache entry for native scopeChain.
5052 JS_ASSERT(scopeChain
->getParent());
5053 JS_ASSERT(!JS_ON_TRACE(cx
));
5055 JSObject
*obj
= scopeChain
;
5058 * Loop over cacheable objects on the scope chain until we find a
5059 * property. We also stop when we reach the global object skipping any
5060 * farther checks or lookups. For details see the JSOP_BINDNAME case of
5063 * The test order here matters because js_IsCacheableNonGlobalScope
5064 * must not be passed a global object (i.e. one with null parent).
5066 for (int scopeIndex
= 0;
5067 !obj
->getParent() || js_IsCacheableNonGlobalScope(obj
);
5071 int protoIndex
= js_LookupPropertyWithFlags(cx
, obj
, id
,
5077 if (!pobj
->isNative()) {
5078 JS_ASSERT(!obj
->getParent());
5081 JS_ASSERT_IF(obj
->getParent(), pobj
->getClass() == obj
->getClass());
5083 PropertyCacheEntry
*entry
=
5085 JS_PROPERTY_CACHE(cx
).fill(cx
, scopeChain
, scopeIndex
, protoIndex
, pobj
,
5091 JSObject
*parent
= obj
->getParent();
5097 /* Loop until we find a property or reach the global object. */
5101 if (!obj
->lookupProperty(cx
, id
, &pobj
, &prop
))
5107 * We conservatively assume that a resolve hook could mutate the scope
5108 * chain during JSObject::lookupProperty. So we must check if parent is
5109 * not null here even if it wasn't before the lookup.
5111 JSObject
*parent
= obj
->getParent();
5115 } while (obj
->getParent());
5119 static JS_ALWAYS_INLINE JSBool
5120 js_NativeGetInline(JSContext
*cx
, JSObject
*receiver
, JSObject
*obj
, JSObject
*pobj
,
5121 const Shape
*shape
, uintN getHow
, Value
*vp
)
5123 LeaveTraceIfGlobalObject(cx
, pobj
);
5128 JS_ASSERT(pobj
->isNative());
5131 if (slot
!= SHAPE_INVALID_SLOT
) {
5132 *vp
= pobj
->nativeGetSlot(slot
);
5133 JS_ASSERT(!vp
->isMagic());
5137 if (shape
->hasDefaultGetter())
5140 if (JS_UNLIKELY(shape
->isMethod()) && (getHow
& JSGET_NO_METHOD_BARRIER
)) {
5141 JS_ASSERT(&shape
->methodObject() == &vp
->toObject());
5145 sample
= cx
->runtime
->propertyRemovals
;
5147 AutoShapeRooter
tvr(cx
, shape
);
5148 AutoObjectRooter
tvr2(cx
, pobj
);
5149 if (!shape
->get(cx
, receiver
, obj
, pobj
, vp
))
5153 if (pobj
->containsSlot(slot
) &&
5154 (JS_LIKELY(cx
->runtime
->propertyRemovals
== sample
) ||
5155 pobj
->nativeContains(*shape
))) {
5156 if (!pobj
->methodWriteBarrier(cx
, *shape
, *vp
))
5158 pobj
->nativeSetSlot(slot
, *vp
);
5165 js_NativeGet(JSContext
*cx
, JSObject
*obj
, JSObject
*pobj
, const Shape
*shape
, uintN getHow
,
5168 return js_NativeGetInline(cx
, obj
, obj
, pobj
, shape
, getHow
, vp
);
5172 js_NativeSet(JSContext
*cx
, JSObject
*obj
, const Shape
*shape
, bool added
, Value
*vp
)
5174 LeaveTraceIfGlobalObject(cx
, obj
);
5179 JS_ASSERT(obj
->isNative());
5182 if (slot
!= SHAPE_INVALID_SLOT
) {
5183 JS_ASSERT(obj
->containsSlot(slot
));
5185 /* If shape has a stub setter, keep obj locked and just store *vp. */
5186 if (shape
->hasDefaultSetter()) {
5187 if (!added
&& !obj
->methodWriteBarrier(cx
, *shape
, *vp
))
5189 AbortRecordingIfUnexpectedGlobalWrite(cx
, obj
, slot
);
5190 obj
->nativeSetSlot(slot
, *vp
);
5195 * Allow API consumers to create shared properties with stub setters.
5196 * Such properties effectively function as data descriptors which are
5197 * not writable, so attempting to set such a property should do nothing
5198 * or throw if we're in strict mode.
5200 if (!shape
->hasGetterValue() && shape
->hasDefaultSetter())
5201 return js_ReportGetterOnlyAssignment(cx
);
5204 sample
= cx
->runtime
->propertyRemovals
;
5206 AutoShapeRooter
tvr(cx
, shape
);
5207 if (!shape
->set(cx
, obj
, vp
))
5210 JS_ASSERT_IF(!obj
->inDictionaryMode(), shape
->slot
== slot
);
5214 if (obj
->containsSlot(slot
) &&
5215 (JS_LIKELY(cx
->runtime
->propertyRemovals
== sample
) ||
5216 obj
->nativeContains(*shape
))) {
5217 if (!added
&& !obj
->methodWriteBarrier(cx
, *shape
, *vp
))
5219 AbortRecordingIfUnexpectedGlobalWrite(cx
, obj
, slot
);
5220 obj
->setSlot(slot
, *vp
);
5226 static JS_ALWAYS_INLINE
bool
5227 js_GetPropertyHelperWithShapeInline(JSContext
*cx
, JSObject
*obj
, JSObject
*receiver
, jsid id
,
5228 uintN getHow
, Value
*vp
,
5229 const Shape
**shapeOut
, JSObject
**holderOut
)
5231 JSObject
*aobj
, *obj2
;
5236 JS_ASSERT_IF(getHow
& JSGET_CACHE_RESULT
, !JS_ON_TRACE(cx
));
5240 /* Convert string indices to integers if appropriate. */
5241 id
= js_CheckForStringIndex(id
);
5243 aobj
= js_GetProtoIfDenseArray(obj
);
5244 /* This call site is hot -- use the always-inlined variant of js_LookupPropertyWithFlags(). */
5245 protoIndex
= js_LookupPropertyWithFlagsInline(cx
, aobj
, id
, cx
->resolveFlags
,
5255 if (!CallJSPropertyOp(cx
, obj
->getClass()->getProperty
, obj
, id
, vp
))
5258 PCMETER(getHow
& JSGET_CACHE_RESULT
&& JS_PROPERTY_CACHE(cx
).nofills
++);
5261 * Give a strict warning if foo.bar is evaluated by a script for an
5262 * object foo with no property named 'bar'.
5265 if (vp
->isUndefined() && ((pc
= js_GetCurrentBytecodePC(cx
)) != NULL
)) {
5270 if (op
== JSOP_TRAP
) {
5271 JS_ASSERT_NOT_ON_TRACE(cx
);
5272 op
= JS_GetTrapOpcode(cx
, cx
->fp()->script(), pc
);
5274 if (op
== JSOP_GETXPROP
) {
5275 flags
= JSREPORT_ERROR
;
5277 if (!JS_HAS_STRICT_OPTION(cx
) ||
5278 (op
!= JSOP_GETPROP
&& op
!= JSOP_GETELEM
) ||
5279 js_CurrentPCIsInImacro(cx
)) {
5284 * XXX do not warn about missing __iterator__ as the function
5285 * may be called from JS_GetMethodById. See bug 355145.
5287 if (JSID_IS_ATOM(id
, cx
->runtime
->atomState
.iteratorAtom
))
5290 /* Do not warn about tests like (obj[prop] == undefined). */
5291 if (cx
->resolveFlags
== JSRESOLVE_INFER
) {
5293 pc
+= js_CodeSpec
[op
].length
;
5294 if (Detecting(cx
, pc
))
5296 } else if (cx
->resolveFlags
& JSRESOLVE_DETECTING
) {
5300 flags
= JSREPORT_WARNING
| JSREPORT_STRICT
;
5303 /* Ok, bad undefined property reference: whine about it. */
5304 if (!js_ReportValueErrorFlags(cx
, flags
, JSMSG_UNDEFINED_PROP
,
5305 JSDVG_IGNORE_STACK
, IdToValue(id
),
5306 NULL
, NULL
, NULL
)) {
5313 if (!obj2
->isNative()) {
5314 return obj2
->isProxy()
5315 ? JSProxy::get(cx
, obj2
, receiver
, id
, vp
)
5316 : obj2
->getProperty(cx
, id
, vp
);
5319 shape
= (Shape
*) prop
;
5322 if (getHow
& JSGET_CACHE_RESULT
) {
5323 JS_ASSERT_NOT_ON_TRACE(cx
);
5324 JS_PROPERTY_CACHE(cx
).fill(cx
, aobj
, 0, protoIndex
, obj2
, shape
);
5327 /* This call site is hot -- use the always-inlined variant of js_NativeGet(). */
5328 if (!js_NativeGetInline(cx
, receiver
, obj
, obj2
, shape
, getHow
, vp
))
5335 js_GetPropertyHelperWithShape(JSContext
*cx
, JSObject
*obj
, JSObject
*receiver
, jsid id
,
5336 uint32 getHow
, Value
*vp
,
5337 const Shape
**shapeOut
, JSObject
**holderOut
)
5339 return js_GetPropertyHelperWithShapeInline(cx
, obj
, receiver
, id
, getHow
, vp
,
5340 shapeOut
, holderOut
);
5343 static JS_ALWAYS_INLINE JSBool
5344 js_GetPropertyHelperInline(JSContext
*cx
, JSObject
*obj
, JSObject
*receiver
, jsid id
,
5345 uint32 getHow
, Value
*vp
)
5349 return js_GetPropertyHelperWithShapeInline(cx
, obj
, receiver
, id
, getHow
, vp
, &shape
, &holder
);
5353 js_GetPropertyHelper(JSContext
*cx
, JSObject
*obj
, jsid id
, uint32 getHow
, Value
*vp
)
5355 return js_GetPropertyHelperInline(cx
, obj
, obj
, id
, getHow
, vp
);
5359 js_GetProperty(JSContext
*cx
, JSObject
*obj
, JSObject
*receiver
, jsid id
, Value
*vp
)
5361 /* This call site is hot -- use the always-inlined variant of js_GetPropertyHelper(). */
5362 return js_GetPropertyHelperInline(cx
, obj
, receiver
, id
, JSGET_METHOD_BARRIER
, vp
);
5366 js::GetPropertyDefault(JSContext
*cx
, JSObject
*obj
, jsid id
, const Value
&def
, Value
*vp
)
5370 if (js_LookupPropertyWithFlags(cx
, obj
, id
, JSRESOLVE_QUALIFIED
, &obj2
, &prop
) < 0)
5378 return js_GetProperty(cx
, obj2
, id
, vp
);
5382 js_GetMethod(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN getHow
, Value
*vp
)
5384 JSAutoResolveFlags
rf(cx
, JSRESOLVE_QUALIFIED
);
5386 PropertyIdOp op
= obj
->getOps()->getProperty
;
5388 #if JS_HAS_XML_SUPPORT
5389 JS_ASSERT(!obj
->isXML());
5391 return js_GetPropertyHelper(cx
, obj
, id
, getHow
, vp
);
5393 JS_ASSERT_IF(getHow
& JSGET_CACHE_RESULT
, obj
->isDenseArray());
5394 #if JS_HAS_XML_SUPPORT
5396 return js_GetXMLMethod(cx
, obj
, id
, vp
);
5398 return op(cx
, obj
, obj
, id
, vp
);
5402 js_CheckUndeclaredVarAssignment(JSContext
*cx
, JSString
*propname
)
5404 JSStackFrame
*const fp
= js_GetTopStackFrame(cx
);
5408 /* If neither cx nor the code is strict, then no check is needed. */
5409 if (!(fp
->isScriptFrame() && fp
->script()->strictModeCode
) &&
5410 !JS_HAS_STRICT_OPTION(cx
)) {
5414 JSAutoByteString
bytes(cx
, propname
);
5416 JS_ReportErrorFlagsAndNumber(cx
,
5417 (JSREPORT_WARNING
| JSREPORT_STRICT
5418 | JSREPORT_STRICT_MODE_ERROR
),
5419 js_GetErrorMessage
, NULL
,
5420 JSMSG_UNDECLARED_VAR
, bytes
.ptr());
5424 JSObject::reportReadOnly(JSContext
* cx
, jsid id
, uintN report
)
5426 return js_ReportValueErrorFlags(cx
, report
, JSMSG_READ_ONLY
,
5427 JSDVG_IGNORE_STACK
, IdToValue(id
), NULL
,
5432 JSObject::reportNotConfigurable(JSContext
* cx
, jsid id
, uintN report
)
5434 return js_ReportValueErrorFlags(cx
, report
, JSMSG_CANT_DELETE
,
5435 JSDVG_IGNORE_STACK
, IdToValue(id
), NULL
,
5440 JSObject::reportNotExtensible(JSContext
*cx
, uintN report
)
5442 return js_ReportValueErrorFlags(cx
, report
, JSMSG_OBJECT_NOT_EXTENSIBLE
,
5443 JSDVG_IGNORE_STACK
, ObjectValue(*this),
5448 js_SetPropertyHelper(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN defineHow
,
5449 Value
*vp
, JSBool strict
)
5458 PropertyOp getter
, setter
;
5461 JS_ASSERT((defineHow
&
5462 ~(JSDNP_CACHE_RESULT
| JSDNP_SET_METHOD
| JSDNP_UNQUALIFIED
)) == 0);
5463 if (defineHow
& JSDNP_CACHE_RESULT
)
5464 JS_ASSERT_NOT_ON_TRACE(cx
);
5466 /* Convert string indices to integers if appropriate. */
5467 id
= js_CheckForStringIndex(id
);
5469 protoIndex
= js_LookupPropertyWithFlags(cx
, obj
, id
, cx
->resolveFlags
,
5474 if (!pobj
->isNative()) {
5475 if (pobj
->isProxy()) {
5476 AutoPropertyDescriptorRooter
pd(cx
);
5477 if (!JSProxy::getPropertyDescriptor(cx
, pobj
, id
, true, &pd
))
5480 if (pd
.attrs
& JSPROP_SHARED
)
5481 return CallSetter(cx
, obj
, id
, pd
.setter
, pd
.attrs
, pd
.shortid
, vp
);
5483 if (pd
.attrs
& JSPROP_READONLY
) {
5485 return obj
->reportReadOnly(cx
, id
);
5486 if (JS_HAS_STRICT_OPTION(cx
))
5487 return obj
->reportReadOnly(cx
, id
, JSREPORT_STRICT
| JSREPORT_WARNING
);
5495 /* We should never add properties to lexical blocks. */
5496 JS_ASSERT(!obj
->isBlock());
5498 if (!obj
->getParent() &&
5499 (defineHow
& JSDNP_UNQUALIFIED
) &&
5500 !js_CheckUndeclaredVarAssignment(cx
, JSID_TO_STRING(id
))) {
5504 shape
= (Shape
*) prop
;
5507 * Now either shape is null, meaning id was not found in obj or one of its
5508 * prototypes; or shape is non-null, meaning id was found directly in pobj.
5510 attrs
= JSPROP_ENUMERATE
;
5513 clasp
= obj
->getClass();
5514 getter
= clasp
->getProperty
;
5515 setter
= clasp
->setProperty
;
5518 /* ES5 8.12.4 [[Put]] step 2. */
5519 if (shape
->isAccessorDescriptor()) {
5520 if (shape
->hasDefaultSetter())
5521 return js_ReportGetterOnlyAssignment(cx
);
5523 JS_ASSERT(shape
->isDataDescriptor());
5525 if (!shape
->writable()) {
5526 PCMETER((defineHow
& JSDNP_CACHE_RESULT
) && JS_PROPERTY_CACHE(cx
).rofills
++);
5528 /* Error in strict mode code, warn with strict option, otherwise do nothing. */
5530 return obj
->reportReadOnly(cx
, id
);
5531 if (JS_HAS_STRICT_OPTION(cx
))
5532 return obj
->reportReadOnly(cx
, id
, JSREPORT_STRICT
| JSREPORT_WARNING
);
5537 attrs
= shape
->attributes();
5540 * We found id in a prototype object: prepare to share or shadow.
5542 if (!shape
->shadowable()) {
5543 if (defineHow
& JSDNP_CACHE_RESULT
)
5544 JS_PROPERTY_CACHE(cx
).fill(cx
, obj
, 0, protoIndex
, pobj
, shape
);
5546 if (shape
->hasDefaultSetter() && !shape
->hasGetterValue())
5549 return shape
->set(cx
, obj
, vp
);
5553 * Preserve attrs except JSPROP_SHARED, getter, and setter when
5554 * shadowing any property that has no slot (is shared). We must
5555 * clear the shared attribute for the shadowing shape so that the
5556 * property in obj that it defines has a slot to retain the value
5557 * being set, in case the setter simply cannot operate on instances
5558 * of obj's class by storing the value in some class-specific
5561 * A subset of slotless shared properties is the set of properties
5562 * with shortids, which must be preserved too. An old API requires
5563 * that the property's getter and setter receive the shortid, not
5564 * id, when they are called on the shadowing property that we are
5565 * about to create in obj.
5567 if (!shape
->hasSlot()) {
5568 defineHow
&= ~JSDNP_SET_METHOD
;
5569 if (shape
->hasShortID()) {
5570 flags
= Shape::HAS_SHORTID
;
5571 shortid
= shape
->shortid
;
5573 attrs
&= ~JSPROP_SHARED
;
5574 getter
= shape
->getter();
5575 setter
= shape
->setter();
5577 /* Restore attrs to the ECMA default for new properties. */
5578 attrs
= JSPROP_ENUMERATE
;
5582 * Forget we found the proto-property now that we've copied any
5583 * needed member values.
5588 JS_ASSERT_IF(shape
&& shape
->isMethod(), pobj
->hasMethodBarrier());
5589 JS_ASSERT_IF(shape
&& shape
->isMethod(),
5590 &pobj
->getSlot(shape
->slot
).toObject() == &shape
->methodObject());
5591 if (shape
&& (defineHow
& JSDNP_SET_METHOD
)) {
5593 * JSOP_SETMETHOD is assigning to an existing own property. If it
5594 * is an identical method property, do nothing. Otherwise downgrade
5595 * to ordinary assignment. Either way, do not fill the property
5596 * cache, as the interpreter has no fast path for these unusual
5599 bool identical
= shape
->isMethod() && &shape
->methodObject() == &vp
->toObject();
5601 shape
= obj
->methodShapeChange(cx
, *shape
);
5605 JSObject
*funobj
= &vp
->toObject();
5606 JSFunction
*fun
= funobj
->getFunctionPrivate();
5607 if (fun
== funobj
) {
5608 funobj
= CloneFunctionObject(cx
, fun
, fun
->parent
);
5611 vp
->setObject(*funobj
);
5614 return identical
|| js_NativeSet(cx
, obj
, shape
, false, vp
);
5620 if (!obj
->isExtensible()) {
5621 /* Error in strict mode code, warn with strict option, otherwise do nothing. */
5623 return obj
->reportNotExtensible(cx
);
5624 if (JS_HAS_STRICT_OPTION(cx
))
5625 return obj
->reportNotExtensible(cx
, JSREPORT_STRICT
| JSREPORT_WARNING
);
5630 * Purge the property cache of now-shadowed id in obj's scope chain.
5631 * Do this early, before locking obj to avoid nesting locks.
5633 js_PurgeScopeChain(cx
, obj
, id
);
5635 /* Find or make a property descriptor with the right heritage. */
5636 if (!obj
->ensureClassReservedSlots(cx
))
5640 * Check for Object class here to avoid defining a method on a class
5641 * with magic resolve, addProperty, getProperty, etc. hooks.
5643 if ((defineHow
& JSDNP_SET_METHOD
) && obj
->canHaveMethodBarrier()) {
5644 JS_ASSERT(IsFunctionObject(*vp
));
5645 JS_ASSERT(!(attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)));
5647 JSObject
*funobj
= &vp
->toObject();
5648 JSFunction
*fun
= GET_FUNCTION_PRIVATE(cx
, funobj
);
5649 if (fun
== funobj
) {
5650 flags
|= Shape::METHOD
;
5651 getter
= CastAsPropertyOp(funobj
);
5655 shape
= obj
->putProperty(cx
, id
, getter
, setter
, SHAPE_INVALID_SLOT
,
5656 attrs
, flags
, shortid
);
5660 if (defineHow
& JSDNP_CACHE_RESULT
)
5661 TRACE_1(AddProperty
, obj
);
5664 * Initialize the new property value (passed to setter) to undefined.
5665 * Note that we store before calling addProperty, to match the order
5666 * in js_DefineNativeProperty.
5668 if (obj
->containsSlot(shape
->slot
))
5669 obj
->nativeSetSlot(shape
->slot
, UndefinedValue());
5671 /* XXXbe called with obj locked */
5672 if (!CallAddPropertyHook(cx
, clasp
, obj
, shape
, vp
)) {
5673 obj
->removeProperty(cx
, id
);
5679 if (defineHow
& JSDNP_CACHE_RESULT
)
5680 JS_PROPERTY_CACHE(cx
).fill(cx
, obj
, 0, 0, obj
, shape
, added
);
5682 return js_NativeSet(cx
, obj
, shape
, added
, vp
);
5685 error
: // TRACE_1 jumps here in case of error.
5691 js_SetProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
, JSBool strict
)
5693 return js_SetPropertyHelper(cx
, obj
, id
, 0, vp
, strict
);
5697 js_GetAttributes(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN
*attrsp
)
5700 if (!js_LookupProperty(cx
, obj
, id
, &obj
, &prop
))
5706 if (!obj
->isNative())
5707 return obj
->getAttributes(cx
, id
, attrsp
);
5709 const Shape
*shape
= (Shape
*)prop
;
5710 *attrsp
= shape
->attributes();
5715 js_SetNativeAttributes(JSContext
*cx
, JSObject
*obj
, Shape
*shape
, uintN attrs
)
5717 JS_ASSERT(obj
->isNative());
5718 return !!js_ChangeNativePropertyAttrs(cx
, obj
, shape
, attrs
, 0,
5719 shape
->getter(), shape
->setter());
5723 js_SetAttributes(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN
*attrsp
)
5726 if (!js_LookupProperty(cx
, obj
, id
, &obj
, &prop
))
5730 return obj
->isNative()
5731 ? js_SetNativeAttributes(cx
, obj
, (Shape
*) prop
, *attrsp
)
5732 : obj
->setAttributes(cx
, id
, attrsp
);
5736 js_DeleteProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*rval
, JSBool strict
)
5742 rval
->setBoolean(true);
5744 /* Convert string indices to integers if appropriate. */
5745 id
= js_CheckForStringIndex(id
);
5747 if (!js_LookupProperty(cx
, obj
, id
, &proto
, &prop
))
5749 if (!prop
|| proto
!= obj
) {
5751 * If the property was found in a native prototype, check whether it's
5752 * shared and permanent. Such a property stands for direct properties
5753 * in all delegating objects, matching ECMA semantics without bloating
5754 * each delegating object.
5756 if (prop
&& proto
->isNative()) {
5757 shape
= (Shape
*)prop
;
5758 if (shape
->isSharedPermanent()) {
5760 return obj
->reportNotConfigurable(cx
, id
);
5761 rval
->setBoolean(false);
5767 * If no property, or the property comes unshared or impermanent from
5768 * a prototype, call the class's delProperty hook, passing rval as the
5771 return CallJSPropertyOp(cx
, obj
->getClass()->delProperty
, obj
, id
, rval
);
5774 shape
= (Shape
*)prop
;
5775 if (!shape
->configurable()) {
5777 return obj
->reportNotConfigurable(cx
, id
);
5778 rval
->setBoolean(false);
5782 if (!CallJSPropertyOp(cx
, obj
->getClass()->delProperty
, obj
, SHAPE_USERID(shape
), rval
))
5785 if (obj
->containsSlot(shape
->slot
)) {
5786 const Value
&v
= obj
->nativeGetSlot(shape
->slot
);
5790 * Delete is rare enough that we can take the hit of checking for an
5791 * active cloned method function object that must be homed to a callee
5792 * slot on the active stack frame before this delete completes, in case
5793 * someone saved the clone and checks it against foo.caller for a foo
5794 * called from the active method.
5796 * We do not check suspended frames. They can't be reached via caller,
5797 * so the only way they could have the method's joined function object
5798 * as callee is through an API abusage. We break any such edge case.
5800 if (obj
->hasMethodBarrier()) {
5803 if (IsFunctionObject(v
, &funobj
)) {
5804 JSFunction
*fun
= GET_FUNCTION_PRIVATE(cx
, funobj
);
5806 if (fun
!= funobj
) {
5807 for (JSStackFrame
*fp
= cx
->maybefp(); fp
; fp
= fp
->prev()) {
5808 if (fp
->isFunctionFrame() &&
5809 &fp
->callee() == &fun
->compiledFunObj() &&
5810 fp
->thisValue().isObject())
5812 JSObject
*tmp
= &fp
->thisValue().toObject();
5815 fp
->calleeValue().setObject(*funobj
);
5818 } while ((tmp
= tmp
->getProto()) != NULL
);
5826 return obj
->removeProperty(cx
, id
) && js_SuppressDeletedProperty(cx
, obj
, id
);
5832 HasNativeMethod(JSObject
*obj
, jsid methodid
, Native native
)
5834 const Shape
*shape
= obj
->nativeLookup(methodid
);
5835 if (!shape
|| !shape
->hasDefaultGetter() || !obj
->containsSlot(shape
->slot
))
5838 const Value
&fval
= obj
->nativeGetSlot(shape
->slot
);
5840 if (!IsFunctionObject(fval
, &funobj
) || funobj
->getFunctionPrivate()->maybeNative() != native
)
5847 * When we have an object of a builtin class, we don't quite know what its
5848 * valueOf/toString methods are, since these methods may have been overwritten
5849 * or shadowed. However, we can still do better than js_TryMethod by
5850 * hard-coding the necessary properties for us to find the native we expect.
5852 * TODO: a per-thread shape-based cache would be faster and simpler.
5854 static JS_ALWAYS_INLINE
bool
5855 ClassMethodIsNative(JSContext
*cx
, JSObject
*obj
, Class
*clasp
, jsid methodid
,
5858 JS_ASSERT(obj
->getClass() == clasp
);
5860 if (HasNativeMethod(obj
, methodid
, native
))
5863 JSObject
*pobj
= obj
->getProto();
5864 return pobj
&& pobj
->getClass() == clasp
&&
5865 HasNativeMethod(pobj
, methodid
, native
);
5869 DefaultValue(JSContext
*cx
, JSObject
*obj
, JSType hint
, Value
*vp
)
5871 JS_ASSERT(hint
!= JSTYPE_OBJECT
&& hint
!= JSTYPE_FUNCTION
);
5873 Value v
= ObjectValue(*obj
);
5874 if (hint
== JSTYPE_STRING
) {
5875 /* Optimize (new String(...)).toString(). */
5876 if (obj
->getClass() == &js_StringClass
&&
5877 ClassMethodIsNative(cx
, obj
,
5879 ATOM_TO_JSID(cx
->runtime
->atomState
.toStringAtom
),
5881 *vp
= obj
->getPrimitiveThis();
5885 if (!js_TryMethod(cx
, obj
, cx
->runtime
->atomState
.toStringAtom
, 0, NULL
, &v
))
5887 if (!v
.isPrimitive()) {
5888 if (!obj
->getClass()->convert(cx
, obj
, hint
, &v
))
5892 /* Optimize (new String(...)).valueOf(). */
5893 Class
*clasp
= obj
->getClass();
5894 if ((clasp
== &js_StringClass
&&
5895 ClassMethodIsNative(cx
, obj
, &js_StringClass
,
5896 ATOM_TO_JSID(cx
->runtime
->atomState
.valueOfAtom
),
5897 js_str_toString
)) ||
5898 (clasp
== &js_NumberClass
&&
5899 ClassMethodIsNative(cx
, obj
, &js_NumberClass
,
5900 ATOM_TO_JSID(cx
->runtime
->atomState
.valueOfAtom
),
5902 *vp
= obj
->getPrimitiveThis();
5906 if (!obj
->getClass()->convert(cx
, obj
, hint
, &v
))
5909 JS_ASSERT(hint
!= TypeOfValue(cx
, v
));
5910 if (!js_TryMethod(cx
, obj
, cx
->runtime
->atomState
.toStringAtom
, 0, NULL
, &v
))
5915 /* Avoid recursive death when decompiling in js_ReportValueError. */
5917 if (hint
== JSTYPE_STRING
) {
5918 str
= JS_InternString(cx
, obj
->getClass()->name
);
5924 vp
->setObject(*obj
);
5925 js_ReportValueError2(cx
, JSMSG_CANT_CONVERT_TO
,
5926 JSDVG_SEARCH_STACK
, *vp
, str
,
5927 (hint
== JSTYPE_VOID
)
5929 : JS_TYPE_STR(hint
));
5936 } /* namespace js */
5938 JS_FRIEND_API(JSBool
)
5939 js_Enumerate(JSContext
*cx
, JSObject
*obj
, JSIterateOp enum_op
, Value
*statep
, jsid
*idp
)
5941 /* If the class has a custom JSCLASS_NEW_ENUMERATE hook, call it. */
5942 Class
*clasp
= obj
->getClass();
5943 JSEnumerateOp enumerate
= clasp
->enumerate
;
5944 if (clasp
->flags
& JSCLASS_NEW_ENUMERATE
) {
5945 JS_ASSERT(enumerate
!= JS_EnumerateStub
);
5946 return ((NewEnumerateOp
) enumerate
)(cx
, obj
, enum_op
, statep
, idp
);
5949 if (!enumerate(cx
, obj
))
5952 /* Tell InitNativeIterator to treat us like a native object. */
5953 JS_ASSERT(enum_op
== JSENUMERATE_INIT
|| enum_op
== JSENUMERATE_INIT_ALL
);
5954 statep
->setMagic(JS_NATIVE_ENUMERATE
);
5961 CheckAccess(JSContext
*cx
, JSObject
*obj
, jsid id
, JSAccessMode mode
,
5962 Value
*vp
, uintN
*attrsp
)
5969 JSSecurityCallbacks
*callbacks
;
5970 CheckAccessOp check
;
5972 while (JS_UNLIKELY(obj
->getClass() == &js_WithClass
))
5973 obj
= obj
->getProto();
5975 writing
= (mode
& JSACC_WRITE
) != 0;
5976 switch (mode
& JSACC_TYPEMASK
) {
5980 vp
->setObjectOrNull(obj
->getProto());
5981 *attrsp
= JSPROP_PERMANENT
;
5985 JS_ASSERT(!writing
);
5987 vp
->setObject(*obj
->getParent());
5988 *attrsp
= JSPROP_READONLY
| JSPROP_PERMANENT
;
5992 if (!obj
->lookupProperty(cx
, id
, &pobj
, &prop
))
6002 if (!pobj
->isNative()) {
6010 shape
= (Shape
*)prop
;
6011 *attrsp
= shape
->attributes();
6013 if (pobj
->containsSlot(shape
->slot
))
6014 *vp
= pobj
->nativeGetSlot(shape
->slot
);
6021 * If obj's class has a stub (null) checkAccess hook, use the per-runtime
6022 * checkObjectAccess callback, if configured.
6024 * We don't want to require all classes to supply a checkAccess hook; we
6025 * need that hook only for certain classes used when precompiling scripts
6026 * and functions ("brutal sharing"). But for general safety of built-in
6027 * magic properties like __proto__, we route all access checks, even for
6028 * classes that stub out checkAccess, through the global checkObjectAccess
6029 * hook. This covers precompilation-based sharing and (possibly
6030 * unintended) runtime sharing across trust boundaries.
6032 clasp
= pobj
->getClass();
6033 check
= clasp
->checkAccess
;
6035 callbacks
= JS_GetSecurityCallbacks(cx
);
6036 check
= callbacks
? Valueify(callbacks
->checkObjectAccess
) : NULL
;
6038 return !check
|| check(cx
, pobj
, id
, mode
, vp
);
6044 js_TypeOf(JSContext
*cx
, JSObject
*obj
)
6047 * ECMA 262, 11.4.3 says that any native object that implements
6048 * [[Call]] should be of type "function". However, RegExp is of
6049 * type "object", not "function", for Web compatibility.
6051 if (obj
->isCallable()) {
6052 return (obj
->getClass() != &js_RegExpClass
)
6057 return JSTYPE_OBJECT
;
6061 js_IsDelegate(JSContext
*cx
, JSObject
*obj
, const Value
&v
)
6063 if (v
.isPrimitive())
6065 JSObject
*obj2
= &v
.toObject();
6066 while ((obj2
= obj2
->getProto()) != NULL
) {
6074 js::FindClassPrototype(JSContext
*cx
, JSObject
*scopeobj
, JSProtoKey protoKey
,
6075 JSObject
**protop
, Class
*clasp
)
6078 if (!js_FindClassObject(cx
, scopeobj
, protoKey
, &v
, clasp
))
6081 if (IsFunctionObject(v
)) {
6082 JSObject
*ctor
= &v
.toObject();
6083 if (!ctor
->getProperty(cx
, ATOM_TO_JSID(cx
->runtime
->atomState
.classPrototypeAtom
), &v
))
6087 *protop
= v
.isObject() ? &v
.toObject() : NULL
;
6092 * The first part of this function has been hand-expanded and optimized into
6093 * NewBuiltinClassInstance in jsobjinlines.h.
6096 js_GetClassPrototype(JSContext
*cx
, JSObject
*scopeobj
, JSProtoKey protoKey
,
6097 JSObject
**protop
, Class
*clasp
)
6099 VOUCH_DOES_NOT_REQUIRE_STACK();
6100 JS_ASSERT(JSProto_Null
<= protoKey
);
6101 JS_ASSERT(protoKey
< JSProto_LIMIT
);
6103 if (protoKey
!= JSProto_Null
) {
6106 scopeobj
= &cx
->fp()->scopeChain();
6108 scopeobj
= cx
->globalObject
;
6115 scopeobj
= scopeobj
->getGlobal();
6116 if (scopeobj
->isGlobal()) {
6117 const Value
&v
= scopeobj
->getReservedSlot(JSProto_LIMIT
+ protoKey
);
6119 *protop
= &v
.toObject();
6125 return FindClassPrototype(cx
, scopeobj
, protoKey
, protop
, clasp
);
6129 js_SetClassPrototype(JSContext
*cx
, JSObject
*ctor
, JSObject
*proto
, uintN attrs
)
6132 * Use the given attributes for the prototype property of the constructor,
6133 * as user-defined constructors have a DontDelete prototype (which may be
6134 * reset), while native or "system" constructors have DontEnum | ReadOnly |
6137 if (!ctor
->defineProperty(cx
, ATOM_TO_JSID(cx
->runtime
->atomState
.classPrototypeAtom
),
6138 ObjectOrNullValue(proto
), PropertyStub
, PropertyStub
, attrs
)) {
6143 * ECMA says that Object.prototype.constructor, or f.prototype.constructor
6144 * for a user-defined function f, is DontEnum.
6146 return proto
->defineProperty(cx
, ATOM_TO_JSID(cx
->runtime
->atomState
.constructorAtom
),
6147 ObjectOrNullValue(ctor
), PropertyStub
, PropertyStub
, 0);
6151 js_PrimitiveToObject(JSContext
*cx
, Value
*vp
)
6154 JS_ASSERT(v
.isPrimitive());
6158 clasp
= &js_NumberClass
;
6159 } else if (v
.isString()) {
6160 clasp
= &js_StringClass
;
6162 JS_ASSERT(v
.isBoolean());
6163 clasp
= &js_BooleanClass
;
6166 JSObject
*obj
= NewBuiltinClassInstance(cx
, clasp
);
6170 obj
->setPrimitiveThis(v
);
6171 vp
->setObject(*obj
);
6176 js_ValueToObjectOrNull(JSContext
*cx
, const Value
&v
, JSObject
**objp
)
6180 if (v
.isObjectOrNull()) {
6181 obj
= v
.toObjectOrNull();
6182 } else if (v
.isUndefined()) {
6186 if (!js_PrimitiveToObject(cx
, &tmp
))
6188 obj
= &tmp
.toObject();
6195 js_ValueToNonNullObject(JSContext
*cx
, const Value
&v
)
6199 if (!js_ValueToObjectOrNull(cx
, v
, &obj
))
6202 js_ReportIsNullOrUndefined(cx
, JSDVG_SEARCH_STACK
, v
, NULL
);
6207 js_TryValueOf(JSContext
*cx
, JSObject
*obj
, JSType type
, Value
*rval
)
6211 argv
[0].setString(ATOM_TO_STRING(cx
->runtime
->atomState
.typeAtoms
[type
]));
6212 return js_TryMethod(cx
, obj
, cx
->runtime
->atomState
.valueOfAtom
,
6217 js_TryMethod(JSContext
*cx
, JSObject
*obj
, JSAtom
*atom
,
6218 uintN argc
, Value
*argv
, Value
*rval
)
6220 JS_CHECK_RECURSION(cx
, return JS_FALSE
);
6223 * Report failure only if an appropriate method was found, and calling it
6224 * returned failure. We propagate failure in this case to make exceptions
6227 JSErrorReporter older
= JS_SetErrorReporter(cx
, NULL
);
6228 jsid id
= ATOM_TO_JSID(atom
);
6230 JSBool ok
= js_GetMethod(cx
, obj
, id
, JSGET_NO_METHOD_BARRIER
, &fval
);
6231 JS_SetErrorReporter(cx
, older
);
6235 if (fval
.isPrimitive())
6237 return ExternalInvoke(cx
, obj
, fval
, argc
, argv
, rval
);
6243 js_XDRObject(JSXDRState
*xdr
, JSObject
**objp
)
6248 uint32 classId
, classDef
;
6249 JSProtoKey protoKey
;
6254 if (xdr
->mode
== JSXDR_ENCODE
) {
6255 clasp
= (*objp
)->getClass();
6256 classId
= JS_XDRFindClassIdByName(xdr
, clasp
->name
);
6257 classDef
= !classId
;
6259 if (!JS_XDRRegisterClass(xdr
, Jsvalify(clasp
), &classId
))
6261 protoKey
= JSCLASS_CACHED_PROTO_KEY(clasp
);
6262 if (protoKey
!= JSProto_Null
) {
6263 classDef
|= (protoKey
<< 1);
6265 atom
= js_Atomize(cx
, clasp
->name
, strlen(clasp
->name
), 0);
6271 clasp
= NULL
; /* quell GCC overwarning */
6276 * XDR a flag word, which could be 0 for a class use, in which case no
6277 * name follows, only the id in xdr's class registry; 1 for a class def,
6278 * in which case the flag word is followed by the class name transferred
6279 * from or to atom; or a value greater than 1, an odd number that when
6280 * divided by two yields the JSProtoKey for class. In the last case, as
6281 * in the 0 classDef case, no name is transferred via atom.
6283 if (!JS_XDRUint32(xdr
, &classDef
))
6285 if (classDef
== 1 && !js_XDRAtom(xdr
, &atom
))
6288 if (!JS_XDRUint32(xdr
, &classId
))
6291 if (xdr
->mode
== JSXDR_DECODE
) {
6293 /* NB: we know that JSProto_Null is 0 here, for backward compat. */
6294 protoKey
= (JSProtoKey
) (classDef
>> 1);
6295 if (!js_GetClassPrototype(cx
, NULL
, protoKey
, &proto
, clasp
))
6297 clasp
= proto
->getClass();
6298 if (!JS_XDRRegisterClass(xdr
, Jsvalify(clasp
), &classId
))
6301 clasp
= Valueify(JS_XDRFindClassById(xdr
, classId
));
6304 JS_snprintf(numBuf
, sizeof numBuf
, "%ld", (long)classId
);
6305 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
6306 JSMSG_CANT_FIND_CLASS
, numBuf
);
6312 if (!clasp
->xdrObject
) {
6313 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
6314 JSMSG_CANT_XDR_CLASS
, clasp
->name
);
6317 return clasp
->xdrObject(xdr
, objp
);
6320 #endif /* JS_HAS_XDR */
6322 #ifdef JS_DUMP_SCOPE_METERS
6326 JSBasicStats js_entry_count_bs
= JS_INIT_STATIC_BASIC_STATS
;
6329 MeterEntryCount(uintN count
)
6331 JS_BASIC_STATS_ACCUM(&js_entry_count_bs
, count
);
6335 js_DumpScopeMeters(JSRuntime
*rt
)
6339 logfp
= fopen("/tmp/scope.stats", "a");
6344 mean
= JS_MeanAndStdDevBS(&js_entry_count_bs
, &sigma
);
6346 fprintf(logfp
, "scopes %u entries %g mean %g sigma %g max %u",
6347 js_entry_count_bs
.num
, js_entry_count_bs
.sum
, mean
, sigma
,
6348 js_entry_count_bs
.max
);
6351 JS_DumpHistogram(&js_entry_count_bs
, logfp
);
6352 JS_BASIC_STATS_INIT(&js_entry_count_bs
);
6359 js_PrintObjectSlotName(JSTracer
*trc
, char *buf
, size_t bufsize
)
6361 JS_ASSERT(trc
->debugPrinter
== js_PrintObjectSlotName
);
6363 JSObject
*obj
= (JSObject
*)trc
->debugPrintArg
;
6364 uint32 slot
= (uint32
)trc
->debugPrintIndex
;
6367 if (obj
->isNative()) {
6368 shape
= obj
->lastProperty();
6369 while (shape
->previous() && shape
->slot
!= slot
)
6370 shape
= shape
->previous();
6371 if (shape
->slot
!= slot
)
6378 const char *slotname
= NULL
;
6379 if (obj
->isGlobal()) {
6380 #define JS_PROTO(name,code,init) \
6381 if ((code) == slot) { slotname = js_##name##_str; goto found; }
6382 #include "jsproto.tbl"
6387 JS_snprintf(buf
, bufsize
, "CLASS_OBJECT(%s)", slotname
);
6389 JS_snprintf(buf
, bufsize
, "**UNKNOWN SLOT %ld**", (long)slot
);
6391 jsid id
= shape
->id
;
6392 if (JSID_IS_INT(id
)) {
6393 JS_snprintf(buf
, bufsize
, "%ld", (long)JSID_TO_INT(id
));
6394 } else if (JSID_IS_ATOM(id
)) {
6395 PutEscapedString(buf
, bufsize
, JSID_TO_ATOM(id
), 0);
6397 JS_snprintf(buf
, bufsize
, "**FINALIZED ATOM KEY**");
6404 js_TraceObject(JSTracer
*trc
, JSObject
*obj
)
6406 JS_ASSERT(obj
->isNative());
6408 JSContext
*cx
= trc
->context
;
6409 if (obj
->hasSlotsArray() && !obj
->nativeEmpty() && IS_GC_MARKING_TRACER(trc
)) {
6411 * Trim overlong dslots allocations from the GC, to avoid thrashing in
6412 * case of delete-happy code that settles down at a given population.
6413 * The !obj->nativeEmpty() guard above is due to the bug described by
6414 * the FIXME comment below.
6416 size_t slots
= obj
->slotSpan();
6417 if (obj
->numSlots() != slots
)
6418 obj
->shrinkSlots(cx
, slots
);
6421 #ifdef JS_DUMP_SCOPE_METERS
6422 MeterEntryCount(obj
->propertyCount
);
6427 if (!JS_CLIST_IS_EMPTY(&cx
->runtime
->watchPointList
))
6428 js_TraceWatchPoints(trc
, obj
);
6430 /* No one runs while the GC is running, so we can use LOCKED_... here. */
6431 Class
*clasp
= obj
->getClass();
6433 if (clasp
->flags
& JSCLASS_MARK_IS_TRACE
)
6434 ((JSTraceOp
) clasp
->mark
)(trc
, obj
);
6435 else if (IS_GC_MARKING_TRACER(trc
))
6436 (void) clasp
->mark(cx
, obj
, trc
);
6438 if (clasp
->flags
& JSCLASS_IS_GLOBAL
) {
6439 JSCompartment
*compartment
= obj
->getCompartment();
6440 compartment
->marked
= true;
6444 * NB: In case clasp->mark mutates something (which would be a bug, but we
6445 * want to be defensive), leave this code here -- don't move it up and
6446 * unify it with the |if (!traceScope)| section above.
6448 * FIXME: We minimize nslots against obj->slotSpan because native objects
6449 * such as Date instances may have failed to advance slotSpan to cover all
6450 * reserved slots (this Date issue may be a bug in JSObject::growSlots, but
6451 * the general problem occurs in other built-in class implementations).
6453 uint32 nslots
= obj
->numSlots();
6454 if (!obj
->nativeEmpty() && obj
->slotSpan() < nslots
)
6455 nslots
= obj
->slotSpan();
6457 for (uint32 i
= 0; i
!= nslots
; ++i
) {
6458 const Value
&v
= obj
->getSlot(i
);
6459 JS_SET_TRACING_DETAILS(trc
, js_PrintObjectSlotName
, obj
, i
);
6460 MarkValueRaw(trc
, v
);
6465 js_ClearNative(JSContext
*cx
, JSObject
*obj
)
6468 * Clear obj of all obj's properties. FIXME: we do not clear reserved slots
6469 * lying below JSSLOT_FREE(clasp). JS_ClearScope does that.
6471 if (!obj
->nativeEmpty()) {
6472 /* Now that we're done using real properties, clear obj. */
6475 /* Clear slot values since obj->clear reset our shape to empty. */
6476 uint32 freeslot
= JSSLOT_FREE(obj
->getClass());
6477 uint32 n
= obj
->numSlots();
6478 for (uint32 i
= freeslot
; i
< n
; ++i
)
6479 obj
->setSlot(i
, UndefinedValue());
6484 js_GetReservedSlot(JSContext
*cx
, JSObject
*obj
, uint32 slot
, Value
*vp
)
6486 if (!obj
->isNative()) {
6491 if (slot
< obj
->numSlots())
6492 *vp
= obj
->getSlot(slot
);
6499 js_SetReservedSlot(JSContext
*cx
, JSObject
*obj
, uint32 slot
, const Value
&v
)
6501 if (!obj
->isNative())
6504 Class
*clasp
= obj
->getClass();
6506 if (slot
>= obj
->numSlots()) {
6507 uint32 nslots
= JSSLOT_FREE(clasp
);
6508 JS_ASSERT(slot
< nslots
);
6509 if (!obj
->allocSlots(cx
, nslots
))
6513 obj
->setSlot(slot
, v
);
6514 GC_POKE(cx
, JS_NULL
);
6519 JSObject::getGlobal() const
6521 JSObject
*obj
= const_cast<JSObject
*>(this);
6522 while (JSObject
*parent
= obj
->getParent())
6528 js_ReportGetterOnlyAssignment(JSContext
*cx
)
6530 return JS_ReportErrorFlagsAndNumber(cx
,
6531 JSREPORT_WARNING
| JSREPORT_STRICT
|
6532 JSREPORT_STRICT_MODE_ERROR
,
6533 js_GetErrorMessage
, NULL
,
6537 JS_FRIEND_API(JSBool
)
6538 js_GetterOnlyPropertyStub(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
6540 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_GETTER_ONLY
);
6547 * Routines to print out values during debugging. These are FRIEND_API to help
6548 * the debugger find them and to support temporarily hacking js_Dump* calls
6553 dumpChars(const jschar
*s
, size_t n
)
6557 if (n
== (size_t) -1) {
6562 for (i
= 0; i
< n
; i
++) {
6564 fprintf(stderr
, "\\n");
6565 else if (s
[i
] == '\t')
6566 fprintf(stderr
, "\\t");
6567 else if (s
[i
] >= 32 && s
[i
] < 127)
6568 fputc(s
[i
], stderr
);
6569 else if (s
[i
] <= 255)
6570 fprintf(stderr
, "\\x%02x", (unsigned int) s
[i
]);
6572 fprintf(stderr
, "\\u%04x", (unsigned int) s
[i
]);
6578 js_DumpChars(const jschar
*s
, size_t n
)
6580 fprintf(stderr
, "jschar * (%p) = ", (void *) s
);
6582 fputc('\n', stderr
);
6586 dumpString(JSString
*str
)
6588 if (const jschar
*chars
= str
->getChars(NULL
))
6589 dumpChars(chars
, str
->length());
6591 fprintf(stderr
, "(oom in dumpString)");
6595 js_DumpString(JSString
*str
)
6597 if (const jschar
*chars
= str
->getChars(NULL
)) {
6598 fprintf(stderr
, "JSString* (%p) = jschar * (%p) = ",
6599 (void *) str
, (void *) chars
);
6602 fprintf(stderr
, "(oom in JS_DumpString)");
6604 fputc('\n', stderr
);
6608 js_DumpAtom(JSAtom
*atom
)
6610 fprintf(stderr
, "JSAtom* (%p) = ", (void *) atom
);
6611 js_DumpString(ATOM_TO_STRING(atom
));
6615 dumpValue(const Value
&v
)
6618 fprintf(stderr
, "null");
6619 else if (v
.isUndefined())
6620 fprintf(stderr
, "undefined");
6621 else if (v
.isInt32())
6622 fprintf(stderr
, "%d", v
.toInt32());
6623 else if (v
.isDouble())
6624 fprintf(stderr
, "%g", v
.toDouble());
6625 else if (v
.isString())
6626 dumpString(v
.toString());
6627 else if (v
.isObject() && v
.toObject().isFunction()) {
6628 JSObject
*funobj
= &v
.toObject();
6629 JSFunction
*fun
= GET_FUNCTION_PRIVATE(cx
, funobj
);
6631 fputs("<function ", stderr
);
6632 FileEscapedString(stderr
, ATOM_TO_STRING(fun
->atom
), 0);
6634 fputs("<unnamed function", stderr
);
6636 if (fun
->isInterpreted()) {
6637 JSScript
*script
= fun
->script();
6638 fprintf(stderr
, " (%s:%u)",
6639 script
->filename
? script
->filename
: "", script
->lineno
);
6641 fprintf(stderr
, " at %p (JSFunction at %p)>", (void *) funobj
, (void *) fun
);
6642 } else if (v
.isObject()) {
6643 JSObject
*obj
= &v
.toObject();
6644 Class
*clasp
= obj
->getClass();
6645 fprintf(stderr
, "<%s%s at %p>",
6647 (clasp
== &js_ObjectClass
) ? "" : " object",
6649 } else if (v
.isBoolean()) {
6651 fprintf(stderr
, "true");
6653 fprintf(stderr
, "false");
6654 } else if (v
.isMagic()) {
6655 fprintf(stderr
, "<invalid");
6657 switch (v
.whyMagic()) {
6658 case JS_ARRAY_HOLE
: fprintf(stderr
, " array hole"); break;
6659 case JS_ARGS_HOLE
: fprintf(stderr
, " args hole"); break;
6660 case JS_NATIVE_ENUMERATE
: fprintf(stderr
, " native enumeration"); break;
6661 case JS_NO_ITER_VALUE
: fprintf(stderr
, " no iter value"); break;
6662 case JS_GENERATOR_CLOSING
: fprintf(stderr
, " generator closing"); break;
6663 default: fprintf(stderr
, " ?!"); break;
6666 fprintf(stderr
, ">");
6668 fprintf(stderr
, "unexpected value");
6673 js_DumpValue(const Value
&val
)
6676 fputc('\n', stderr
);
6682 fprintf(stderr
, "jsid %p = ", (void *) JSID_BITS(id
));
6683 dumpValue(IdToValue(id
));
6684 fputc('\n', stderr
);
6688 DumpShape(const Shape
&shape
)
6691 uint8 attrs
= shape
.attributes();
6693 fprintf(stderr
, " ");
6694 if (attrs
& JSPROP_ENUMERATE
) fprintf(stderr
, "enumerate ");
6695 if (attrs
& JSPROP_READONLY
) fprintf(stderr
, "readonly ");
6696 if (attrs
& JSPROP_PERMANENT
) fprintf(stderr
, "permanent ");
6697 if (attrs
& JSPROP_GETTER
) fprintf(stderr
, "getter ");
6698 if (attrs
& JSPROP_SETTER
) fprintf(stderr
, "setter ");
6699 if (attrs
& JSPROP_SHARED
) fprintf(stderr
, "shared ");
6700 if (shape
.isAlias()) fprintf(stderr
, "alias ");
6701 if (shape
.isMethod()) fprintf(stderr
, "method(%p) ", (void *) &shape
.methodObject());
6703 if (JSID_IS_ATOM(id
))
6704 dumpString(JSID_TO_STRING(id
));
6705 else if (JSID_IS_INT(id
))
6706 fprintf(stderr
, "%d", (int) JSID_TO_INT(id
));
6708 fprintf(stderr
, "unknown jsid %p", (void *) JSID_BITS(id
));
6709 fprintf(stderr
, ": slot %d", shape
.slot
);
6710 fprintf(stderr
, "\n");
6714 js_DumpObject(JSObject
*obj
)
6716 fprintf(stderr
, "object %p\n", (void *) obj
);
6717 Class
*clasp
= obj
->getClass();
6718 fprintf(stderr
, "class %p %s\n", (void *)clasp
, clasp
->name
);
6720 fprintf(stderr
, "flags:");
6721 uint32 flags
= obj
->flags
;
6722 if (flags
& JSObject::DELEGATE
) fprintf(stderr
, " delegate");
6723 if (flags
& JSObject::SYSTEM
) fprintf(stderr
, " system");
6724 if (flags
& JSObject::NOT_EXTENSIBLE
) fprintf(stderr
, " not extensible");
6725 if (flags
& JSObject::BRANDED
) fprintf(stderr
, " branded");
6726 if (flags
& JSObject::GENERIC
) fprintf(stderr
, " generic");
6727 if (flags
& JSObject::METHOD_BARRIER
) fprintf(stderr
, " method_barrier");
6728 if (flags
& JSObject::INDEXED
) fprintf(stderr
, " indexed");
6729 if (flags
& JSObject::OWN_SHAPE
) fprintf(stderr
, " own_shape");
6730 if (flags
& JSObject::HAS_EQUALITY
) fprintf(stderr
, " has_equality");
6732 bool anyFlags
= flags
!= 0;
6733 if (obj
->isNative()) {
6734 if (obj
->inDictionaryMode()) {
6735 fprintf(stderr
, " inDictionaryMode");
6738 if (obj
->hasPropertyTable()) {
6739 fprintf(stderr
, " hasPropertyTable");
6744 fprintf(stderr
, " none");
6745 fprintf(stderr
, "\n");
6747 if (obj
->isDenseArray()) {
6748 unsigned slots
= JS_MIN(obj
->getArrayLength(), obj
->getDenseArrayCapacity());
6749 fprintf(stderr
, "elements\n");
6750 for (unsigned i
= 0; i
< slots
; i
++) {
6751 fprintf(stderr
, " %3d: ", i
);
6752 dumpValue(obj
->getDenseArrayElement(i
));
6753 fprintf(stderr
, "\n");
6759 if (obj
->isNative()) {
6760 fprintf(stderr
, "properties:\n");
6761 for (Shape::Range r
= obj
->lastProperty()->all(); !r
.empty(); r
.popFront())
6762 DumpShape(r
.front());
6764 if (!obj
->isNative())
6765 fprintf(stderr
, "not native\n");
6768 fprintf(stderr
, "proto ");
6769 dumpValue(ObjectOrNullValue(obj
->getProto()));
6770 fputc('\n', stderr
);
6772 fprintf(stderr
, "parent ");
6773 dumpValue(ObjectOrNullValue(obj
->getParent()));
6774 fputc('\n', stderr
);
6776 if (clasp
->flags
& JSCLASS_HAS_PRIVATE
)
6777 fprintf(stderr
, "private %p\n", obj
->getPrivate());
6779 fprintf(stderr
, "slots:\n");
6780 unsigned reservedEnd
= JSCLASS_RESERVED_SLOTS(clasp
);
6781 unsigned slots
= obj
->slotSpan();
6782 for (unsigned i
= 0; i
< slots
; i
++) {
6783 fprintf(stderr
, " %3d ", i
);
6784 if (i
< reservedEnd
)
6785 fprintf(stderr
, "(reserved) ");
6786 fprintf(stderr
, "= ");
6787 dumpValue(obj
->getSlot(i
));
6788 fputc('\n', stderr
);
6790 fputc('\n', stderr
);
6794 MaybeDumpObject(const char *name
, JSObject
*obj
)
6797 fprintf(stderr
, " %s: ", name
);
6798 dumpValue(ObjectValue(*obj
));
6799 fputc('\n', stderr
);
6804 MaybeDumpValue(const char *name
, const Value
&v
)
6807 fprintf(stderr
, " %s: ", name
);
6809 fputc('\n', stderr
);
6814 js_DumpStackFrame(JSContext
*cx
, JSStackFrame
*start
)
6816 /* This should only called during live debugging. */
6817 VOUCH_DOES_NOT_REQUIRE_STACK();
6820 start
= cx
->maybefp();
6821 FrameRegsIter
i(cx
);
6822 while (!i
.done() && i
.fp() != start
)
6826 fprintf(stderr
, "fp = %p not found in cx = %p\n", (void *)start
, (void *)cx
);
6830 for (; !i
.done(); ++i
) {
6831 JSStackFrame
*const fp
= i
.fp();
6833 fprintf(stderr
, "JSStackFrame at %p\n", (void *) fp
);
6834 if (fp
->isFunctionFrame()) {
6835 fprintf(stderr
, "callee fun: ");
6836 dumpValue(ObjectValue(fp
->callee()));
6838 fprintf(stderr
, "global frame, no callee");
6840 fputc('\n', stderr
);
6842 if (fp
->isScriptFrame()) {
6843 fprintf(stderr
, "file %s line %u\n",
6844 fp
->script()->filename
, (unsigned) fp
->script()->lineno
);
6847 if (jsbytecode
*pc
= i
.pc()) {
6848 if (!fp
->isScriptFrame()) {
6849 fprintf(stderr
, "*** pc && !script, skipping frame\n\n");
6852 if (fp
->hasImacropc()) {
6853 fprintf(stderr
, " pc in imacro at %p\n called from ", pc
);
6854 pc
= fp
->imacropc();
6856 fprintf(stderr
, " ");
6858 fprintf(stderr
, "pc = %p\n", pc
);
6859 fprintf(stderr
, " current op: %s\n", js_CodeName
[*pc
]);
6862 fprintf(stderr
, " slots: %p\n", (void *) fp
->slots());
6863 fprintf(stderr
, " sp: %p = slots + %u\n", (void *) sp
, (unsigned) (sp
- fp
->slots()));
6864 if (sp
- fp
->slots() < 10000) { // sanity
6865 for (Value
*p
= fp
->slots(); p
< sp
; p
++) {
6866 fprintf(stderr
, " %p: ", (void *) p
);
6868 fputc('\n', stderr
);
6871 if (fp
->isFunctionFrame() && !fp
->isEvalFrame()) {
6872 fprintf(stderr
, " actuals: %p (%u) ", (void *) fp
->actualArgs(), (unsigned) fp
->numActualArgs());
6873 fprintf(stderr
, " formals: %p (%u)\n", (void *) fp
->formalArgs(), (unsigned) fp
->numFormalArgs());
6875 MaybeDumpObject("callobj", fp
->maybeCallObj());
6876 MaybeDumpObject("argsobj", fp
->maybeArgsObj());
6877 if (!fp
->isDummyFrame()) {
6878 MaybeDumpValue("this", fp
->thisValue());
6879 fprintf(stderr
, " rval: ");
6880 dumpValue(fp
->returnValue());
6882 fprintf(stderr
, "dummy frame");
6884 fputc('\n', stderr
);
6886 fprintf(stderr
, " flags:");
6887 if (fp
->isConstructing())
6888 fprintf(stderr
, " constructing");
6889 if (fp
->hasOverriddenArgs())
6890 fprintf(stderr
, " overridden_args");
6891 if (fp
->isAssigning())
6892 fprintf(stderr
, " assigning");
6893 if (fp
->isDebuggerFrame())
6894 fprintf(stderr
, " debugger");
6895 if (fp
->isEvalFrame())
6896 fprintf(stderr
, " eval");
6897 if (fp
->isYielding())
6898 fprintf(stderr
, " yielding");
6899 if (fp
->isGeneratorFrame())
6900 fprintf(stderr
, " generator");
6901 fputc('\n', stderr
);
6903 fprintf(stderr
, " scopeChain: (JSObject *) %p\n", (void *) &fp
->scopeChain());
6905 fputc('\n', stderr
);
6911 IsSaneThisObject(JSObject
&obj
)
6913 Class
*clasp
= obj
.getClass();
6914 return clasp
!= &js_CallClass
&&
6915 clasp
!= &js_BlockClass
&&
6916 clasp
!= &js_DeclEnvClass
&&
6917 clasp
!= &js_WithClass
;