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 StrictPropertyStub
, /* 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
, JSBool strict
, 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
, JSBool strict
, 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
)
478 jschar
*chars
, *ochars
, *vsharp
;
479 const jschar
*idstrchars
, *vchars
;
480 size_t nchars
, idstrlength
, gsoplength
, vlength
, vsharplength
, curlen
;
486 JSString
*valstr
, *str
;
487 JSLinearString
*idstr
;
489 JS_CHECK_RECURSION(cx
, return JS_FALSE
);
492 PodArrayZero(localroot
);
493 AutoArrayRooter
tvr(cx
, JS_ARRAY_LENGTH(localroot
), localroot
);
495 /* If outermost, we need parentheses to be an expression, not a block. */
496 JSBool outermost
= (cx
->sharpObjectMap
.depth
== 0);
498 JSObject
*obj
= ToObject(cx
, &vp
[1]);
502 if (!(he
= js_EnterSharpObject(cx
, obj
, &ida
, &chars
))) {
508 * We didn't enter -- obj is already "sharp", meaning we've visited it
509 * already in our depth first search, and therefore chars contains a
510 * string of the form "#n#".
513 #if JS_HAS_SHARP_VARS
514 nchars
= js_strlen(chars
);
527 /* If outermost, allocate 4 + 1 for "({})" and the terminator. */
528 chars
= (jschar
*) cx
->runtime
->malloc(((outermost
? 4 : 2) + 1) * sizeof(jschar
));
533 chars
[nchars
++] = '(';
535 /* js_EnterSharpObject returned a string of the form "#n=" in chars. */
537 nchars
= js_strlen(chars
);
539 js_realloc((ochars
= chars
), (nchars
+ 2 + 1) * sizeof(jschar
));
546 * No need for parentheses around the whole shebang, because #n=
547 * unambiguously begins an object initializer, and never a block
550 outermost
= JS_FALSE
;
554 chars
[nchars
++] = '{';
559 * We have four local roots for cooked and raw value GC safety. Hoist the
560 * "localroot + 2" out of the loop using the val local, which refers to
561 * the raw (unconverted, "uncooked") values.
565 for (jsint i
= 0, length
= ida
->length
; i
< length
; i
++) {
566 /* Get strings for id and value and GC-root them via vp. */
567 jsid id
= ida
->vector
[i
];
569 ok
= obj
->lookupProperty(cx
, id
, &obj2
, &prop
);
574 * Convert id to a value and then to a string. Decide early whether we
575 * prefer get/set or old getter/setter syntax.
577 JSString
*s
= js_ValueToString(cx
, IdToValue(id
));
578 if (!s
|| !(idstr
= s
->ensureLinear(cx
))) {
582 vp
->setString(idstr
); /* local root */
587 if (obj2
->isNative()) {
588 const Shape
*shape
= (Shape
*) prop
;
589 unsigned attrs
= shape
->attributes();
590 if (attrs
& JSPROP_GETTER
) {
592 val
[valcnt
] = shape
->getterValue();
593 gsop
[valcnt
] = ATOM_TO_STRING(cx
->runtime
->atomState
.getAtom
);
596 if (attrs
& JSPROP_SETTER
) {
598 val
[valcnt
] = shape
->setterValue();
599 gsop
[valcnt
] = ATOM_TO_STRING(cx
->runtime
->atomState
.setAtom
);
606 ok
= obj
->getProperty(cx
, id
, &val
[0]);
613 * If id is a string that's not an identifier, or if it's a negative
614 * integer, then it must be quoted.
616 bool idIsLexicalIdentifier
= js_IsIdentifier(idstr
);
618 ? !idIsLexicalIdentifier
619 : (!JSID_IS_INT(id
) || JSID_TO_INT(id
) < 0)) {
620 s
= js_QuoteString(cx
, idstr
, jschar('\''));
621 if (!s
|| !(idstr
= s
->ensureLinear(cx
))) {
625 vp
->setString(idstr
); /* local root */
627 idstrlength
= idstr
->length();
628 idstrchars
= idstr
->getChars(cx
);
634 for (jsint j
= 0; j
< valcnt
; j
++) {
636 * Censor an accessor descriptor getter or setter part if it's
639 if (gsop
[j
] && val
[j
].isUndefined())
642 /* Convert val[j] to its canonical source form. */
643 valstr
= js_ValueToSource(cx
, val
[j
]);
648 localroot
[j
].setString(valstr
); /* local root */
649 vchars
= valstr
->getChars(cx
);
654 vlength
= valstr
->length();
657 * If val[j] is a non-sharp object, and we're not serializing an
658 * accessor (ECMA syntax can't accommodate sharpened accessors),
659 * consider sharpening it.
663 #if JS_HAS_SHARP_VARS
664 if (!gsop
[j
] && val
[j
].isObject() && vchars
[0] != '#') {
665 he
= js_EnterSharpObject(cx
, &val
[j
].toObject(), NULL
, &vsharp
);
672 vlength
= js_strlen(vchars
);
675 vsharplength
= js_strlen(vsharp
);
678 js_LeaveSharpObject(cx
, NULL
);
684 * Remove '(function ' from the beginning of valstr and ')' from the
685 * end so that we can put "get" in front of the function definition.
687 if (gsop
[j
] && IsFunctionObject(val
[j
])) {
688 const jschar
*start
= vchars
;
689 const jschar
*end
= vchars
+ vlength
;
691 uint8 parenChomp
= 0;
692 if (vchars
[0] == '(') {
697 /* Try to jump "function" keyword. */
699 vchars
= js_strchr_limit(vchars
, ' ', end
);
702 * Jump over the function's name: it can't be encoded as part
703 * of an ECMA getter or setter.
706 vchars
= js_strchr_limit(vchars
, '(', end
);
711 vlength
= end
- vchars
- parenChomp
;
718 #define SAFE_ADD(n) \
729 SAFE_ADD(idstrlength
+ 1);
731 SAFE_ADD(gsop
[j
]->length() + 1);
732 SAFE_ADD(vsharplength
);
734 /* Account for the trailing null. */
735 SAFE_ADD((outermost
? 2 : 1) + 1);
738 if (curlen
> size_t(-1) / sizeof(jschar
))
741 /* Allocate 1 + 1 at end for closing brace and terminating 0. */
742 chars
= (jschar
*) js_realloc((ochars
= chars
), curlen
* sizeof(jschar
));
749 chars
[nchars
++] = comma
[0];
750 chars
[nchars
++] = comma
[1];
755 gsoplength
= gsop
[j
]->length();
756 const jschar
*gsopchars
= gsop
[j
]->getChars(cx
);
759 js_strncpy(&chars
[nchars
], gsopchars
, gsoplength
);
760 nchars
+= gsoplength
;
761 chars
[nchars
++] = ' ';
763 js_strncpy(&chars
[nchars
], idstrchars
, idstrlength
);
764 nchars
+= idstrlength
;
765 /* Extraneous space after id here will be extracted later */
766 chars
[nchars
++] = gsop
[j
] ? ' ' : ':';
769 js_strncpy(&chars
[nchars
], vsharp
, vsharplength
);
770 nchars
+= vsharplength
;
772 js_strncpy(&chars
[nchars
], vchars
, vlength
);
780 chars
[nchars
++] = '}';
782 chars
[nchars
++] = ')';
786 js_LeaveSharpObject(cx
, &ida
);
795 JS_ReportOutOfMemory(cx
);
800 str
= js_NewString(cx
, chars
, nchars
);
817 #endif /* JS_HAS_TOSOURCE */
822 obj_toStringHelper(JSContext
*cx
, JSObject
*obj
)
825 return JSProxy::obj_toString(cx
, obj
);
827 const char *clazz
= obj
->getClass()->name
;
828 size_t nchars
= 9 + strlen(clazz
); /* 9 for "[object ]" */
829 jschar
*chars
= (jschar
*) cx
->malloc((nchars
+ 1) * sizeof(jschar
));
833 const char *prefix
= "[object ";
835 while ((chars
[nchars
] = (jschar
)*prefix
) != 0)
837 while ((chars
[nchars
] = (jschar
)*clazz
) != 0)
839 chars
[nchars
++] = ']';
842 JSString
*str
= js_NewString(cx
, chars
, nchars
);
850 /* ES5 15.2.4.2. Note steps 1 and 2 are errata. */
852 obj_toString(JSContext
*cx
, uintN argc
, Value
*vp
)
854 Value
&thisv
= vp
[1];
857 if (thisv
.isUndefined()) {
858 vp
->setString(ATOM_TO_STRING(cx
->runtime
->atomState
.objectUndefinedAtom
));
863 if (thisv
.isNull()) {
864 vp
->setString(ATOM_TO_STRING(cx
->runtime
->atomState
.objectNullAtom
));
869 JSObject
*obj
= ToObject(cx
, &thisv
);
874 JSString
*str
= js::obj_toStringHelper(cx
, obj
);
882 obj_toLocaleString(JSContext
*cx
, uintN argc
, Value
*vp
)
884 JSObject
*obj
= ToObject(cx
, &vp
[1]);
888 JSString
*str
= js_ValueToString(cx
, ObjectValue(*obj
));
897 obj_valueOf(JSContext
*cx
, uintN argc
, Value
*vp
)
899 JSObject
*obj
= ToObject(cx
, &vp
[1]);
907 * Check if CSP allows new Function() or eval() to run in the current
911 js_CheckContentSecurityPolicy(JSContext
*cx
, JSObject
*scopeobj
)
913 // CSP is static per document, so if our check said yes before, that
914 // answer is still valid.
915 JSObject
*global
= scopeobj
->getGlobal();
916 Value v
= global
->getReservedSlot(JSRESERVED_GLOBAL_EVAL_ALLOWED
);
917 if (v
.isUndefined()) {
918 JSSecurityCallbacks
*callbacks
= JS_GetSecurityCallbacks(cx
);
920 // if there are callbacks, make sure that the CSP callback is installed and
921 // that it permits eval().
922 v
.setBoolean((!callbacks
|| !callbacks
->contentSecurityPolicyAllows
) ||
923 callbacks
->contentSecurityPolicyAllows(cx
));
925 // update the cache in the global object for the result of the security check
926 js_SetReservedSlot(cx
, global
, JSRESERVED_GLOBAL_EVAL_ALLOWED
, v
);
932 * Check whether principals subsumes scopeobj's principals, and return true
933 * if so (or if scopeobj has no principals, for backward compatibility with
934 * the JS API, which does not require principals), and false otherwise.
937 js_CheckPrincipalsAccess(JSContext
*cx
, JSObject
*scopeobj
,
938 JSPrincipals
*principals
, JSAtom
*caller
)
940 JSSecurityCallbacks
*callbacks
;
941 JSPrincipals
*scopePrincipals
;
943 callbacks
= JS_GetSecurityCallbacks(cx
);
944 if (callbacks
&& callbacks
->findObjectPrincipals
) {
945 scopePrincipals
= callbacks
->findObjectPrincipals(cx
, scopeobj
);
946 if (!principals
|| !scopePrincipals
||
947 !principals
->subsume(principals
, scopePrincipals
)) {
948 JSAutoByteString callerstr
;
949 if (!js_AtomToPrintableString(cx
, caller
, &callerstr
))
951 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
952 JSMSG_BAD_INDIRECT_CALL
, callerstr
.ptr());
960 CheckScopeChainValidity(JSContext
*cx
, JSObject
*scopeobj
)
962 JSObject
*inner
= scopeobj
;
963 OBJ_TO_INNER_OBJECT(cx
, inner
);
966 JS_ASSERT(inner
== scopeobj
);
968 /* XXX This is an awful gross hack. */
970 JSObjectOp op
= scopeobj
->getClass()->ext
.innerObject
;
971 if (op
&& op(cx
, scopeobj
) != scopeobj
) {
972 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_BAD_INDIRECT_CALL
,
976 scopeobj
= scopeobj
->getParent();
983 js_ComputeFilename(JSContext
*cx
, JSStackFrame
*caller
,
984 JSPrincipals
*principals
, uintN
*linenop
)
988 JSSecurityCallbacks
*callbacks
= JS_GetSecurityCallbacks(cx
);
991 JS_ASSERT(principals
|| !(callbacks
&& callbacks
->findObjectPrincipals
));
992 flags
= JS_GetScriptFilenameFlags(caller
->script());
993 if ((flags
& JSFILENAME_PROTECTED
) &&
995 strcmp(principals
->codebase
, "[System Principal]")) {
997 return principals
->codebase
;
1000 jsbytecode
*pc
= caller
->pc(cx
);
1001 if (pc
&& js_GetOpcode(cx
, caller
->script(), pc
) == JSOP_EVAL
) {
1002 JS_ASSERT(js_GetOpcode(cx
, caller
->script(), pc
+ JSOP_EVAL_LENGTH
) == JSOP_LINENO
);
1003 *linenop
= GET_UINT16(pc
+ JSOP_EVAL_LENGTH
);
1005 *linenop
= js_FramePCToLineNumber(cx
, caller
);
1007 return caller
->script()->filename
;
1010 #ifndef EVAL_CACHE_CHAIN_LIMIT
1011 # define EVAL_CACHE_CHAIN_LIMIT 4
1014 static inline JSScript
**
1015 EvalCacheHash(JSContext
*cx
, JSLinearString
*str
)
1017 const jschar
*s
= str
->chars();
1018 size_t n
= str
->length();
1023 for (h
= 0; n
; s
++, n
--)
1024 h
= JS_ROTATE_LEFT32(h
, 4) ^ *s
;
1026 h
*= JS_GOLDEN_RATIO
;
1027 h
>>= 32 - JS_EVAL_CACHE_SHIFT
;
1028 return &JS_SCRIPTS_TO_GC(cx
)[h
];
1031 static JS_ALWAYS_INLINE JSScript
*
1032 EvalCacheLookup(JSContext
*cx
, JSLinearString
*str
, JSStackFrame
*caller
, uintN staticLevel
,
1033 JSPrincipals
*principals
, JSObject
*scopeobj
, JSScript
**bucket
)
1036 * Cache local eval scripts indexed by source qualified by scope.
1038 * An eval cache entry should never be considered a hit unless its
1039 * strictness matches that of the new eval code. The existing code takes
1040 * care of this, because hits are qualified by the function from which
1041 * eval was called, whose strictness doesn't change. (We don't cache evals
1042 * in eval code, so the calling function corresponds to the calling script,
1043 * and its strictness never varies.) Scripts produced by calls to eval from
1044 * global code aren't cached.
1046 * FIXME bug 620141: Qualify hits by calling script rather than function.
1047 * Then we wouldn't need the unintuitive !isEvalFrame() hack in EvalKernel
1048 * to avoid caching nested evals in functions (thus potentially mismatching
1049 * on strict mode), and we could cache evals in global code if desired.
1052 JSScript
**scriptp
= bucket
;
1054 EVAL_CACHE_METER(probe
);
1055 JSVersion version
= cx
->findVersion();
1057 while ((script
= *scriptp
) != NULL
) {
1058 if (script
->savedCallerFun
&&
1059 script
->staticLevel
== staticLevel
&&
1060 script
->getVersion() == version
&&
1061 !script
->hasSingletons
&&
1062 (script
->principals
== principals
||
1063 (principals
->subsume(principals
, script
->principals
) &&
1064 script
->principals
->subsume(script
->principals
, principals
)))) {
1066 * Get the prior (cache-filling) eval's saved caller function.
1067 * See Compiler::compileScript in jsparse.cpp.
1069 JSFunction
*fun
= script
->getFunction(0);
1071 if (fun
== caller
->fun()) {
1073 * Get the source string passed for safekeeping in the
1074 * atom map by the prior eval to Compiler::compileScript.
1076 JSAtom
*src
= script
->atomMap
.vector
[0];
1078 if (src
== str
|| EqualStrings(src
, str
)) {
1080 * Source matches, qualify by comparing scopeobj to the
1081 * COMPILE_N_GO-memoized parent of the first literal
1082 * function or regexp object if any. If none, then this
1083 * script has no compiled-in dependencies on the prior
1086 JSObjectArray
*objarray
= script
->objects();
1089 if (objarray
->length
== 1) {
1090 if (JSScript::isValidOffset(script
->regexpsOffset
)) {
1091 objarray
= script
->regexps();
1094 EVAL_CACHE_METER(noscope
);
1099 objarray
->vector
[i
]->getParent() == scopeobj
) {
1100 JS_ASSERT(staticLevel
== script
->staticLevel
);
1101 EVAL_CACHE_METER(hit
);
1102 *scriptp
= script
->u
.nextToGC
;
1103 script
->u
.nextToGC
= NULL
;
1110 if (++count
== EVAL_CACHE_CHAIN_LIMIT
)
1112 EVAL_CACHE_METER(step
);
1113 scriptp
= &script
->u
.nextToGC
;
1120 eval(JSContext
*cx
, uintN argc
, Value
*vp
)
1123 * NB: This method handles only indirect eval: direct eval is handled by
1127 JSStackFrame
*caller
= js_GetScriptedCaller(cx
, NULL
);
1129 /* FIXME Bug 602994: This really should be perfectly cromulent. */
1131 /* Eval code needs to inherit principals from the caller. */
1132 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1133 JSMSG_BAD_INDIRECT_CALL
, js_eval_str
);
1137 return EvalKernel(cx
, argc
, vp
, INDIRECT_EVAL
, caller
, vp
[0].toObject().getGlobal());
1143 EvalKernel(JSContext
*cx
, uintN argc
, Value
*vp
, EvalType evalType
, JSStackFrame
*caller
,
1147 * FIXME Bug 602994: Calls with no scripted caller should be permitted and
1148 * should be implemented as indirect calls.
1151 JS_ASSERT(scopeobj
);
1154 * We once supported a second argument to eval to use as the scope chain
1155 * when evaluating the code string. Warn when such uses are seen so that
1156 * authors will know that support for eval(s, o) has been removed.
1158 JSScript
*callerScript
= caller
->script();
1159 if (argc
> 1 && !callerScript
->warnedAboutTwoArgumentEval
) {
1160 static const char TWO_ARGUMENT_WARNING
[] =
1161 "Support for eval(code, scopeObject) has been removed. "
1162 "Use |with (scopeObject) eval(code);| instead.";
1163 if (!JS_ReportWarning(cx
, TWO_ARGUMENT_WARNING
))
1165 callerScript
->warnedAboutTwoArgumentEval
= true;
1169 * CSP check: Is eval() allowed at all?
1170 * Report errors via CSP is done in the script security mgr.
1172 if (!js_CheckContentSecurityPolicy(cx
, scopeobj
)) {
1173 JS_ReportError(cx
, "call to eval() blocked by CSP");
1177 /* ES5 15.1.2.1 step 1. */
1182 if (!vp
[2].isString()) {
1186 JSString
*str
= vp
[2].toString();
1188 /* ES5 15.1.2.1 steps 2-8. */
1189 JSObject
*callee
= JSVAL_TO_OBJECT(JS_CALLEE(cx
, Jsvalify(vp
)));
1190 JS_ASSERT(IsBuiltinEvalFunction(callee
->getFunctionPrivate()));
1191 JSPrincipals
*principals
= js_EvalFramePrincipals(cx
, callee
, caller
);
1194 * Per ES5, indirect eval runs in the global scope. (eval is specified this
1195 * way so that the compiler can make assumptions about what bindings may or
1196 * may not exist in the current frame if it doesn't see 'eval'.)
1199 if (evalType
== DIRECT_EVAL
) {
1200 staticLevel
= caller
->script()->staticLevel
+ 1;
1203 jsbytecode
*callerPC
= caller
->pc(cx
);
1204 JS_ASSERT_IF(caller
->isFunctionFrame(), caller
->hasCallObj());
1205 JS_ASSERT(callerPC
&& js_GetOpcode(cx
, caller
->script(), callerPC
) == JSOP_EVAL
);
1208 /* Pretend that we're top level. */
1211 JS_ASSERT(scopeobj
== scopeobj
->getGlobal());
1212 JS_ASSERT(scopeobj
->isGlobal());
1215 /* Ensure we compile this eval with the right object in the scope chain. */
1216 if (!CheckScopeChainValidity(cx
, scopeobj
))
1219 JSLinearString
*linearStr
= str
->ensureLinear(cx
);
1222 const jschar
*chars
= linearStr
->chars();
1223 size_t length
= linearStr
->length();
1226 * If the eval string starts with '(' and ends with ')', it may be JSON.
1227 * Try the JSON parser first because it's much faster. If the eval string
1228 * isn't JSON, JSON parsing will probably fail quickly, so little time
1231 if (length
> 2 && chars
[0] == '(' && chars
[length
- 1] == ')') {
1232 JSONParser
*jp
= js_BeginJSONParse(cx
, vp
, /* suppressErrors = */true);
1234 /* Run JSON-parser on string inside ( and ). */
1235 bool ok
= js_ConsumeJSONText(cx
, jp
, chars
+ 1, length
- 2);
1236 ok
&= js_FinishJSONParse(cx
, jp
, NullValue());
1243 * Direct calls to eval are supposed to see the caller's |this|. If we
1244 * haven't wrapped that yet, do so now, before we make a copy of it for
1245 * the eval code to use.
1247 if (evalType
== DIRECT_EVAL
&& !caller
->computeThis(cx
))
1250 JSScript
*script
= NULL
;
1251 JSScript
**bucket
= EvalCacheHash(cx
, linearStr
);
1252 if (evalType
== DIRECT_EVAL
&& caller
->isFunctionFrame() && !caller
->isEvalFrame()) {
1253 script
= EvalCacheLookup(cx
, linearStr
, caller
, staticLevel
, principals
, scopeobj
, bucket
);
1256 * Although the eval cache keeps a script alive from the perspective of
1257 * the JS engine, from a jsdbgapi user's perspective each eval()
1258 * creates and destroys a script. This hides implementation details and
1259 * allows jsdbgapi clients to avoid calling JS_GetScriptObject after a
1260 * script has been returned to the eval cache, which is invalid since
1261 * script->u.object aliases script->u.nextToGC.
1264 js_CallNewScriptHook(cx
, script
, NULL
);
1265 MUST_FLOW_THROUGH("destroy");
1270 * We can't have a callerFrame (down in js::Execute's terms) if we're in
1271 * global code (or if we're an indirect eval).
1273 JSStackFrame
*callerFrame
= (staticLevel
!= 0) ? caller
: NULL
;
1276 const char *filename
= js_ComputeFilename(cx
, caller
, principals
, &lineno
);
1278 uint32 tcflags
= TCF_COMPILE_N_GO
| TCF_NEED_MUTABLE_SCRIPT
| TCF_COMPILE_FOR_EVAL
;
1279 script
= Compiler::compileScript(cx
, scopeobj
, callerFrame
,
1280 principals
, tcflags
, chars
, length
,
1281 filename
, lineno
, cx
->findVersion(),
1282 linearStr
, staticLevel
);
1287 assertSameCompartment(cx
, scopeobj
, script
);
1290 * Belt-and-braces: check that the lesser of eval's principals and the
1291 * caller's principals has access to scopeobj.
1293 JSBool ok
= js_CheckPrincipalsAccess(cx
, scopeobj
, principals
,
1294 cx
->runtime
->atomState
.evalAtom
) &&
1295 Execute(cx
, scopeobj
, script
, callerFrame
, JSFRAME_EVAL
, vp
);
1297 MUST_FLOW_LABEL(destroy
);
1298 js_CallDestroyScriptHook(cx
, script
);
1300 script
->u
.nextToGC
= *bucket
;
1302 #ifdef CHECK_SCRIPT_OWNER
1303 script
->owner
= NULL
;
1310 IsBuiltinEvalFunction(JSFunction
*fun
)
1312 return fun
->maybeNative() == eval
;
1317 #if JS_HAS_OBJ_WATCHPOINT
1320 obj_watch_handler(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval old
,
1321 jsval
*nvp
, void *closure
)
1324 JSSecurityCallbacks
*callbacks
;
1325 JSStackFrame
*caller
;
1326 JSPrincipals
*subject
, *watcher
;
1328 JSResolvingEntry
*entry
;
1333 callable
= (JSObject
*) closure
;
1335 callbacks
= JS_GetSecurityCallbacks(cx
);
1336 if (callbacks
&& callbacks
->findObjectPrincipals
) {
1337 /* Skip over any obj_watch_* frames between us and the real subject. */
1338 caller
= js_GetScriptedCaller(cx
, NULL
);
1341 * Only call the watch handler if the watcher is allowed to watch
1342 * the currently executing script.
1344 watcher
= callbacks
->findObjectPrincipals(cx
, callable
);
1345 subject
= js_StackFramePrincipals(cx
, caller
);
1347 if (watcher
&& subject
&& !watcher
->subsume(watcher
, subject
)) {
1348 /* Silently don't call the watch handler. */
1354 /* Avoid recursion on (obj, id) already being watched on cx. */
1357 if (!js_StartResolving(cx
, &key
, JSRESFLAG_WATCH
, &entry
))
1361 generation
= cx
->resolvingTable
->generation
;
1363 argv
[0] = IdToValue(id
);
1364 argv
[1] = Valueify(old
);
1365 argv
[2] = Valueify(*nvp
);
1366 ok
= ExternalInvoke(cx
, ObjectValue(*obj
), ObjectOrNullValue(callable
), 3, argv
,
1368 js_StopResolving(cx
, &key
, JSRESFLAG_WATCH
, entry
, generation
);
1373 obj_watch(JSContext
*cx
, uintN argc
, Value
*vp
)
1376 js_ReportMissingArg(cx
, *vp
, 1);
1380 JSObject
*callable
= js_ValueToCallableObject(cx
, &vp
[3], 0);
1384 /* Compute the unique int/atom symbol id needed by js_LookupProperty. */
1386 if (!ValueToId(cx
, vp
[2], &propid
))
1389 JSObject
*obj
= ToObject(cx
, &vp
[1]);
1395 if (!CheckAccess(cx
, obj
, propid
, JSACC_WATCH
, &tmp
, &attrs
))
1400 if (attrs
& JSPROP_READONLY
)
1402 if (obj
->isDenseArray() && !obj
->makeDenseArraySlow(cx
))
1404 return JS_SetWatchPoint(cx
, obj
, propid
, obj_watch_handler
, callable
);
1408 obj_unwatch(JSContext
*cx
, uintN argc
, Value
*vp
)
1410 JSObject
*obj
= ToObject(cx
, &vp
[1]);
1416 if (!ValueToId(cx
, vp
[2], &id
))
1421 return JS_ClearWatchPoint(cx
, obj
, id
, NULL
, NULL
);
1424 #endif /* JS_HAS_OBJ_WATCHPOINT */
1427 * Prototype and property query methods, to complement the 'in' and
1428 * 'instanceof' operators.
1431 /* Proposed ECMA 15.2.4.5. */
1433 obj_hasOwnProperty(JSContext
*cx
, uintN argc
, Value
*vp
)
1435 JSObject
*obj
= ToObject(cx
, &vp
[1]);
1438 return js_HasOwnPropertyHelper(cx
, obj
->getOps()->lookupProperty
, argc
, vp
);
1442 js_HasOwnPropertyHelper(JSContext
*cx
, LookupPropOp lookup
, uintN argc
,
1446 if (!ValueToId(cx
, argc
!= 0 ? vp
[2] : UndefinedValue(), &id
))
1449 JSObject
*obj
= ToObject(cx
, &vp
[1]);
1454 if (obj
->isProxy()) {
1456 if (!JSProxy::hasOwn(cx
, obj
, id
, &has
))
1458 vp
->setBoolean(has
);
1461 if (!js_HasOwnProperty(cx
, lookup
, obj
, id
, &obj2
, &prop
))
1463 vp
->setBoolean(!!prop
);
1468 js_HasOwnProperty(JSContext
*cx
, LookupPropOp lookup
, JSObject
*obj
, jsid id
,
1469 JSObject
**objp
, JSProperty
**propp
)
1471 JSAutoResolveFlags
rf(cx
, JSRESOLVE_QUALIFIED
| JSRESOLVE_DETECTING
);
1472 if (!(lookup
? lookup
: js_LookupProperty
)(cx
, obj
, id
, objp
, propp
))
1480 Class
*clasp
= (*objp
)->getClass();
1481 JSObject
*outer
= NULL
;
1482 if (JSObjectOp op
= (*objp
)->getClass()->ext
.outerObject
) {
1483 outer
= op(cx
, *objp
);
1488 if (outer
!= *objp
) {
1489 if ((*objp
)->isNative() && obj
->getClass() == clasp
) {
1491 * The combination of JSPROP_SHARED and JSPROP_PERMANENT in a
1492 * delegated property makes that property appear to be direct in
1493 * all delegating instances of the same native class. This hack
1494 * avoids bloating every function instance with its own 'length'
1495 * (AKA 'arity') property. But it must not extend across class
1496 * boundaries, to avoid making hasOwnProperty lie (bug 320854).
1498 * It's not really a hack, of course: a permanent property can't
1499 * be deleted, and JSPROP_SHARED means "don't allocate a slot in
1500 * any instance, prototype or delegating". Without a slot, and
1501 * without the ability to remove and recreate (with differences)
1502 * the property, there is no way to tell whether it is directly
1503 * owned, or indirectly delegated.
1505 Shape
*shape
= reinterpret_cast<Shape
*>(*propp
);
1506 if (shape
->isSharedPermanent())
1517 obj_isPrototypeOf(JSContext
*cx
, uintN argc
, Value
*vp
)
1520 if (argc
< 1 || !vp
[2].isObject()) {
1521 vp
->setBoolean(false);
1526 JSObject
*obj
= ToObject(cx
, &vp
[1]);
1531 vp
->setBoolean(js_IsDelegate(cx
, obj
, vp
[2]));
1537 obj_propertyIsEnumerable(JSContext
*cx
, uintN argc
, Value
*vp
)
1541 if (!ValueToId(cx
, argc
!= 0 ? vp
[2] : UndefinedValue(), &id
))
1545 JSObject
*obj
= ToObject(cx
, &vp
[1]);
1550 return js_PropertyIsEnumerable(cx
, obj
, id
, vp
);
1554 js_PropertyIsEnumerable(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
1558 if (!obj
->lookupProperty(cx
, id
, &pobj
, &prop
))
1562 vp
->setBoolean(false);
1567 * XXX ECMA spec error compatible: return false unless hasOwnProperty.
1568 * The ECMA spec really should be fixed so propertyIsEnumerable and the
1569 * for..in loop agree on whether prototype properties are enumerable,
1570 * obviously by fixing this method (not by breaking the for..in loop!).
1572 * We check here for shared permanent prototype properties, which should
1573 * be treated as if they are local to obj. They are an implementation
1574 * technique used to satisfy ECMA requirements; users should not be able
1575 * to distinguish a shared permanent proto-property from a local one.
1579 if (pobj
->isNative()) {
1580 Shape
*shape
= (Shape
*) prop
;
1581 shared
= shape
->isSharedPermanent();
1582 attrs
= shape
->attributes();
1585 if (!pobj
->getAttributes(cx
, id
, &attrs
))
1588 if (pobj
!= obj
&& !shared
) {
1589 vp
->setBoolean(false);
1592 vp
->setBoolean((attrs
& JSPROP_ENUMERATE
) != 0);
1596 #if OLD_GETTER_SETTER_METHODS
1598 const char js_defineGetter_str
[] = "__defineGetter__";
1599 const char js_defineSetter_str
[] = "__defineSetter__";
1600 const char js_lookupGetter_str
[] = "__lookupGetter__";
1601 const char js_lookupSetter_str
[] = "__lookupSetter__";
1603 JS_FRIEND_API(JSBool
)
1604 js_obj_defineGetter(JSContext
*cx
, uintN argc
, Value
*vp
)
1606 if (!BoxThisForVp(cx
, vp
))
1608 JSObject
*obj
= &vp
[1].toObject();
1610 if (argc
<= 1 || !js_IsCallable(vp
[3])) {
1611 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1612 JSMSG_BAD_GETTER_OR_SETTER
,
1616 PropertyOp getter
= CastAsPropertyOp(&vp
[3].toObject());
1619 if (!ValueToId(cx
, vp
[2], &id
))
1621 if (!CheckRedeclaration(cx
, obj
, id
, JSPROP_GETTER
))
1624 * Getters and setters are just like watchpoints from an access
1625 * control point of view.
1629 if (!CheckAccess(cx
, obj
, id
, JSACC_WATCH
, &junk
, &attrs
))
1632 return obj
->defineProperty(cx
, id
, UndefinedValue(), getter
, StrictPropertyStub
,
1633 JSPROP_ENUMERATE
| JSPROP_GETTER
| JSPROP_SHARED
);
1636 JS_FRIEND_API(JSBool
)
1637 js_obj_defineSetter(JSContext
*cx
, uintN argc
, Value
*vp
)
1639 if (!BoxThisForVp(cx
, vp
))
1641 JSObject
*obj
= &vp
[1].toObject();
1643 if (argc
<= 1 || !js_IsCallable(vp
[3])) {
1644 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1645 JSMSG_BAD_GETTER_OR_SETTER
,
1649 StrictPropertyOp setter
= CastAsStrictPropertyOp(&vp
[3].toObject());
1652 if (!ValueToId(cx
, vp
[2], &id
))
1654 if (!CheckRedeclaration(cx
, obj
, id
, JSPROP_SETTER
))
1657 * Getters and setters are just like watchpoints from an access
1658 * control point of view.
1662 if (!CheckAccess(cx
, obj
, id
, JSACC_WATCH
, &junk
, &attrs
))
1665 return obj
->defineProperty(cx
, id
, UndefinedValue(), PropertyStub
, setter
,
1666 JSPROP_ENUMERATE
| JSPROP_SETTER
| JSPROP_SHARED
);
1670 obj_lookupGetter(JSContext
*cx
, uintN argc
, Value
*vp
)
1673 if (!ValueToId(cx
, argc
!= 0 ? vp
[2] : UndefinedValue(), &id
))
1675 JSObject
*obj
= ToObject(cx
, &vp
[1]);
1680 if (!obj
->lookupProperty(cx
, id
, &pobj
, &prop
))
1684 if (pobj
->isNative()) {
1685 Shape
*shape
= (Shape
*) prop
;
1686 if (shape
->hasGetterValue())
1687 *vp
= shape
->getterValue();
1694 obj_lookupSetter(JSContext
*cx
, uintN argc
, Value
*vp
)
1697 if (!ValueToId(cx
, argc
!= 0 ? vp
[2] : UndefinedValue(), &id
))
1699 JSObject
*obj
= ToObject(cx
, &vp
[1]);
1704 if (!obj
->lookupProperty(cx
, id
, &pobj
, &prop
))
1708 if (pobj
->isNative()) {
1709 Shape
*shape
= (Shape
*) prop
;
1710 if (shape
->hasSetterValue())
1711 *vp
= shape
->setterValue();
1716 #endif /* OLD_GETTER_SETTER_METHODS */
1719 obj_getPrototypeOf(JSContext
*cx
, uintN argc
, Value
*vp
)
1722 js_ReportMissingArg(cx
, *vp
, 0);
1726 if (vp
[2].isPrimitive()) {
1727 char *bytes
= DecompileValueGenerator(cx
, JSDVG_SEARCH_STACK
, vp
[2], NULL
);
1730 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1731 JSMSG_UNEXPECTED_TYPE
, bytes
, "not an object");
1736 JSObject
*obj
= &vp
[2].toObject();
1738 return CheckAccess(cx
, obj
, ATOM_TO_JSID(cx
->runtime
->atomState
.protoAtom
),
1739 JSACC_PROTO
, vp
, &attrs
);
1743 js_NewPropertyDescriptorObject(JSContext
*cx
, jsid id
, uintN attrs
,
1744 const Value
&getter
, const Value
&setter
,
1745 const Value
&value
, Value
*vp
)
1747 /* We have our own property, so start creating the descriptor. */
1748 JSObject
*desc
= NewBuiltinClassInstance(cx
, &js_ObjectClass
);
1751 vp
->setObject(*desc
); /* Root and return. */
1753 const JSAtomState
&atomState
= cx
->runtime
->atomState
;
1754 if (attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)) {
1755 if (!desc
->defineProperty(cx
, ATOM_TO_JSID(atomState
.getAtom
), getter
,
1756 PropertyStub
, StrictPropertyStub
, JSPROP_ENUMERATE
) ||
1757 !desc
->defineProperty(cx
, ATOM_TO_JSID(atomState
.setAtom
), setter
,
1758 PropertyStub
, StrictPropertyStub
, JSPROP_ENUMERATE
)) {
1762 if (!desc
->defineProperty(cx
, ATOM_TO_JSID(atomState
.valueAtom
), value
,
1763 PropertyStub
, StrictPropertyStub
, JSPROP_ENUMERATE
) ||
1764 !desc
->defineProperty(cx
, ATOM_TO_JSID(atomState
.writableAtom
),
1765 BooleanValue((attrs
& JSPROP_READONLY
) == 0),
1766 PropertyStub
, StrictPropertyStub
, JSPROP_ENUMERATE
)) {
1771 return desc
->defineProperty(cx
, ATOM_TO_JSID(atomState
.enumerableAtom
),
1772 BooleanValue((attrs
& JSPROP_ENUMERATE
) != 0),
1773 PropertyStub
, StrictPropertyStub
, JSPROP_ENUMERATE
) &&
1774 desc
->defineProperty(cx
, ATOM_TO_JSID(atomState
.configurableAtom
),
1775 BooleanValue((attrs
& JSPROP_PERMANENT
) == 0),
1776 PropertyStub
, StrictPropertyStub
, JSPROP_ENUMERATE
);
1780 js_GetOwnPropertyDescriptor(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
1783 return JSProxy::getOwnPropertyDescriptor(cx
, obj
, id
, false, vp
);
1787 if (!js_HasOwnProperty(cx
, obj
->getOps()->lookupProperty
, obj
, id
, &pobj
, &prop
))
1794 Value roots
[] = { UndefinedValue(), UndefinedValue(), UndefinedValue() };
1795 AutoArrayRooter
tvr(cx
, JS_ARRAY_LENGTH(roots
), roots
);
1798 if (pobj
->isNative()) {
1799 Shape
*shape
= (Shape
*) prop
;
1800 attrs
= shape
->attributes();
1801 if (attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)) {
1803 if (attrs
& JSPROP_GETTER
)
1804 roots
[0] = shape
->getterValue();
1805 if (attrs
& JSPROP_SETTER
)
1806 roots
[1] = shape
->setterValue();
1809 if (!pobj
->getAttributes(cx
, id
, &attrs
))
1813 if (doGet
&& !obj
->getProperty(cx
, id
, &roots
[2]))
1816 return js_NewPropertyDescriptorObject(cx
, id
,
1818 roots
[0], /* getter */
1819 roots
[1], /* setter */
1820 roots
[2], /* value */
1825 GetFirstArgumentAsObject(JSContext
*cx
, uintN argc
, Value
*vp
, const char *method
, JSObject
**objp
)
1828 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_MORE_ARGS_NEEDED
,
1833 const Value
&v
= vp
[2];
1834 if (!v
.isObject()) {
1835 char *bytes
= DecompileValueGenerator(cx
, JSDVG_SEARCH_STACK
, v
, NULL
);
1838 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_UNEXPECTED_TYPE
,
1839 bytes
, "not an object");
1844 *objp
= &v
.toObject();
1849 obj_getOwnPropertyDescriptor(JSContext
*cx
, uintN argc
, Value
*vp
)
1852 if (!GetFirstArgumentAsObject(cx
, argc
, vp
, "Object.getOwnPropertyDescriptor", &obj
))
1854 AutoIdRooter
nameidr(cx
);
1855 if (!ValueToId(cx
, argc
>= 2 ? vp
[3] : UndefinedValue(), nameidr
.addr()))
1857 return js_GetOwnPropertyDescriptor(cx
, obj
, nameidr
.id(), vp
);
1861 obj_keys(JSContext
*cx
, uintN argc
, Value
*vp
)
1864 if (!GetFirstArgumentAsObject(cx
, argc
, vp
, "Object.keys", &obj
))
1867 AutoIdVector
props(cx
);
1868 if (!GetPropertyNames(cx
, obj
, JSITER_OWNONLY
, &props
))
1871 AutoValueVector
vals(cx
);
1872 if (!vals
.reserve(props
.length()))
1874 for (size_t i
= 0, len
= props
.length(); i
< len
; i
++) {
1876 if (JSID_IS_STRING(id
)) {
1877 JS_ALWAYS_TRUE(vals
.append(StringValue(JSID_TO_STRING(id
))));
1878 } else if (JSID_IS_INT(id
)) {
1879 JSString
*str
= js_IntToString(cx
, JSID_TO_INT(id
));
1882 JS_ALWAYS_TRUE(vals
.append(StringValue(str
)));
1884 JS_ASSERT(JSID_IS_OBJECT(id
));
1888 JS_ASSERT(props
.length() <= UINT32_MAX
);
1889 JSObject
*aobj
= NewDenseCopiedArray(cx
, jsuint(vals
.length()), vals
.begin());
1892 vp
->setObject(*aobj
);
1898 HasProperty(JSContext
* cx
, JSObject
* obj
, jsid id
, Value
* vp
, bool *foundp
)
1900 if (!obj
->hasProperty(cx
, id
, foundp
, JSRESOLVE_QUALIFIED
| JSRESOLVE_DETECTING
))
1908 * We must go through the method read barrier in case id is 'get' or 'set'.
1909 * There is no obvious way to defer cloning a joined function object whose
1910 * identity will be used by DefinePropertyOnObject, e.g., or reflected via
1911 * js_GetOwnPropertyDescriptor, as the getter or setter callable object.
1913 return !!obj
->getProperty(cx
, id
, vp
);
1916 PropDesc::PropDesc()
1917 : pd(UndefinedValue()),
1919 value(UndefinedValue()),
1920 get(UndefinedValue()),
1921 set(UndefinedValue()),
1927 hasEnumerable(false),
1928 hasConfigurable(false)
1933 PropDesc::initialize(JSContext
* cx
, jsid id
, const Value
&origval
)
1939 if (v
.isPrimitive()) {
1940 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_NOT_NONNULL_OBJECT
);
1943 JSObject
* desc
= &v
.toObject();
1945 /* Make a copy of the descriptor. We might need it later. */
1948 /* Start with the proper defaults. */
1949 attrs
= JSPROP_PERMANENT
| JSPROP_READONLY
;
1954 #ifdef __GNUC__ /* quell GCC overwarning */
1957 if (!HasProperty(cx
, desc
, ATOM_TO_JSID(cx
->runtime
->atomState
.enumerableAtom
), &v
, &found
))
1960 hasEnumerable
= JS_TRUE
;
1961 if (js_ValueToBoolean(v
))
1962 attrs
|= JSPROP_ENUMERATE
;
1966 if (!HasProperty(cx
, desc
, ATOM_TO_JSID(cx
->runtime
->atomState
.configurableAtom
), &v
, &found
))
1969 hasConfigurable
= JS_TRUE
;
1970 if (js_ValueToBoolean(v
))
1971 attrs
&= ~JSPROP_PERMANENT
;
1975 if (!HasProperty(cx
, desc
, ATOM_TO_JSID(cx
->runtime
->atomState
.valueAtom
), &v
, &found
))
1983 if (!HasProperty(cx
, desc
, ATOM_TO_JSID(cx
->runtime
->atomState
.writableAtom
), &v
, &found
))
1986 hasWritable
= JS_TRUE
;
1987 if (js_ValueToBoolean(v
))
1988 attrs
&= ~JSPROP_READONLY
;
1992 if (!HasProperty(cx
, desc
, ATOM_TO_JSID(cx
->runtime
->atomState
.getAtom
), &v
, &found
))
1995 if ((v
.isPrimitive() || !js_IsCallable(v
)) && !v
.isUndefined()) {
1996 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_BAD_GET_SET_FIELD
,
2002 attrs
|= JSPROP_GETTER
| JSPROP_SHARED
;
2006 if (!HasProperty(cx
, desc
, ATOM_TO_JSID(cx
->runtime
->atomState
.setAtom
), &v
, &found
))
2009 if ((v
.isPrimitive() || !js_IsCallable(v
)) && !v
.isUndefined()) {
2010 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_BAD_GET_SET_FIELD
,
2016 attrs
|= JSPROP_SETTER
| JSPROP_SHARED
;
2020 if ((hasGet
|| hasSet
) && (hasValue
|| hasWritable
)) {
2021 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_INVALID_DESCRIPTOR
);
2029 Reject(JSContext
*cx
, uintN errorNumber
, bool throwError
, jsid id
, bool *rval
)
2033 if (!js_ValueToStringId(cx
, IdToValue(id
), &idstr
))
2035 JSAutoByteString
bytes(cx
, JSID_TO_STRING(idstr
));
2038 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, errorNumber
, bytes
.ptr());
2047 Reject(JSContext
*cx
, JSObject
*obj
, uintN errorNumber
, bool throwError
, bool *rval
)
2050 if (js_ErrorFormatString
[errorNumber
].argCount
== 1) {
2051 js_ReportValueErrorFlags(cx
, JSREPORT_ERROR
, errorNumber
,
2052 JSDVG_IGNORE_STACK
, ObjectValue(*obj
),
2055 JS_ASSERT(js_ErrorFormatString
[errorNumber
].argCount
== 0);
2056 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, errorNumber
);
2066 DefinePropertyOnObject(JSContext
*cx
, JSObject
*obj
, const PropDesc
&desc
,
2067 bool throwError
, bool *rval
)
2069 /* 8.12.9 step 1. */
2070 JSProperty
*current
;
2072 JS_ASSERT(!obj
->getOps()->lookupProperty
);
2073 if (!js_HasOwnProperty(cx
, NULL
, obj
, desc
.id
, &obj2
, ¤t
))
2076 JS_ASSERT(!obj
->getOps()->defineProperty
);
2079 * If we find a shared permanent property in a different object obj2 from
2080 * obj, then if the property is shared permanent (an old hack to optimize
2081 * per-object properties into one prototype property), ignore that lookup
2082 * result (null current).
2084 * FIXME: bug 575997 (see also bug 607863).
2086 if (current
&& obj2
!= obj
&& obj2
->isNative()) {
2087 /* See same assertion with comment further below. */
2088 JS_ASSERT(obj2
->getClass() == obj
->getClass());
2090 Shape
*shape
= (Shape
*) current
;
2091 if (shape
->isSharedPermanent())
2095 /* 8.12.9 steps 2-4. */
2097 if (!obj
->isExtensible())
2098 return Reject(cx
, obj
, JSMSG_OBJECT_NOT_EXTENSIBLE
, throwError
, rval
);
2102 if (desc
.isGenericDescriptor() || desc
.isDataDescriptor()) {
2103 JS_ASSERT(!obj
->getOps()->defineProperty
);
2104 return js_DefineProperty(cx
, obj
, desc
.id
, &desc
.value
,
2105 PropertyStub
, StrictPropertyStub
, desc
.attrs
);
2108 JS_ASSERT(desc
.isAccessorDescriptor());
2111 * Getters and setters are just like watchpoints from an access
2112 * control point of view.
2116 if (!CheckAccess(cx
, obj
, desc
.id
, JSACC_WATCH
, &dummy
, &dummyAttrs
))
2119 Value tmp
= UndefinedValue();
2120 return js_DefineProperty(cx
, obj
, desc
.id
, &tmp
,
2121 desc
.getter(), desc
.setter(), desc
.attrs
);
2124 /* 8.12.9 steps 5-6 (note 5 is merely a special case of 6). */
2125 Value v
= UndefinedValue();
2128 * In the special case of shared permanent properties, the "own" property
2129 * can be found on a different object. In that case the returned property
2130 * might not be native, except: the shared permanent property optimization
2131 * is not applied if the objects have different classes (bug 320854), as
2132 * must be enforced by js_HasOwnProperty for the Shape cast below to be
2135 JS_ASSERT(obj
->getClass() == obj2
->getClass());
2137 const Shape
*shape
= reinterpret_cast<Shape
*>(current
);
2139 if (desc
.isAccessorDescriptor()) {
2140 if (!shape
->isAccessorDescriptor())
2145 if (!SameValue(cx
, desc
.getterValue(), shape
->getterOrUndefined(), &same
))
2153 if (!SameValue(cx
, desc
.setterValue(), shape
->setterOrUndefined(), &same
))
2160 * Determine the current value of the property once, if the current
2161 * value might actually need to be used or preserved later. NB: we
2162 * guard on whether the current property is a data descriptor to
2163 * avoid calling a getter; we won't need the value if it's not a
2166 if (shape
->isDataDescriptor()) {
2168 * We must rule out a non-configurable js::PropertyOp-guarded
2169 * property becoming a writable unguarded data property, since
2170 * such a property can have its value changed to one the getter
2171 * and setter preclude.
2173 * A desc lacking writable but with value is a data descriptor
2174 * and we must reject it as if it had writable: true if current
2177 if (!shape
->configurable() &&
2178 (!shape
->hasDefaultGetter() || !shape
->hasDefaultSetter()) &&
2179 desc
.isDataDescriptor() &&
2180 (desc
.hasWritable
? desc
.writable() : shape
->writable()))
2182 return Reject(cx
, JSMSG_CANT_REDEFINE_PROP
, throwError
, desc
.id
, rval
);
2185 if (!js_NativeGet(cx
, obj
, obj2
, shape
, JSGET_NO_METHOD_BARRIER
, &v
))
2189 if (desc
.isDataDescriptor()) {
2190 if (!shape
->isDataDescriptor())
2194 if (desc
.hasValue
) {
2195 if (!SameValue(cx
, desc
.value
, v
, &same
))
2199 * Insist that a non-configurable js::PropertyOp data
2200 * property is frozen at exactly the last-got value.
2202 * Duplicate the first part of the big conjunction that
2203 * we tested above, rather than add a local bool flag.
2204 * Likewise, don't try to keep shape->writable() in a
2205 * flag we veto from true to false for non-configurable
2206 * PropertyOp-based data properties and test before the
2207 * SameValue check later on in order to re-use that "if
2208 * (!SameValue) Reject" logic.
2210 * This function is large and complex enough that it
2211 * seems best to repeat a small bit of code and return
2212 * Reject(...) ASAP, instead of being clever.
2214 if (!shape
->configurable() &&
2215 (!shape
->hasDefaultGetter() || !shape
->hasDefaultSetter()))
2217 return Reject(cx
, JSMSG_CANT_REDEFINE_PROP
, throwError
, desc
.id
, rval
);
2222 if (desc
.hasWritable
&& desc
.writable() != shape
->writable())
2225 /* The only fields in desc will be handled below. */
2226 JS_ASSERT(desc
.isGenericDescriptor());
2230 if (desc
.hasConfigurable
&& desc
.configurable() != shape
->configurable())
2232 if (desc
.hasEnumerable
&& desc
.enumerable() != shape
->enumerable())
2235 /* The conditions imposed by step 5 or step 6 apply. */
2240 /* 8.12.9 step 7. */
2241 if (!shape
->configurable()) {
2243 * Since [[Configurable]] defaults to false, we don't need to check
2244 * whether it was specified. We can't do likewise for [[Enumerable]]
2245 * because its putative value is used in a comparison -- a comparison
2246 * whose result must always be false per spec if the [[Enumerable]]
2247 * field is not present. Perfectly pellucid logic, eh?
2249 JS_ASSERT_IF(!desc
.hasConfigurable
, !desc
.configurable());
2250 if (desc
.configurable() ||
2251 (desc
.hasEnumerable
&& desc
.enumerable() != shape
->enumerable())) {
2252 return Reject(cx
, JSMSG_CANT_REDEFINE_PROP
, throwError
, desc
.id
, rval
);
2256 bool callDelProperty
= false;
2258 if (desc
.isGenericDescriptor()) {
2259 /* 8.12.9 step 8, no validation required */
2260 } else if (desc
.isDataDescriptor() != shape
->isDataDescriptor()) {
2261 /* 8.12.9 step 9. */
2262 if (!shape
->configurable())
2263 return Reject(cx
, JSMSG_CANT_REDEFINE_PROP
, throwError
, desc
.id
, rval
);
2264 } else if (desc
.isDataDescriptor()) {
2265 /* 8.12.9 step 10. */
2266 JS_ASSERT(shape
->isDataDescriptor());
2267 if (!shape
->configurable() && !shape
->writable()) {
2268 if (desc
.hasWritable
&& desc
.writable())
2269 return Reject(cx
, JSMSG_CANT_REDEFINE_PROP
, throwError
, desc
.id
, rval
);
2270 if (desc
.hasValue
) {
2272 if (!SameValue(cx
, desc
.value
, v
, &same
))
2275 return Reject(cx
, JSMSG_CANT_REDEFINE_PROP
, throwError
, desc
.id
, rval
);
2279 callDelProperty
= !shape
->hasDefaultGetter() || !shape
->hasDefaultSetter();
2281 /* 8.12.9 step 11. */
2282 JS_ASSERT(desc
.isAccessorDescriptor() && shape
->isAccessorDescriptor());
2283 if (!shape
->configurable()) {
2286 if (!SameValue(cx
, desc
.setterValue(), shape
->setterOrUndefined(), &same
))
2289 return Reject(cx
, JSMSG_CANT_REDEFINE_PROP
, throwError
, desc
.id
, rval
);
2294 if (!SameValue(cx
, desc
.getterValue(), shape
->getterOrUndefined(), &same
))
2297 return Reject(cx
, JSMSG_CANT_REDEFINE_PROP
, throwError
, desc
.id
, rval
);
2302 /* 8.12.9 step 12. */
2305 StrictPropertyOp setter
;
2306 if (desc
.isGenericDescriptor()) {
2308 if (desc
.hasConfigurable
)
2309 changed
|= JSPROP_PERMANENT
;
2310 if (desc
.hasEnumerable
)
2311 changed
|= JSPROP_ENUMERATE
;
2313 attrs
= (shape
->attributes() & ~changed
) | (desc
.attrs
& changed
);
2314 if (shape
->isMethod()) {
2315 JS_ASSERT(!(attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)));
2316 getter
= PropertyStub
;
2317 setter
= StrictPropertyStub
;
2319 getter
= shape
->getter();
2320 setter
= shape
->setter();
2322 } else if (desc
.isDataDescriptor()) {
2323 uintN unchanged
= 0;
2324 if (!desc
.hasConfigurable
)
2325 unchanged
|= JSPROP_PERMANENT
;
2326 if (!desc
.hasEnumerable
)
2327 unchanged
|= JSPROP_ENUMERATE
;
2328 if (!desc
.hasWritable
)
2329 unchanged
|= JSPROP_READONLY
;
2333 attrs
= (desc
.attrs
& ~unchanged
) | (shape
->attributes() & unchanged
);
2334 getter
= PropertyStub
;
2335 setter
= StrictPropertyStub
;
2337 JS_ASSERT(desc
.isAccessorDescriptor());
2340 * Getters and setters are just like watchpoints from an access
2341 * control point of view.
2344 if (!CheckAccess(cx
, obj2
, desc
.id
, JSACC_WATCH
, &dummy
, &attrs
))
2347 JS_ASSERT_IF(shape
->isMethod(), !(attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)));
2349 /* 8.12.9 step 12. */
2351 if (desc
.hasConfigurable
)
2352 changed
|= JSPROP_PERMANENT
;
2353 if (desc
.hasEnumerable
)
2354 changed
|= JSPROP_ENUMERATE
;
2356 changed
|= JSPROP_GETTER
| JSPROP_SHARED
;
2358 changed
|= JSPROP_SETTER
| JSPROP_SHARED
;
2360 attrs
= (desc
.attrs
& changed
) | (shape
->attributes() & ~changed
);
2362 getter
= desc
.getter();
2364 getter
= (shape
->isMethod() || (shape
->hasDefaultGetter() && !shape
->hasGetterValue()))
2369 setter
= desc
.setter();
2371 setter
= (shape
->hasDefaultSetter() && !shape
->hasSetterValue())
2372 ? StrictPropertyStub
2380 * Since "data" properties implemented using native C functions may rely on
2381 * side effects during setting, we must make them aware that they have been
2382 * "assigned"; deleting the property before redefining it does the trick.
2383 * See bug 539766, where we ran into problems when we redefined
2384 * arguments.length without making the property aware that its value had
2385 * been changed (which would have happened if we had deleted it before
2386 * redefining it or we had invoked its setter to change its value).
2388 if (callDelProperty
) {
2389 Value dummy
= UndefinedValue();
2390 if (!CallJSPropertyOp(cx
, obj2
->getClass()->delProperty
, obj2
, desc
.id
, &dummy
))
2394 return js_DefineProperty(cx
, obj
, desc
.id
, &v
, getter
, setter
, attrs
);
2398 DefinePropertyOnArray(JSContext
*cx
, JSObject
*obj
, const PropDesc
&desc
,
2399 bool throwError
, bool *rval
)
2402 * We probably should optimize dense array property definitions where
2403 * the descriptor describes a traditional array property (enumerable,
2404 * configurable, writable, numeric index or length without altering its
2405 * attributes). Such definitions are probably unlikely, so we don't bother
2408 if (obj
->isDenseArray() && !obj
->makeDenseArraySlow(cx
))
2411 jsuint oldLen
= obj
->getArrayLength();
2413 if (JSID_IS_ATOM(desc
.id
, cx
->runtime
->atomState
.lengthAtom
)) {
2415 * Our optimization of storage of the length property of arrays makes
2416 * it very difficult to properly implement defining the property. For
2417 * now simply throw an exception (NB: not merely Reject) on any attempt
2418 * to define the "length" property, rather than attempting to implement
2419 * some difficult-for-authors-to-grasp subset of that functionality.
2421 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_CANT_DEFINE_ARRAY_LENGTH
);
2426 if (js_IdIsIndex(desc
.id
, &index
)) {
2428 // Disabled until we support defining "length":
2429 if (index >= oldLen && lengthPropertyNotWritable())
2430 return ThrowTypeError(cx, JSMSG_CANT_APPEND_TO_ARRAY);
2432 if (!DefinePropertyOnObject(cx
, obj
, desc
, false, rval
))
2435 return Reject(cx
, obj
, JSMSG_CANT_DEFINE_ARRAY_INDEX
, throwError
, rval
);
2437 if (index
>= oldLen
) {
2438 JS_ASSERT(index
!= UINT32_MAX
);
2439 obj
->setArrayLength(index
+ 1);
2446 return DefinePropertyOnObject(cx
, obj
, desc
, throwError
, rval
);
2450 DefineProperty(JSContext
*cx
, JSObject
*obj
, const PropDesc
&desc
, bool throwError
,
2454 return DefinePropertyOnArray(cx
, obj
, desc
, throwError
, rval
);
2456 if (obj
->getOps()->lookupProperty
) {
2458 return JSProxy::defineProperty(cx
, obj
, desc
.id
, desc
.pd
);
2459 return Reject(cx
, obj
, JSMSG_OBJECT_NOT_EXTENSIBLE
, throwError
, rval
);
2462 return DefinePropertyOnObject(cx
, obj
, desc
, throwError
, rval
);
2466 js_DefineOwnProperty(JSContext
*cx
, JSObject
*obj
, jsid id
,
2467 const Value
&descriptor
, JSBool
*bp
)
2469 AutoPropDescArrayRooter
descs(cx
);
2470 PropDesc
*desc
= descs
.append();
2471 if (!desc
|| !desc
->initialize(cx
, id
, descriptor
))
2475 if (!DefineProperty(cx
, obj
, *desc
, true, &rval
))
2481 /* ES5 15.2.3.6: Object.defineProperty(O, P, Attributes) */
2483 obj_defineProperty(JSContext
* cx
, uintN argc
, Value
* vp
)
2485 /* 15.2.3.6 steps 1 and 5. */
2487 if (!GetFirstArgumentAsObject(cx
, argc
, vp
, "Object.defineProperty", &obj
))
2489 vp
->setObject(*obj
);
2491 /* 15.2.3.6 step 2. */
2492 AutoIdRooter
nameidr(cx
);
2493 if (!ValueToId(cx
, argc
>= 2 ? vp
[3] : UndefinedValue(), nameidr
.addr()))
2496 /* 15.2.3.6 step 3. */
2497 const Value
&descval
= argc
>= 3 ? vp
[4] : UndefinedValue();
2499 /* 15.2.3.6 step 4 */
2501 return js_DefineOwnProperty(cx
, obj
, nameidr
.id(), descval
, &junk
);
2505 DefineProperties(JSContext
*cx
, JSObject
*obj
, JSObject
*props
)
2507 AutoIdArray
ida(cx
, JS_Enumerate(cx
, props
));
2511 AutoPropDescArrayRooter
descs(cx
);
2512 size_t len
= ida
.length();
2513 for (size_t i
= 0; i
< len
; i
++) {
2515 PropDesc
* desc
= descs
.append();
2516 AutoValueRooter
tvr(cx
);
2518 !JS_GetPropertyById(cx
, props
, id
, tvr
.jsval_addr()) ||
2519 !desc
->initialize(cx
, id
, tvr
.value())) {
2525 for (size_t i
= 0; i
< len
; i
++) {
2526 if (!DefineProperty(cx
, obj
, descs
[i
], true, &dummy
))
2534 js_PopulateObject(JSContext
*cx
, JSObject
*newborn
, JSObject
*props
)
2536 return DefineProperties(cx
, newborn
, props
);
2539 /* ES5 15.2.3.7: Object.defineProperties(O, Properties) */
2541 obj_defineProperties(JSContext
* cx
, uintN argc
, Value
* vp
)
2543 /* 15.2.3.6 steps 1 and 5. */
2545 if (!GetFirstArgumentAsObject(cx
, argc
, vp
, "Object.defineProperties", &obj
))
2547 vp
->setObject(*obj
);
2550 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_MORE_ARGS_NEEDED
,
2551 "Object.defineProperties", "0", "s");
2555 JSObject
* props
= js_ValueToNonNullObject(cx
, vp
[3]);
2558 vp
[3].setObject(*props
);
2560 return DefineProperties(cx
, obj
, props
);
2563 /* ES5 15.2.3.5: Object.create(O [, Properties]) */
2565 obj_create(JSContext
*cx
, uintN argc
, Value
*vp
)
2568 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_MORE_ARGS_NEEDED
,
2569 "Object.create", "0", "s");
2573 const Value
&v
= vp
[2];
2574 if (!v
.isObjectOrNull()) {
2575 char *bytes
= DecompileValueGenerator(cx
, JSDVG_SEARCH_STACK
, v
, NULL
);
2578 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_UNEXPECTED_TYPE
,
2579 bytes
, "not an object or null");
2585 * Use the callee's global as the parent of the new object to avoid dynamic
2586 * scoping (i.e., using the caller's global).
2588 JSObject
*obj
= NewNonFunction
<WithProto::Given
>(cx
, &js_ObjectClass
, v
.toObjectOrNull(),
2589 vp
->toObject().getGlobal());
2592 vp
->setObject(*obj
); /* Root and prepare for eventual return. */
2594 /* 15.2.3.5 step 4. */
2595 if (argc
> 1 && !vp
[3].isUndefined()) {
2596 if (vp
[3].isPrimitive()) {
2597 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_NOT_NONNULL_OBJECT
);
2601 JSObject
*props
= &vp
[3].toObject();
2602 AutoIdArray
ida(cx
, JS_Enumerate(cx
, props
));
2606 AutoPropDescArrayRooter
descs(cx
);
2607 size_t len
= ida
.length();
2608 for (size_t i
= 0; i
< len
; i
++) {
2610 PropDesc
*desc
= descs
.append();
2611 if (!desc
|| !JS_GetPropertyById(cx
, props
, id
, Jsvalify(&vp
[1])) ||
2612 !desc
->initialize(cx
, id
, vp
[1])) {
2618 for (size_t i
= 0; i
< len
; i
++) {
2619 if (!DefineProperty(cx
, obj
, descs
[i
], true, &dummy
))
2624 /* 5. Return obj. */
2629 obj_getOwnPropertyNames(JSContext
*cx
, uintN argc
, Value
*vp
)
2632 if (!GetFirstArgumentAsObject(cx
, argc
, vp
, "Object.getOwnPropertyNames", &obj
))
2635 AutoIdVector
keys(cx
);
2636 if (!GetPropertyNames(cx
, obj
, JSITER_OWNONLY
| JSITER_HIDDEN
, &keys
))
2639 AutoValueVector
vals(cx
);
2640 if (!vals
.resize(keys
.length()))
2643 for (size_t i
= 0, len
= keys
.length(); i
< len
; i
++) {
2645 if (JSID_IS_INT(id
)) {
2646 JSString
*str
= js_ValueToString(cx
, Int32Value(JSID_TO_INT(id
)));
2649 vals
[i
].setString(str
);
2650 } else if (JSID_IS_ATOM(id
)) {
2651 vals
[i
].setString(JSID_TO_STRING(id
));
2653 vals
[i
].setObject(*JSID_TO_OBJECT(id
));
2657 JSObject
*aobj
= NewDenseCopiedArray(cx
, vals
.length(), vals
.begin());
2661 vp
->setObject(*aobj
);
2666 obj_isExtensible(JSContext
*cx
, uintN argc
, Value
*vp
)
2669 if (!GetFirstArgumentAsObject(cx
, argc
, vp
, "Object.isExtensible", &obj
))
2672 vp
->setBoolean(obj
->isExtensible());
2677 obj_preventExtensions(JSContext
*cx
, uintN argc
, Value
*vp
)
2680 if (!GetFirstArgumentAsObject(cx
, argc
, vp
, "Object.preventExtensions", &obj
))
2683 vp
->setObject(*obj
);
2684 if (!obj
->isExtensible())
2687 AutoIdVector
props(cx
);
2688 return obj
->preventExtensions(cx
, &props
);
2692 JSObject::sealOrFreeze(JSContext
*cx
, ImmutabilityType it
)
2694 assertSameCompartment(cx
, this);
2695 JS_ASSERT(it
== SEAL
|| it
== FREEZE
);
2697 AutoIdVector
props(cx
);
2698 if (isExtensible()) {
2699 if (!preventExtensions(cx
, &props
))
2702 if (!GetPropertyNames(cx
, this, JSITER_HIDDEN
| JSITER_OWNONLY
, &props
))
2706 /* preventExtensions must slowify dense arrays, so we can assign to holes without checks. */
2707 JS_ASSERT(!isDenseArray());
2709 for (size_t i
= 0, len
= props
.length(); i
< len
; i
++) {
2713 if (!getAttributes(cx
, id
, &attrs
))
2716 /* Make all attributes permanent; if freezing, make data attributes read-only. */
2718 if (it
== FREEZE
&& !(attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)))
2719 new_attrs
= JSPROP_PERMANENT
| JSPROP_READONLY
;
2721 new_attrs
= JSPROP_PERMANENT
;
2723 /* If we already have the attributes we need, skip the setAttributes call. */
2724 if ((attrs
| new_attrs
) == attrs
)
2728 if (!setAttributes(cx
, id
, &attrs
))
2736 obj_freeze(JSContext
*cx
, uintN argc
, Value
*vp
)
2739 if (!GetFirstArgumentAsObject(cx
, argc
, vp
, "Object.freeze", &obj
))
2742 vp
->setObject(*obj
);
2744 return obj
->freeze(cx
);
2748 obj_isFrozen(JSContext
*cx
, uintN argc
, Value
*vp
)
2751 if (!GetFirstArgumentAsObject(cx
, argc
, vp
, "Object.preventExtensions", &obj
))
2754 vp
->setBoolean(false);
2756 if (obj
->isExtensible())
2757 return true; /* The JavaScript value returned is false. */
2759 AutoIdVector
props(cx
);
2760 if (!GetPropertyNames(cx
, obj
, JSITER_HIDDEN
| JSITER_OWNONLY
, &props
))
2763 for (size_t i
= 0, len
= props
.length(); i
< len
; i
++) {
2767 if (!obj
->getAttributes(cx
, id
, &attrs
))
2770 /* The property must be non-configurable and either read-only or an accessor. */
2771 if (!(attrs
& JSPROP_PERMANENT
) ||
2772 !(attrs
& (JSPROP_READONLY
| JSPROP_GETTER
| JSPROP_SETTER
)))
2773 return true; /* The JavaScript value returned is false. */
2776 /* It really was sealed, so return true. */
2777 vp
->setBoolean(true);
2782 obj_seal(JSContext
*cx
, uintN argc
, Value
*vp
)
2785 if (!GetFirstArgumentAsObject(cx
, argc
, vp
, "Object.seal", &obj
))
2788 vp
->setObject(*obj
);
2790 return obj
->seal(cx
);
2794 obj_isSealed(JSContext
*cx
, uintN argc
, Value
*vp
)
2797 if (!GetFirstArgumentAsObject(cx
, argc
, vp
, "Object.isSealed", &obj
))
2800 /* Assume not sealed until proven otherwise. */
2801 vp
->setBoolean(false);
2803 if (obj
->isExtensible())
2804 return true; /* The JavaScript value returned is false. */
2806 AutoIdVector
props(cx
);
2807 if (!GetPropertyNames(cx
, obj
, JSITER_HIDDEN
| JSITER_OWNONLY
, &props
))
2810 for (size_t i
= 0, len
= props
.length(); i
< len
; i
++) {
2814 if (!obj
->getAttributes(cx
, id
, &attrs
))
2817 if (!(attrs
& JSPROP_PERMANENT
))
2818 return true; /* The JavaScript value returned is false. */
2821 /* It really was sealed, so return true. */
2822 vp
->setBoolean(true);
2826 #if JS_HAS_OBJ_WATCHPOINT
2827 const char js_watch_str
[] = "watch";
2828 const char js_unwatch_str
[] = "unwatch";
2830 const char js_hasOwnProperty_str
[] = "hasOwnProperty";
2831 const char js_isPrototypeOf_str
[] = "isPrototypeOf";
2832 const char js_propertyIsEnumerable_str
[] = "propertyIsEnumerable";
2834 static JSFunctionSpec object_methods
[] = {
2836 JS_FN(js_toSource_str
, obj_toSource
, 0,0),
2838 JS_FN(js_toString_str
, obj_toString
, 0,0),
2839 JS_FN(js_toLocaleString_str
, obj_toLocaleString
, 0,0),
2840 JS_FN(js_valueOf_str
, obj_valueOf
, 0,0),
2841 #if JS_HAS_OBJ_WATCHPOINT
2842 JS_FN(js_watch_str
, obj_watch
, 2,0),
2843 JS_FN(js_unwatch_str
, obj_unwatch
, 1,0),
2845 JS_FN(js_hasOwnProperty_str
, obj_hasOwnProperty
, 1,0),
2846 JS_FN(js_isPrototypeOf_str
, obj_isPrototypeOf
, 1,0),
2847 JS_FN(js_propertyIsEnumerable_str
, obj_propertyIsEnumerable
, 1,0),
2848 #if OLD_GETTER_SETTER_METHODS
2849 JS_FN(js_defineGetter_str
, js_obj_defineGetter
, 2,0),
2850 JS_FN(js_defineSetter_str
, js_obj_defineSetter
, 2,0),
2851 JS_FN(js_lookupGetter_str
, obj_lookupGetter
, 1,0),
2852 JS_FN(js_lookupSetter_str
, obj_lookupSetter
, 1,0),
2857 static JSFunctionSpec object_static_methods
[] = {
2858 JS_FN("getPrototypeOf", obj_getPrototypeOf
, 1,0),
2859 JS_FN("getOwnPropertyDescriptor", obj_getOwnPropertyDescriptor
,2,0),
2860 JS_FN("keys", obj_keys
, 1,0),
2861 JS_FN("defineProperty", obj_defineProperty
, 3,0),
2862 JS_FN("defineProperties", obj_defineProperties
, 2,0),
2863 JS_FN("create", obj_create
, 2,0),
2864 JS_FN("getOwnPropertyNames", obj_getOwnPropertyNames
, 1,0),
2865 JS_FN("isExtensible", obj_isExtensible
, 1,0),
2866 JS_FN("preventExtensions", obj_preventExtensions
, 1,0),
2867 JS_FN("freeze", obj_freeze
, 1,0),
2868 JS_FN("isFrozen", obj_isFrozen
, 1,0),
2869 JS_FN("seal", obj_seal
, 1,0),
2870 JS_FN("isSealed", obj_isSealed
, 1,0),
2875 js_Object(JSContext
*cx
, uintN argc
, Value
*vp
)
2879 /* Trigger logic below to construct a blank object. */
2882 /* If argv[0] is null or undefined, obj comes back null. */
2883 if (!js_ValueToObjectOrNull(cx
, vp
[2], &obj
))
2887 /* Make an object whether this was called with 'new' or not. */
2888 JS_ASSERT(!argc
|| vp
[2].isNull() || vp
[2].isUndefined());
2889 gc::FinalizeKind kind
= NewObjectGCKind(cx
, &js_ObjectClass
);
2890 obj
= NewBuiltinClassInstance(cx
, &js_ObjectClass
, kind
);
2894 vp
->setObject(*obj
);
2899 js_CreateThis(JSContext
*cx
, JSObject
*callee
)
2901 Class
*clasp
= callee
->getClass();
2903 Class
*newclasp
= &js_ObjectClass
;
2904 if (clasp
== &js_FunctionClass
) {
2905 JSFunction
*fun
= callee
->getFunctionPrivate();
2906 if (fun
->isNative() && fun
->u
.n
.clasp
)
2907 newclasp
= fun
->u
.n
.clasp
;
2911 if (!callee
->getProperty(cx
, ATOM_TO_JSID(cx
->runtime
->atomState
.classPrototypeAtom
), &protov
))
2914 JSObject
*proto
= protov
.isObjectOrNull() ? protov
.toObjectOrNull() : NULL
;
2915 JSObject
*parent
= callee
->getParent();
2916 gc::FinalizeKind kind
= NewObjectGCKind(cx
, newclasp
);
2917 JSObject
*obj
= NewObject
<WithProto::Class
>(cx
, newclasp
, proto
, parent
, kind
);
2919 obj
->syncSpecialEquality();
2924 js_CreateThisForFunctionWithProto(JSContext
*cx
, JSObject
*callee
, JSObject
*proto
)
2926 gc::FinalizeKind kind
= NewObjectGCKind(cx
, &js_ObjectClass
);
2927 return NewNonFunction
<WithProto::Class
>(cx
, &js_ObjectClass
, proto
, callee
->getParent(), kind
);
2931 js_CreateThisForFunction(JSContext
*cx
, JSObject
*callee
)
2934 if (!callee
->getProperty(cx
,
2935 ATOM_TO_JSID(cx
->runtime
->atomState
.classPrototypeAtom
),
2939 JSObject
*proto
= protov
.isObject() ? &protov
.toObject() : NULL
;
2940 return js_CreateThisForFunctionWithProto(cx
, callee
, proto
);
2945 static JS_ALWAYS_INLINE JSObject
*
2946 NewObjectWithClassProto(JSContext
*cx
, Class
*clasp
, JSObject
*proto
,
2947 /*gc::FinalizeKind*/ unsigned _kind
)
2949 JS_ASSERT(clasp
->isNative());
2950 gc::FinalizeKind kind
= gc::FinalizeKind(_kind
);
2952 JSObject
* obj
= js_NewGCObject(cx
, kind
);
2956 if (!obj
->initSharingEmptyShape(cx
, clasp
, proto
, proto
->getParent(), NULL
, kind
))
2962 js_Object_tn(JSContext
* cx
, JSObject
* proto
)
2964 JS_ASSERT(!(js_ObjectClass
.flags
& JSCLASS_HAS_PRIVATE
));
2965 return NewObjectWithClassProto(cx
, &js_ObjectClass
, proto
, FINALIZE_OBJECT8
);
2968 JS_DEFINE_TRCINFO_1(js_Object
,
2969 (2, (extern, CONSTRUCTOR_RETRY
, js_Object_tn
, CONTEXT
, CALLEE_PROTOTYPE
, 0,
2970 nanojit::ACCSET_STORE_ANY
)))
2973 js_InitializerObject(JSContext
* cx
, JSObject
*proto
, JSObject
*baseobj
)
2976 gc::FinalizeKind kind
= GuessObjectGCKind(0, false);
2977 return NewObjectWithClassProto(cx
, &js_ObjectClass
, proto
, kind
);
2980 return CopyInitializerObject(cx
, baseobj
);
2983 JS_DEFINE_CALLINFO_3(extern, OBJECT
, js_InitializerObject
, CONTEXT
, OBJECT
, OBJECT
,
2984 0, nanojit::ACCSET_STORE_ANY
)
2987 js_String_tn(JSContext
* cx
, JSObject
* proto
, JSString
* str
)
2989 JS_ASSERT(JS_ON_TRACE(cx
));
2990 JSObject
*obj
= NewObjectWithClassProto(cx
, &js_StringClass
, proto
, FINALIZE_OBJECT2
);
2993 obj
->setPrimitiveThis(StringValue(str
));
2996 JS_DEFINE_CALLINFO_3(extern, OBJECT
, js_String_tn
, CONTEXT
, CALLEE_PROTOTYPE
, STRING
, 0,
2997 nanojit::ACCSET_STORE_ANY
)
3000 js_CreateThisFromTrace(JSContext
*cx
, Class
*clasp
, JSObject
*ctor
)
3002 JS_ASSERT(JS_ON_TRACE(cx
));
3003 JS_ASSERT(ctor
->isFunction());
3005 if (!ctor
->ensureClassReservedSlots(cx
))
3008 jsid classPrototypeId
= ATOM_TO_JSID(cx
->runtime
->atomState
.classPrototypeAtom
);
3009 const Shape
*shape
= ctor
->nativeLookup(classPrototypeId
);
3010 Value pval
= shape
? ctor
->getSlot(shape
->slot
) : MagicValue(JS_GENERIC_MAGIC
);
3012 JSObject
*parent
= ctor
->getParent();
3014 if (pval
.isObject()) {
3015 /* An object in ctor.prototype, let's use it as the new instance's proto. */
3016 proto
= &pval
.toObject();
3018 /* A hole or a primitive: either way, we need to get Object.prototype. */
3019 if (!js_GetClassPrototype(cx
, parent
, JSProto_Object
, &proto
))
3022 if (pval
.isMagic(JS_GENERIC_MAGIC
)) {
3024 * No ctor.prototype was set, so we inline-expand and optimize
3025 * fun_resolve's prototype creation code.
3027 proto
= NewNativeClassInstance(cx
, clasp
, proto
, parent
);
3030 JSFunction
*fun
= ctor
->getFunctionPrivate();
3031 if (!fun
->isNative() && !fun
->isFunctionPrototype()) {
3032 if (!js_SetClassPrototype(cx
, ctor
, proto
, JSPROP_ENUMERATE
| JSPROP_PERMANENT
))
3037 * A primitive value in .prototype means to use Object.prototype
3038 * for proto. See ES5 13.2.2 step 7.
3044 * FIXME: 561785 at least. Quasi-natives including XML objects prevent us
3045 * from easily or unconditionally calling NewNativeClassInstance here.
3047 gc::FinalizeKind kind
= NewObjectGCKind(cx
, clasp
);
3048 return NewNonFunction
<WithProto::Given
>(cx
, clasp
, proto
, parent
, kind
);
3051 JS_DEFINE_CALLINFO_3(extern, CONSTRUCTOR_RETRY
, js_CreateThisFromTrace
, CONTEXT
, CLASS
, OBJECT
, 0,
3052 nanojit::ACCSET_STORE_ANY
)
3054 #else /* !JS_TRACER */
3056 # define js_Object_trcinfo NULL
3058 #endif /* !JS_TRACER */
3061 * Given pc pointing after a property accessing bytecode, return true if the
3062 * access is "object-detecting" in the sense used by web scripts, e.g., when
3063 * checking whether document.all is defined.
3065 JS_REQUIRES_STACK JSBool
3066 Detecting(JSContext
*cx
, jsbytecode
*pc
)
3073 script
= cx
->fp()->script();
3074 endpc
= script
->code
+ script
->length
;
3075 for (;; pc
+= js_CodeSpec
[op
].length
) {
3076 JS_ASSERT_IF(!cx
->fp()->hasImacropc(), script
->code
<= pc
&& pc
< endpc
);
3078 /* General case: a branch or equality op follows the access. */
3079 op
= js_GetOpcode(cx
, script
, pc
);
3080 if (js_CodeSpec
[op
].format
& JOF_DETECTING
)
3086 * Special case #1: handle (document.all == null). Don't sweat
3087 * about JS1.2's revision of the equality operators here.
3090 op
= js_GetOpcode(cx
, script
, pc
);
3091 return *pc
== JSOP_EQ
|| *pc
== JSOP_NE
;
3098 * Special case #2: handle (document.all == undefined). Don't
3099 * worry about someone redefining undefined, which was added by
3100 * Edition 3, so is read/write for backward compatibility.
3102 GET_ATOM_FROM_BYTECODE(script
, pc
, 0, atom
);
3103 if (atom
== cx
->runtime
->atomState
.typeAtoms
[JSTYPE_VOID
] &&
3104 (pc
+= js_CodeSpec
[op
].length
) < endpc
) {
3105 op
= js_GetOpcode(cx
, script
, pc
);
3106 return op
== JSOP_EQ
|| op
== JSOP_NE
||
3107 op
== JSOP_STRICTEQ
|| op
== JSOP_STRICTNE
;
3113 * At this point, anything but an extended atom index prefix means
3114 * we're not detecting.
3116 if (!(js_CodeSpec
[op
].format
& JOF_INDEXBASE
))
3124 * Infer lookup flags from the currently executing bytecode. This does
3125 * not attempt to infer JSRESOLVE_WITH, because the current bytecode
3126 * does not indicate whether we are in a with statement. Return defaultFlags
3127 * if a currently executing bytecode cannot be determined.
3130 js_InferFlags(JSContext
*cx
, uintN defaultFlags
)
3133 if (JS_ON_TRACE(cx
))
3134 return JS_TRACE_MONITOR_ON_TRACE(cx
)->bailExit
->lookupFlags
;
3137 JS_ASSERT_NOT_ON_TRACE(cx
);
3140 const JSCodeSpec
*cs
;
3144 JSStackFrame
*const fp
= js_GetTopStackFrame(cx
);
3145 if (!fp
|| !(pc
= cx
->regs
->pc
))
3146 return defaultFlags
;
3147 cs
= &js_CodeSpec
[js_GetOpcode(cx
, fp
->script(), pc
)];
3148 format
= cs
->format
;
3149 if (JOF_MODE(format
) != JOF_NAME
)
3150 flags
|= JSRESOLVE_QUALIFIED
;
3151 if ((format
& (JOF_SET
| JOF_FOR
)) || fp
->isAssigning()) {
3152 flags
|= JSRESOLVE_ASSIGNING
;
3153 } else if (cs
->length
>= 0) {
3155 JSScript
*script
= cx
->fp()->script();
3156 if (pc
< script
->code
+ script
->length
&& Detecting(cx
, pc
))
3157 flags
|= JSRESOLVE_DETECTING
;
3159 if (format
& JOF_DECLARING
)
3160 flags
|= JSRESOLVE_DECLARING
;
3165 * ObjectOps and Class for with-statement stack objects.
3168 with_LookupProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, JSObject
**objp
,
3171 /* Fixes bug 463997 */
3172 uintN flags
= cx
->resolveFlags
;
3173 if (flags
== JSRESOLVE_INFER
)
3174 flags
= js_InferFlags(cx
, flags
);
3175 flags
|= JSRESOLVE_WITH
;
3176 JSAutoResolveFlags
rf(cx
, flags
);
3177 return obj
->getProto()->lookupProperty(cx
, id
, objp
, propp
);
3181 with_GetProperty(JSContext
*cx
, JSObject
*obj
, JSObject
*receiver
, jsid id
, Value
*vp
)
3183 return obj
->getProto()->getProperty(cx
, id
, vp
);
3187 with_SetProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
, JSBool strict
)
3189 return obj
->getProto()->setProperty(cx
, id
, vp
, strict
);
3193 with_GetAttributes(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN
*attrsp
)
3195 return obj
->getProto()->getAttributes(cx
, id
, attrsp
);
3199 with_SetAttributes(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN
*attrsp
)
3201 return obj
->getProto()->setAttributes(cx
, id
, attrsp
);
3205 with_DeleteProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*rval
, JSBool strict
)
3207 return obj
->getProto()->deleteProperty(cx
, id
, rval
, strict
);
3211 with_Enumerate(JSContext
*cx
, JSObject
*obj
, JSIterateOp enum_op
,
3212 Value
*statep
, jsid
*idp
)
3214 return obj
->getProto()->enumerate(cx
, enum_op
, statep
, idp
);
3218 with_TypeOf(JSContext
*cx
, JSObject
*obj
)
3220 return JSTYPE_OBJECT
;
3224 with_ThisObject(JSContext
*cx
, JSObject
*obj
)
3226 return obj
->getWithThis();
3229 Class js_WithClass
= {
3231 JSCLASS_HAS_PRIVATE
| JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS
,
3232 PropertyStub
, /* addProperty */
3233 PropertyStub
, /* delProperty */
3234 PropertyStub
, /* getProperty */
3235 StrictPropertyStub
, /* setProperty */
3239 NULL
, /* finalize */
3240 NULL
, /* reserved */
3241 NULL
, /* checkAccess */
3243 NULL
, /* construct */
3244 NULL
, /* xdrObject */
3245 NULL
, /* hasInstance */
3249 with_LookupProperty
,
3250 NULL
, /* defineProperty */
3255 with_DeleteProperty
,
3265 JS_REQUIRES_STACK JSObject
*
3266 js_NewWithObject(JSContext
*cx
, JSObject
*proto
, JSObject
*parent
, jsint depth
)
3270 obj
= js_NewGCObject(cx
, FINALIZE_OBJECT2
);
3274 JSStackFrame
*priv
= js_FloatingFrameIfGenerator(cx
, cx
->fp());
3276 obj
->init(cx
, &js_WithClass
, proto
, parent
, priv
, false);
3277 obj
->setMap(cx
->compartment
->emptyWithShape
);
3278 OBJ_SET_BLOCK_DEPTH(cx
, obj
, depth
);
3280 AutoObjectRooter
tvr(cx
, obj
);
3281 JSObject
*thisp
= proto
->thisObject(cx
);
3285 assertSameCompartment(cx
, obj
, thisp
);
3287 obj
->setWithThis(thisp
);
3292 js_NewBlockObject(JSContext
*cx
)
3295 * Null obj's proto slot so that Object.prototype.* does not pollute block
3296 * scopes and to give the block object its own scope.
3298 JSObject
*blockObj
= js_NewGCObject(cx
, FINALIZE_OBJECT2
);
3302 blockObj
->init(cx
, &js_BlockClass
, NULL
, NULL
, NULL
, false);
3303 blockObj
->setMap(cx
->compartment
->emptyBlockShape
);
3308 js_CloneBlockObject(JSContext
*cx
, JSObject
*proto
, JSStackFrame
*fp
)
3310 JS_ASSERT(proto
->isStaticBlock());
3312 size_t count
= OBJ_BLOCK_COUNT(cx
, proto
);
3313 gc::FinalizeKind kind
= gc::GetGCObjectKind(count
+ 1);
3315 JSObject
*clone
= js_NewGCObject(cx
, kind
);
3319 JSStackFrame
*priv
= js_FloatingFrameIfGenerator(cx
, fp
);
3321 /* The caller sets parent on its own. */
3322 clone
->init(cx
, &js_BlockClass
, proto
, NULL
, priv
, false);
3324 clone
->setMap(proto
->map
);
3325 if (!clone
->ensureInstanceReservedSlots(cx
, count
+ 1))
3328 clone
->setSlot(JSSLOT_BLOCK_DEPTH
, proto
->getSlot(JSSLOT_BLOCK_DEPTH
));
3330 JS_ASSERT(clone
->isClonedBlock());
3334 JS_REQUIRES_STACK JSBool
3335 js_PutBlockObject(JSContext
*cx
, JSBool normalUnwind
)
3337 JSStackFrame
*const fp
= cx
->fp();
3338 JSObject
*obj
= &fp
->scopeChain();
3339 JS_ASSERT(obj
->isClonedBlock());
3340 JS_ASSERT(obj
->getPrivate() == js_FloatingFrameIfGenerator(cx
, cx
->fp()));
3342 /* Block objects should have all reserved slots allocated early. */
3343 uintN count
= OBJ_BLOCK_COUNT(cx
, obj
);
3344 JS_ASSERT(obj
->numSlots() >= JSSLOT_BLOCK_DEPTH
+ 1 + count
);
3346 /* The block and its locals must be on the current stack for GC safety. */
3347 uintN depth
= OBJ_BLOCK_DEPTH(cx
, obj
);
3348 JS_ASSERT(depth
<= size_t(cx
->regs
->sp
- fp
->base()));
3349 JS_ASSERT(count
<= size_t(cx
->regs
->sp
- fp
->base() - depth
));
3351 /* See comments in CheckDestructuring from jsparse.cpp. */
3352 JS_ASSERT(count
>= 1);
3355 uintN slot
= JSSLOT_BLOCK_FIRST_FREE_SLOT
;
3356 depth
+= fp
->numFixed();
3357 memcpy(obj
->getSlots() + slot
, fp
->slots() + depth
, count
* sizeof(Value
));
3360 /* We must clear the private slot even with errors. */
3361 obj
->setPrivate(NULL
);
3362 fp
->setScopeChainNoCallObj(*obj
->getParent());
3363 return normalUnwind
;
3367 block_getProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
3370 * Block objects are never exposed to script, and the engine handles them
3371 * with care. So unlike other getters, this one can assert (rather than
3372 * check) certain invariants about obj.
3374 JS_ASSERT(obj
->isClonedBlock());
3375 uintN index
= (uintN
) JSID_TO_INT(id
);
3376 JS_ASSERT(index
< OBJ_BLOCK_COUNT(cx
, obj
));
3378 JSStackFrame
*fp
= (JSStackFrame
*) obj
->getPrivate();
3380 fp
= js_LiveFrameIfGenerator(fp
);
3381 index
+= fp
->numFixed() + OBJ_BLOCK_DEPTH(cx
, obj
);
3382 JS_ASSERT(index
< fp
->numSlots());
3383 *vp
= fp
->slots()[index
];
3387 /* Values are in slots immediately following the class-reserved ones. */
3388 JS_ASSERT(obj
->getSlot(JSSLOT_FREE(&js_BlockClass
) + index
) == *vp
);
3393 block_setProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, JSBool strict
, Value
*vp
)
3395 JS_ASSERT(obj
->isClonedBlock());
3396 uintN index
= (uintN
) JSID_TO_INT(id
);
3397 JS_ASSERT(index
< OBJ_BLOCK_COUNT(cx
, obj
));
3399 JSStackFrame
*fp
= (JSStackFrame
*) obj
->getPrivate();
3401 fp
= js_LiveFrameIfGenerator(fp
);
3402 index
+= fp
->numFixed() + OBJ_BLOCK_DEPTH(cx
, obj
);
3403 JS_ASSERT(index
< fp
->numSlots());
3404 fp
->slots()[index
] = *vp
;
3409 * The value in *vp will be written back to the slot in obj that was
3410 * allocated when this let binding was defined.
3416 JSObject::defineBlockVariable(JSContext
*cx
, jsid id
, intN index
)
3418 JS_ASSERT(isStaticBlock());
3420 /* Use JSPROP_ENUMERATE to aid the disassembler. */
3421 uint32 slot
= JSSLOT_FREE(&js_BlockClass
) + index
;
3422 const Shape
*shape
= addProperty(cx
, id
,
3423 block_getProperty
, block_setProperty
,
3424 slot
, JSPROP_ENUMERATE
| JSPROP_PERMANENT
,
3425 Shape::HAS_SHORTID
, index
);
3428 if (slot
>= numSlots() && !growSlots(cx
, slot
+ 1))
3434 GetObjectSize(JSObject
*obj
)
3436 return (obj
->isFunction() && !obj
->getPrivate())
3437 ? sizeof(JSFunction
)
3438 : sizeof(JSObject
) + sizeof(js::Value
) * obj
->numFixedSlots();
3442 JSObject::copyPropertiesFrom(JSContext
*cx
, JSObject
*obj
)
3444 // If we're not native, then we cannot copy properties.
3445 JS_ASSERT(isNative() == obj
->isNative());
3449 AutoShapeVector
shapes(cx
);
3450 for (Shape::Range
r(obj
->lastProperty()); !r
.empty(); r
.popFront()) {
3451 if (!shapes
.append(&r
.front()))
3455 size_t n
= shapes
.length();
3457 const Shape
*shape
= shapes
[--n
];
3458 uintN attrs
= shape
->attributes();
3459 PropertyOp getter
= shape
->getter();
3460 if ((attrs
& JSPROP_GETTER
) && !cx
->compartment
->wrap(cx
, &getter
))
3462 StrictPropertyOp setter
= shape
->setter();
3463 if ((attrs
& JSPROP_SETTER
) && !cx
->compartment
->wrap(cx
, &setter
))
3465 Value v
= shape
->hasSlot() ? obj
->getSlot(shape
->slot
) : UndefinedValue();
3466 if (!cx
->compartment
->wrap(cx
, &v
))
3468 if (!defineProperty(cx
, shape
->id
, v
, getter
, setter
, attrs
))
3475 CopySlots(JSContext
*cx
, JSObject
*from
, JSObject
*to
)
3477 JS_ASSERT(!from
->isNative() && !to
->isNative());
3478 size_t nslots
= from
->numSlots();
3479 if (to
->ensureSlots(cx
, nslots
))
3483 if (to
->isWrapper() &&
3484 (JSWrapper::wrapperHandler(to
)->flags() & JSWrapper::CROSS_COMPARTMENT
)) {
3485 to
->slots
[0] = from
->slots
[0];
3486 to
->slots
[1] = from
->slots
[1];
3490 for (; n
< nslots
; ++n
) {
3491 Value v
= from
->slots
[n
];
3492 if (!cx
->compartment
->wrap(cx
, &v
))
3500 JSObject::clone(JSContext
*cx
, JSObject
*proto
, JSObject
*parent
)
3503 * We can only clone native objects and proxies. Dense arrays are slowified if
3504 * we try to clone them.
3507 if (isDenseArray()) {
3508 if (!makeDenseArraySlow(cx
))
3510 } else if (!isProxy()) {
3511 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
3512 JSMSG_CANT_CLONE_OBJECT
);
3516 JSObject
*clone
= NewObject
<WithProto::Given
>(cx
, getClass(),
3518 gc::FinalizeKind(finalizeKind()));
3522 if (clone
->isFunction() && (compartment() != clone
->compartment())) {
3523 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
3524 JSMSG_CANT_CLONE_OBJECT
);
3528 if (getClass()->flags
& JSCLASS_HAS_PRIVATE
)
3529 clone
->setPrivate(getPrivate());
3531 JS_ASSERT(isProxy());
3532 if (!CopySlots(cx
, this, clone
))
3539 TradeGuts(JSObject
*a
, JSObject
*b
)
3541 JS_ASSERT(a
->compartment() == b
->compartment());
3542 JS_ASSERT(a
->isFunction() == b
->isFunction());
3545 * Regexp guts are more complicated -- we would need to migrate the
3546 * refcounted JIT code blob for them across compartments instead of just
3549 JS_ASSERT(!a
->isRegExp() && !b
->isRegExp());
3551 bool aInline
= !a
->hasSlotsArray();
3552 bool bInline
= !b
->hasSlotsArray();
3554 /* Trade the guts of the objects. */
3555 const size_t size
= GetObjectSize(a
);
3556 if (size
== GetObjectSize(b
)) {
3558 * If the objects are the same size, then we make no assumptions about
3559 * whether they have dynamically allocated slots and instead just copy
3560 * them over wholesale.
3562 char tmp
[tl::Max
<sizeof(JSFunction
), sizeof(JSObject_Slots16
)>::result
];
3563 JS_ASSERT(size
<= sizeof(tmp
));
3565 memcpy(tmp
, a
, size
);
3567 memcpy(b
, tmp
, size
);
3569 /* Fixup pointers for inline slots on the objects. */
3571 b
->slots
= b
->fixedSlots();
3573 a
->slots
= a
->fixedSlots();
3576 * If the objects are of differing sizes, then we only copy over the
3577 * JSObject portion (things like class, etc.) and leave it to
3578 * JSObject::clone to copy over the dynamic slots for us.
3580 if (a
->isFunction()) {
3582 memcpy(&tmp
, a
, sizeof tmp
);
3583 memcpy(a
, b
, sizeof tmp
);
3584 memcpy(b
, &tmp
, sizeof tmp
);
3587 memcpy(&tmp
, a
, sizeof tmp
);
3588 memcpy(a
, b
, sizeof tmp
);
3589 memcpy(b
, &tmp
, sizeof tmp
);
3592 JS_ASSERT(!aInline
);
3593 JS_ASSERT(!bInline
);
3598 * Use this method with extreme caution. It trades the guts of two objects and updates
3599 * scope ownership. This operation is not thread-safe, just as fast array to slow array
3600 * transitions are inherently not thread-safe. Don't perform a swap operation on objects
3601 * shared across threads or, or bad things will happen. You have been warned.
3604 JSObject::swap(JSContext
*cx
, JSObject
*other
)
3607 * If we are swapping objects with a different number of builtin slots, force
3608 * both to not use their inline slots.
3610 if (GetObjectSize(this) != GetObjectSize(other
)) {
3611 if (!hasSlotsArray()) {
3612 if (!allocSlots(cx
, numSlots()))
3615 if (!other
->hasSlotsArray()) {
3616 if (!other
->allocSlots(cx
, other
->numSlots()))
3621 if (this->compartment() == other
->compartment()) {
3622 TradeGuts(this, other
);
3626 JSObject
*thisClone
;
3627 JSObject
*otherClone
;
3629 AutoCompartment
ac(cx
, other
);
3632 thisClone
= this->clone(cx
, other
->getProto(), other
->getParent());
3633 if (!thisClone
|| !thisClone
->copyPropertiesFrom(cx
, this))
3637 AutoCompartment
ac(cx
, this);
3640 otherClone
= other
->clone(cx
, other
->getProto(), other
->getParent());
3641 if (!otherClone
|| !otherClone
->copyPropertiesFrom(cx
, other
))
3644 TradeGuts(this, otherClone
);
3645 TradeGuts(other
, thisClone
);
3652 #define NO_PARENT_INDEX ((uint32)-1)
3655 FindObjectIndex(JSObjectArray
*array
, JSObject
*obj
)
3663 if (array
->vector
[--i
] == obj
)
3668 return NO_PARENT_INDEX
;
3672 js_XDRBlockObject(JSXDRState
*xdr
, JSObject
**objp
)
3676 JSObject
*obj
, *parent
;
3678 uint32 depthAndCount
;
3683 obj
= NULL
; /* quell GCC overwarning */
3686 if (xdr
->mode
== JSXDR_ENCODE
) {
3688 parent
= obj
->getParent();
3689 parentId
= JSScript::isValidOffset(xdr
->script
->objectsOffset
)
3690 ? FindObjectIndex(xdr
->script
->objects(), parent
)
3692 depth
= (uint16
)OBJ_BLOCK_DEPTH(cx
, obj
);
3693 count
= (uint16
)OBJ_BLOCK_COUNT(cx
, obj
);
3694 depthAndCount
= (uint32
)(depth
<< 16) | count
;
3696 #ifdef __GNUC__ /* suppress bogus gcc warnings */
3700 /* First, XDR the parent atomid. */
3701 if (!JS_XDRUint32(xdr
, &parentId
))
3704 if (xdr
->mode
== JSXDR_DECODE
) {
3705 obj
= js_NewBlockObject(cx
);
3711 * If there's a parent id, then get the parent out of our script's
3712 * object array. We know that we XDR block object in outer-to-inner
3713 * order, which means that getting the parent now will work.
3715 if (parentId
== NO_PARENT_INDEX
)
3718 parent
= xdr
->script
->getObject(parentId
);
3719 obj
->setParent(parent
);
3722 AutoObjectRooter
tvr(cx
, obj
);
3724 if (!JS_XDRUint32(xdr
, &depthAndCount
))
3727 if (xdr
->mode
== JSXDR_DECODE
) {
3728 depth
= (uint16
)(depthAndCount
>> 16);
3729 count
= (uint16
)depthAndCount
;
3730 obj
->setSlot(JSSLOT_BLOCK_DEPTH
, Value(Int32Value(depth
)));
3733 * XDR the block object's properties. We know that there are 'count'
3734 * properties to XDR, stored as id/shortid pairs.
3736 for (uintN i
= 0; i
< count
; i
++) {
3740 /* XDR the real id, then the shortid. */
3741 if (!js_XDRAtom(xdr
, &atom
) || !JS_XDRUint16(xdr
, &shortid
))
3744 if (!obj
->defineBlockVariable(cx
, ATOM_TO_JSID(atom
), shortid
))
3748 AutoShapeVector
shapes(cx
);
3749 shapes
.growBy(count
);
3751 for (Shape::Range
r(obj
->lastProperty()); !r
.empty(); r
.popFront()) {
3753 shapes
[shape
->shortid
] = shape
;
3757 * XDR the block object's properties. We know that there are 'count'
3758 * properties to XDR, stored as id/shortid pairs.
3760 for (uintN i
= 0; i
< count
; i
++) {
3762 JS_ASSERT(shape
->getter() == block_getProperty
);
3764 jsid propid
= shape
->id
;
3765 JS_ASSERT(JSID_IS_ATOM(propid
));
3766 JSAtom
*atom
= JSID_TO_ATOM(propid
);
3768 uint16 shortid
= uint16(shape
->shortid
);
3769 JS_ASSERT(shortid
== i
);
3771 /* XDR the real id, then the shortid. */
3772 if (!js_XDRAtom(xdr
, &atom
) || !JS_XDRUint16(xdr
, &shortid
))
3781 Class js_BlockClass
= {
3783 JSCLASS_HAS_PRIVATE
| JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_IS_ANONYMOUS
,
3784 PropertyStub
, /* addProperty */
3785 PropertyStub
, /* delProperty */
3786 PropertyStub
, /* getProperty */
3787 StrictPropertyStub
, /* setProperty */
3794 js_InitObjectClass(JSContext
*cx
, JSObject
*obj
)
3796 JSObject
*proto
= js_InitClass(cx
, obj
, NULL
, &js_ObjectClass
, js_Object
, 1,
3797 object_props
, object_methods
, NULL
, object_static_methods
);
3801 /* ECMA (15.1.2.1) says 'eval' is a property of the global object. */
3802 jsid id
= ATOM_TO_JSID(cx
->runtime
->atomState
.evalAtom
);
3803 if (!js_DefineFunction(cx
, obj
, id
, eval
, 1, JSFUN_STUB_GSOPS
))
3810 DefineStandardSlot(JSContext
*cx
, JSObject
*obj
, JSProtoKey key
, JSAtom
*atom
,
3811 const Value
&v
, uint32 attrs
, bool &named
)
3813 jsid id
= ATOM_TO_JSID(atom
);
3815 if (key
!= JSProto_Null
) {
3817 * Initializing an actual standard class on a global object. If the
3818 * property is not yet present, force it into a new one bound to a
3819 * reserved slot. Otherwise, go through the normal property path.
3821 JS_ASSERT(obj
->isGlobal());
3822 JS_ASSERT(obj
->isNative());
3824 if (!obj
->ensureClassReservedSlots(cx
))
3827 const Shape
*shape
= obj
->nativeLookup(id
);
3829 uint32 slot
= 2 * JSProto_LIMIT
+ key
;
3830 if (!js_SetReservedSlot(cx
, obj
, slot
, v
))
3832 if (!obj
->addProperty(cx
, id
, PropertyStub
, StrictPropertyStub
, slot
, attrs
, 0, 0))
3840 named
= obj
->defineProperty(cx
, id
, v
, PropertyStub
, StrictPropertyStub
, attrs
);
3847 DefineConstructorAndPrototype(JSContext
*cx
, JSObject
*obj
, JSProtoKey key
, JSAtom
*atom
,
3848 JSObject
*protoProto
, Class
*clasp
,
3849 Native constructor
, uintN nargs
,
3850 JSPropertySpec
*ps
, JSFunctionSpec
*fs
,
3851 JSPropertySpec
*static_ps
, JSFunctionSpec
*static_fs
)
3854 * Create a prototype object for this class.
3856 * FIXME: lazy standard (built-in) class initialization and even older
3857 * eager boostrapping code rely on all of these properties:
3859 * 1. NewObject attempting to compute a default prototype object when
3860 * passed null for proto; and
3862 * 2. NewObject tolerating no default prototype (null proto slot value)
3863 * due to this js_InitClass call coming from js_InitFunctionClass on an
3864 * otherwise-uninitialized global.
3866 * 3. NewObject allocating a JSFunction-sized GC-thing when clasp is
3867 * &js_FunctionClass, not a JSObject-sized (smaller) GC-thing.
3869 * The JS_NewObjectForGivenProto and JS_NewObject APIs also allow clasp to
3870 * be &js_FunctionClass (we could break compatibility easily). But fixing
3871 * (3) is not enough without addressing the bootstrapping dependency on (1)
3874 JSObject
*proto
= NewObject
<WithProto::Class
>(cx
, clasp
, protoProto
, obj
);
3878 proto
->syncSpecialEquality();
3880 /* After this point, control must exit via label bad or out. */
3881 AutoObjectRooter
tvr(cx
, proto
);
3887 * Lacking a constructor, name the prototype (e.g., Math) unless this
3888 * class (a) is anonymous, i.e. for internal use only; (b) the class
3889 * of obj (the global object) is has a reserved slot indexed by key;
3890 * and (c) key is not the null key.
3892 if (!(clasp
->flags
& JSCLASS_IS_ANONYMOUS
) || !obj
->isGlobal() || key
== JSProto_Null
) {
3893 uint32 attrs
= (clasp
->flags
& JSCLASS_IS_ANONYMOUS
)
3894 ? JSPROP_READONLY
| JSPROP_PERMANENT
3896 if (!DefineStandardSlot(cx
, obj
, key
, atom
, ObjectValue(*proto
), attrs
, named
))
3902 JSFunction
*fun
= js_NewFunction(cx
, NULL
, constructor
, nargs
, JSFUN_CONSTRUCTOR
, obj
, atom
);
3906 AutoValueRooter
tvr2(cx
, ObjectValue(*fun
));
3907 if (!DefineStandardSlot(cx
, obj
, key
, atom
, tvr2
.value(), 0, named
))
3911 * Remember the class this function is a constructor for so that
3912 * we know to create an object of this class when we call the
3915 FUN_CLASP(fun
) = clasp
;
3918 * Optionally construct the prototype object, before the class has
3919 * been fully initialized. Allow the ctor to replace proto with a
3920 * different object, as is done for operator new -- and as at least
3921 * XML support requires.
3923 ctor
= FUN_OBJECT(fun
);
3924 if (clasp
->flags
& JSCLASS_CONSTRUCT_PROTOTYPE
) {
3926 if (!InvokeConstructorWithGivenThis(cx
, proto
, ObjectOrNullValue(ctor
),
3930 if (rval
.isObject() && &rval
.toObject() != proto
)
3931 proto
= &rval
.toObject();
3934 /* Connect constructor and prototype by named properties. */
3935 if (!js_SetClassPrototype(cx
, ctor
, proto
,
3936 JSPROP_READONLY
| JSPROP_PERMANENT
)) {
3940 /* Bootstrap Function.prototype (see also JS_InitStandardClasses). */
3941 if (ctor
->getClass() == clasp
)
3942 ctor
->setProto(proto
);
3945 /* Add properties and methods to the prototype and the constructor. */
3946 if ((ps
&& !JS_DefineProperties(cx
, proto
, ps
)) ||
3947 (fs
&& !JS_DefineFunctions(cx
, proto
, fs
)) ||
3948 (static_ps
&& !JS_DefineProperties(cx
, ctor
, static_ps
)) ||
3949 (static_fs
&& !JS_DefineFunctions(cx
, ctor
, static_fs
))) {
3954 * Pre-brand the prototype and constructor if they have built-in methods.
3955 * This avoids extra shape guard branch exits in the tracejitted code.
3959 if (ctor
!= proto
&& static_fs
)
3963 * Make sure proto's emptyShape is available to be shared by objects of
3964 * this class. JSObject::emptyShape is a one-slot cache. If we omit this,
3965 * some other class could snap it up. (The risk is particularly great for
3966 * Object.prototype.)
3968 * All callers of JSObject::initSharingEmptyShape depend on this.
3970 * FIXME: bug 592296 -- js_InitArrayClass should pass &js_SlowArrayClass
3971 * and make the Array.prototype slow from the start.
3973 JS_ASSERT_IF(proto
->clasp
!= clasp
,
3974 clasp
== &js_ArrayClass
&& proto
->clasp
== &js_SlowArrayClass
);
3975 if (!proto
->getEmptyShape(cx
, proto
->clasp
, FINALIZE_OBJECT0
))
3978 if (clasp
->flags
& (JSCLASS_FREEZE_PROTO
|JSCLASS_FREEZE_CTOR
)) {
3979 JS_ASSERT_IF(ctor
== proto
, !(clasp
->flags
& JSCLASS_FREEZE_CTOR
));
3980 if (proto
&& (clasp
->flags
& JSCLASS_FREEZE_PROTO
) && !proto
->freeze(cx
))
3982 if (ctor
&& (clasp
->flags
& JSCLASS_FREEZE_CTOR
) && !ctor
->freeze(cx
))
3986 /* If this is a standard class, cache its prototype. */
3987 if (key
!= JSProto_Null
&& !js_SetClassObject(cx
, obj
, key
, ctor
, proto
))
3995 obj
->deleteProperty(cx
, ATOM_TO_JSID(atom
), &rval
, false);
4003 js_InitClass(JSContext
*cx
, JSObject
*obj
, JSObject
*protoProto
,
4004 Class
*clasp
, Native constructor
, uintN nargs
,
4005 JSPropertySpec
*ps
, JSFunctionSpec
*fs
,
4006 JSPropertySpec
*static_ps
, JSFunctionSpec
*static_fs
)
4008 JSAtom
*atom
= js_Atomize(cx
, clasp
->name
, strlen(clasp
->name
), 0);
4013 * All instances of the class will inherit properties from the prototype
4014 * object we are about to create (in DefineConstructorAndPrototype), which
4015 * in turn will inherit from protoProto.
4017 * When initializing a standard class (other than Object), if protoProto is
4018 * null, default to the Object prototype object. The engine's internal uses
4019 * of js_InitClass depend on this nicety. Note that in
4020 * js_InitFunctionAndObjectClasses, we specially hack the resolving table
4021 * and then depend on js_GetClassPrototype here leaving protoProto NULL and
4024 JSProtoKey key
= JSCLASS_CACHED_PROTO_KEY(clasp
);
4025 if (key
!= JSProto_Null
&&
4027 !js_GetClassPrototype(cx
, obj
, JSProto_Object
, &protoProto
)) {
4031 return DefineConstructorAndPrototype(cx
, obj
, key
, atom
, protoProto
, clasp
, constructor
, nargs
,
4032 ps
, fs
, static_ps
, static_fs
);
4036 JSObject::allocSlots(JSContext
*cx
, size_t newcap
)
4038 uint32 oldcap
= numSlots();
4040 JS_ASSERT(newcap
>= oldcap
&& !hasSlotsArray());
4042 if (newcap
> NSLOTS_LIMIT
) {
4043 if (!JS_ON_TRACE(cx
))
4044 js_ReportAllocationOverflow(cx
);
4048 Value
*tmpslots
= (Value
*) cx
->malloc(newcap
* sizeof(Value
));
4050 return false; /* Leave slots at inline buffer. */
4054 /* Copy over anything from the inline buffer. */
4055 memcpy(slots
, fixedSlots(), oldcap
* sizeof(Value
));
4056 ClearValueRange(slots
+ oldcap
, newcap
- oldcap
, isDenseArray());
4061 JSObject::growSlots(JSContext
*cx
, size_t newcap
)
4064 * When an object with CAPACITY_DOUBLING_MAX or fewer slots needs to
4065 * grow, double its capacity, to add N elements in amortized O(N) time.
4067 * Above this limit, grow by 12.5% each time. Speed is still amortized
4068 * O(N), with a higher constant factor, and we waste less space.
4070 static const size_t CAPACITY_DOUBLING_MAX
= 1024 * 1024;
4071 static const size_t CAPACITY_CHUNK
= CAPACITY_DOUBLING_MAX
/ sizeof(Value
);
4073 uint32 oldcap
= numSlots();
4074 JS_ASSERT(oldcap
< newcap
);
4076 uint32 nextsize
= (oldcap
<= CAPACITY_DOUBLING_MAX
)
4078 : oldcap
+ (oldcap
>> 3);
4080 uint32 actualCapacity
= JS_MAX(newcap
, nextsize
);
4081 if (actualCapacity
>= CAPACITY_CHUNK
)
4082 actualCapacity
= JS_ROUNDUP(actualCapacity
, CAPACITY_CHUNK
);
4083 else if (actualCapacity
< SLOT_CAPACITY_MIN
)
4084 actualCapacity
= SLOT_CAPACITY_MIN
;
4086 /* Don't let nslots get close to wrapping around uint32. */
4087 if (actualCapacity
>= NSLOTS_LIMIT
) {
4088 JS_ReportOutOfMemory(cx
);
4092 /* If nothing was allocated yet, treat it as initial allocation. */
4093 if (!hasSlotsArray())
4094 return allocSlots(cx
, actualCapacity
);
4096 Value
*tmpslots
= (Value
*) cx
->realloc(slots
, oldcap
* sizeof(Value
), actualCapacity
* sizeof(Value
));
4098 return false; /* Leave dslots as its old size. */
4100 capacity
= actualCapacity
;
4102 /* Initialize the additional slots we added. */
4103 ClearValueRange(slots
+ oldcap
, actualCapacity
- oldcap
, isDenseArray());
4108 JSObject::shrinkSlots(JSContext
*cx
, size_t newcap
)
4110 uint32 oldcap
= numSlots();
4111 JS_ASSERT(newcap
<= oldcap
);
4112 JS_ASSERT(newcap
>= slotSpan());
4114 if (oldcap
<= SLOT_CAPACITY_MIN
|| !hasSlotsArray()) {
4115 /* We won't shrink the slots any more. Clear excess holes. */
4116 ClearValueRange(slots
+ newcap
, oldcap
- newcap
, isDenseArray());
4120 uint32 fill
= newcap
;
4121 if (newcap
< SLOT_CAPACITY_MIN
)
4122 newcap
= SLOT_CAPACITY_MIN
;
4123 if (newcap
< numFixedSlots())
4124 newcap
= numFixedSlots();
4126 Value
*tmpslots
= (Value
*) cx
->realloc(slots
, newcap
* sizeof(Value
));
4128 return; /* Leave slots at its old size. */
4132 if (fill
< newcap
) {
4133 /* Clear any excess holes if we tried to shrink below SLOT_CAPACITY_MIN. */
4134 ClearValueRange(slots
+ fill
, newcap
- fill
, isDenseArray());
4139 JSObject::ensureInstanceReservedSlots(JSContext
*cx
, size_t nreserved
)
4141 JS_ASSERT_IF(isNative(),
4142 isBlock() || isCall() || (isFunction() && isBoundFunction()));
4144 uintN nslots
= JSSLOT_FREE(clasp
) + nreserved
;
4145 return nslots
<= numSlots() || allocSlots(cx
, nslots
);
4149 js_InitNullClass(JSContext
*cx
, JSObject
*obj
)
4155 #define JS_PROTO(name,code,init) extern JSObject *init(JSContext *, JSObject *);
4156 #include "jsproto.tbl"
4159 static JSObjectOp lazy_prototype_init
[JSProto_LIMIT
] = {
4160 #define JS_PROTO(name,code,init) init,
4161 #include "jsproto.tbl"
4168 SetProto(JSContext
*cx
, JSObject
*obj
, JSObject
*proto
, bool checkForCycles
)
4170 JS_ASSERT_IF(!checkForCycles
, obj
!= proto
);
4171 JS_ASSERT(obj
->isExtensible());
4173 if (obj
->isNative()) {
4174 if (!obj
->ensureClassReservedSlots(cx
))
4179 * Regenerate property cache shape ids for all of the scopes along the
4180 * old prototype chain to invalidate their property cache entries, in
4181 * case any entries were filled by looking up through obj.
4183 JSObject
*oldproto
= obj
;
4184 while (oldproto
&& oldproto
->isNative()) {
4185 oldproto
->protoShapeChange(cx
);
4186 oldproto
= oldproto
->getProto();
4189 if (!proto
|| !checkForCycles
) {
4190 obj
->setProto(proto
);
4191 } else if (!SetProtoCheckingForCycles(cx
, obj
, proto
)) {
4192 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_CYCLIC_VALUE
, js_proto_str
);
4201 js_GetClassObject(JSContext
*cx
, JSObject
*obj
, JSProtoKey key
,
4205 JSResolvingKey rkey
;
4206 JSResolvingEntry
*rentry
;
4211 obj
= obj
->getGlobal();
4212 if (!obj
->isGlobal()) {
4217 v
= obj
->getReservedSlot(key
);
4219 *objp
= &v
.toObject();
4224 rkey
.id
= ATOM_TO_JSID(cx
->runtime
->atomState
.classAtoms
[key
]);
4225 if (!js_StartResolving(cx
, &rkey
, JSRESFLAG_LOOKUP
, &rentry
))
4228 /* Already caching key in obj -- suppress recursion. */
4232 generation
= cx
->resolvingTable
->generation
;
4236 init
= lazy_prototype_init
[key
];
4238 if (!init(cx
, obj
)) {
4241 v
= obj
->getReservedSlot(key
);
4243 cobj
= &v
.toObject();
4247 js_StopResolving(cx
, &rkey
, JSRESFLAG_LOOKUP
, rentry
, generation
);
4253 js_SetClassObject(JSContext
*cx
, JSObject
*obj
, JSProtoKey key
, JSObject
*cobj
, JSObject
*proto
)
4255 JS_ASSERT(!obj
->getParent());
4256 if (!obj
->isGlobal())
4259 return js_SetReservedSlot(cx
, obj
, key
, ObjectOrNullValue(cobj
)) &&
4260 js_SetReservedSlot(cx
, obj
, JSProto_LIMIT
+ key
, ObjectOrNullValue(proto
));
4264 js_FindClassObject(JSContext
*cx
, JSObject
*start
, JSProtoKey protoKey
,
4265 Value
*vp
, Class
*clasp
)
4268 JSObject
*obj
, *cobj
, *pobj
;
4274 * Find the global object. Use cx->fp() directly to avoid falling off
4275 * trace; all JIT-elided stack frames have the same global object as
4278 VOUCH_DOES_NOT_REQUIRE_STACK();
4279 if (!start
&& (fp
= cx
->maybefp()) != NULL
)
4280 start
= &fp
->scopeChain();
4283 /* Find the topmost object in the scope chain. */
4286 start
= obj
->getParent();
4289 obj
= cx
->globalObject
;
4296 OBJ_TO_INNER_OBJECT(cx
, obj
);
4300 if (protoKey
!= JSProto_Null
) {
4301 JS_ASSERT(JSProto_Null
< protoKey
);
4302 JS_ASSERT(protoKey
< JSProto_LIMIT
);
4303 if (!js_GetClassObject(cx
, obj
, protoKey
, &cobj
))
4306 vp
->setObject(*cobj
);
4309 id
= ATOM_TO_JSID(cx
->runtime
->atomState
.classAtoms
[protoKey
]);
4311 JSAtom
*atom
= js_Atomize(cx
, clasp
->name
, strlen(clasp
->name
), 0);
4314 id
= ATOM_TO_JSID(atom
);
4317 JS_ASSERT(obj
->isNative());
4318 if (js_LookupPropertyWithFlags(cx
, obj
, id
, JSRESOLVE_CLASSNAME
,
4319 &pobj
, &prop
) < 0) {
4322 Value v
= UndefinedValue();
4323 if (prop
&& pobj
->isNative()) {
4324 shape
= (Shape
*) prop
;
4325 if (pobj
->containsSlot(shape
->slot
)) {
4326 v
= pobj
->nativeGetSlot(shape
->slot
);
4327 if (v
.isPrimitive())
4336 js_ConstructObject(JSContext
*cx
, Class
*clasp
, JSObject
*proto
, JSObject
*parent
,
4337 uintN argc
, Value
*argv
)
4339 AutoArrayRooter
argtvr(cx
, argc
, argv
);
4341 JSProtoKey protoKey
= GetClassProtoKey(clasp
);
4343 /* Protect constructor in case a crazy getter for .prototype uproots it. */
4344 AutoValueRooter
tvr(cx
);
4345 if (!js_FindClassObject(cx
, parent
, protoKey
, tvr
.addr(), clasp
))
4348 const Value
&cval
= tvr
.value();
4349 if (tvr
.value().isPrimitive()) {
4350 js_ReportIsNotFunction(cx
, tvr
.addr(), JSV2F_CONSTRUCT
| JSV2F_SEARCH_STACK
);
4355 * If proto is NULL, set it to Constructor.prototype, just like JSOP_NEW
4356 * does, likewise for the new object's parent.
4358 JSObject
*ctor
= &cval
.toObject();
4360 parent
= ctor
->getParent();
4363 if (!ctor
->getProperty(cx
, ATOM_TO_JSID(cx
->runtime
->atomState
.classPrototypeAtom
),
4367 if (rval
.isObjectOrNull())
4368 proto
= rval
.toObjectOrNull();
4371 JSObject
*obj
= NewObject
<WithProto::Class
>(cx
, clasp
, proto
, parent
);
4375 obj
->syncSpecialEquality();
4378 if (!InvokeConstructorWithGivenThis(cx
, obj
, cval
, argc
, argv
, &rval
))
4381 if (rval
.isPrimitive())
4385 * If the instance's class differs from what was requested, throw a type
4386 * error. If the given class has both the JSCLASS_HAS_PRIVATE and the
4387 * JSCLASS_CONSTRUCT_PROTOTYPE flags, and the instance does not have its
4388 * private data set at this point, then the constructor was replaced and
4389 * we should throw a type error.
4391 obj
= &rval
.toObject();
4392 if (obj
->getClass() != clasp
||
4393 (!(~clasp
->flags
& (JSCLASS_HAS_PRIVATE
|
4394 JSCLASS_CONSTRUCT_PROTOTYPE
)) &&
4395 !obj
->getPrivate())) {
4396 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
4397 JSMSG_WRONG_CONSTRUCTOR
, clasp
->name
);
4404 JSObject::allocSlot(JSContext
*cx
, uint32
*slotp
)
4406 uint32 slot
= slotSpan();
4407 JS_ASSERT(slot
>= JSSLOT_FREE(clasp
));
4410 * If this object is in dictionary mode and it has a property table, try to
4411 * pull a free slot from the property table's slot-number freelist.
4413 if (inDictionaryMode() && lastProp
->hasTable()) {
4414 uint32
&last
= lastProp
->getTable()->freelist
;
4415 if (last
!= SHAPE_INVALID_SLOT
) {
4417 JS_ASSERT(last
< slot
);
4418 uint32 next
= getSlot(last
).toPrivateUint32();
4419 JS_ASSERT_IF(next
!= SHAPE_INVALID_SLOT
, next
< slot
);
4424 Value
&vref
= getSlotRef(last
);
4425 last
= vref
.toPrivateUint32();
4426 vref
.setUndefined();
4431 if (slot
>= numSlots() && !growSlots(cx
, slot
+ 1))
4434 /* JSObject::growSlots or JSObject::freeSlot should set the free slots to void. */
4435 JS_ASSERT(getSlot(slot
).isUndefined());
4441 JSObject::freeSlot(JSContext
*cx
, uint32 slot
)
4443 uint32 limit
= slotSpan();
4444 JS_ASSERT(slot
< limit
);
4446 Value
&vref
= getSlotRef(slot
);
4447 if (inDictionaryMode() && lastProp
->hasTable()) {
4448 uint32
&last
= lastProp
->getTable()->freelist
;
4450 /* Can't afford to check the whole freelist, but let's check the head. */
4451 JS_ASSERT_IF(last
!= SHAPE_INVALID_SLOT
, last
< limit
&& last
!= slot
);
4454 * Freeing a slot other than the last one mapped by this object's
4455 * shape (and not a reserved slot; see bug 595230): push the slot onto
4456 * the dictionary property table's freelist. We want to let the last
4457 * slot be freed by shrinking the dslots vector; see js_TraceObject.
4459 if (JSSLOT_FREE(clasp
) <= slot
&& slot
+ 1 < limit
) {
4460 JS_ASSERT_IF(last
!= SHAPE_INVALID_SLOT
, last
< slotSpan());
4461 vref
.setPrivateUint32(last
);
4466 vref
.setUndefined();
4471 js_UnbrandAndClearSlots(JSContext
*cx
, JSObject
*obj
)
4473 JS_ASSERT(obj
->isNative());
4474 JS_ASSERT(obj
->isGlobal());
4476 /* This can return false but that doesn't mean it failed. */
4480 * Clear the prototype cache. We must not clear the other global
4481 * reserved slots, as other code will crash if they are arbitrarily
4482 * reset (e.g., regexp statics).
4484 for (int key
= JSProto_Null
; key
< JSRESERVED_GLOBAL_THIS
; key
++)
4485 JS_SetReservedSlot(cx
, obj
, key
, JSVAL_VOID
);
4488 * Clear the non-reserved slots.
4490 ClearValueRange(obj
->slots
+ JSCLASS_RESERVED_SLOTS(obj
->clasp
),
4491 obj
->capacity
- JSCLASS_RESERVED_SLOTS(obj
->clasp
),
4492 obj
->clasp
== &js_ArrayClass
);
4495 * We just overwrote all slots to undefined, so the freelist has
4496 * been trashed. We need to clear the head pointer or else we will
4497 * crash later. This leaks slots but the object is all but dead
4500 if (obj
->hasPropertyTable())
4501 obj
->lastProperty()->getTable()->freelist
= SHAPE_INVALID_SLOT
;
4504 /* JSBOXEDWORD_INT_MAX as a string */
4505 #define JSBOXEDWORD_INT_MAX_STRING "1073741823"
4508 * Convert string indexes that convert to int jsvals as ints to save memory.
4509 * Care must be taken to use this macro every time a property name is used, or
4510 * else double-sets, incorrect property cache misses, or other mistakes could
4514 js_CheckForStringIndex(jsid id
)
4516 if (!JSID_IS_ATOM(id
))
4519 JSAtom
*atom
= JSID_TO_ATOM(id
);
4520 JSString
*str
= ATOM_TO_STRING(atom
);
4521 const jschar
*s
= str
->flatChars();
4524 JSBool negative
= (ch
== '-');
4531 size_t n
= str
->flatLength() - negative
;
4532 if (n
> sizeof(JSBOXEDWORD_INT_MAX_STRING
) - 1)
4535 const jschar
*cp
= s
;
4536 const jschar
*end
= s
+ n
;
4538 jsuint index
= JS7_UNDEC(*cp
++);
4539 jsuint oldIndex
= 0;
4543 while (JS7_ISDEC(*cp
)) {
4546 index
= 10 * index
+ c
;
4552 * Non-integer indexes can't be represented as integers. Also, distinguish
4553 * index "-0" from "0", because JSBOXEDWORD_INT cannot.
4555 if (cp
!= end
|| (negative
&& index
== 0))
4559 if (oldIndex
< -(JSID_INT_MIN
/ 10) ||
4560 (oldIndex
== -(JSID_INT_MIN
/ 10) && c
<= (-JSID_INT_MIN
% 10)))
4562 id
= INT_TO_JSID(-jsint(index
));
4565 if (oldIndex
< JSID_INT_MAX
/ 10 ||
4566 (oldIndex
== JSID_INT_MAX
/ 10 && c
<= (JSID_INT_MAX
% 10)))
4568 id
= INT_TO_JSID(jsint(index
));
4576 PurgeProtoChain(JSContext
*cx
, JSObject
*obj
, jsid id
)
4581 if (!obj
->isNative()) {
4582 obj
= obj
->getProto();
4585 shape
= obj
->nativeLookup(id
);
4587 PCMETER(JS_PROPERTY_CACHE(cx
).pcpurges
++);
4588 obj
->shadowingShapeChange(cx
, *shape
);
4590 if (!obj
->getParent()) {
4592 * All scope chains end in a global object, so this will change
4593 * the global shape. jstracer.cpp assumes that the global shape
4594 * never changes on trace, so we must deep-bail here.
4600 obj
= obj
->getProto();
4606 js_PurgeScopeChainHelper(JSContext
*cx
, JSObject
*obj
, jsid id
)
4608 JS_ASSERT(obj
->isDelegate());
4609 PurgeProtoChain(cx
, obj
->getProto(), id
);
4612 * We must purge the scope chain only for Call objects as they are the only
4613 * kind of cacheable non-global object that can gain properties after outer
4614 * properties with the same names have been cached or traced. Call objects
4615 * may gain such properties via eval introducing new vars; see bug 490364.
4617 if (obj
->isCall()) {
4618 while ((obj
= obj
->getParent()) != NULL
) {
4619 if (PurgeProtoChain(cx
, obj
, id
))
4626 js_AddNativeProperty(JSContext
*cx
, JSObject
*obj
, jsid id
,
4627 PropertyOp getter
, StrictPropertyOp setter
, uint32 slot
,
4628 uintN attrs
, uintN flags
, intN shortid
)
4630 JS_ASSERT(!(flags
& Shape::METHOD
));
4633 * Purge the property cache of now-shadowed id in obj's scope chain. Do
4634 * this optimistically (assuming no failure below) before locking obj, so
4635 * we can lock the shadowed scope.
4637 js_PurgeScopeChain(cx
, obj
, id
);
4639 if (!obj
->ensureClassReservedSlots(cx
))
4642 /* Convert string indices to integers if appropriate. */
4643 id
= js_CheckForStringIndex(id
);
4644 return obj
->putProperty(cx
, id
, getter
, setter
, slot
, attrs
, flags
, shortid
);
4648 js_ChangeNativePropertyAttrs(JSContext
*cx
, JSObject
*obj
,
4649 const Shape
*shape
, uintN attrs
, uintN mask
,
4650 PropertyOp getter
, StrictPropertyOp setter
)
4652 if (!obj
->ensureClassReservedSlots(cx
))
4654 return obj
->changeProperty(cx
, shape
, attrs
, mask
, getter
, setter
);
4658 js_DefineProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, const Value
*value
,
4659 PropertyOp getter
, StrictPropertyOp setter
, uintN attrs
)
4661 return js_DefineNativeProperty(cx
, obj
, id
, *value
, getter
, setter
, attrs
,
4666 * Backward compatibility requires allowing addProperty hooks to mutate the
4667 * nominal initial value of a slotful property, while GC safety wants that
4668 * value to be stored before the call-out through the hook. Optimize to do
4669 * both while saving cycles for classes that stub their addProperty hook.
4672 CallAddPropertyHook(JSContext
*cx
, Class
*clasp
, JSObject
*obj
, const Shape
*shape
, Value
*vp
)
4674 if (clasp
->addProperty
!= PropertyStub
) {
4675 Value nominal
= *vp
;
4677 if (!CallJSPropertyOp(cx
, clasp
->addProperty
, obj
, shape
->id
, vp
))
4679 if (*vp
!= nominal
) {
4680 if (obj
->containsSlot(shape
->slot
))
4681 obj
->nativeSetSlot(shape
->slot
, *vp
);
4688 js_DefineNativeProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, const Value
&value
,
4689 PropertyOp getter
, StrictPropertyOp setter
, uintN attrs
,
4690 uintN flags
, intN shortid
, JSProperty
**propp
,
4691 uintN defineHow
/* = 0 */)
4693 JS_ASSERT((defineHow
& ~(JSDNP_CACHE_RESULT
| JSDNP_DONT_PURGE
| JSDNP_SET_METHOD
)) == 0);
4694 LeaveTraceIfGlobalObject(cx
, obj
);
4696 /* Convert string indices to integers if appropriate. */
4697 id
= js_CheckForStringIndex(id
);
4700 * If defining a getter or setter, we must check for its counterpart and
4701 * update the attributes and property ops. A getter or setter is really
4702 * only half of a property.
4704 const Shape
*shape
= NULL
;
4705 if (attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)) {
4710 * If JS_THREADSAFE and id is found, js_LookupProperty returns with
4711 * shape non-null and pobj locked. If pobj == obj, the property is
4712 * already in obj and obj has its own (mutable) scope. So if we are
4713 * defining a getter whose setter was already defined, or vice versa,
4714 * finish the job via obj->changeProperty, and refresh the property
4715 * cache line for (obj, id) to map shape.
4717 if (!js_LookupProperty(cx
, obj
, id
, &pobj
, &prop
))
4719 shape
= (Shape
*) prop
;
4720 if (shape
&& pobj
== obj
&& shape
->isAccessorDescriptor()) {
4721 shape
= obj
->changeProperty(cx
, shape
, attrs
,
4722 JSPROP_GETTER
| JSPROP_SETTER
,
4723 (attrs
& JSPROP_GETTER
)
4726 (attrs
& JSPROP_SETTER
)
4739 * Purge the property cache of any properties named by id that are about
4740 * to be shadowed in obj's scope chain unless it is known a priori that it
4741 * is not possible. We do this before locking obj to avoid nesting locks.
4743 if (!(defineHow
& JSDNP_DONT_PURGE
))
4744 js_PurgeScopeChain(cx
, obj
, id
);
4747 * Check whether a readonly property or setter is being defined on a known
4748 * prototype object. See the comment in jscntxt.h before protoHazardShape's
4749 * member declaration.
4751 if (obj
->isDelegate() && (attrs
& (JSPROP_READONLY
| JSPROP_SETTER
)))
4752 cx
->runtime
->protoHazardShape
= js_GenerateShape(cx
);
4754 /* Use the object's class getter and setter by default. */
4755 Class
*clasp
= obj
->getClass();
4756 if (!(defineHow
& JSDNP_SET_METHOD
)) {
4757 if (!getter
&& !(attrs
& JSPROP_GETTER
))
4758 getter
= clasp
->getProperty
;
4759 if (!setter
&& !(attrs
& JSPROP_SETTER
))
4760 setter
= clasp
->setProperty
;
4763 /* Get obj's own scope if it has one, or create a new one for obj. */
4764 if (!obj
->ensureClassReservedSlots(cx
))
4769 /* Add a new property, or replace an existing one of the same id. */
4770 if (defineHow
& JSDNP_SET_METHOD
) {
4771 JS_ASSERT(clasp
== &js_ObjectClass
);
4772 JS_ASSERT(IsFunctionObject(value
));
4773 JS_ASSERT(!(attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)));
4774 JS_ASSERT(!getter
&& !setter
);
4776 JSObject
*funobj
= &value
.toObject();
4777 if (FUN_OBJECT(GET_FUNCTION_PRIVATE(cx
, funobj
)) == funobj
) {
4778 flags
|= Shape::METHOD
;
4779 getter
= CastAsPropertyOp(funobj
);
4783 if (const Shape
*existingShape
= obj
->nativeLookup(id
)) {
4784 if (existingShape
->hasSlot())
4785 AbortRecordingIfUnexpectedGlobalWrite(cx
, obj
, existingShape
->slot
);
4790 uint32 oldShape
= obj
->shape();
4791 shape
= obj
->putProperty(cx
, id
, getter
, setter
, SHAPE_INVALID_SLOT
,
4792 attrs
, flags
, shortid
);
4797 * If shape is a method, the above call to putProperty suffices to
4798 * update the shape if necessary. But if scope->branded(), the shape
4799 * may not have changed and we may be overwriting a function-valued
4800 * property. See bug 560998.
4802 if (obj
->shape() == oldShape
&& obj
->branded() && shape
->slot
!= SHAPE_INVALID_SLOT
) {
4804 const Shape
*newshape
=
4806 obj
->methodWriteBarrier(cx
, *shape
, value
);
4807 JS_ASSERT(newshape
== shape
);
4811 /* Store value before calling addProperty, in case the latter GC's. */
4812 if (obj
->containsSlot(shape
->slot
))
4813 obj
->nativeSetSlot(shape
->slot
, value
);
4815 /* XXXbe called with lock held */
4816 Value valueCopy
= value
;
4817 if (!CallAddPropertyHook(cx
, clasp
, obj
, shape
, &valueCopy
)) {
4818 obj
->removeProperty(cx
, id
);
4822 if (defineHow
& JSDNP_CACHE_RESULT
) {
4823 JS_ASSERT_NOT_ON_TRACE(cx
);
4825 JS_PROPERTY_CACHE(cx
).fill(cx
, obj
, 0, 0, obj
, shape
, true);
4826 TRACE_1(AddProperty
, obj
);
4830 *propp
= (JSProperty
*) shape
;
4834 error
: // TRACE_1 jumps here on error.
4839 #define SCOPE_DEPTH_ACCUM(bs,val) \
4840 JS_SCOPE_DEPTH_METERING(JS_BASIC_STATS_ACCUM(bs, val))
4843 * Call obj's resolve hook. obj is a native object and the caller holds its
4846 * cx, start, id, and flags are the parameters initially passed to the ongoing
4847 * lookup; objp and propp are its out parameters. obj is an object along
4848 * start's prototype chain.
4850 * There are four possible outcomes:
4852 * - On failure, report an error or exception, unlock obj, and return false.
4854 * - If we are alrady resolving a property of *curobjp, set *recursedp = true,
4855 * unlock obj, and return true.
4857 * - If the resolve hook finds or defines the sought property, set *objp and
4858 * *propp appropriately, set *recursedp = false, and return true with *objp's
4861 * - Otherwise no property was resolved. Set *propp = NULL and *recursedp = false
4865 CallResolveOp(JSContext
*cx
, JSObject
*start
, JSObject
*obj
, jsid id
, uintN flags
,
4866 JSObject
**objp
, JSProperty
**propp
, bool *recursedp
)
4868 Class
*clasp
= obj
->getClass();
4869 JSResolveOp resolve
= clasp
->resolve
;
4872 * Avoid recursion on (obj, id) already being resolved on cx.
4874 * Once we have successfully added an entry for (obj, key) to
4875 * cx->resolvingTable, control must go through cleanup: before
4876 * returning. But note that JS_DHASH_ADD may find an existing
4877 * entry, in which case we bail to suppress runaway recursion.
4879 JSResolvingKey key
= {obj
, id
};
4880 JSResolvingEntry
*entry
;
4881 if (!js_StartResolving(cx
, &key
, JSRESFLAG_LOOKUP
, &entry
))
4884 /* Already resolving id in obj -- suppress recursion. */
4888 uint32 generation
= cx
->resolvingTable
->generation
;
4894 const Shape
*shape
= NULL
;
4895 if (clasp
->flags
& JSCLASS_NEW_RESOLVE
) {
4896 JSNewResolveOp newresolve
= (JSNewResolveOp
)resolve
;
4897 if (flags
== JSRESOLVE_INFER
)
4898 flags
= js_InferFlags(cx
, 0);
4899 JSObject
*obj2
= (clasp
->flags
& JSCLASS_NEW_RESOLVE_GETS_START
) ? start
: NULL
;
4902 /* Protect id and all atoms from a GC nested in resolve. */
4903 AutoKeepAtoms
keep(cx
->runtime
);
4904 ok
= newresolve(cx
, obj
, id
, flags
, &obj2
);
4910 /* Resolved: lookup id again for backward compatibility. */
4911 if (!obj2
->isNative()) {
4912 /* Whoops, newresolve handed back a foreign obj2. */
4913 JS_ASSERT(obj2
!= obj
);
4914 ok
= obj2
->lookupProperty(cx
, id
, objp
, propp
);
4919 * Require that obj2 not be empty now, as we do for old-style
4920 * resolve. If it doesn't, then id was not truly resolved, and
4921 * we'll find it in the proto chain, or miss it if obj2's proto
4922 * is not on obj's proto chain. That last case is a "too bad!"
4925 if (!obj2
->nativeEmpty())
4926 shape
= obj2
->nativeLookup(id
);
4929 JS_ASSERT(!obj2
->nativeEmpty());
4935 * Old resolve always requires id re-lookup if obj is not empty after
4938 ok
= resolve(cx
, obj
, id
);
4941 JS_ASSERT(obj
->isNative());
4942 if (!obj
->nativeEmpty())
4943 shape
= obj
->nativeLookup(id
);
4949 *propp
= (JSProperty
*) shape
;
4951 js_StopResolving(cx
, &key
, JSRESFLAG_LOOKUP
, entry
, generation
);
4955 static JS_ALWAYS_INLINE
int
4956 js_LookupPropertyWithFlagsInline(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN flags
,
4957 JSObject
**objp
, JSProperty
**propp
)
4959 /* We should not get string indices which aren't already integers here. */
4960 JS_ASSERT(id
== js_CheckForStringIndex(id
));
4962 /* Search scopes starting with obj and following the prototype link. */
4963 JSObject
*start
= obj
;
4965 for (protoIndex
= 0; ; protoIndex
++) {
4966 const Shape
*shape
= obj
->nativeLookup(id
);
4968 SCOPE_DEPTH_ACCUM(&cx
->runtime
->protoLookupDepthStats
, protoIndex
);
4970 *propp
= (JSProperty
*) shape
;
4974 /* Try obj's class resolve hook if id was not found in obj's scope. */
4975 if (!shape
&& obj
->getClass()->resolve
!= JS_ResolveStub
) {
4977 if (!CallResolveOp(cx
, start
, obj
, id
, flags
, objp
, propp
, &recursed
))
4982 /* Recalculate protoIndex in case it was resolved on some other object. */
4984 for (JSObject
*proto
= start
; proto
&& proto
!= *objp
; proto
= proto
->getProto())
4986 SCOPE_DEPTH_ACCUM(&cx
->runtime
->protoLookupDepthStats
, protoIndex
);
4991 JSObject
*proto
= obj
->getProto();
4994 if (!proto
->isNative()) {
4995 if (!proto
->lookupProperty(cx
, id
, objp
, propp
))
4999 * Non-native objects must have either non-native lookup results,
5000 * or else native results from the non-native's prototype chain.
5002 * See JSStackFrame::getValidCalleeObject, where we depend on this
5003 * fact to force a prototype-delegated joined method accessed via
5004 * arguments.callee through the delegating |this| object's method
5007 if (*propp
&& (*objp
)->isNative()) {
5008 while ((proto
= proto
->getProto()) != *objp
)
5012 return protoIndex
+ 1;
5023 JS_FRIEND_API(JSBool
)
5024 js_LookupProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, JSObject
**objp
,
5027 /* Convert string indices to integers if appropriate. */
5028 id
= js_CheckForStringIndex(id
);
5030 return js_LookupPropertyWithFlagsInline(cx
, obj
, id
, cx
->resolveFlags
, objp
, propp
) >= 0;
5034 js_LookupPropertyWithFlags(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN flags
,
5035 JSObject
**objp
, JSProperty
**propp
)
5037 /* Convert string indices to integers if appropriate. */
5038 id
= js_CheckForStringIndex(id
);
5040 return js_LookupPropertyWithFlagsInline(cx
, obj
, id
, flags
, objp
, propp
);
5043 PropertyCacheEntry
*
5044 js_FindPropertyHelper(JSContext
*cx
, jsid id
, JSBool cacheResult
,
5045 JSObject
**objp
, JSObject
**pobjp
, JSProperty
**propp
)
5047 JSObject
*scopeChain
, *obj
, *parent
, *pobj
;
5048 PropertyCacheEntry
*entry
;
5049 int scopeIndex
, protoIndex
;
5052 JS_ASSERT_IF(cacheResult
, !JS_ON_TRACE(cx
));
5053 scopeChain
= &js_GetTopStackFrame(cx
)->scopeChain();
5055 /* Scan entries on the scope chain that we can cache across. */
5056 entry
= JS_NO_PROP_CACHE_FILL
;
5058 parent
= obj
->getParent();
5059 for (scopeIndex
= 0;
5061 ? js_IsCacheableNonGlobalScope(obj
)
5062 : !obj
->getOps()->lookupProperty
;
5065 js_LookupPropertyWithFlags(cx
, obj
, id
, cx
->resolveFlags
,
5073 Class
*clasp
= obj
->getClass();
5074 JS_ASSERT(pobj
->isNative());
5075 JS_ASSERT(pobj
->getClass() == clasp
);
5076 if (clasp
== &js_BlockClass
) {
5078 * A block instance on the scope chain is immutable and it
5079 * shares its shapes with its compile-time prototype.
5081 JS_ASSERT(pobj
== obj
);
5082 JS_ASSERT(pobj
->isClonedBlock());
5083 JS_ASSERT(protoIndex
== 0);
5085 /* Call and DeclEnvClass objects have no prototypes. */
5086 JS_ASSERT(!obj
->getProto());
5087 JS_ASSERT(protoIndex
== 0);
5090 JS_ASSERT(obj
->isNative());
5094 * We must check if pobj is native as a global object can have
5095 * non-native prototype.
5097 if (cacheResult
&& pobj
->isNative()) {
5098 entry
= JS_PROPERTY_CACHE(cx
).fill(cx
, scopeChain
, scopeIndex
,
5102 SCOPE_DEPTH_ACCUM(&cx
->runtime
->scopeSearchDepthStats
, scopeIndex
);
5111 parent
= obj
->getParent();
5115 if (!obj
->lookupProperty(cx
, id
, &pobj
, &prop
))
5118 PCMETER(JS_PROPERTY_CACHE(cx
).nofills
++);
5123 * We conservatively assume that a resolve hook could mutate the scope
5124 * chain during JSObject::lookupProperty. So we read parent here again.
5126 parent
= obj
->getParent();
5135 JS_ASSERT(!!pobj
== !!prop
);
5143 * On return, if |*pobjp| is a native object, then |*propp| is a |Shape *|.
5144 * Otherwise, its type and meaning depends on the host object's implementation.
5146 JS_FRIEND_API(JSBool
)
5147 js_FindProperty(JSContext
*cx
, jsid id
, JSObject
**objp
, JSObject
**pobjp
,
5150 return !!js_FindPropertyHelper(cx
, id
, false, objp
, pobjp
, propp
);
5154 js_FindIdentifierBase(JSContext
*cx
, JSObject
*scopeChain
, jsid id
)
5157 * This function should not be called for a global object or from the
5158 * trace and should have a valid cache entry for native scopeChain.
5160 JS_ASSERT(scopeChain
->getParent());
5161 JS_ASSERT(!JS_ON_TRACE(cx
));
5163 JSObject
*obj
= scopeChain
;
5166 * Loop over cacheable objects on the scope chain until we find a
5167 * property. We also stop when we reach the global object skipping any
5168 * farther checks or lookups. For details see the JSOP_BINDNAME case of
5171 * The test order here matters because js_IsCacheableNonGlobalScope
5172 * must not be passed a global object (i.e. one with null parent).
5174 for (int scopeIndex
= 0;
5175 !obj
->getParent() || js_IsCacheableNonGlobalScope(obj
);
5179 int protoIndex
= js_LookupPropertyWithFlags(cx
, obj
, id
,
5185 if (!pobj
->isNative()) {
5186 JS_ASSERT(!obj
->getParent());
5189 JS_ASSERT_IF(obj
->getParent(), pobj
->getClass() == obj
->getClass());
5191 PropertyCacheEntry
*entry
=
5193 JS_PROPERTY_CACHE(cx
).fill(cx
, scopeChain
, scopeIndex
, protoIndex
, pobj
,
5199 JSObject
*parent
= obj
->getParent();
5205 /* Loop until we find a property or reach the global object. */
5209 if (!obj
->lookupProperty(cx
, id
, &pobj
, &prop
))
5215 * We conservatively assume that a resolve hook could mutate the scope
5216 * chain during JSObject::lookupProperty. So we must check if parent is
5217 * not null here even if it wasn't before the lookup.
5219 JSObject
*parent
= obj
->getParent();
5223 } while (obj
->getParent());
5227 static JS_ALWAYS_INLINE JSBool
5228 js_NativeGetInline(JSContext
*cx
, JSObject
*receiver
, JSObject
*obj
, JSObject
*pobj
,
5229 const Shape
*shape
, uintN getHow
, Value
*vp
)
5231 LeaveTraceIfGlobalObject(cx
, pobj
);
5236 JS_ASSERT(pobj
->isNative());
5239 if (slot
!= SHAPE_INVALID_SLOT
) {
5240 *vp
= pobj
->nativeGetSlot(slot
);
5241 JS_ASSERT(!vp
->isMagic());
5245 if (shape
->hasDefaultGetter())
5248 if (JS_UNLIKELY(shape
->isMethod()) && (getHow
& JSGET_NO_METHOD_BARRIER
)) {
5249 JS_ASSERT(&shape
->methodObject() == &vp
->toObject());
5253 sample
= cx
->runtime
->propertyRemovals
;
5255 AutoShapeRooter
tvr(cx
, shape
);
5256 AutoObjectRooter
tvr2(cx
, pobj
);
5257 if (!shape
->get(cx
, receiver
, obj
, pobj
, vp
))
5261 if (pobj
->containsSlot(slot
) &&
5262 (JS_LIKELY(cx
->runtime
->propertyRemovals
== sample
) ||
5263 pobj
->nativeContains(*shape
))) {
5264 if (!pobj
->methodWriteBarrier(cx
, *shape
, *vp
))
5266 pobj
->nativeSetSlot(slot
, *vp
);
5273 js_NativeGet(JSContext
*cx
, JSObject
*obj
, JSObject
*pobj
, const Shape
*shape
, uintN getHow
,
5276 return js_NativeGetInline(cx
, obj
, obj
, pobj
, shape
, getHow
, vp
);
5280 js_NativeSet(JSContext
*cx
, JSObject
*obj
, const Shape
*shape
, bool added
, bool strict
, Value
*vp
)
5282 LeaveTraceIfGlobalObject(cx
, obj
);
5287 JS_ASSERT(obj
->isNative());
5290 if (slot
!= SHAPE_INVALID_SLOT
) {
5291 JS_ASSERT(obj
->containsSlot(slot
));
5293 /* If shape has a stub setter, keep obj locked and just store *vp. */
5294 if (shape
->hasDefaultSetter()) {
5296 AbortRecordingIfUnexpectedGlobalWrite(cx
, obj
, slot
);
5298 /* FIXME: This should pass *shape, not slot, but see bug 630354. */
5299 if (!obj
->methodWriteBarrier(cx
, slot
, *vp
))
5302 obj
->nativeSetSlot(slot
, *vp
);
5307 * Allow API consumers to create shared properties with stub setters.
5308 * Such properties effectively function as data descriptors which are
5309 * not writable, so attempting to set such a property should do nothing
5310 * or throw if we're in strict mode.
5312 if (!shape
->hasGetterValue() && shape
->hasDefaultSetter())
5313 return js_ReportGetterOnlyAssignment(cx
);
5316 sample
= cx
->runtime
->propertyRemovals
;
5318 AutoShapeRooter
tvr(cx
, shape
);
5319 if (!shape
->set(cx
, obj
, strict
, vp
))
5322 JS_ASSERT_IF(!obj
->inDictionaryMode(), shape
->slot
== slot
);
5326 if (obj
->containsSlot(slot
) &&
5327 (JS_LIKELY(cx
->runtime
->propertyRemovals
== sample
) ||
5328 obj
->nativeContains(*shape
))) {
5330 AbortRecordingIfUnexpectedGlobalWrite(cx
, obj
, slot
);
5331 if (!obj
->methodWriteBarrier(cx
, *shape
, *vp
))
5334 obj
->setSlot(slot
, *vp
);
5340 static JS_ALWAYS_INLINE
bool
5341 js_GetPropertyHelperWithShapeInline(JSContext
*cx
, JSObject
*obj
, JSObject
*receiver
, jsid id
,
5342 uintN getHow
, Value
*vp
,
5343 const Shape
**shapeOut
, JSObject
**holderOut
)
5345 JSObject
*aobj
, *obj2
;
5350 JS_ASSERT_IF(getHow
& JSGET_CACHE_RESULT
, !JS_ON_TRACE(cx
));
5354 /* Convert string indices to integers if appropriate. */
5355 id
= js_CheckForStringIndex(id
);
5357 aobj
= js_GetProtoIfDenseArray(obj
);
5358 /* This call site is hot -- use the always-inlined variant of js_LookupPropertyWithFlags(). */
5359 protoIndex
= js_LookupPropertyWithFlagsInline(cx
, aobj
, id
, cx
->resolveFlags
,
5369 if (!CallJSPropertyOp(cx
, obj
->getClass()->getProperty
, obj
, id
, vp
))
5372 PCMETER(getHow
& JSGET_CACHE_RESULT
&& JS_PROPERTY_CACHE(cx
).nofills
++);
5375 * Give a strict warning if foo.bar is evaluated by a script for an
5376 * object foo with no property named 'bar'.
5379 if (vp
->isUndefined() && ((pc
= js_GetCurrentBytecodePC(cx
)) != NULL
)) {
5384 if (op
== JSOP_TRAP
) {
5385 JS_ASSERT_NOT_ON_TRACE(cx
);
5386 op
= JS_GetTrapOpcode(cx
, cx
->fp()->script(), pc
);
5388 if (op
== JSOP_GETXPROP
) {
5389 flags
= JSREPORT_ERROR
;
5391 if (!cx
->hasStrictOption() ||
5392 (op
!= JSOP_GETPROP
&& op
!= JSOP_GETELEM
) ||
5393 js_CurrentPCIsInImacro(cx
)) {
5398 * XXX do not warn about missing __iterator__ as the function
5399 * may be called from JS_GetMethodById. See bug 355145.
5401 if (JSID_IS_ATOM(id
, cx
->runtime
->atomState
.iteratorAtom
))
5404 /* Do not warn about tests like (obj[prop] == undefined). */
5405 if (cx
->resolveFlags
== JSRESOLVE_INFER
) {
5407 pc
+= js_CodeSpec
[op
].length
;
5408 if (Detecting(cx
, pc
))
5410 } else if (cx
->resolveFlags
& JSRESOLVE_DETECTING
) {
5414 flags
= JSREPORT_WARNING
| JSREPORT_STRICT
;
5417 /* Ok, bad undefined property reference: whine about it. */
5418 if (!js_ReportValueErrorFlags(cx
, flags
, JSMSG_UNDEFINED_PROP
,
5419 JSDVG_IGNORE_STACK
, IdToValue(id
),
5420 NULL
, NULL
, NULL
)) {
5427 if (!obj2
->isNative()) {
5428 return obj2
->isProxy()
5429 ? JSProxy::get(cx
, obj2
, receiver
, id
, vp
)
5430 : obj2
->getProperty(cx
, id
, vp
);
5433 shape
= (Shape
*) prop
;
5436 if (getHow
& JSGET_CACHE_RESULT
) {
5437 JS_ASSERT_NOT_ON_TRACE(cx
);
5438 JS_PROPERTY_CACHE(cx
).fill(cx
, aobj
, 0, protoIndex
, obj2
, shape
);
5441 /* This call site is hot -- use the always-inlined variant of js_NativeGet(). */
5442 if (!js_NativeGetInline(cx
, receiver
, obj
, obj2
, shape
, getHow
, vp
))
5449 js_GetPropertyHelperWithShape(JSContext
*cx
, JSObject
*obj
, JSObject
*receiver
, jsid id
,
5450 uint32 getHow
, Value
*vp
,
5451 const Shape
**shapeOut
, JSObject
**holderOut
)
5453 return js_GetPropertyHelperWithShapeInline(cx
, obj
, receiver
, id
, getHow
, vp
,
5454 shapeOut
, holderOut
);
5457 static JS_ALWAYS_INLINE JSBool
5458 js_GetPropertyHelperInline(JSContext
*cx
, JSObject
*obj
, JSObject
*receiver
, jsid id
,
5459 uint32 getHow
, Value
*vp
)
5463 return js_GetPropertyHelperWithShapeInline(cx
, obj
, receiver
, id
, getHow
, vp
, &shape
, &holder
);
5467 js_GetPropertyHelper(JSContext
*cx
, JSObject
*obj
, jsid id
, uint32 getHow
, Value
*vp
)
5469 return js_GetPropertyHelperInline(cx
, obj
, obj
, id
, getHow
, vp
);
5473 js_GetProperty(JSContext
*cx
, JSObject
*obj
, JSObject
*receiver
, jsid id
, Value
*vp
)
5475 /* This call site is hot -- use the always-inlined variant of js_GetPropertyHelper(). */
5476 return js_GetPropertyHelperInline(cx
, obj
, receiver
, id
, JSGET_METHOD_BARRIER
, vp
);
5480 js::GetPropertyDefault(JSContext
*cx
, JSObject
*obj
, jsid id
, const Value
&def
, Value
*vp
)
5484 if (js_LookupPropertyWithFlags(cx
, obj
, id
, JSRESOLVE_QUALIFIED
, &obj2
, &prop
) < 0)
5492 return js_GetProperty(cx
, obj2
, id
, vp
);
5496 js_GetMethod(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN getHow
, Value
*vp
)
5498 JSAutoResolveFlags
rf(cx
, JSRESOLVE_QUALIFIED
);
5500 PropertyIdOp op
= obj
->getOps()->getProperty
;
5502 #if JS_HAS_XML_SUPPORT
5503 JS_ASSERT(!obj
->isXML());
5505 return js_GetPropertyHelper(cx
, obj
, id
, getHow
, vp
);
5507 JS_ASSERT_IF(getHow
& JSGET_CACHE_RESULT
, obj
->isDenseArray());
5508 #if JS_HAS_XML_SUPPORT
5510 return js_GetXMLMethod(cx
, obj
, id
, vp
);
5512 return op(cx
, obj
, obj
, id
, vp
);
5516 js_CheckUndeclaredVarAssignment(JSContext
*cx
, JSString
*propname
)
5518 JSStackFrame
*const fp
= js_GetTopStackFrame(cx
);
5522 /* If neither cx nor the code is strict, then no check is needed. */
5523 if (!(fp
->isScriptFrame() && fp
->script()->strictModeCode
) &&
5524 !cx
->hasStrictOption()) {
5528 JSAutoByteString
bytes(cx
, propname
);
5530 JS_ReportErrorFlagsAndNumber(cx
,
5531 (JSREPORT_WARNING
| JSREPORT_STRICT
5532 | JSREPORT_STRICT_MODE_ERROR
),
5533 js_GetErrorMessage
, NULL
,
5534 JSMSG_UNDECLARED_VAR
, bytes
.ptr());
5538 JSObject::reportReadOnly(JSContext
* cx
, jsid id
, uintN report
)
5540 return js_ReportValueErrorFlags(cx
, report
, JSMSG_READ_ONLY
,
5541 JSDVG_IGNORE_STACK
, IdToValue(id
), NULL
,
5546 JSObject::reportNotConfigurable(JSContext
* cx
, jsid id
, uintN report
)
5548 return js_ReportValueErrorFlags(cx
, report
, JSMSG_CANT_DELETE
,
5549 JSDVG_IGNORE_STACK
, IdToValue(id
), NULL
,
5554 JSObject::reportNotExtensible(JSContext
*cx
, uintN report
)
5556 return js_ReportValueErrorFlags(cx
, report
, JSMSG_OBJECT_NOT_EXTENSIBLE
,
5557 JSDVG_IGNORE_STACK
, ObjectValue(*this),
5562 js_SetPropertyHelper(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN defineHow
,
5563 Value
*vp
, JSBool strict
)
5573 StrictPropertyOp setter
;
5576 JS_ASSERT((defineHow
&
5577 ~(JSDNP_CACHE_RESULT
| JSDNP_SET_METHOD
| JSDNP_UNQUALIFIED
)) == 0);
5578 if (defineHow
& JSDNP_CACHE_RESULT
)
5579 JS_ASSERT_NOT_ON_TRACE(cx
);
5581 /* Convert string indices to integers if appropriate. */
5582 id
= js_CheckForStringIndex(id
);
5584 protoIndex
= js_LookupPropertyWithFlags(cx
, obj
, id
, cx
->resolveFlags
,
5589 if (!pobj
->isNative()) {
5590 if (pobj
->isProxy()) {
5591 AutoPropertyDescriptorRooter
pd(cx
);
5592 if (!JSProxy::getPropertyDescriptor(cx
, pobj
, id
, true, &pd
))
5595 if (pd
.attrs
& JSPROP_SHARED
)
5596 return CallSetter(cx
, obj
, id
, pd
.setter
, pd
.attrs
, pd
.shortid
, strict
, vp
);
5598 if (pd
.attrs
& JSPROP_READONLY
) {
5600 return obj
->reportReadOnly(cx
, id
);
5601 if (cx
->hasStrictOption())
5602 return obj
->reportReadOnly(cx
, id
, JSREPORT_STRICT
| JSREPORT_WARNING
);
5610 /* We should never add properties to lexical blocks. */
5611 JS_ASSERT(!obj
->isBlock());
5613 if (!obj
->getParent() &&
5614 (defineHow
& JSDNP_UNQUALIFIED
) &&
5615 !js_CheckUndeclaredVarAssignment(cx
, JSID_TO_STRING(id
))) {
5619 shape
= (Shape
*) prop
;
5622 * Now either shape is null, meaning id was not found in obj or one of its
5623 * prototypes; or shape is non-null, meaning id was found directly in pobj.
5625 attrs
= JSPROP_ENUMERATE
;
5628 clasp
= obj
->getClass();
5629 getter
= clasp
->getProperty
;
5630 setter
= clasp
->setProperty
;
5633 /* ES5 8.12.4 [[Put]] step 2. */
5634 if (shape
->isAccessorDescriptor()) {
5635 if (shape
->hasDefaultSetter())
5636 return js_ReportGetterOnlyAssignment(cx
);
5638 JS_ASSERT(shape
->isDataDescriptor());
5640 if (!shape
->writable()) {
5641 PCMETER((defineHow
& JSDNP_CACHE_RESULT
) && JS_PROPERTY_CACHE(cx
).rofills
++);
5643 /* Error in strict mode code, warn with strict option, otherwise do nothing. */
5645 return obj
->reportReadOnly(cx
, id
);
5646 if (cx
->hasStrictOption())
5647 return obj
->reportReadOnly(cx
, id
, JSREPORT_STRICT
| JSREPORT_WARNING
);
5652 attrs
= shape
->attributes();
5655 * We found id in a prototype object: prepare to share or shadow.
5657 if (!shape
->shadowable()) {
5658 if (defineHow
& JSDNP_CACHE_RESULT
)
5659 JS_PROPERTY_CACHE(cx
).fill(cx
, obj
, 0, protoIndex
, pobj
, shape
);
5661 if (shape
->hasDefaultSetter() && !shape
->hasGetterValue())
5664 return shape
->set(cx
, obj
, strict
, vp
);
5668 * Preserve attrs except JSPROP_SHARED, getter, and setter when
5669 * shadowing any property that has no slot (is shared). We must
5670 * clear the shared attribute for the shadowing shape so that the
5671 * property in obj that it defines has a slot to retain the value
5672 * being set, in case the setter simply cannot operate on instances
5673 * of obj's class by storing the value in some class-specific
5676 * A subset of slotless shared properties is the set of properties
5677 * with shortids, which must be preserved too. An old API requires
5678 * that the property's getter and setter receive the shortid, not
5679 * id, when they are called on the shadowing property that we are
5680 * about to create in obj.
5682 if (!shape
->hasSlot()) {
5683 defineHow
&= ~JSDNP_SET_METHOD
;
5684 if (shape
->hasShortID()) {
5685 flags
= Shape::HAS_SHORTID
;
5686 shortid
= shape
->shortid
;
5688 attrs
&= ~JSPROP_SHARED
;
5689 getter
= shape
->getter();
5690 setter
= shape
->setter();
5692 /* Restore attrs to the ECMA default for new properties. */
5693 attrs
= JSPROP_ENUMERATE
;
5697 * Forget we found the proto-property now that we've copied any
5698 * needed member values.
5703 JS_ASSERT_IF(shape
&& shape
->isMethod(), pobj
->hasMethodBarrier());
5704 JS_ASSERT_IF(shape
&& shape
->isMethod(),
5705 &pobj
->getSlot(shape
->slot
).toObject() == &shape
->methodObject());
5706 if (shape
&& (defineHow
& JSDNP_SET_METHOD
)) {
5708 * JSOP_SETMETHOD is assigning to an existing own property. If it
5709 * is an identical method property, do nothing. Otherwise downgrade
5710 * to ordinary assignment. Either way, do not fill the property
5711 * cache, as the interpreter has no fast path for these unusual
5714 bool identical
= shape
->isMethod() && &shape
->methodObject() == &vp
->toObject();
5716 shape
= obj
->methodShapeChange(cx
, *shape
);
5720 JSObject
*funobj
= &vp
->toObject();
5721 JSFunction
*fun
= funobj
->getFunctionPrivate();
5722 if (fun
== funobj
) {
5723 funobj
= CloneFunctionObject(cx
, fun
, fun
->parent
);
5726 vp
->setObject(*funobj
);
5729 return identical
|| js_NativeSet(cx
, obj
, shape
, false, strict
, vp
);
5735 if (!obj
->isExtensible()) {
5736 /* Error in strict mode code, warn with strict option, otherwise do nothing. */
5738 return obj
->reportNotExtensible(cx
);
5739 if (cx
->hasStrictOption())
5740 return obj
->reportNotExtensible(cx
, JSREPORT_STRICT
| JSREPORT_WARNING
);
5745 * Purge the property cache of now-shadowed id in obj's scope chain.
5746 * Do this early, before locking obj to avoid nesting locks.
5748 js_PurgeScopeChain(cx
, obj
, id
);
5750 /* Find or make a property descriptor with the right heritage. */
5751 if (!obj
->ensureClassReservedSlots(cx
))
5755 * Check for Object class here to avoid defining a method on a class
5756 * with magic resolve, addProperty, getProperty, etc. hooks.
5758 if ((defineHow
& JSDNP_SET_METHOD
) && obj
->canHaveMethodBarrier()) {
5759 JS_ASSERT(IsFunctionObject(*vp
));
5760 JS_ASSERT(!(attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)));
5762 JSObject
*funobj
= &vp
->toObject();
5763 JSFunction
*fun
= GET_FUNCTION_PRIVATE(cx
, funobj
);
5764 if (fun
== funobj
) {
5765 flags
|= Shape::METHOD
;
5766 getter
= CastAsPropertyOp(funobj
);
5770 shape
= obj
->putProperty(cx
, id
, getter
, setter
, SHAPE_INVALID_SLOT
,
5771 attrs
, flags
, shortid
);
5775 if (defineHow
& JSDNP_CACHE_RESULT
)
5776 TRACE_1(AddProperty
, obj
);
5779 * Initialize the new property value (passed to setter) to undefined.
5780 * Note that we store before calling addProperty, to match the order
5781 * in js_DefineNativeProperty.
5783 if (obj
->containsSlot(shape
->slot
))
5784 obj
->nativeSetSlot(shape
->slot
, UndefinedValue());
5786 /* XXXbe called with obj locked */
5787 if (!CallAddPropertyHook(cx
, clasp
, obj
, shape
, vp
)) {
5788 obj
->removeProperty(cx
, id
);
5794 if (defineHow
& JSDNP_CACHE_RESULT
)
5795 JS_PROPERTY_CACHE(cx
).fill(cx
, obj
, 0, 0, obj
, shape
, added
);
5797 return js_NativeSet(cx
, obj
, shape
, added
, strict
, vp
);
5800 error
: // TRACE_1 jumps here in case of error.
5806 js_SetProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
, JSBool strict
)
5808 return js_SetPropertyHelper(cx
, obj
, id
, 0, vp
, strict
);
5812 js_GetAttributes(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN
*attrsp
)
5815 if (!js_LookupProperty(cx
, obj
, id
, &obj
, &prop
))
5821 if (!obj
->isNative())
5822 return obj
->getAttributes(cx
, id
, attrsp
);
5824 const Shape
*shape
= (Shape
*)prop
;
5825 *attrsp
= shape
->attributes();
5830 js_SetNativeAttributes(JSContext
*cx
, JSObject
*obj
, Shape
*shape
, uintN attrs
)
5832 JS_ASSERT(obj
->isNative());
5833 return !!js_ChangeNativePropertyAttrs(cx
, obj
, shape
, attrs
, 0,
5834 shape
->getter(), shape
->setter());
5838 js_SetAttributes(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN
*attrsp
)
5841 if (!js_LookupProperty(cx
, obj
, id
, &obj
, &prop
))
5845 return obj
->isNative()
5846 ? js_SetNativeAttributes(cx
, obj
, (Shape
*) prop
, *attrsp
)
5847 : obj
->setAttributes(cx
, id
, attrsp
);
5851 js_DeleteProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*rval
, JSBool strict
)
5857 rval
->setBoolean(true);
5859 /* Convert string indices to integers if appropriate. */
5860 id
= js_CheckForStringIndex(id
);
5862 if (!js_LookupProperty(cx
, obj
, id
, &proto
, &prop
))
5864 if (!prop
|| proto
!= obj
) {
5866 * If the property was found in a native prototype, check whether it's
5867 * shared and permanent. Such a property stands for direct properties
5868 * in all delegating objects, matching ECMA semantics without bloating
5869 * each delegating object.
5871 if (prop
&& proto
->isNative()) {
5872 shape
= (Shape
*)prop
;
5873 if (shape
->isSharedPermanent()) {
5875 return obj
->reportNotConfigurable(cx
, id
);
5876 rval
->setBoolean(false);
5882 * If no property, or the property comes unshared or impermanent from
5883 * a prototype, call the class's delProperty hook, passing rval as the
5886 return CallJSPropertyOp(cx
, obj
->getClass()->delProperty
, obj
, id
, rval
);
5889 shape
= (Shape
*)prop
;
5890 if (!shape
->configurable()) {
5892 return obj
->reportNotConfigurable(cx
, id
);
5893 rval
->setBoolean(false);
5897 if (!CallJSPropertyOp(cx
, obj
->getClass()->delProperty
, obj
, SHAPE_USERID(shape
), rval
))
5900 if (obj
->containsSlot(shape
->slot
)) {
5901 const Value
&v
= obj
->nativeGetSlot(shape
->slot
);
5905 * Delete is rare enough that we can take the hit of checking for an
5906 * active cloned method function object that must be homed to a callee
5907 * slot on the active stack frame before this delete completes, in case
5908 * someone saved the clone and checks it against foo.caller for a foo
5909 * called from the active method.
5911 * We do not check suspended frames. They can't be reached via caller,
5912 * so the only way they could have the method's joined function object
5913 * as callee is through an API abusage. We break any such edge case.
5915 if (obj
->hasMethodBarrier()) {
5918 if (IsFunctionObject(v
, &funobj
)) {
5919 JSFunction
*fun
= GET_FUNCTION_PRIVATE(cx
, funobj
);
5921 if (fun
!= funobj
) {
5922 for (JSStackFrame
*fp
= cx
->maybefp(); fp
; fp
= fp
->prev()) {
5923 if (fp
->isFunctionFrame() &&
5924 &fp
->callee() == &fun
->compiledFunObj() &&
5925 fp
->thisValue().isObject())
5927 JSObject
*tmp
= &fp
->thisValue().toObject();
5930 fp
->calleeValue().setObject(*funobj
);
5933 } while ((tmp
= tmp
->getProto()) != NULL
);
5941 return obj
->removeProperty(cx
, id
) && js_SuppressDeletedProperty(cx
, obj
, id
);
5947 HasNativeMethod(JSObject
*obj
, jsid methodid
, Native native
)
5949 const Shape
*shape
= obj
->nativeLookup(methodid
);
5950 if (!shape
|| !shape
->hasDefaultGetter() || !obj
->containsSlot(shape
->slot
))
5953 const Value
&fval
= obj
->nativeGetSlot(shape
->slot
);
5955 if (!IsFunctionObject(fval
, &funobj
) || funobj
->getFunctionPrivate()->maybeNative() != native
)
5962 DefaultValue(JSContext
*cx
, JSObject
*obj
, JSType hint
, Value
*vp
)
5964 JS_ASSERT(hint
!= JSTYPE_OBJECT
&& hint
!= JSTYPE_FUNCTION
);
5966 Value v
= ObjectValue(*obj
);
5967 if (hint
== JSTYPE_STRING
) {
5968 /* Optimize (new String(...)).toString(). */
5969 if (obj
->getClass() == &js_StringClass
&&
5970 ClassMethodIsNative(cx
, obj
,
5972 ATOM_TO_JSID(cx
->runtime
->atomState
.toStringAtom
),
5974 *vp
= obj
->getPrimitiveThis();
5978 if (!js_TryMethod(cx
, obj
, cx
->runtime
->atomState
.toStringAtom
, 0, NULL
, &v
))
5980 if (!v
.isPrimitive()) {
5981 if (!obj
->getClass()->convert(cx
, obj
, hint
, &v
))
5985 /* Optimize (new String(...)).valueOf(). */
5986 Class
*clasp
= obj
->getClass();
5987 if ((clasp
== &js_StringClass
&&
5988 ClassMethodIsNative(cx
, obj
, &js_StringClass
,
5989 ATOM_TO_JSID(cx
->runtime
->atomState
.valueOfAtom
),
5990 js_str_toString
)) ||
5991 (clasp
== &js_NumberClass
&&
5992 ClassMethodIsNative(cx
, obj
, &js_NumberClass
,
5993 ATOM_TO_JSID(cx
->runtime
->atomState
.valueOfAtom
),
5995 *vp
= obj
->getPrimitiveThis();
5999 if (!obj
->getClass()->convert(cx
, obj
, hint
, &v
))
6002 JS_ASSERT(hint
!= TypeOfValue(cx
, v
));
6003 if (!js_TryMethod(cx
, obj
, cx
->runtime
->atomState
.toStringAtom
, 0, NULL
, &v
))
6008 /* Avoid recursive death when decompiling in js_ReportValueError. */
6010 if (hint
== JSTYPE_STRING
) {
6011 str
= JS_InternString(cx
, obj
->getClass()->name
);
6017 vp
->setObject(*obj
);
6018 js_ReportValueError2(cx
, JSMSG_CANT_CONVERT_TO
,
6019 JSDVG_SEARCH_STACK
, *vp
, str
,
6020 (hint
== JSTYPE_VOID
)
6022 : JS_TYPE_STR(hint
));
6029 } /* namespace js */
6031 JS_FRIEND_API(JSBool
)
6032 js_Enumerate(JSContext
*cx
, JSObject
*obj
, JSIterateOp enum_op
, Value
*statep
, jsid
*idp
)
6034 /* If the class has a custom JSCLASS_NEW_ENUMERATE hook, call it. */
6035 Class
*clasp
= obj
->getClass();
6036 JSEnumerateOp enumerate
= clasp
->enumerate
;
6037 if (clasp
->flags
& JSCLASS_NEW_ENUMERATE
) {
6038 JS_ASSERT(enumerate
!= JS_EnumerateStub
);
6039 return ((NewEnumerateOp
) enumerate
)(cx
, obj
, enum_op
, statep
, idp
);
6042 if (!enumerate(cx
, obj
))
6045 /* Tell InitNativeIterator to treat us like a native object. */
6046 JS_ASSERT(enum_op
== JSENUMERATE_INIT
|| enum_op
== JSENUMERATE_INIT_ALL
);
6047 statep
->setMagic(JS_NATIVE_ENUMERATE
);
6054 CheckAccess(JSContext
*cx
, JSObject
*obj
, jsid id
, JSAccessMode mode
,
6055 Value
*vp
, uintN
*attrsp
)
6062 JSSecurityCallbacks
*callbacks
;
6063 CheckAccessOp check
;
6065 while (JS_UNLIKELY(obj
->getClass() == &js_WithClass
))
6066 obj
= obj
->getProto();
6068 writing
= (mode
& JSACC_WRITE
) != 0;
6069 switch (mode
& JSACC_TYPEMASK
) {
6073 vp
->setObjectOrNull(obj
->getProto());
6074 *attrsp
= JSPROP_PERMANENT
;
6078 JS_ASSERT(!writing
);
6080 vp
->setObject(*obj
->getParent());
6081 *attrsp
= JSPROP_READONLY
| JSPROP_PERMANENT
;
6085 if (!obj
->lookupProperty(cx
, id
, &pobj
, &prop
))
6095 if (!pobj
->isNative()) {
6103 shape
= (Shape
*)prop
;
6104 *attrsp
= shape
->attributes();
6106 if (pobj
->containsSlot(shape
->slot
))
6107 *vp
= pobj
->nativeGetSlot(shape
->slot
);
6114 * If obj's class has a stub (null) checkAccess hook, use the per-runtime
6115 * checkObjectAccess callback, if configured.
6117 * We don't want to require all classes to supply a checkAccess hook; we
6118 * need that hook only for certain classes used when precompiling scripts
6119 * and functions ("brutal sharing"). But for general safety of built-in
6120 * magic properties like __proto__, we route all access checks, even for
6121 * classes that stub out checkAccess, through the global checkObjectAccess
6122 * hook. This covers precompilation-based sharing and (possibly
6123 * unintended) runtime sharing across trust boundaries.
6125 clasp
= pobj
->getClass();
6126 check
= clasp
->checkAccess
;
6128 callbacks
= JS_GetSecurityCallbacks(cx
);
6129 check
= callbacks
? Valueify(callbacks
->checkObjectAccess
) : NULL
;
6131 return !check
|| check(cx
, pobj
, id
, mode
, vp
);
6137 js_TypeOf(JSContext
*cx
, JSObject
*obj
)
6140 * ECMA 262, 11.4.3 says that any native object that implements
6141 * [[Call]] should be of type "function". However, RegExp is of
6142 * type "object", not "function", for Web compatibility.
6144 if (obj
->isCallable()) {
6145 return (obj
->getClass() != &js_RegExpClass
)
6150 return JSTYPE_OBJECT
;
6154 js_IsDelegate(JSContext
*cx
, JSObject
*obj
, const Value
&v
)
6156 if (v
.isPrimitive())
6158 JSObject
*obj2
= &v
.toObject();
6159 while ((obj2
= obj2
->getProto()) != NULL
) {
6167 js::FindClassPrototype(JSContext
*cx
, JSObject
*scopeobj
, JSProtoKey protoKey
,
6168 JSObject
**protop
, Class
*clasp
)
6171 if (!js_FindClassObject(cx
, scopeobj
, protoKey
, &v
, clasp
))
6174 if (IsFunctionObject(v
)) {
6175 JSObject
*ctor
= &v
.toObject();
6176 if (!ctor
->getProperty(cx
, ATOM_TO_JSID(cx
->runtime
->atomState
.classPrototypeAtom
), &v
))
6180 *protop
= v
.isObject() ? &v
.toObject() : NULL
;
6185 * The first part of this function has been hand-expanded and optimized into
6186 * NewBuiltinClassInstance in jsobjinlines.h.
6189 js_GetClassPrototype(JSContext
*cx
, JSObject
*scopeobj
, JSProtoKey protoKey
,
6190 JSObject
**protop
, Class
*clasp
)
6192 VOUCH_DOES_NOT_REQUIRE_STACK();
6193 JS_ASSERT(JSProto_Null
<= protoKey
);
6194 JS_ASSERT(protoKey
< JSProto_LIMIT
);
6196 if (protoKey
!= JSProto_Null
) {
6199 scopeobj
= &cx
->fp()->scopeChain();
6201 scopeobj
= cx
->globalObject
;
6208 scopeobj
= scopeobj
->getGlobal();
6209 if (scopeobj
->isGlobal()) {
6210 const Value
&v
= scopeobj
->getReservedSlot(JSProto_LIMIT
+ protoKey
);
6212 *protop
= &v
.toObject();
6218 return FindClassPrototype(cx
, scopeobj
, protoKey
, protop
, clasp
);
6222 js_SetClassPrototype(JSContext
*cx
, JSObject
*ctor
, JSObject
*proto
, uintN attrs
)
6225 * Use the given attributes for the prototype property of the constructor,
6226 * as user-defined constructors have a DontDelete prototype (which may be
6227 * reset), while native or "system" constructors have DontEnum | ReadOnly |
6230 if (!ctor
->defineProperty(cx
, ATOM_TO_JSID(cx
->runtime
->atomState
.classPrototypeAtom
),
6231 ObjectOrNullValue(proto
), PropertyStub
, StrictPropertyStub
, attrs
)) {
6236 * ECMA says that Object.prototype.constructor, or f.prototype.constructor
6237 * for a user-defined function f, is DontEnum.
6239 return proto
->defineProperty(cx
, ATOM_TO_JSID(cx
->runtime
->atomState
.constructorAtom
),
6240 ObjectOrNullValue(ctor
), PropertyStub
, StrictPropertyStub
, 0);
6244 PrimitiveToObject(JSContext
*cx
, const Value
&v
)
6246 JS_ASSERT(v
.isPrimitive());
6250 clasp
= &js_NumberClass
;
6251 } else if (v
.isString()) {
6252 clasp
= &js_StringClass
;
6254 JS_ASSERT(v
.isBoolean());
6255 clasp
= &js_BooleanClass
;
6258 JSObject
*obj
= NewBuiltinClassInstance(cx
, clasp
);
6262 obj
->setPrimitiveThis(v
);
6267 js_PrimitiveToObject(JSContext
*cx
, Value
*vp
)
6269 JSObject
*obj
= PrimitiveToObject(cx
, *vp
);
6273 vp
->setObject(*obj
);
6278 js_ValueToObjectOrNull(JSContext
*cx
, const Value
&v
, JSObject
**objp
)
6282 if (v
.isObjectOrNull()) {
6283 obj
= v
.toObjectOrNull();
6284 } else if (v
.isUndefined()) {
6287 obj
= PrimitiveToObject(cx
, v
);
6297 /* Callers must handle the already-object case . */
6299 ToObjectSlow(JSContext
*cx
, Value
*vp
)
6301 JS_ASSERT(!vp
->isMagic());
6302 JS_ASSERT(!vp
->isObject());
6304 if (vp
->isNullOrUndefined()) {
6305 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_CANT_CONVERT_TO
,
6306 vp
->isNull() ? "null" : "undefined", "object");
6310 JSObject
*obj
= PrimitiveToObject(cx
, *vp
);
6312 vp
->setObject(*obj
);
6319 js_ValueToNonNullObject(JSContext
*cx
, const Value
&v
)
6323 if (!js_ValueToObjectOrNull(cx
, v
, &obj
))
6326 js_ReportIsNullOrUndefined(cx
, JSDVG_SEARCH_STACK
, v
, NULL
);
6331 js_TryValueOf(JSContext
*cx
, JSObject
*obj
, JSType type
, Value
*rval
)
6335 argv
[0].setString(ATOM_TO_STRING(cx
->runtime
->atomState
.typeAtoms
[type
]));
6336 return js_TryMethod(cx
, obj
, cx
->runtime
->atomState
.valueOfAtom
,
6341 js_TryMethod(JSContext
*cx
, JSObject
*obj
, JSAtom
*atom
,
6342 uintN argc
, Value
*argv
, Value
*rval
)
6344 JS_CHECK_RECURSION(cx
, return JS_FALSE
);
6347 * Report failure only if an appropriate method was found, and calling it
6348 * returned failure. We propagate failure in this case to make exceptions
6351 JSErrorReporter older
= JS_SetErrorReporter(cx
, NULL
);
6352 jsid id
= ATOM_TO_JSID(atom
);
6354 JSBool ok
= js_GetMethod(cx
, obj
, id
, JSGET_NO_METHOD_BARRIER
, &fval
);
6355 JS_SetErrorReporter(cx
, older
);
6359 if (fval
.isPrimitive())
6361 return ExternalInvoke(cx
, ObjectValue(*obj
), fval
, argc
, argv
, rval
);
6367 js_XDRObject(JSXDRState
*xdr
, JSObject
**objp
)
6372 uint32 classId
, classDef
;
6373 JSProtoKey protoKey
;
6378 if (xdr
->mode
== JSXDR_ENCODE
) {
6379 clasp
= (*objp
)->getClass();
6380 classId
= JS_XDRFindClassIdByName(xdr
, clasp
->name
);
6381 classDef
= !classId
;
6383 if (!JS_XDRRegisterClass(xdr
, Jsvalify(clasp
), &classId
))
6385 protoKey
= JSCLASS_CACHED_PROTO_KEY(clasp
);
6386 if (protoKey
!= JSProto_Null
) {
6387 classDef
|= (protoKey
<< 1);
6389 atom
= js_Atomize(cx
, clasp
->name
, strlen(clasp
->name
), 0);
6395 clasp
= NULL
; /* quell GCC overwarning */
6400 * XDR a flag word, which could be 0 for a class use, in which case no
6401 * name follows, only the id in xdr's class registry; 1 for a class def,
6402 * in which case the flag word is followed by the class name transferred
6403 * from or to atom; or a value greater than 1, an odd number that when
6404 * divided by two yields the JSProtoKey for class. In the last case, as
6405 * in the 0 classDef case, no name is transferred via atom.
6407 if (!JS_XDRUint32(xdr
, &classDef
))
6409 if (classDef
== 1 && !js_XDRAtom(xdr
, &atom
))
6412 if (!JS_XDRUint32(xdr
, &classId
))
6415 if (xdr
->mode
== JSXDR_DECODE
) {
6417 /* NB: we know that JSProto_Null is 0 here, for backward compat. */
6418 protoKey
= (JSProtoKey
) (classDef
>> 1);
6419 if (!js_GetClassPrototype(cx
, NULL
, protoKey
, &proto
, clasp
))
6421 clasp
= proto
->getClass();
6422 if (!JS_XDRRegisterClass(xdr
, Jsvalify(clasp
), &classId
))
6425 clasp
= Valueify(JS_XDRFindClassById(xdr
, classId
));
6428 JS_snprintf(numBuf
, sizeof numBuf
, "%ld", (long)classId
);
6429 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
6430 JSMSG_CANT_FIND_CLASS
, numBuf
);
6436 if (!clasp
->xdrObject
) {
6437 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
6438 JSMSG_CANT_XDR_CLASS
, clasp
->name
);
6441 return clasp
->xdrObject(xdr
, objp
);
6444 #endif /* JS_HAS_XDR */
6446 #ifdef JS_DUMP_SCOPE_METERS
6450 JSBasicStats js_entry_count_bs
= JS_INIT_STATIC_BASIC_STATS
;
6453 MeterEntryCount(uintN count
)
6455 JS_BASIC_STATS_ACCUM(&js_entry_count_bs
, count
);
6459 js_DumpScopeMeters(JSRuntime
*rt
)
6463 logfp
= fopen("/tmp/scope.stats", "a");
6468 mean
= JS_MeanAndStdDevBS(&js_entry_count_bs
, &sigma
);
6470 fprintf(logfp
, "scopes %u entries %g mean %g sigma %g max %u",
6471 js_entry_count_bs
.num
, js_entry_count_bs
.sum
, mean
, sigma
,
6472 js_entry_count_bs
.max
);
6475 JS_DumpHistogram(&js_entry_count_bs
, logfp
);
6476 JS_BASIC_STATS_INIT(&js_entry_count_bs
);
6483 js_PrintObjectSlotName(JSTracer
*trc
, char *buf
, size_t bufsize
)
6485 JS_ASSERT(trc
->debugPrinter
== js_PrintObjectSlotName
);
6487 JSObject
*obj
= (JSObject
*)trc
->debugPrintArg
;
6488 uint32 slot
= (uint32
)trc
->debugPrintIndex
;
6491 if (obj
->isNative()) {
6492 shape
= obj
->lastProperty();
6493 while (shape
->previous() && shape
->slot
!= slot
)
6494 shape
= shape
->previous();
6495 if (shape
->slot
!= slot
)
6502 const char *slotname
= NULL
;
6503 if (obj
->isGlobal()) {
6504 #define JS_PROTO(name,code,init) \
6505 if ((code) == slot) { slotname = js_##name##_str; goto found; }
6506 #include "jsproto.tbl"
6511 JS_snprintf(buf
, bufsize
, "CLASS_OBJECT(%s)", slotname
);
6513 JS_snprintf(buf
, bufsize
, "**UNKNOWN SLOT %ld**", (long)slot
);
6515 jsid id
= shape
->id
;
6516 if (JSID_IS_INT(id
)) {
6517 JS_snprintf(buf
, bufsize
, "%ld", (long)JSID_TO_INT(id
));
6518 } else if (JSID_IS_ATOM(id
)) {
6519 PutEscapedString(buf
, bufsize
, JSID_TO_ATOM(id
), 0);
6521 JS_snprintf(buf
, bufsize
, "**FINALIZED ATOM KEY**");
6528 js_TraceObject(JSTracer
*trc
, JSObject
*obj
)
6530 JS_ASSERT(obj
->isNative());
6532 JSContext
*cx
= trc
->context
;
6533 if (obj
->hasSlotsArray() && !obj
->nativeEmpty() && IS_GC_MARKING_TRACER(trc
)) {
6535 * Trim overlong dslots allocations from the GC, to avoid thrashing in
6536 * case of delete-happy code that settles down at a given population.
6537 * The !obj->nativeEmpty() guard above is due to the bug described by
6538 * the FIXME comment below.
6540 size_t slots
= obj
->slotSpan();
6541 if (obj
->numSlots() != slots
)
6542 obj
->shrinkSlots(cx
, slots
);
6545 #ifdef JS_DUMP_SCOPE_METERS
6546 MeterEntryCount(obj
->propertyCount
);
6551 if (!JS_CLIST_IS_EMPTY(&cx
->runtime
->watchPointList
))
6552 js_TraceWatchPoints(trc
, obj
);
6554 /* No one runs while the GC is running, so we can use LOCKED_... here. */
6555 Class
*clasp
= obj
->getClass();
6557 if (clasp
->flags
& JSCLASS_MARK_IS_TRACE
)
6558 ((JSTraceOp
) clasp
->mark
)(trc
, obj
);
6559 else if (IS_GC_MARKING_TRACER(trc
))
6560 (void) clasp
->mark(cx
, obj
, trc
);
6562 if (clasp
->flags
& JSCLASS_IS_GLOBAL
) {
6563 JSCompartment
*compartment
= obj
->getCompartment();
6564 compartment
->mark(trc
);
6568 * NB: clasp->mark could mutate something (which would be a bug, but we are
6569 * defensive), so don't hoist this above calling clasp->mark.
6571 uint32 nslots
= Min(obj
->numSlots(), obj
->slotSpan());
6572 for (uint32 i
= 0; i
!= nslots
; ++i
) {
6573 const Value
&v
= obj
->getSlot(i
);
6574 JS_SET_TRACING_DETAILS(trc
, js_PrintObjectSlotName
, obj
, i
);
6575 MarkValueRaw(trc
, v
);
6580 js_ClearNative(JSContext
*cx
, JSObject
*obj
)
6583 * Clear obj of all obj's properties. FIXME: we do not clear reserved slots
6584 * lying below JSSLOT_FREE(clasp). JS_ClearScope does that.
6586 if (!obj
->nativeEmpty()) {
6587 /* Now that we're done using real properties, clear obj. */
6590 /* Clear slot values since obj->clear reset our shape to empty. */
6591 uint32 freeslot
= JSSLOT_FREE(obj
->getClass());
6592 uint32 n
= obj
->numSlots();
6593 for (uint32 i
= freeslot
; i
< n
; ++i
)
6594 obj
->setSlot(i
, UndefinedValue());
6599 js_GetReservedSlot(JSContext
*cx
, JSObject
*obj
, uint32 slot
, Value
*vp
)
6601 if (!obj
->isNative()) {
6606 if (slot
< obj
->numSlots())
6607 *vp
= obj
->getSlot(slot
);
6614 js_SetReservedSlot(JSContext
*cx
, JSObject
*obj
, uint32 slot
, const Value
&v
)
6616 if (!obj
->isNative())
6619 Class
*clasp
= obj
->getClass();
6621 if (slot
>= obj
->numSlots()) {
6622 uint32 nslots
= JSSLOT_FREE(clasp
);
6623 JS_ASSERT(slot
< nslots
);
6624 if (!obj
->allocSlots(cx
, nslots
))
6628 obj
->setSlot(slot
, v
);
6629 GC_POKE(cx
, JS_NULL
);
6634 JSObject::getGlobal() const
6636 JSObject
*obj
= const_cast<JSObject
*>(this);
6637 while (JSObject
*parent
= obj
->getParent())
6643 js_ReportGetterOnlyAssignment(JSContext
*cx
)
6645 return JS_ReportErrorFlagsAndNumber(cx
,
6646 JSREPORT_WARNING
| JSREPORT_STRICT
|
6647 JSREPORT_STRICT_MODE_ERROR
,
6648 js_GetErrorMessage
, NULL
,
6652 JS_FRIEND_API(JSBool
)
6653 js_GetterOnlyPropertyStub(JSContext
*cx
, JSObject
*obj
, jsid id
, JSBool strict
, jsval
*vp
)
6655 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_GETTER_ONLY
);
6662 * Routines to print out values during debugging. These are FRIEND_API to help
6663 * the debugger find them and to support temporarily hacking js_Dump* calls
6668 dumpChars(const jschar
*s
, size_t n
)
6672 if (n
== (size_t) -1) {
6677 for (i
= 0; i
< n
; i
++) {
6679 fprintf(stderr
, "\\n");
6680 else if (s
[i
] == '\t')
6681 fprintf(stderr
, "\\t");
6682 else if (s
[i
] >= 32 && s
[i
] < 127)
6683 fputc(s
[i
], stderr
);
6684 else if (s
[i
] <= 255)
6685 fprintf(stderr
, "\\x%02x", (unsigned int) s
[i
]);
6687 fprintf(stderr
, "\\u%04x", (unsigned int) s
[i
]);
6693 js_DumpChars(const jschar
*s
, size_t n
)
6695 fprintf(stderr
, "jschar * (%p) = ", (void *) s
);
6697 fputc('\n', stderr
);
6701 dumpString(JSString
*str
)
6703 if (const jschar
*chars
= str
->getChars(NULL
))
6704 dumpChars(chars
, str
->length());
6706 fprintf(stderr
, "(oom in dumpString)");
6710 js_DumpString(JSString
*str
)
6712 if (const jschar
*chars
= str
->getChars(NULL
)) {
6713 fprintf(stderr
, "JSString* (%p) = jschar * (%p) = ",
6714 (void *) str
, (void *) chars
);
6717 fprintf(stderr
, "(oom in JS_DumpString)");
6719 fputc('\n', stderr
);
6723 js_DumpAtom(JSAtom
*atom
)
6725 fprintf(stderr
, "JSAtom* (%p) = ", (void *) atom
);
6726 js_DumpString(ATOM_TO_STRING(atom
));
6730 dumpValue(const Value
&v
)
6733 fprintf(stderr
, "null");
6734 else if (v
.isUndefined())
6735 fprintf(stderr
, "undefined");
6736 else if (v
.isInt32())
6737 fprintf(stderr
, "%d", v
.toInt32());
6738 else if (v
.isDouble())
6739 fprintf(stderr
, "%g", v
.toDouble());
6740 else if (v
.isString())
6741 dumpString(v
.toString());
6742 else if (v
.isObject() && v
.toObject().isFunction()) {
6743 JSObject
*funobj
= &v
.toObject();
6744 JSFunction
*fun
= GET_FUNCTION_PRIVATE(cx
, funobj
);
6746 fputs("<function ", stderr
);
6747 FileEscapedString(stderr
, ATOM_TO_STRING(fun
->atom
), 0);
6749 fputs("<unnamed function", stderr
);
6751 if (fun
->isInterpreted()) {
6752 JSScript
*script
= fun
->script();
6753 fprintf(stderr
, " (%s:%u)",
6754 script
->filename
? script
->filename
: "", script
->lineno
);
6756 fprintf(stderr
, " at %p (JSFunction at %p)>", (void *) funobj
, (void *) fun
);
6757 } else if (v
.isObject()) {
6758 JSObject
*obj
= &v
.toObject();
6759 Class
*clasp
= obj
->getClass();
6760 fprintf(stderr
, "<%s%s at %p>",
6762 (clasp
== &js_ObjectClass
) ? "" : " object",
6764 } else if (v
.isBoolean()) {
6766 fprintf(stderr
, "true");
6768 fprintf(stderr
, "false");
6769 } else if (v
.isMagic()) {
6770 fprintf(stderr
, "<invalid");
6772 switch (v
.whyMagic()) {
6773 case JS_ARRAY_HOLE
: fprintf(stderr
, " array hole"); break;
6774 case JS_ARGS_HOLE
: fprintf(stderr
, " args hole"); break;
6775 case JS_NATIVE_ENUMERATE
: fprintf(stderr
, " native enumeration"); break;
6776 case JS_NO_ITER_VALUE
: fprintf(stderr
, " no iter value"); break;
6777 case JS_GENERATOR_CLOSING
: fprintf(stderr
, " generator closing"); break;
6778 default: fprintf(stderr
, " ?!"); break;
6781 fprintf(stderr
, ">");
6783 fprintf(stderr
, "unexpected value");
6788 js_DumpValue(const Value
&val
)
6791 fputc('\n', stderr
);
6797 fprintf(stderr
, "jsid %p = ", (void *) JSID_BITS(id
));
6798 dumpValue(IdToValue(id
));
6799 fputc('\n', stderr
);
6803 DumpShape(const Shape
&shape
)
6806 uint8 attrs
= shape
.attributes();
6808 fprintf(stderr
, " ");
6809 if (attrs
& JSPROP_ENUMERATE
) fprintf(stderr
, "enumerate ");
6810 if (attrs
& JSPROP_READONLY
) fprintf(stderr
, "readonly ");
6811 if (attrs
& JSPROP_PERMANENT
) fprintf(stderr
, "permanent ");
6812 if (attrs
& JSPROP_GETTER
) fprintf(stderr
, "getter ");
6813 if (attrs
& JSPROP_SETTER
) fprintf(stderr
, "setter ");
6814 if (attrs
& JSPROP_SHARED
) fprintf(stderr
, "shared ");
6815 if (shape
.isAlias()) fprintf(stderr
, "alias ");
6816 if (shape
.isMethod()) fprintf(stderr
, "method(%p) ", (void *) &shape
.methodObject());
6818 if (JSID_IS_ATOM(id
))
6819 dumpString(JSID_TO_STRING(id
));
6820 else if (JSID_IS_INT(id
))
6821 fprintf(stderr
, "%d", (int) JSID_TO_INT(id
));
6823 fprintf(stderr
, "unknown jsid %p", (void *) JSID_BITS(id
));
6824 fprintf(stderr
, ": slot %d", shape
.slot
);
6825 fprintf(stderr
, "\n");
6829 js_DumpObject(JSObject
*obj
)
6831 fprintf(stderr
, "object %p\n", (void *) obj
);
6832 Class
*clasp
= obj
->getClass();
6833 fprintf(stderr
, "class %p %s\n", (void *)clasp
, clasp
->name
);
6835 fprintf(stderr
, "flags:");
6836 uint32 flags
= obj
->flags
;
6837 if (flags
& JSObject::DELEGATE
) fprintf(stderr
, " delegate");
6838 if (flags
& JSObject::SYSTEM
) fprintf(stderr
, " system");
6839 if (flags
& JSObject::NOT_EXTENSIBLE
) fprintf(stderr
, " not extensible");
6840 if (flags
& JSObject::BRANDED
) fprintf(stderr
, " branded");
6841 if (flags
& JSObject::GENERIC
) fprintf(stderr
, " generic");
6842 if (flags
& JSObject::METHOD_BARRIER
) fprintf(stderr
, " method_barrier");
6843 if (flags
& JSObject::INDEXED
) fprintf(stderr
, " indexed");
6844 if (flags
& JSObject::OWN_SHAPE
) fprintf(stderr
, " own_shape");
6845 if (flags
& JSObject::HAS_EQUALITY
) fprintf(stderr
, " has_equality");
6847 bool anyFlags
= flags
!= 0;
6848 if (obj
->isNative()) {
6849 if (obj
->inDictionaryMode()) {
6850 fprintf(stderr
, " inDictionaryMode");
6853 if (obj
->hasPropertyTable()) {
6854 fprintf(stderr
, " hasPropertyTable");
6859 fprintf(stderr
, " none");
6860 fprintf(stderr
, "\n");
6862 if (obj
->isDenseArray()) {
6863 unsigned slots
= JS_MIN(obj
->getArrayLength(), obj
->getDenseArrayCapacity());
6864 fprintf(stderr
, "elements\n");
6865 for (unsigned i
= 0; i
< slots
; i
++) {
6866 fprintf(stderr
, " %3d: ", i
);
6867 dumpValue(obj
->getDenseArrayElement(i
));
6868 fprintf(stderr
, "\n");
6874 if (obj
->isNative()) {
6875 fprintf(stderr
, "properties:\n");
6876 for (Shape::Range r
= obj
->lastProperty()->all(); !r
.empty(); r
.popFront())
6877 DumpShape(r
.front());
6879 if (!obj
->isNative())
6880 fprintf(stderr
, "not native\n");
6883 fprintf(stderr
, "proto ");
6884 dumpValue(ObjectOrNullValue(obj
->getProto()));
6885 fputc('\n', stderr
);
6887 fprintf(stderr
, "parent ");
6888 dumpValue(ObjectOrNullValue(obj
->getParent()));
6889 fputc('\n', stderr
);
6891 if (clasp
->flags
& JSCLASS_HAS_PRIVATE
)
6892 fprintf(stderr
, "private %p\n", obj
->getPrivate());
6894 fprintf(stderr
, "slots:\n");
6895 unsigned reservedEnd
= JSCLASS_RESERVED_SLOTS(clasp
);
6896 unsigned slots
= obj
->slotSpan();
6897 for (unsigned i
= 0; i
< slots
; i
++) {
6898 fprintf(stderr
, " %3d ", i
);
6899 if (i
< reservedEnd
)
6900 fprintf(stderr
, "(reserved) ");
6901 fprintf(stderr
, "= ");
6902 dumpValue(obj
->getSlot(i
));
6903 fputc('\n', stderr
);
6905 fputc('\n', stderr
);
6909 MaybeDumpObject(const char *name
, JSObject
*obj
)
6912 fprintf(stderr
, " %s: ", name
);
6913 dumpValue(ObjectValue(*obj
));
6914 fputc('\n', stderr
);
6919 MaybeDumpValue(const char *name
, const Value
&v
)
6922 fprintf(stderr
, " %s: ", name
);
6924 fputc('\n', stderr
);
6929 js_DumpStackFrame(JSContext
*cx
, JSStackFrame
*start
)
6931 /* This should only called during live debugging. */
6932 VOUCH_DOES_NOT_REQUIRE_STACK();
6935 start
= cx
->maybefp();
6936 FrameRegsIter
i(cx
);
6937 while (!i
.done() && i
.fp() != start
)
6941 fprintf(stderr
, "fp = %p not found in cx = %p\n", (void *)start
, (void *)cx
);
6945 for (; !i
.done(); ++i
) {
6946 JSStackFrame
*const fp
= i
.fp();
6948 fprintf(stderr
, "JSStackFrame at %p\n", (void *) fp
);
6949 if (fp
->isFunctionFrame()) {
6950 fprintf(stderr
, "callee fun: ");
6951 dumpValue(ObjectValue(fp
->callee()));
6953 fprintf(stderr
, "global frame, no callee");
6955 fputc('\n', stderr
);
6957 if (fp
->isScriptFrame()) {
6958 fprintf(stderr
, "file %s line %u\n",
6959 fp
->script()->filename
, (unsigned) fp
->script()->lineno
);
6962 if (jsbytecode
*pc
= i
.pc()) {
6963 if (!fp
->isScriptFrame()) {
6964 fprintf(stderr
, "*** pc && !script, skipping frame\n\n");
6967 if (fp
->hasImacropc()) {
6968 fprintf(stderr
, " pc in imacro at %p\n called from ", pc
);
6969 pc
= fp
->imacropc();
6971 fprintf(stderr
, " ");
6973 fprintf(stderr
, "pc = %p\n", pc
);
6974 fprintf(stderr
, " current op: %s\n", js_CodeName
[*pc
]);
6977 fprintf(stderr
, " slots: %p\n", (void *) fp
->slots());
6978 fprintf(stderr
, " sp: %p = slots + %u\n", (void *) sp
, (unsigned) (sp
- fp
->slots()));
6979 if (sp
- fp
->slots() < 10000) { // sanity
6980 for (Value
*p
= fp
->slots(); p
< sp
; p
++) {
6981 fprintf(stderr
, " %p: ", (void *) p
);
6983 fputc('\n', stderr
);
6986 if (fp
->isFunctionFrame() && !fp
->isEvalFrame()) {
6987 fprintf(stderr
, " actuals: %p (%u) ", (void *) fp
->actualArgs(), (unsigned) fp
->numActualArgs());
6988 fprintf(stderr
, " formals: %p (%u)\n", (void *) fp
->formalArgs(), (unsigned) fp
->numFormalArgs());
6990 MaybeDumpObject("callobj", fp
->maybeCallObj());
6991 MaybeDumpObject("argsobj", fp
->maybeArgsObj());
6992 if (!fp
->isDummyFrame()) {
6993 MaybeDumpValue("this", fp
->thisValue());
6994 fprintf(stderr
, " rval: ");
6995 dumpValue(fp
->returnValue());
6997 fprintf(stderr
, "dummy frame");
6999 fputc('\n', stderr
);
7001 fprintf(stderr
, " flags:");
7002 if (fp
->isConstructing())
7003 fprintf(stderr
, " constructing");
7004 if (fp
->hasOverriddenArgs())
7005 fprintf(stderr
, " overridden_args");
7006 if (fp
->isAssigning())
7007 fprintf(stderr
, " assigning");
7008 if (fp
->isDebuggerFrame())
7009 fprintf(stderr
, " debugger");
7010 if (fp
->isEvalFrame())
7011 fprintf(stderr
, " eval");
7012 if (fp
->isYielding())
7013 fprintf(stderr
, " yielding");
7014 if (fp
->isGeneratorFrame())
7015 fprintf(stderr
, " generator");
7016 fputc('\n', stderr
);
7018 fprintf(stderr
, " scopeChain: (JSObject *) %p\n", (void *) &fp
->scopeChain());
7020 fputc('\n', stderr
);
7026 IsSaneThisObject(JSObject
&obj
)
7028 Class
*clasp
= obj
.getClass();
7029 return clasp
!= &js_CallClass
&&
7030 clasp
!= &js_BlockClass
&&
7031 clasp
!= &js_DeclEnvClass
&&
7032 clasp
!= &js_WithClass
;