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
, JSObject
*ctor
, uintN protoSlot
)
3003 JS_ASSERT(ctor
->isFunction());
3004 JS_ASSERT(ctor
->getFunctionPrivate()->isInterpreted());
3005 jsid id
= ATOM_TO_JSID(cx
->runtime
->atomState
.classPrototypeAtom
);
3006 const Shape
*shape
= ctor
->nativeLookup(id
);
3007 JS_ASSERT(shape
->slot
== protoSlot
);
3008 JS_ASSERT(!shape
->configurable());
3009 JS_ASSERT(!shape
->isMethod());
3012 JSObject
*parent
= ctor
->getParent();
3014 const Value
&protov
= ctor
->getSlotRef(protoSlot
);
3015 if (protov
.isObject()) {
3016 proto
= &protov
.toObject();
3019 * GetInterpretedFunctionPrototype found that ctor.prototype is
3020 * primitive. Use Object.prototype for proto, per ES5 13.2.2 step 7.
3022 if (!js_GetClassPrototype(cx
, parent
, JSProto_Object
, &proto
))
3026 gc::FinalizeKind kind
= NewObjectGCKind(cx
, &js_ObjectClass
);
3027 return NewNativeClassInstance(cx
, &js_ObjectClass
, proto
, parent
, kind
);
3029 JS_DEFINE_CALLINFO_3(extern, CONSTRUCTOR_RETRY
, js_CreateThisFromTrace
, CONTEXT
, OBJECT
, UINTN
, 0,
3030 nanojit::ACCSET_STORE_ANY
)
3032 #else /* !JS_TRACER */
3034 # define js_Object_trcinfo NULL
3036 #endif /* !JS_TRACER */
3039 * Given pc pointing after a property accessing bytecode, return true if the
3040 * access is "object-detecting" in the sense used by web scripts, e.g., when
3041 * checking whether document.all is defined.
3043 JS_REQUIRES_STACK JSBool
3044 Detecting(JSContext
*cx
, jsbytecode
*pc
)
3051 script
= cx
->fp()->script();
3052 endpc
= script
->code
+ script
->length
;
3053 for (;; pc
+= js_CodeSpec
[op
].length
) {
3054 JS_ASSERT_IF(!cx
->fp()->hasImacropc(), script
->code
<= pc
&& pc
< endpc
);
3056 /* General case: a branch or equality op follows the access. */
3057 op
= js_GetOpcode(cx
, script
, pc
);
3058 if (js_CodeSpec
[op
].format
& JOF_DETECTING
)
3064 * Special case #1: handle (document.all == null). Don't sweat
3065 * about JS1.2's revision of the equality operators here.
3068 op
= js_GetOpcode(cx
, script
, pc
);
3069 return *pc
== JSOP_EQ
|| *pc
== JSOP_NE
;
3076 * Special case #2: handle (document.all == undefined). Don't
3077 * worry about someone redefining undefined, which was added by
3078 * Edition 3, so is read/write for backward compatibility.
3080 GET_ATOM_FROM_BYTECODE(script
, pc
, 0, atom
);
3081 if (atom
== cx
->runtime
->atomState
.typeAtoms
[JSTYPE_VOID
] &&
3082 (pc
+= js_CodeSpec
[op
].length
) < endpc
) {
3083 op
= js_GetOpcode(cx
, script
, pc
);
3084 return op
== JSOP_EQ
|| op
== JSOP_NE
||
3085 op
== JSOP_STRICTEQ
|| op
== JSOP_STRICTNE
;
3091 * At this point, anything but an extended atom index prefix means
3092 * we're not detecting.
3094 if (!(js_CodeSpec
[op
].format
& JOF_INDEXBASE
))
3102 * Infer lookup flags from the currently executing bytecode. This does
3103 * not attempt to infer JSRESOLVE_WITH, because the current bytecode
3104 * does not indicate whether we are in a with statement. Return defaultFlags
3105 * if a currently executing bytecode cannot be determined.
3108 js_InferFlags(JSContext
*cx
, uintN defaultFlags
)
3111 if (JS_ON_TRACE(cx
))
3112 return JS_TRACE_MONITOR_ON_TRACE(cx
)->bailExit
->lookupFlags
;
3115 JS_ASSERT_NOT_ON_TRACE(cx
);
3118 const JSCodeSpec
*cs
;
3122 JSStackFrame
*const fp
= js_GetTopStackFrame(cx
);
3123 if (!fp
|| !(pc
= cx
->regs
->pc
))
3124 return defaultFlags
;
3125 cs
= &js_CodeSpec
[js_GetOpcode(cx
, fp
->script(), pc
)];
3126 format
= cs
->format
;
3127 if (JOF_MODE(format
) != JOF_NAME
)
3128 flags
|= JSRESOLVE_QUALIFIED
;
3129 if ((format
& (JOF_SET
| JOF_FOR
)) || fp
->isAssigning()) {
3130 flags
|= JSRESOLVE_ASSIGNING
;
3131 } else if (cs
->length
>= 0) {
3133 JSScript
*script
= cx
->fp()->script();
3134 if (pc
< script
->code
+ script
->length
&& Detecting(cx
, pc
))
3135 flags
|= JSRESOLVE_DETECTING
;
3137 if (format
& JOF_DECLARING
)
3138 flags
|= JSRESOLVE_DECLARING
;
3143 * ObjectOps and Class for with-statement stack objects.
3146 with_LookupProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, JSObject
**objp
,
3149 /* Fixes bug 463997 */
3150 uintN flags
= cx
->resolveFlags
;
3151 if (flags
== JSRESOLVE_INFER
)
3152 flags
= js_InferFlags(cx
, flags
);
3153 flags
|= JSRESOLVE_WITH
;
3154 JSAutoResolveFlags
rf(cx
, flags
);
3155 return obj
->getProto()->lookupProperty(cx
, id
, objp
, propp
);
3159 with_GetProperty(JSContext
*cx
, JSObject
*obj
, JSObject
*receiver
, jsid id
, Value
*vp
)
3161 return obj
->getProto()->getProperty(cx
, id
, vp
);
3165 with_SetProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
, JSBool strict
)
3167 return obj
->getProto()->setProperty(cx
, id
, vp
, strict
);
3171 with_GetAttributes(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN
*attrsp
)
3173 return obj
->getProto()->getAttributes(cx
, id
, attrsp
);
3177 with_SetAttributes(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN
*attrsp
)
3179 return obj
->getProto()->setAttributes(cx
, id
, attrsp
);
3183 with_DeleteProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*rval
, JSBool strict
)
3185 return obj
->getProto()->deleteProperty(cx
, id
, rval
, strict
);
3189 with_Enumerate(JSContext
*cx
, JSObject
*obj
, JSIterateOp enum_op
,
3190 Value
*statep
, jsid
*idp
)
3192 return obj
->getProto()->enumerate(cx
, enum_op
, statep
, idp
);
3196 with_TypeOf(JSContext
*cx
, JSObject
*obj
)
3198 return JSTYPE_OBJECT
;
3202 with_ThisObject(JSContext
*cx
, JSObject
*obj
)
3204 return obj
->getWithThis();
3207 Class js_WithClass
= {
3209 JSCLASS_HAS_PRIVATE
| JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS
,
3210 PropertyStub
, /* addProperty */
3211 PropertyStub
, /* delProperty */
3212 PropertyStub
, /* getProperty */
3213 StrictPropertyStub
, /* setProperty */
3217 NULL
, /* finalize */
3218 NULL
, /* reserved */
3219 NULL
, /* checkAccess */
3221 NULL
, /* construct */
3222 NULL
, /* xdrObject */
3223 NULL
, /* hasInstance */
3227 with_LookupProperty
,
3228 NULL
, /* defineProperty */
3233 with_DeleteProperty
,
3243 JS_REQUIRES_STACK JSObject
*
3244 js_NewWithObject(JSContext
*cx
, JSObject
*proto
, JSObject
*parent
, jsint depth
)
3248 obj
= js_NewGCObject(cx
, FINALIZE_OBJECT2
);
3252 JSStackFrame
*priv
= js_FloatingFrameIfGenerator(cx
, cx
->fp());
3254 obj
->init(cx
, &js_WithClass
, proto
, parent
, priv
, false);
3255 obj
->setMap(cx
->compartment
->emptyWithShape
);
3256 OBJ_SET_BLOCK_DEPTH(cx
, obj
, depth
);
3258 AutoObjectRooter
tvr(cx
, obj
);
3259 JSObject
*thisp
= proto
->thisObject(cx
);
3263 assertSameCompartment(cx
, obj
, thisp
);
3265 obj
->setWithThis(thisp
);
3270 js_NewBlockObject(JSContext
*cx
)
3273 * Null obj's proto slot so that Object.prototype.* does not pollute block
3274 * scopes and to give the block object its own scope.
3276 JSObject
*blockObj
= js_NewGCObject(cx
, FINALIZE_OBJECT2
);
3280 blockObj
->init(cx
, &js_BlockClass
, NULL
, NULL
, NULL
, false);
3281 blockObj
->setMap(cx
->compartment
->emptyBlockShape
);
3286 js_CloneBlockObject(JSContext
*cx
, JSObject
*proto
, JSStackFrame
*fp
)
3288 JS_ASSERT(proto
->isStaticBlock());
3290 size_t count
= OBJ_BLOCK_COUNT(cx
, proto
);
3291 gc::FinalizeKind kind
= gc::GetGCObjectKind(count
+ 1);
3293 JSObject
*clone
= js_NewGCObject(cx
, kind
);
3297 JSStackFrame
*priv
= js_FloatingFrameIfGenerator(cx
, fp
);
3299 /* The caller sets parent on its own. */
3300 clone
->init(cx
, &js_BlockClass
, proto
, NULL
, priv
, false);
3302 clone
->setMap(proto
->map
);
3303 if (!clone
->ensureInstanceReservedSlots(cx
, count
+ 1))
3306 clone
->setSlot(JSSLOT_BLOCK_DEPTH
, proto
->getSlot(JSSLOT_BLOCK_DEPTH
));
3308 JS_ASSERT(clone
->isClonedBlock());
3312 JS_REQUIRES_STACK JSBool
3313 js_PutBlockObject(JSContext
*cx
, JSBool normalUnwind
)
3315 JSStackFrame
*const fp
= cx
->fp();
3316 JSObject
*obj
= &fp
->scopeChain();
3317 JS_ASSERT(obj
->isClonedBlock());
3318 JS_ASSERT(obj
->getPrivate() == js_FloatingFrameIfGenerator(cx
, cx
->fp()));
3320 /* Block objects should have all reserved slots allocated early. */
3321 uintN count
= OBJ_BLOCK_COUNT(cx
, obj
);
3322 JS_ASSERT(obj
->numSlots() >= JSSLOT_BLOCK_DEPTH
+ 1 + count
);
3324 /* The block and its locals must be on the current stack for GC safety. */
3325 uintN depth
= OBJ_BLOCK_DEPTH(cx
, obj
);
3326 JS_ASSERT(depth
<= size_t(cx
->regs
->sp
- fp
->base()));
3327 JS_ASSERT(count
<= size_t(cx
->regs
->sp
- fp
->base() - depth
));
3329 /* See comments in CheckDestructuring from jsparse.cpp. */
3330 JS_ASSERT(count
>= 1);
3333 uintN slot
= JSSLOT_BLOCK_FIRST_FREE_SLOT
;
3334 depth
+= fp
->numFixed();
3335 memcpy(obj
->getSlots() + slot
, fp
->slots() + depth
, count
* sizeof(Value
));
3338 /* We must clear the private slot even with errors. */
3339 obj
->setPrivate(NULL
);
3340 fp
->setScopeChainNoCallObj(*obj
->getParent());
3341 return normalUnwind
;
3345 block_getProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
3348 * Block objects are never exposed to script, and the engine handles them
3349 * with care. So unlike other getters, this one can assert (rather than
3350 * check) certain invariants about obj.
3352 JS_ASSERT(obj
->isClonedBlock());
3353 uintN index
= (uintN
) JSID_TO_INT(id
);
3354 JS_ASSERT(index
< OBJ_BLOCK_COUNT(cx
, obj
));
3356 JSStackFrame
*fp
= (JSStackFrame
*) obj
->getPrivate();
3358 fp
= js_LiveFrameIfGenerator(fp
);
3359 index
+= fp
->numFixed() + OBJ_BLOCK_DEPTH(cx
, obj
);
3360 JS_ASSERT(index
< fp
->numSlots());
3361 *vp
= fp
->slots()[index
];
3365 /* Values are in slots immediately following the class-reserved ones. */
3366 JS_ASSERT(obj
->getSlot(JSSLOT_FREE(&js_BlockClass
) + index
) == *vp
);
3371 block_setProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, JSBool strict
, Value
*vp
)
3373 JS_ASSERT(obj
->isClonedBlock());
3374 uintN index
= (uintN
) JSID_TO_INT(id
);
3375 JS_ASSERT(index
< OBJ_BLOCK_COUNT(cx
, obj
));
3377 JSStackFrame
*fp
= (JSStackFrame
*) obj
->getPrivate();
3379 fp
= js_LiveFrameIfGenerator(fp
);
3380 index
+= fp
->numFixed() + OBJ_BLOCK_DEPTH(cx
, obj
);
3381 JS_ASSERT(index
< fp
->numSlots());
3382 fp
->slots()[index
] = *vp
;
3387 * The value in *vp will be written back to the slot in obj that was
3388 * allocated when this let binding was defined.
3394 JSObject::defineBlockVariable(JSContext
*cx
, jsid id
, intN index
)
3396 JS_ASSERT(isStaticBlock());
3398 /* Use JSPROP_ENUMERATE to aid the disassembler. */
3399 uint32 slot
= JSSLOT_FREE(&js_BlockClass
) + index
;
3400 const Shape
*shape
= addProperty(cx
, id
,
3401 block_getProperty
, block_setProperty
,
3402 slot
, JSPROP_ENUMERATE
| JSPROP_PERMANENT
,
3403 Shape::HAS_SHORTID
, index
);
3406 if (slot
>= numSlots() && !growSlots(cx
, slot
+ 1))
3412 GetObjectSize(JSObject
*obj
)
3414 return (obj
->isFunction() && !obj
->getPrivate())
3415 ? sizeof(JSFunction
)
3416 : sizeof(JSObject
) + sizeof(js::Value
) * obj
->numFixedSlots();
3420 JSObject::copyPropertiesFrom(JSContext
*cx
, JSObject
*obj
)
3422 // If we're not native, then we cannot copy properties.
3423 JS_ASSERT(isNative() == obj
->isNative());
3427 AutoShapeVector
shapes(cx
);
3428 for (Shape::Range
r(obj
->lastProperty()); !r
.empty(); r
.popFront()) {
3429 if (!shapes
.append(&r
.front()))
3433 size_t n
= shapes
.length();
3435 const Shape
*shape
= shapes
[--n
];
3436 uintN attrs
= shape
->attributes();
3437 PropertyOp getter
= shape
->getter();
3438 if ((attrs
& JSPROP_GETTER
) && !cx
->compartment
->wrap(cx
, &getter
))
3440 StrictPropertyOp setter
= shape
->setter();
3441 if ((attrs
& JSPROP_SETTER
) && !cx
->compartment
->wrap(cx
, &setter
))
3443 Value v
= shape
->hasSlot() ? obj
->getSlot(shape
->slot
) : UndefinedValue();
3444 if (!cx
->compartment
->wrap(cx
, &v
))
3446 if (!defineProperty(cx
, shape
->id
, v
, getter
, setter
, attrs
))
3453 CopySlots(JSContext
*cx
, JSObject
*from
, JSObject
*to
)
3455 JS_ASSERT(!from
->isNative() && !to
->isNative());
3456 size_t nslots
= from
->numSlots();
3457 if (to
->ensureSlots(cx
, nslots
))
3461 if (to
->isWrapper() &&
3462 (JSWrapper::wrapperHandler(to
)->flags() & JSWrapper::CROSS_COMPARTMENT
)) {
3463 to
->slots
[0] = from
->slots
[0];
3464 to
->slots
[1] = from
->slots
[1];
3468 for (; n
< nslots
; ++n
) {
3469 Value v
= from
->slots
[n
];
3470 if (!cx
->compartment
->wrap(cx
, &v
))
3478 JSObject::clone(JSContext
*cx
, JSObject
*proto
, JSObject
*parent
)
3481 * We can only clone native objects and proxies. Dense arrays are slowified if
3482 * we try to clone them.
3485 if (isDenseArray()) {
3486 if (!makeDenseArraySlow(cx
))
3488 } else if (!isProxy()) {
3489 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
3490 JSMSG_CANT_CLONE_OBJECT
);
3494 JSObject
*clone
= NewObject
<WithProto::Given
>(cx
, getClass(),
3496 gc::FinalizeKind(finalizeKind()));
3500 if (clone
->isFunction() && (compartment() != clone
->compartment())) {
3501 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
3502 JSMSG_CANT_CLONE_OBJECT
);
3506 if (getClass()->flags
& JSCLASS_HAS_PRIVATE
)
3507 clone
->setPrivate(getPrivate());
3509 JS_ASSERT(isProxy());
3510 if (!CopySlots(cx
, this, clone
))
3517 TradeGuts(JSObject
*a
, JSObject
*b
)
3519 JS_ASSERT(a
->compartment() == b
->compartment());
3520 JS_ASSERT(a
->isFunction() == b
->isFunction());
3523 * Regexp guts are more complicated -- we would need to migrate the
3524 * refcounted JIT code blob for them across compartments instead of just
3527 JS_ASSERT(!a
->isRegExp() && !b
->isRegExp());
3529 bool aInline
= !a
->hasSlotsArray();
3530 bool bInline
= !b
->hasSlotsArray();
3532 /* Trade the guts of the objects. */
3533 const size_t size
= GetObjectSize(a
);
3534 if (size
== GetObjectSize(b
)) {
3536 * If the objects are the same size, then we make no assumptions about
3537 * whether they have dynamically allocated slots and instead just copy
3538 * them over wholesale.
3540 char tmp
[tl::Max
<sizeof(JSFunction
), sizeof(JSObject_Slots16
)>::result
];
3541 JS_ASSERT(size
<= sizeof(tmp
));
3543 memcpy(tmp
, a
, size
);
3545 memcpy(b
, tmp
, size
);
3547 /* Fixup pointers for inline slots on the objects. */
3549 b
->slots
= b
->fixedSlots();
3551 a
->slots
= a
->fixedSlots();
3554 * If the objects are of differing sizes, then we only copy over the
3555 * JSObject portion (things like class, etc.) and leave it to
3556 * JSObject::clone to copy over the dynamic slots for us.
3558 if (a
->isFunction()) {
3560 memcpy(&tmp
, a
, sizeof tmp
);
3561 memcpy(a
, b
, sizeof tmp
);
3562 memcpy(b
, &tmp
, sizeof tmp
);
3565 memcpy(&tmp
, a
, sizeof tmp
);
3566 memcpy(a
, b
, sizeof tmp
);
3567 memcpy(b
, &tmp
, sizeof tmp
);
3570 JS_ASSERT(!aInline
);
3571 JS_ASSERT(!bInline
);
3576 * Use this method with extreme caution. It trades the guts of two objects and updates
3577 * scope ownership. This operation is not thread-safe, just as fast array to slow array
3578 * transitions are inherently not thread-safe. Don't perform a swap operation on objects
3579 * shared across threads or, or bad things will happen. You have been warned.
3582 JSObject::swap(JSContext
*cx
, JSObject
*other
)
3585 * If we are swapping objects with a different number of builtin slots, force
3586 * both to not use their inline slots.
3588 if (GetObjectSize(this) != GetObjectSize(other
)) {
3589 if (!hasSlotsArray()) {
3590 if (!allocSlots(cx
, numSlots()))
3593 if (!other
->hasSlotsArray()) {
3594 if (!other
->allocSlots(cx
, other
->numSlots()))
3599 if (this->compartment() == other
->compartment()) {
3600 TradeGuts(this, other
);
3604 JSObject
*thisClone
;
3605 JSObject
*otherClone
;
3607 AutoCompartment
ac(cx
, other
);
3610 thisClone
= this->clone(cx
, other
->getProto(), other
->getParent());
3611 if (!thisClone
|| !thisClone
->copyPropertiesFrom(cx
, this))
3615 AutoCompartment
ac(cx
, this);
3618 otherClone
= other
->clone(cx
, other
->getProto(), other
->getParent());
3619 if (!otherClone
|| !otherClone
->copyPropertiesFrom(cx
, other
))
3622 TradeGuts(this, otherClone
);
3623 TradeGuts(other
, thisClone
);
3630 #define NO_PARENT_INDEX ((uint32)-1)
3633 FindObjectIndex(JSObjectArray
*array
, JSObject
*obj
)
3641 if (array
->vector
[--i
] == obj
)
3646 return NO_PARENT_INDEX
;
3650 js_XDRBlockObject(JSXDRState
*xdr
, JSObject
**objp
)
3654 JSObject
*obj
, *parent
;
3656 uint32 depthAndCount
;
3661 obj
= NULL
; /* quell GCC overwarning */
3664 if (xdr
->mode
== JSXDR_ENCODE
) {
3666 parent
= obj
->getParent();
3667 parentId
= JSScript::isValidOffset(xdr
->script
->objectsOffset
)
3668 ? FindObjectIndex(xdr
->script
->objects(), parent
)
3670 depth
= (uint16
)OBJ_BLOCK_DEPTH(cx
, obj
);
3671 count
= (uint16
)OBJ_BLOCK_COUNT(cx
, obj
);
3672 depthAndCount
= (uint32
)(depth
<< 16) | count
;
3674 #ifdef __GNUC__ /* suppress bogus gcc warnings */
3678 /* First, XDR the parent atomid. */
3679 if (!JS_XDRUint32(xdr
, &parentId
))
3682 if (xdr
->mode
== JSXDR_DECODE
) {
3683 obj
= js_NewBlockObject(cx
);
3689 * If there's a parent id, then get the parent out of our script's
3690 * object array. We know that we XDR block object in outer-to-inner
3691 * order, which means that getting the parent now will work.
3693 if (parentId
== NO_PARENT_INDEX
)
3696 parent
= xdr
->script
->getObject(parentId
);
3697 obj
->setParent(parent
);
3700 AutoObjectRooter
tvr(cx
, obj
);
3702 if (!JS_XDRUint32(xdr
, &depthAndCount
))
3705 if (xdr
->mode
== JSXDR_DECODE
) {
3706 depth
= (uint16
)(depthAndCount
>> 16);
3707 count
= (uint16
)depthAndCount
;
3708 obj
->setSlot(JSSLOT_BLOCK_DEPTH
, Value(Int32Value(depth
)));
3711 * XDR the block object's properties. We know that there are 'count'
3712 * properties to XDR, stored as id/shortid pairs.
3714 for (uintN i
= 0; i
< count
; i
++) {
3718 /* XDR the real id, then the shortid. */
3719 if (!js_XDRAtom(xdr
, &atom
) || !JS_XDRUint16(xdr
, &shortid
))
3722 if (!obj
->defineBlockVariable(cx
, ATOM_TO_JSID(atom
), shortid
))
3726 AutoShapeVector
shapes(cx
);
3727 shapes
.growBy(count
);
3729 for (Shape::Range
r(obj
->lastProperty()); !r
.empty(); r
.popFront()) {
3731 shapes
[shape
->shortid
] = shape
;
3735 * XDR the block object's properties. We know that there are 'count'
3736 * properties to XDR, stored as id/shortid pairs.
3738 for (uintN i
= 0; i
< count
; i
++) {
3740 JS_ASSERT(shape
->getter() == block_getProperty
);
3742 jsid propid
= shape
->id
;
3743 JS_ASSERT(JSID_IS_ATOM(propid
));
3744 JSAtom
*atom
= JSID_TO_ATOM(propid
);
3746 uint16 shortid
= uint16(shape
->shortid
);
3747 JS_ASSERT(shortid
== i
);
3749 /* XDR the real id, then the shortid. */
3750 if (!js_XDRAtom(xdr
, &atom
) || !JS_XDRUint16(xdr
, &shortid
))
3759 Class js_BlockClass
= {
3761 JSCLASS_HAS_PRIVATE
| JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_IS_ANONYMOUS
,
3762 PropertyStub
, /* addProperty */
3763 PropertyStub
, /* delProperty */
3764 PropertyStub
, /* getProperty */
3765 StrictPropertyStub
, /* setProperty */
3772 js_InitObjectClass(JSContext
*cx
, JSObject
*obj
)
3774 JSObject
*proto
= js_InitClass(cx
, obj
, NULL
, &js_ObjectClass
, js_Object
, 1,
3775 object_props
, object_methods
, NULL
, object_static_methods
);
3779 /* ECMA (15.1.2.1) says 'eval' is a property of the global object. */
3780 jsid id
= ATOM_TO_JSID(cx
->runtime
->atomState
.evalAtom
);
3781 if (!js_DefineFunction(cx
, obj
, id
, eval
, 1, JSFUN_STUB_GSOPS
))
3788 DefineStandardSlot(JSContext
*cx
, JSObject
*obj
, JSProtoKey key
, JSAtom
*atom
,
3789 const Value
&v
, uint32 attrs
, bool &named
)
3791 jsid id
= ATOM_TO_JSID(atom
);
3793 if (key
!= JSProto_Null
) {
3795 * Initializing an actual standard class on a global object. If the
3796 * property is not yet present, force it into a new one bound to a
3797 * reserved slot. Otherwise, go through the normal property path.
3799 JS_ASSERT(obj
->isGlobal());
3800 JS_ASSERT(obj
->isNative());
3802 if (!obj
->ensureClassReservedSlots(cx
))
3805 const Shape
*shape
= obj
->nativeLookup(id
);
3807 uint32 slot
= 2 * JSProto_LIMIT
+ key
;
3808 if (!js_SetReservedSlot(cx
, obj
, slot
, v
))
3810 if (!obj
->addProperty(cx
, id
, PropertyStub
, StrictPropertyStub
, slot
, attrs
, 0, 0))
3818 named
= obj
->defineProperty(cx
, id
, v
, PropertyStub
, StrictPropertyStub
, attrs
);
3825 DefineConstructorAndPrototype(JSContext
*cx
, JSObject
*obj
, JSProtoKey key
, JSAtom
*atom
,
3826 JSObject
*protoProto
, Class
*clasp
,
3827 Native constructor
, uintN nargs
,
3828 JSPropertySpec
*ps
, JSFunctionSpec
*fs
,
3829 JSPropertySpec
*static_ps
, JSFunctionSpec
*static_fs
)
3832 * Create a prototype object for this class.
3834 * FIXME: lazy standard (built-in) class initialization and even older
3835 * eager boostrapping code rely on all of these properties:
3837 * 1. NewObject attempting to compute a default prototype object when
3838 * passed null for proto; and
3840 * 2. NewObject tolerating no default prototype (null proto slot value)
3841 * due to this js_InitClass call coming from js_InitFunctionClass on an
3842 * otherwise-uninitialized global.
3844 * 3. NewObject allocating a JSFunction-sized GC-thing when clasp is
3845 * &js_FunctionClass, not a JSObject-sized (smaller) GC-thing.
3847 * The JS_NewObjectForGivenProto and JS_NewObject APIs also allow clasp to
3848 * be &js_FunctionClass (we could break compatibility easily). But fixing
3849 * (3) is not enough without addressing the bootstrapping dependency on (1)
3852 JSObject
*proto
= NewObject
<WithProto::Class
>(cx
, clasp
, protoProto
, obj
);
3856 proto
->syncSpecialEquality();
3858 /* After this point, control must exit via label bad or out. */
3859 AutoObjectRooter
tvr(cx
, proto
);
3865 * Lacking a constructor, name the prototype (e.g., Math) unless this
3866 * class (a) is anonymous, i.e. for internal use only; (b) the class
3867 * of obj (the global object) is has a reserved slot indexed by key;
3868 * and (c) key is not the null key.
3870 if (!(clasp
->flags
& JSCLASS_IS_ANONYMOUS
) || !obj
->isGlobal() || key
== JSProto_Null
) {
3871 uint32 attrs
= (clasp
->flags
& JSCLASS_IS_ANONYMOUS
)
3872 ? JSPROP_READONLY
| JSPROP_PERMANENT
3874 if (!DefineStandardSlot(cx
, obj
, key
, atom
, ObjectValue(*proto
), attrs
, named
))
3880 JSFunction
*fun
= js_NewFunction(cx
, NULL
, constructor
, nargs
, JSFUN_CONSTRUCTOR
, obj
, atom
);
3884 AutoValueRooter
tvr2(cx
, ObjectValue(*fun
));
3885 if (!DefineStandardSlot(cx
, obj
, key
, atom
, tvr2
.value(), 0, named
))
3889 * Remember the class this function is a constructor for so that
3890 * we know to create an object of this class when we call the
3893 FUN_CLASP(fun
) = clasp
;
3896 * Optionally construct the prototype object, before the class has
3897 * been fully initialized. Allow the ctor to replace proto with a
3898 * different object, as is done for operator new -- and as at least
3899 * XML support requires.
3901 ctor
= FUN_OBJECT(fun
);
3902 if (clasp
->flags
& JSCLASS_CONSTRUCT_PROTOTYPE
) {
3904 if (!InvokeConstructorWithGivenThis(cx
, proto
, ObjectOrNullValue(ctor
),
3908 if (rval
.isObject() && &rval
.toObject() != proto
)
3909 proto
= &rval
.toObject();
3912 /* Connect constructor and prototype by named properties. */
3913 if (!js_SetClassPrototype(cx
, ctor
, proto
,
3914 JSPROP_READONLY
| JSPROP_PERMANENT
)) {
3918 /* Bootstrap Function.prototype (see also JS_InitStandardClasses). */
3919 if (ctor
->getClass() == clasp
)
3920 ctor
->setProto(proto
);
3923 /* Add properties and methods to the prototype and the constructor. */
3924 if ((ps
&& !JS_DefineProperties(cx
, proto
, ps
)) ||
3925 (fs
&& !JS_DefineFunctions(cx
, proto
, fs
)) ||
3926 (static_ps
&& !JS_DefineProperties(cx
, ctor
, static_ps
)) ||
3927 (static_fs
&& !JS_DefineFunctions(cx
, ctor
, static_fs
))) {
3932 * Pre-brand the prototype and constructor if they have built-in methods.
3933 * This avoids extra shape guard branch exits in the tracejitted code.
3937 if (ctor
!= proto
&& static_fs
)
3941 * Make sure proto's emptyShape is available to be shared by objects of
3942 * this class. JSObject::emptyShape is a one-slot cache. If we omit this,
3943 * some other class could snap it up. (The risk is particularly great for
3944 * Object.prototype.)
3946 * All callers of JSObject::initSharingEmptyShape depend on this.
3948 * FIXME: bug 592296 -- js_InitArrayClass should pass &js_SlowArrayClass
3949 * and make the Array.prototype slow from the start.
3951 JS_ASSERT_IF(proto
->clasp
!= clasp
,
3952 clasp
== &js_ArrayClass
&& proto
->clasp
== &js_SlowArrayClass
);
3953 if (!proto
->getEmptyShape(cx
, proto
->clasp
, FINALIZE_OBJECT0
))
3956 if (clasp
->flags
& (JSCLASS_FREEZE_PROTO
|JSCLASS_FREEZE_CTOR
)) {
3957 JS_ASSERT_IF(ctor
== proto
, !(clasp
->flags
& JSCLASS_FREEZE_CTOR
));
3958 if (proto
&& (clasp
->flags
& JSCLASS_FREEZE_PROTO
) && !proto
->freeze(cx
))
3960 if (ctor
&& (clasp
->flags
& JSCLASS_FREEZE_CTOR
) && !ctor
->freeze(cx
))
3964 /* If this is a standard class, cache its prototype. */
3965 if (key
!= JSProto_Null
&& !js_SetClassObject(cx
, obj
, key
, ctor
, proto
))
3973 obj
->deleteProperty(cx
, ATOM_TO_JSID(atom
), &rval
, false);
3981 js_InitClass(JSContext
*cx
, JSObject
*obj
, JSObject
*protoProto
,
3982 Class
*clasp
, Native constructor
, uintN nargs
,
3983 JSPropertySpec
*ps
, JSFunctionSpec
*fs
,
3984 JSPropertySpec
*static_ps
, JSFunctionSpec
*static_fs
)
3986 JSAtom
*atom
= js_Atomize(cx
, clasp
->name
, strlen(clasp
->name
), 0);
3991 * All instances of the class will inherit properties from the prototype
3992 * object we are about to create (in DefineConstructorAndPrototype), which
3993 * in turn will inherit from protoProto.
3995 * When initializing a standard class (other than Object), if protoProto is
3996 * null, default to the Object prototype object. The engine's internal uses
3997 * of js_InitClass depend on this nicety. Note that in
3998 * js_InitFunctionAndObjectClasses, we specially hack the resolving table
3999 * and then depend on js_GetClassPrototype here leaving protoProto NULL and
4002 JSProtoKey key
= JSCLASS_CACHED_PROTO_KEY(clasp
);
4003 if (key
!= JSProto_Null
&&
4005 !js_GetClassPrototype(cx
, obj
, JSProto_Object
, &protoProto
)) {
4009 return DefineConstructorAndPrototype(cx
, obj
, key
, atom
, protoProto
, clasp
, constructor
, nargs
,
4010 ps
, fs
, static_ps
, static_fs
);
4014 JSObject::allocSlots(JSContext
*cx
, size_t newcap
)
4016 uint32 oldcap
= numSlots();
4018 JS_ASSERT(newcap
>= oldcap
&& !hasSlotsArray());
4020 if (newcap
> NSLOTS_LIMIT
) {
4021 if (!JS_ON_TRACE(cx
))
4022 js_ReportAllocationOverflow(cx
);
4026 Value
*tmpslots
= (Value
*) cx
->malloc(newcap
* sizeof(Value
));
4028 return false; /* Leave slots at inline buffer. */
4032 /* Copy over anything from the inline buffer. */
4033 memcpy(slots
, fixedSlots(), oldcap
* sizeof(Value
));
4034 ClearValueRange(slots
+ oldcap
, newcap
- oldcap
, isDenseArray());
4039 JSObject::growSlots(JSContext
*cx
, size_t newcap
)
4042 * When an object with CAPACITY_DOUBLING_MAX or fewer slots needs to
4043 * grow, double its capacity, to add N elements in amortized O(N) time.
4045 * Above this limit, grow by 12.5% each time. Speed is still amortized
4046 * O(N), with a higher constant factor, and we waste less space.
4048 static const size_t CAPACITY_DOUBLING_MAX
= 1024 * 1024;
4049 static const size_t CAPACITY_CHUNK
= CAPACITY_DOUBLING_MAX
/ sizeof(Value
);
4051 uint32 oldcap
= numSlots();
4052 JS_ASSERT(oldcap
< newcap
);
4054 uint32 nextsize
= (oldcap
<= CAPACITY_DOUBLING_MAX
)
4056 : oldcap
+ (oldcap
>> 3);
4058 uint32 actualCapacity
= JS_MAX(newcap
, nextsize
);
4059 if (actualCapacity
>= CAPACITY_CHUNK
)
4060 actualCapacity
= JS_ROUNDUP(actualCapacity
, CAPACITY_CHUNK
);
4061 else if (actualCapacity
< SLOT_CAPACITY_MIN
)
4062 actualCapacity
= SLOT_CAPACITY_MIN
;
4064 /* Don't let nslots get close to wrapping around uint32. */
4065 if (actualCapacity
>= NSLOTS_LIMIT
) {
4066 JS_ReportOutOfMemory(cx
);
4070 /* If nothing was allocated yet, treat it as initial allocation. */
4071 if (!hasSlotsArray())
4072 return allocSlots(cx
, actualCapacity
);
4074 Value
*tmpslots
= (Value
*) cx
->realloc(slots
, oldcap
* sizeof(Value
), actualCapacity
* sizeof(Value
));
4076 return false; /* Leave dslots as its old size. */
4078 capacity
= actualCapacity
;
4080 /* Initialize the additional slots we added. */
4081 ClearValueRange(slots
+ oldcap
, actualCapacity
- oldcap
, isDenseArray());
4086 JSObject::shrinkSlots(JSContext
*cx
, size_t newcap
)
4088 uint32 oldcap
= numSlots();
4089 JS_ASSERT(newcap
<= oldcap
);
4090 JS_ASSERT(newcap
>= slotSpan());
4092 if (oldcap
<= SLOT_CAPACITY_MIN
|| !hasSlotsArray()) {
4093 /* We won't shrink the slots any more. Clear excess holes. */
4094 ClearValueRange(slots
+ newcap
, oldcap
- newcap
, isDenseArray());
4098 uint32 fill
= newcap
;
4099 if (newcap
< SLOT_CAPACITY_MIN
)
4100 newcap
= SLOT_CAPACITY_MIN
;
4101 if (newcap
< numFixedSlots())
4102 newcap
= numFixedSlots();
4104 Value
*tmpslots
= (Value
*) cx
->realloc(slots
, newcap
* sizeof(Value
));
4106 return; /* Leave slots at its old size. */
4110 if (fill
< newcap
) {
4111 /* Clear any excess holes if we tried to shrink below SLOT_CAPACITY_MIN. */
4112 ClearValueRange(slots
+ fill
, newcap
- fill
, isDenseArray());
4117 JSObject::ensureInstanceReservedSlots(JSContext
*cx
, size_t nreserved
)
4119 JS_ASSERT_IF(isNative(),
4120 isBlock() || isCall() || (isFunction() && isBoundFunction()));
4122 uintN nslots
= JSSLOT_FREE(clasp
) + nreserved
;
4123 return nslots
<= numSlots() || allocSlots(cx
, nslots
);
4127 js_InitNullClass(JSContext
*cx
, JSObject
*obj
)
4133 #define JS_PROTO(name,code,init) extern JSObject *init(JSContext *, JSObject *);
4134 #include "jsproto.tbl"
4137 static JSObjectOp lazy_prototype_init
[JSProto_LIMIT
] = {
4138 #define JS_PROTO(name,code,init) init,
4139 #include "jsproto.tbl"
4146 SetProto(JSContext
*cx
, JSObject
*obj
, JSObject
*proto
, bool checkForCycles
)
4148 JS_ASSERT_IF(!checkForCycles
, obj
!= proto
);
4149 JS_ASSERT(obj
->isExtensible());
4151 if (obj
->isNative()) {
4152 if (!obj
->ensureClassReservedSlots(cx
))
4157 * Regenerate property cache shape ids for all of the scopes along the
4158 * old prototype chain to invalidate their property cache entries, in
4159 * case any entries were filled by looking up through obj.
4161 JSObject
*oldproto
= obj
;
4162 while (oldproto
&& oldproto
->isNative()) {
4163 oldproto
->protoShapeChange(cx
);
4164 oldproto
= oldproto
->getProto();
4167 if (!proto
|| !checkForCycles
) {
4168 obj
->setProto(proto
);
4169 } else if (!SetProtoCheckingForCycles(cx
, obj
, proto
)) {
4170 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_CYCLIC_VALUE
, js_proto_str
);
4179 js_GetClassObject(JSContext
*cx
, JSObject
*obj
, JSProtoKey key
,
4183 JSResolvingKey rkey
;
4184 JSResolvingEntry
*rentry
;
4189 obj
= obj
->getGlobal();
4190 if (!obj
->isGlobal()) {
4195 v
= obj
->getReservedSlot(key
);
4197 *objp
= &v
.toObject();
4202 rkey
.id
= ATOM_TO_JSID(cx
->runtime
->atomState
.classAtoms
[key
]);
4203 if (!js_StartResolving(cx
, &rkey
, JSRESFLAG_LOOKUP
, &rentry
))
4206 /* Already caching key in obj -- suppress recursion. */
4210 generation
= cx
->resolvingTable
->generation
;
4214 init
= lazy_prototype_init
[key
];
4216 if (!init(cx
, obj
)) {
4219 v
= obj
->getReservedSlot(key
);
4221 cobj
= &v
.toObject();
4225 js_StopResolving(cx
, &rkey
, JSRESFLAG_LOOKUP
, rentry
, generation
);
4231 js_SetClassObject(JSContext
*cx
, JSObject
*obj
, JSProtoKey key
, JSObject
*cobj
, JSObject
*proto
)
4233 JS_ASSERT(!obj
->getParent());
4234 if (!obj
->isGlobal())
4237 return js_SetReservedSlot(cx
, obj
, key
, ObjectOrNullValue(cobj
)) &&
4238 js_SetReservedSlot(cx
, obj
, JSProto_LIMIT
+ key
, ObjectOrNullValue(proto
));
4242 js_FindClassObject(JSContext
*cx
, JSObject
*start
, JSProtoKey protoKey
,
4243 Value
*vp
, Class
*clasp
)
4246 JSObject
*obj
, *cobj
, *pobj
;
4252 * Find the global object. Use cx->fp() directly to avoid falling off
4253 * trace; all JIT-elided stack frames have the same global object as
4256 VOUCH_DOES_NOT_REQUIRE_STACK();
4257 if (!start
&& (fp
= cx
->maybefp()) != NULL
)
4258 start
= &fp
->scopeChain();
4261 /* Find the topmost object in the scope chain. */
4264 start
= obj
->getParent();
4267 obj
= cx
->globalObject
;
4274 OBJ_TO_INNER_OBJECT(cx
, obj
);
4278 if (protoKey
!= JSProto_Null
) {
4279 JS_ASSERT(JSProto_Null
< protoKey
);
4280 JS_ASSERT(protoKey
< JSProto_LIMIT
);
4281 if (!js_GetClassObject(cx
, obj
, protoKey
, &cobj
))
4284 vp
->setObject(*cobj
);
4287 id
= ATOM_TO_JSID(cx
->runtime
->atomState
.classAtoms
[protoKey
]);
4289 JSAtom
*atom
= js_Atomize(cx
, clasp
->name
, strlen(clasp
->name
), 0);
4292 id
= ATOM_TO_JSID(atom
);
4295 JS_ASSERT(obj
->isNative());
4296 if (js_LookupPropertyWithFlags(cx
, obj
, id
, JSRESOLVE_CLASSNAME
,
4297 &pobj
, &prop
) < 0) {
4300 Value v
= UndefinedValue();
4301 if (prop
&& pobj
->isNative()) {
4302 shape
= (Shape
*) prop
;
4303 if (pobj
->containsSlot(shape
->slot
)) {
4304 v
= pobj
->nativeGetSlot(shape
->slot
);
4305 if (v
.isPrimitive())
4314 js_ConstructObject(JSContext
*cx
, Class
*clasp
, JSObject
*proto
, JSObject
*parent
,
4315 uintN argc
, Value
*argv
)
4317 AutoArrayRooter
argtvr(cx
, argc
, argv
);
4319 JSProtoKey protoKey
= GetClassProtoKey(clasp
);
4321 /* Protect constructor in case a crazy getter for .prototype uproots it. */
4322 AutoValueRooter
tvr(cx
);
4323 if (!js_FindClassObject(cx
, parent
, protoKey
, tvr
.addr(), clasp
))
4326 const Value
&cval
= tvr
.value();
4327 if (tvr
.value().isPrimitive()) {
4328 js_ReportIsNotFunction(cx
, tvr
.addr(), JSV2F_CONSTRUCT
| JSV2F_SEARCH_STACK
);
4333 * If proto is NULL, set it to Constructor.prototype, just like JSOP_NEW
4334 * does, likewise for the new object's parent.
4336 JSObject
*ctor
= &cval
.toObject();
4338 parent
= ctor
->getParent();
4341 if (!ctor
->getProperty(cx
, ATOM_TO_JSID(cx
->runtime
->atomState
.classPrototypeAtom
),
4345 if (rval
.isObjectOrNull())
4346 proto
= rval
.toObjectOrNull();
4349 JSObject
*obj
= NewObject
<WithProto::Class
>(cx
, clasp
, proto
, parent
);
4353 obj
->syncSpecialEquality();
4356 if (!InvokeConstructorWithGivenThis(cx
, obj
, cval
, argc
, argv
, &rval
))
4359 if (rval
.isPrimitive())
4363 * If the instance's class differs from what was requested, throw a type
4364 * error. If the given class has both the JSCLASS_HAS_PRIVATE and the
4365 * JSCLASS_CONSTRUCT_PROTOTYPE flags, and the instance does not have its
4366 * private data set at this point, then the constructor was replaced and
4367 * we should throw a type error.
4369 obj
= &rval
.toObject();
4370 if (obj
->getClass() != clasp
||
4371 (!(~clasp
->flags
& (JSCLASS_HAS_PRIVATE
|
4372 JSCLASS_CONSTRUCT_PROTOTYPE
)) &&
4373 !obj
->getPrivate())) {
4374 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
4375 JSMSG_WRONG_CONSTRUCTOR
, clasp
->name
);
4382 JSObject::allocSlot(JSContext
*cx
, uint32
*slotp
)
4384 uint32 slot
= slotSpan();
4385 JS_ASSERT(slot
>= JSSLOT_FREE(clasp
));
4388 * If this object is in dictionary mode and it has a property table, try to
4389 * pull a free slot from the property table's slot-number freelist.
4391 if (inDictionaryMode() && lastProp
->hasTable()) {
4392 uint32
&last
= lastProp
->getTable()->freelist
;
4393 if (last
!= SHAPE_INVALID_SLOT
) {
4395 JS_ASSERT(last
< slot
);
4396 uint32 next
= getSlot(last
).toPrivateUint32();
4397 JS_ASSERT_IF(next
!= SHAPE_INVALID_SLOT
, next
< slot
);
4402 Value
&vref
= getSlotRef(last
);
4403 last
= vref
.toPrivateUint32();
4404 vref
.setUndefined();
4409 if (slot
>= numSlots() && !growSlots(cx
, slot
+ 1))
4412 /* JSObject::growSlots or JSObject::freeSlot should set the free slots to void. */
4413 JS_ASSERT(getSlot(slot
).isUndefined());
4419 JSObject::freeSlot(JSContext
*cx
, uint32 slot
)
4421 uint32 limit
= slotSpan();
4422 JS_ASSERT(slot
< limit
);
4424 Value
&vref
= getSlotRef(slot
);
4425 if (inDictionaryMode() && lastProp
->hasTable()) {
4426 uint32
&last
= lastProp
->getTable()->freelist
;
4428 /* Can't afford to check the whole freelist, but let's check the head. */
4429 JS_ASSERT_IF(last
!= SHAPE_INVALID_SLOT
, last
< limit
&& last
!= slot
);
4432 * Freeing a slot other than the last one mapped by this object's
4433 * shape (and not a reserved slot; see bug 595230): push the slot onto
4434 * the dictionary property table's freelist. We want to let the last
4435 * slot be freed by shrinking the dslots vector; see js_TraceObject.
4437 if (JSSLOT_FREE(clasp
) <= slot
&& slot
+ 1 < limit
) {
4438 JS_ASSERT_IF(last
!= SHAPE_INVALID_SLOT
, last
< slotSpan());
4439 vref
.setPrivateUint32(last
);
4444 vref
.setUndefined();
4448 /* JSBOXEDWORD_INT_MAX as a string */
4449 #define JSBOXEDWORD_INT_MAX_STRING "1073741823"
4452 * Convert string indexes that convert to int jsvals as ints to save memory.
4453 * Care must be taken to use this macro every time a property name is used, or
4454 * else double-sets, incorrect property cache misses, or other mistakes could
4458 js_CheckForStringIndex(jsid id
)
4460 if (!JSID_IS_ATOM(id
))
4463 JSAtom
*atom
= JSID_TO_ATOM(id
);
4464 JSString
*str
= ATOM_TO_STRING(atom
);
4465 const jschar
*s
= str
->flatChars();
4468 JSBool negative
= (ch
== '-');
4475 size_t n
= str
->flatLength() - negative
;
4476 if (n
> sizeof(JSBOXEDWORD_INT_MAX_STRING
) - 1)
4479 const jschar
*cp
= s
;
4480 const jschar
*end
= s
+ n
;
4482 jsuint index
= JS7_UNDEC(*cp
++);
4483 jsuint oldIndex
= 0;
4487 while (JS7_ISDEC(*cp
)) {
4490 index
= 10 * index
+ c
;
4496 * Non-integer indexes can't be represented as integers. Also, distinguish
4497 * index "-0" from "0", because JSBOXEDWORD_INT cannot.
4499 if (cp
!= end
|| (negative
&& index
== 0))
4503 if (oldIndex
< -(JSID_INT_MIN
/ 10) ||
4504 (oldIndex
== -(JSID_INT_MIN
/ 10) && c
<= (-JSID_INT_MIN
% 10)))
4506 id
= INT_TO_JSID(-jsint(index
));
4509 if (oldIndex
< JSID_INT_MAX
/ 10 ||
4510 (oldIndex
== JSID_INT_MAX
/ 10 && c
<= (JSID_INT_MAX
% 10)))
4512 id
= INT_TO_JSID(jsint(index
));
4520 PurgeProtoChain(JSContext
*cx
, JSObject
*obj
, jsid id
)
4525 if (!obj
->isNative()) {
4526 obj
= obj
->getProto();
4529 shape
= obj
->nativeLookup(id
);
4531 PCMETER(JS_PROPERTY_CACHE(cx
).pcpurges
++);
4532 obj
->shadowingShapeChange(cx
, *shape
);
4534 if (!obj
->getParent()) {
4536 * All scope chains end in a global object, so this will change
4537 * the global shape. jstracer.cpp assumes that the global shape
4538 * never changes on trace, so we must deep-bail here.
4544 obj
= obj
->getProto();
4550 js_PurgeScopeChainHelper(JSContext
*cx
, JSObject
*obj
, jsid id
)
4552 JS_ASSERT(obj
->isDelegate());
4553 PurgeProtoChain(cx
, obj
->getProto(), id
);
4556 * We must purge the scope chain only for Call objects as they are the only
4557 * kind of cacheable non-global object that can gain properties after outer
4558 * properties with the same names have been cached or traced. Call objects
4559 * may gain such properties via eval introducing new vars; see bug 490364.
4561 if (obj
->isCall()) {
4562 while ((obj
= obj
->getParent()) != NULL
) {
4563 if (PurgeProtoChain(cx
, obj
, id
))
4570 js_AddNativeProperty(JSContext
*cx
, JSObject
*obj
, jsid id
,
4571 PropertyOp getter
, StrictPropertyOp setter
, uint32 slot
,
4572 uintN attrs
, uintN flags
, intN shortid
)
4574 JS_ASSERT(!(flags
& Shape::METHOD
));
4577 * Purge the property cache of now-shadowed id in obj's scope chain. Do
4578 * this optimistically (assuming no failure below) before locking obj, so
4579 * we can lock the shadowed scope.
4581 js_PurgeScopeChain(cx
, obj
, id
);
4583 if (!obj
->ensureClassReservedSlots(cx
))
4586 /* Convert string indices to integers if appropriate. */
4587 id
= js_CheckForStringIndex(id
);
4588 return obj
->putProperty(cx
, id
, getter
, setter
, slot
, attrs
, flags
, shortid
);
4592 js_ChangeNativePropertyAttrs(JSContext
*cx
, JSObject
*obj
,
4593 const Shape
*shape
, uintN attrs
, uintN mask
,
4594 PropertyOp getter
, StrictPropertyOp setter
)
4596 if (!obj
->ensureClassReservedSlots(cx
))
4600 * Check for freezing an object with shape-memoized methods here, on a
4601 * shape-by-shape basis. Note that getter may be a pun of the method's
4602 * joined function object value, to indicate "no getter change". In this
4603 * case we must null getter to get the desired PropertyStub behavior.
4605 if ((attrs
& JSPROP_READONLY
) && shape
->isMethod()) {
4606 JSObject
*funobj
= &shape
->methodObject();
4607 Value v
= ObjectValue(*funobj
);
4609 shape
= obj
->methodReadBarrier(cx
, *shape
, &v
);
4613 if (CastAsObject(getter
) == funobj
) {
4614 JS_ASSERT(!(attrs
& JSPROP_GETTER
));
4619 return obj
->changeProperty(cx
, shape
, attrs
, mask
, getter
, setter
);
4623 js_DefineProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, const Value
*value
,
4624 PropertyOp getter
, StrictPropertyOp setter
, uintN attrs
)
4626 return js_DefineNativeProperty(cx
, obj
, id
, *value
, getter
, setter
, attrs
,
4631 * Backward compatibility requires allowing addProperty hooks to mutate the
4632 * nominal initial value of a slotful property, while GC safety wants that
4633 * value to be stored before the call-out through the hook. Optimize to do
4634 * both while saving cycles for classes that stub their addProperty hook.
4637 CallAddPropertyHook(JSContext
*cx
, Class
*clasp
, JSObject
*obj
, const Shape
*shape
, Value
*vp
)
4639 if (clasp
->addProperty
!= PropertyStub
) {
4640 Value nominal
= *vp
;
4642 if (!CallJSPropertyOp(cx
, clasp
->addProperty
, obj
, shape
->id
, vp
))
4644 if (*vp
!= nominal
) {
4645 if (obj
->containsSlot(shape
->slot
))
4646 obj
->nativeSetSlot(shape
->slot
, *vp
);
4653 js_DefineNativeProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, const Value
&value
,
4654 PropertyOp getter
, StrictPropertyOp setter
, uintN attrs
,
4655 uintN flags
, intN shortid
, JSProperty
**propp
,
4656 uintN defineHow
/* = 0 */)
4658 JS_ASSERT((defineHow
& ~(JSDNP_CACHE_RESULT
| JSDNP_DONT_PURGE
| JSDNP_SET_METHOD
)) == 0);
4659 LeaveTraceIfGlobalObject(cx
, obj
);
4661 /* Convert string indices to integers if appropriate. */
4662 id
= js_CheckForStringIndex(id
);
4665 * If defining a getter or setter, we must check for its counterpart and
4666 * update the attributes and property ops. A getter or setter is really
4667 * only half of a property.
4669 const Shape
*shape
= NULL
;
4670 if (attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)) {
4675 * If JS_THREADSAFE and id is found, js_LookupProperty returns with
4676 * shape non-null and pobj locked. If pobj == obj, the property is
4677 * already in obj and obj has its own (mutable) scope. So if we are
4678 * defining a getter whose setter was already defined, or vice versa,
4679 * finish the job via obj->changeProperty, and refresh the property
4680 * cache line for (obj, id) to map shape.
4682 if (!js_LookupProperty(cx
, obj
, id
, &pobj
, &prop
))
4684 shape
= (Shape
*) prop
;
4685 if (shape
&& pobj
== obj
&& shape
->isAccessorDescriptor()) {
4686 shape
= obj
->changeProperty(cx
, shape
, attrs
,
4687 JSPROP_GETTER
| JSPROP_SETTER
,
4688 (attrs
& JSPROP_GETTER
)
4691 (attrs
& JSPROP_SETTER
)
4704 * Purge the property cache of any properties named by id that are about
4705 * to be shadowed in obj's scope chain unless it is known a priori that it
4706 * is not possible. We do this before locking obj to avoid nesting locks.
4708 if (!(defineHow
& JSDNP_DONT_PURGE
))
4709 js_PurgeScopeChain(cx
, obj
, id
);
4712 * Check whether a readonly property or setter is being defined on a known
4713 * prototype object. See the comment in jscntxt.h before protoHazardShape's
4714 * member declaration.
4716 if (obj
->isDelegate() && (attrs
& (JSPROP_READONLY
| JSPROP_SETTER
)))
4717 cx
->runtime
->protoHazardShape
= js_GenerateShape(cx
);
4719 /* Use the object's class getter and setter by default. */
4720 Class
*clasp
= obj
->getClass();
4721 if (!(defineHow
& JSDNP_SET_METHOD
)) {
4722 if (!getter
&& !(attrs
& JSPROP_GETTER
))
4723 getter
= clasp
->getProperty
;
4724 if (!setter
&& !(attrs
& JSPROP_SETTER
))
4725 setter
= clasp
->setProperty
;
4728 /* Get obj's own scope if it has one, or create a new one for obj. */
4729 if (!obj
->ensureClassReservedSlots(cx
))
4733 * Make a local copy of value, in case a method barrier needs to update the
4734 * value to define, and just so addProperty can mutate its inout parameter.
4736 Value valueCopy
= value
;
4737 bool adding
= false;
4740 /* Add a new property, or replace an existing one of the same id. */
4741 if (defineHow
& JSDNP_SET_METHOD
) {
4742 JS_ASSERT(clasp
== &js_ObjectClass
);
4743 JS_ASSERT(IsFunctionObject(value
));
4744 JS_ASSERT(!(attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)));
4745 JS_ASSERT(!getter
&& !setter
);
4747 JSObject
*funobj
= &value
.toObject();
4748 if (FUN_OBJECT(GET_FUNCTION_PRIVATE(cx
, funobj
)) == funobj
) {
4749 flags
|= Shape::METHOD
;
4750 getter
= CastAsPropertyOp(funobj
);
4754 if (const Shape
*existingShape
= obj
->nativeLookup(id
)) {
4755 if (existingShape
->hasSlot())
4756 AbortRecordingIfUnexpectedGlobalWrite(cx
, obj
, existingShape
->slot
);
4758 if (existingShape
->isMethod() &&
4759 ObjectValue(existingShape
->methodObject()) == valueCopy
)
4762 * Redefining an existing shape-memoized method object without
4763 * changing the property's value, perhaps to change attributes.
4764 * Clone now via the method read barrier.
4766 * But first, assert that our caller is not trying to preserve
4767 * the joined function object value as the getter object for
4768 * the redefined property. The joined function object cannot
4769 * yet have leaked, so only an internal code path could attempt
4770 * such a thing. Any such path would be a bug to fix.
4772 JS_ASSERT(existingShape
->getter() != getter
);
4774 if (!obj
->methodReadBarrier(cx
, *existingShape
, &valueCopy
))
4781 uint32 oldShape
= obj
->shape();
4782 shape
= obj
->putProperty(cx
, id
, getter
, setter
, SHAPE_INVALID_SLOT
,
4783 attrs
, flags
, shortid
);
4788 * If shape is a joined method, the above call to putProperty suffices
4789 * to update the object's shape id if need be (because the shape's hash
4790 * identity includes the method value).
4792 * But if scope->branded(), the object's shape id may not have changed
4793 * and we may be overwriting a cached function-valued property (note
4794 * how methodWriteBarrier checks previous vs. would-be current value).
4797 if (obj
->shape() == oldShape
&& obj
->branded() && shape
->slot
!= SHAPE_INVALID_SLOT
) {
4799 const Shape
*newshape
=
4801 obj
->methodWriteBarrier(cx
, *shape
, valueCopy
);
4802 JS_ASSERT(newshape
== shape
);
4806 /* Store valueCopy before calling addProperty, in case the latter GC's. */
4807 if (obj
->containsSlot(shape
->slot
))
4808 obj
->nativeSetSlot(shape
->slot
, valueCopy
);
4810 /* XXXbe called with lock held */
4811 if (!CallAddPropertyHook(cx
, clasp
, obj
, shape
, &valueCopy
)) {
4812 obj
->removeProperty(cx
, id
);
4816 if (defineHow
& JSDNP_CACHE_RESULT
) {
4817 JS_ASSERT_NOT_ON_TRACE(cx
);
4819 JS_PROPERTY_CACHE(cx
).fill(cx
, obj
, 0, 0, obj
, shape
, true);
4820 TRACE_1(AddProperty
, obj
);
4824 *propp
= (JSProperty
*) shape
;
4828 error
: // TRACE_1 jumps here on error.
4833 #define SCOPE_DEPTH_ACCUM(bs,val) \
4834 JS_SCOPE_DEPTH_METERING(JS_BASIC_STATS_ACCUM(bs, val))
4837 * Call obj's resolve hook. obj is a native object and the caller holds its
4840 * cx, start, id, and flags are the parameters initially passed to the ongoing
4841 * lookup; objp and propp are its out parameters. obj is an object along
4842 * start's prototype chain.
4844 * There are four possible outcomes:
4846 * - On failure, report an error or exception, unlock obj, and return false.
4848 * - If we are alrady resolving a property of *curobjp, set *recursedp = true,
4849 * unlock obj, and return true.
4851 * - If the resolve hook finds or defines the sought property, set *objp and
4852 * *propp appropriately, set *recursedp = false, and return true with *objp's
4855 * - Otherwise no property was resolved. Set *propp = NULL and *recursedp = false
4859 CallResolveOp(JSContext
*cx
, JSObject
*start
, JSObject
*obj
, jsid id
, uintN flags
,
4860 JSObject
**objp
, JSProperty
**propp
, bool *recursedp
)
4862 Class
*clasp
= obj
->getClass();
4863 JSResolveOp resolve
= clasp
->resolve
;
4866 * Avoid recursion on (obj, id) already being resolved on cx.
4868 * Once we have successfully added an entry for (obj, key) to
4869 * cx->resolvingTable, control must go through cleanup: before
4870 * returning. But note that JS_DHASH_ADD may find an existing
4871 * entry, in which case we bail to suppress runaway recursion.
4873 JSResolvingKey key
= {obj
, id
};
4874 JSResolvingEntry
*entry
;
4875 if (!js_StartResolving(cx
, &key
, JSRESFLAG_LOOKUP
, &entry
))
4878 /* Already resolving id in obj -- suppress recursion. */
4882 uint32 generation
= cx
->resolvingTable
->generation
;
4888 const Shape
*shape
= NULL
;
4889 if (clasp
->flags
& JSCLASS_NEW_RESOLVE
) {
4890 JSNewResolveOp newresolve
= (JSNewResolveOp
)resolve
;
4891 if (flags
== JSRESOLVE_INFER
)
4892 flags
= js_InferFlags(cx
, 0);
4893 JSObject
*obj2
= (clasp
->flags
& JSCLASS_NEW_RESOLVE_GETS_START
) ? start
: NULL
;
4896 /* Protect id and all atoms from a GC nested in resolve. */
4897 AutoKeepAtoms
keep(cx
->runtime
);
4898 ok
= newresolve(cx
, obj
, id
, flags
, &obj2
);
4904 /* Resolved: lookup id again for backward compatibility. */
4905 if (!obj2
->isNative()) {
4906 /* Whoops, newresolve handed back a foreign obj2. */
4907 JS_ASSERT(obj2
!= obj
);
4908 ok
= obj2
->lookupProperty(cx
, id
, objp
, propp
);
4913 * Require that obj2 not be empty now, as we do for old-style
4914 * resolve. If it doesn't, then id was not truly resolved, and
4915 * we'll find it in the proto chain, or miss it if obj2's proto
4916 * is not on obj's proto chain. That last case is a "too bad!"
4919 if (!obj2
->nativeEmpty())
4920 shape
= obj2
->nativeLookup(id
);
4923 JS_ASSERT(!obj2
->nativeEmpty());
4929 * Old resolve always requires id re-lookup if obj is not empty after
4932 ok
= resolve(cx
, obj
, id
);
4935 JS_ASSERT(obj
->isNative());
4936 if (!obj
->nativeEmpty())
4937 shape
= obj
->nativeLookup(id
);
4943 *propp
= (JSProperty
*) shape
;
4945 js_StopResolving(cx
, &key
, JSRESFLAG_LOOKUP
, entry
, generation
);
4949 static JS_ALWAYS_INLINE
int
4950 js_LookupPropertyWithFlagsInline(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN flags
,
4951 JSObject
**objp
, JSProperty
**propp
)
4953 /* We should not get string indices which aren't already integers here. */
4954 JS_ASSERT(id
== js_CheckForStringIndex(id
));
4956 /* Search scopes starting with obj and following the prototype link. */
4957 JSObject
*start
= obj
;
4959 for (protoIndex
= 0; ; protoIndex
++) {
4960 const Shape
*shape
= obj
->nativeLookup(id
);
4962 SCOPE_DEPTH_ACCUM(&cx
->runtime
->protoLookupDepthStats
, protoIndex
);
4964 *propp
= (JSProperty
*) shape
;
4968 /* Try obj's class resolve hook if id was not found in obj's scope. */
4969 if (!shape
&& obj
->getClass()->resolve
!= JS_ResolveStub
) {
4971 if (!CallResolveOp(cx
, start
, obj
, id
, flags
, objp
, propp
, &recursed
))
4976 /* Recalculate protoIndex in case it was resolved on some other object. */
4978 for (JSObject
*proto
= start
; proto
&& proto
!= *objp
; proto
= proto
->getProto())
4980 SCOPE_DEPTH_ACCUM(&cx
->runtime
->protoLookupDepthStats
, protoIndex
);
4985 JSObject
*proto
= obj
->getProto();
4988 if (!proto
->isNative()) {
4989 if (!proto
->lookupProperty(cx
, id
, objp
, propp
))
4993 * Non-native objects must have either non-native lookup results,
4994 * or else native results from the non-native's prototype chain.
4996 * See JSStackFrame::getValidCalleeObject, where we depend on this
4997 * fact to force a prototype-delegated joined method accessed via
4998 * arguments.callee through the delegating |this| object's method
5001 if (*propp
&& (*objp
)->isNative()) {
5002 while ((proto
= proto
->getProto()) != *objp
)
5006 return protoIndex
+ 1;
5017 JS_FRIEND_API(JSBool
)
5018 js_LookupProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, JSObject
**objp
,
5021 /* Convert string indices to integers if appropriate. */
5022 id
= js_CheckForStringIndex(id
);
5024 return js_LookupPropertyWithFlagsInline(cx
, obj
, id
, cx
->resolveFlags
, objp
, propp
) >= 0;
5028 js_LookupPropertyWithFlags(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN flags
,
5029 JSObject
**objp
, JSProperty
**propp
)
5031 /* Convert string indices to integers if appropriate. */
5032 id
= js_CheckForStringIndex(id
);
5034 return js_LookupPropertyWithFlagsInline(cx
, obj
, id
, flags
, objp
, propp
);
5037 PropertyCacheEntry
*
5038 js_FindPropertyHelper(JSContext
*cx
, jsid id
, JSBool cacheResult
,
5039 JSObject
**objp
, JSObject
**pobjp
, JSProperty
**propp
)
5041 JSObject
*scopeChain
, *obj
, *parent
, *pobj
;
5042 PropertyCacheEntry
*entry
;
5043 int scopeIndex
, protoIndex
;
5046 JS_ASSERT_IF(cacheResult
, !JS_ON_TRACE(cx
));
5047 scopeChain
= &js_GetTopStackFrame(cx
)->scopeChain();
5049 /* Scan entries on the scope chain that we can cache across. */
5050 entry
= JS_NO_PROP_CACHE_FILL
;
5052 parent
= obj
->getParent();
5053 for (scopeIndex
= 0;
5055 ? js_IsCacheableNonGlobalScope(obj
)
5056 : !obj
->getOps()->lookupProperty
;
5059 js_LookupPropertyWithFlags(cx
, obj
, id
, cx
->resolveFlags
,
5067 Class
*clasp
= obj
->getClass();
5068 JS_ASSERT(pobj
->isNative());
5069 JS_ASSERT(pobj
->getClass() == clasp
);
5070 if (clasp
== &js_BlockClass
) {
5072 * A block instance on the scope chain is immutable and it
5073 * shares its shapes with its compile-time prototype.
5075 JS_ASSERT(pobj
== obj
);
5076 JS_ASSERT(pobj
->isClonedBlock());
5077 JS_ASSERT(protoIndex
== 0);
5079 /* Call and DeclEnvClass objects have no prototypes. */
5080 JS_ASSERT(!obj
->getProto());
5081 JS_ASSERT(protoIndex
== 0);
5084 JS_ASSERT(obj
->isNative());
5088 * We must check if pobj is native as a global object can have
5089 * non-native prototype.
5091 if (cacheResult
&& pobj
->isNative()) {
5092 entry
= JS_PROPERTY_CACHE(cx
).fill(cx
, scopeChain
, scopeIndex
,
5096 SCOPE_DEPTH_ACCUM(&cx
->runtime
->scopeSearchDepthStats
, scopeIndex
);
5105 parent
= obj
->getParent();
5109 if (!obj
->lookupProperty(cx
, id
, &pobj
, &prop
))
5112 PCMETER(JS_PROPERTY_CACHE(cx
).nofills
++);
5117 * We conservatively assume that a resolve hook could mutate the scope
5118 * chain during JSObject::lookupProperty. So we read parent here again.
5120 parent
= obj
->getParent();
5129 JS_ASSERT(!!pobj
== !!prop
);
5137 * On return, if |*pobjp| is a native object, then |*propp| is a |Shape *|.
5138 * Otherwise, its type and meaning depends on the host object's implementation.
5140 JS_FRIEND_API(JSBool
)
5141 js_FindProperty(JSContext
*cx
, jsid id
, JSObject
**objp
, JSObject
**pobjp
,
5144 return !!js_FindPropertyHelper(cx
, id
, false, objp
, pobjp
, propp
);
5148 js_FindIdentifierBase(JSContext
*cx
, JSObject
*scopeChain
, jsid id
)
5151 * This function should not be called for a global object or from the
5152 * trace and should have a valid cache entry for native scopeChain.
5154 JS_ASSERT(scopeChain
->getParent());
5155 JS_ASSERT(!JS_ON_TRACE(cx
));
5157 JSObject
*obj
= scopeChain
;
5160 * Loop over cacheable objects on the scope chain until we find a
5161 * property. We also stop when we reach the global object skipping any
5162 * farther checks or lookups. For details see the JSOP_BINDNAME case of
5165 * The test order here matters because js_IsCacheableNonGlobalScope
5166 * must not be passed a global object (i.e. one with null parent).
5168 for (int scopeIndex
= 0;
5169 !obj
->getParent() || js_IsCacheableNonGlobalScope(obj
);
5173 int protoIndex
= js_LookupPropertyWithFlags(cx
, obj
, id
,
5179 if (!pobj
->isNative()) {
5180 JS_ASSERT(!obj
->getParent());
5183 JS_ASSERT_IF(obj
->getParent(), pobj
->getClass() == obj
->getClass());
5185 PropertyCacheEntry
*entry
=
5187 JS_PROPERTY_CACHE(cx
).fill(cx
, scopeChain
, scopeIndex
, protoIndex
, pobj
,
5193 JSObject
*parent
= obj
->getParent();
5199 /* Loop until we find a property or reach the global object. */
5203 if (!obj
->lookupProperty(cx
, id
, &pobj
, &prop
))
5209 * We conservatively assume that a resolve hook could mutate the scope
5210 * chain during JSObject::lookupProperty. So we must check if parent is
5211 * not null here even if it wasn't before the lookup.
5213 JSObject
*parent
= obj
->getParent();
5217 } while (obj
->getParent());
5221 static JS_ALWAYS_INLINE JSBool
5222 js_NativeGetInline(JSContext
*cx
, JSObject
*receiver
, JSObject
*obj
, JSObject
*pobj
,
5223 const Shape
*shape
, uintN getHow
, Value
*vp
)
5225 LeaveTraceIfGlobalObject(cx
, pobj
);
5230 JS_ASSERT(pobj
->isNative());
5233 if (slot
!= SHAPE_INVALID_SLOT
) {
5234 *vp
= pobj
->nativeGetSlot(slot
);
5235 JS_ASSERT(!vp
->isMagic());
5239 if (shape
->hasDefaultGetter())
5242 if (JS_UNLIKELY(shape
->isMethod()) && (getHow
& JSGET_NO_METHOD_BARRIER
)) {
5243 JS_ASSERT(&shape
->methodObject() == &vp
->toObject());
5247 sample
= cx
->runtime
->propertyRemovals
;
5249 AutoShapeRooter
tvr(cx
, shape
);
5250 AutoObjectRooter
tvr2(cx
, pobj
);
5251 if (!shape
->get(cx
, receiver
, obj
, pobj
, vp
))
5255 if (pobj
->containsSlot(slot
) &&
5256 (JS_LIKELY(cx
->runtime
->propertyRemovals
== sample
) ||
5257 pobj
->nativeContains(*shape
))) {
5258 if (!pobj
->methodWriteBarrier(cx
, *shape
, *vp
))
5260 pobj
->nativeSetSlot(slot
, *vp
);
5267 js_NativeGet(JSContext
*cx
, JSObject
*obj
, JSObject
*pobj
, const Shape
*shape
, uintN getHow
,
5270 return js_NativeGetInline(cx
, obj
, obj
, pobj
, shape
, getHow
, vp
);
5274 js_NativeSet(JSContext
*cx
, JSObject
*obj
, const Shape
*shape
, bool added
, bool strict
, Value
*vp
)
5276 LeaveTraceIfGlobalObject(cx
, obj
);
5281 JS_ASSERT(obj
->isNative());
5284 if (slot
!= SHAPE_INVALID_SLOT
) {
5285 JS_ASSERT(obj
->containsSlot(slot
));
5287 /* If shape has a stub setter, keep obj locked and just store *vp. */
5288 if (shape
->hasDefaultSetter()) {
5290 AbortRecordingIfUnexpectedGlobalWrite(cx
, obj
, slot
);
5292 /* FIXME: This should pass *shape, not slot, but see bug 630354. */
5293 if (!obj
->methodWriteBarrier(cx
, slot
, *vp
))
5296 obj
->nativeSetSlot(slot
, *vp
);
5301 * Allow API consumers to create shared properties with stub setters.
5302 * Such properties effectively function as data descriptors which are
5303 * not writable, so attempting to set such a property should do nothing
5304 * or throw if we're in strict mode.
5306 if (!shape
->hasGetterValue() && shape
->hasDefaultSetter())
5307 return js_ReportGetterOnlyAssignment(cx
);
5310 sample
= cx
->runtime
->propertyRemovals
;
5312 AutoShapeRooter
tvr(cx
, shape
);
5313 if (!shape
->set(cx
, obj
, strict
, vp
))
5316 JS_ASSERT_IF(!obj
->inDictionaryMode(), shape
->slot
== slot
);
5320 if (obj
->containsSlot(slot
) &&
5321 (JS_LIKELY(cx
->runtime
->propertyRemovals
== sample
) ||
5322 obj
->nativeContains(*shape
))) {
5324 AbortRecordingIfUnexpectedGlobalWrite(cx
, obj
, slot
);
5325 if (!obj
->methodWriteBarrier(cx
, *shape
, *vp
))
5328 obj
->setSlot(slot
, *vp
);
5334 static JS_ALWAYS_INLINE
bool
5335 js_GetPropertyHelperWithShapeInline(JSContext
*cx
, JSObject
*obj
, JSObject
*receiver
, jsid id
,
5336 uintN getHow
, Value
*vp
,
5337 const Shape
**shapeOut
, JSObject
**holderOut
)
5339 JSObject
*aobj
, *obj2
;
5344 JS_ASSERT_IF(getHow
& JSGET_CACHE_RESULT
, !JS_ON_TRACE(cx
));
5348 /* Convert string indices to integers if appropriate. */
5349 id
= js_CheckForStringIndex(id
);
5351 aobj
= js_GetProtoIfDenseArray(obj
);
5352 /* This call site is hot -- use the always-inlined variant of js_LookupPropertyWithFlags(). */
5353 protoIndex
= js_LookupPropertyWithFlagsInline(cx
, aobj
, id
, cx
->resolveFlags
,
5363 if (!CallJSPropertyOp(cx
, obj
->getClass()->getProperty
, obj
, id
, vp
))
5366 PCMETER(getHow
& JSGET_CACHE_RESULT
&& JS_PROPERTY_CACHE(cx
).nofills
++);
5369 * Give a strict warning if foo.bar is evaluated by a script for an
5370 * object foo with no property named 'bar'.
5373 if (vp
->isUndefined() && ((pc
= js_GetCurrentBytecodePC(cx
)) != NULL
)) {
5378 if (op
== JSOP_TRAP
) {
5379 JS_ASSERT_NOT_ON_TRACE(cx
);
5380 op
= JS_GetTrapOpcode(cx
, cx
->fp()->script(), pc
);
5382 if (op
== JSOP_GETXPROP
) {
5383 flags
= JSREPORT_ERROR
;
5385 if (!cx
->hasStrictOption() ||
5386 (op
!= JSOP_GETPROP
&& op
!= JSOP_GETELEM
) ||
5387 js_CurrentPCIsInImacro(cx
)) {
5392 * XXX do not warn about missing __iterator__ as the function
5393 * may be called from JS_GetMethodById. See bug 355145.
5395 if (JSID_IS_ATOM(id
, cx
->runtime
->atomState
.iteratorAtom
))
5398 /* Do not warn about tests like (obj[prop] == undefined). */
5399 if (cx
->resolveFlags
== JSRESOLVE_INFER
) {
5401 pc
+= js_CodeSpec
[op
].length
;
5402 if (Detecting(cx
, pc
))
5404 } else if (cx
->resolveFlags
& JSRESOLVE_DETECTING
) {
5408 flags
= JSREPORT_WARNING
| JSREPORT_STRICT
;
5411 /* Ok, bad undefined property reference: whine about it. */
5412 if (!js_ReportValueErrorFlags(cx
, flags
, JSMSG_UNDEFINED_PROP
,
5413 JSDVG_IGNORE_STACK
, IdToValue(id
),
5414 NULL
, NULL
, NULL
)) {
5421 if (!obj2
->isNative()) {
5422 return obj2
->isProxy()
5423 ? JSProxy::get(cx
, obj2
, receiver
, id
, vp
)
5424 : obj2
->getProperty(cx
, id
, vp
);
5427 shape
= (Shape
*) prop
;
5430 if (getHow
& JSGET_CACHE_RESULT
) {
5431 JS_ASSERT_NOT_ON_TRACE(cx
);
5432 JS_PROPERTY_CACHE(cx
).fill(cx
, aobj
, 0, protoIndex
, obj2
, shape
);
5435 /* This call site is hot -- use the always-inlined variant of js_NativeGet(). */
5436 if (!js_NativeGetInline(cx
, receiver
, obj
, obj2
, shape
, getHow
, vp
))
5443 js_GetPropertyHelperWithShape(JSContext
*cx
, JSObject
*obj
, JSObject
*receiver
, jsid id
,
5444 uint32 getHow
, Value
*vp
,
5445 const Shape
**shapeOut
, JSObject
**holderOut
)
5447 return js_GetPropertyHelperWithShapeInline(cx
, obj
, receiver
, id
, getHow
, vp
,
5448 shapeOut
, holderOut
);
5451 static JS_ALWAYS_INLINE JSBool
5452 js_GetPropertyHelperInline(JSContext
*cx
, JSObject
*obj
, JSObject
*receiver
, jsid id
,
5453 uint32 getHow
, Value
*vp
)
5457 return js_GetPropertyHelperWithShapeInline(cx
, obj
, receiver
, id
, getHow
, vp
, &shape
, &holder
);
5461 js_GetPropertyHelper(JSContext
*cx
, JSObject
*obj
, jsid id
, uint32 getHow
, Value
*vp
)
5463 return js_GetPropertyHelperInline(cx
, obj
, obj
, id
, getHow
, vp
);
5467 js_GetProperty(JSContext
*cx
, JSObject
*obj
, JSObject
*receiver
, jsid id
, Value
*vp
)
5469 /* This call site is hot -- use the always-inlined variant of js_GetPropertyHelper(). */
5470 return js_GetPropertyHelperInline(cx
, obj
, receiver
, id
, JSGET_METHOD_BARRIER
, vp
);
5474 js::GetPropertyDefault(JSContext
*cx
, JSObject
*obj
, jsid id
, const Value
&def
, Value
*vp
)
5478 if (js_LookupPropertyWithFlags(cx
, obj
, id
, JSRESOLVE_QUALIFIED
, &obj2
, &prop
) < 0)
5486 return js_GetProperty(cx
, obj2
, id
, vp
);
5490 js_GetMethod(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN getHow
, Value
*vp
)
5492 JSAutoResolveFlags
rf(cx
, JSRESOLVE_QUALIFIED
);
5494 PropertyIdOp op
= obj
->getOps()->getProperty
;
5496 #if JS_HAS_XML_SUPPORT
5497 JS_ASSERT(!obj
->isXML());
5499 return js_GetPropertyHelper(cx
, obj
, id
, getHow
, vp
);
5501 JS_ASSERT_IF(getHow
& JSGET_CACHE_RESULT
, obj
->isDenseArray());
5502 #if JS_HAS_XML_SUPPORT
5504 return js_GetXMLMethod(cx
, obj
, id
, vp
);
5506 return op(cx
, obj
, obj
, id
, vp
);
5510 js_CheckUndeclaredVarAssignment(JSContext
*cx
, JSString
*propname
)
5512 JSStackFrame
*const fp
= js_GetTopStackFrame(cx
);
5516 /* If neither cx nor the code is strict, then no check is needed. */
5517 if (!(fp
->isScriptFrame() && fp
->script()->strictModeCode
) &&
5518 !cx
->hasStrictOption()) {
5522 JSAutoByteString
bytes(cx
, propname
);
5524 JS_ReportErrorFlagsAndNumber(cx
,
5525 (JSREPORT_WARNING
| JSREPORT_STRICT
5526 | JSREPORT_STRICT_MODE_ERROR
),
5527 js_GetErrorMessage
, NULL
,
5528 JSMSG_UNDECLARED_VAR
, bytes
.ptr());
5532 JSObject::reportReadOnly(JSContext
* cx
, jsid id
, uintN report
)
5534 return js_ReportValueErrorFlags(cx
, report
, JSMSG_READ_ONLY
,
5535 JSDVG_IGNORE_STACK
, IdToValue(id
), NULL
,
5540 JSObject::reportNotConfigurable(JSContext
* cx
, jsid id
, uintN report
)
5542 return js_ReportValueErrorFlags(cx
, report
, JSMSG_CANT_DELETE
,
5543 JSDVG_IGNORE_STACK
, IdToValue(id
), NULL
,
5548 JSObject::reportNotExtensible(JSContext
*cx
, uintN report
)
5550 return js_ReportValueErrorFlags(cx
, report
, JSMSG_OBJECT_NOT_EXTENSIBLE
,
5551 JSDVG_IGNORE_STACK
, ObjectValue(*this),
5556 js_SetPropertyHelper(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN defineHow
,
5557 Value
*vp
, JSBool strict
)
5567 StrictPropertyOp setter
;
5570 JS_ASSERT((defineHow
&
5571 ~(JSDNP_CACHE_RESULT
| JSDNP_SET_METHOD
| JSDNP_UNQUALIFIED
)) == 0);
5572 if (defineHow
& JSDNP_CACHE_RESULT
)
5573 JS_ASSERT_NOT_ON_TRACE(cx
);
5575 /* Convert string indices to integers if appropriate. */
5576 id
= js_CheckForStringIndex(id
);
5578 protoIndex
= js_LookupPropertyWithFlags(cx
, obj
, id
, cx
->resolveFlags
,
5583 if (!pobj
->isNative()) {
5584 if (pobj
->isProxy()) {
5585 AutoPropertyDescriptorRooter
pd(cx
);
5586 if (!JSProxy::getPropertyDescriptor(cx
, pobj
, id
, true, &pd
))
5589 if (pd
.attrs
& JSPROP_SHARED
)
5590 return CallSetter(cx
, obj
, id
, pd
.setter
, pd
.attrs
, pd
.shortid
, strict
, vp
);
5592 if (pd
.attrs
& JSPROP_READONLY
) {
5594 return obj
->reportReadOnly(cx
, id
);
5595 if (cx
->hasStrictOption())
5596 return obj
->reportReadOnly(cx
, id
, JSREPORT_STRICT
| JSREPORT_WARNING
);
5604 /* We should never add properties to lexical blocks. */
5605 JS_ASSERT(!obj
->isBlock());
5607 if (!obj
->getParent() &&
5608 (defineHow
& JSDNP_UNQUALIFIED
) &&
5609 !js_CheckUndeclaredVarAssignment(cx
, JSID_TO_STRING(id
))) {
5613 shape
= (Shape
*) prop
;
5616 * Now either shape is null, meaning id was not found in obj or one of its
5617 * prototypes; or shape is non-null, meaning id was found directly in pobj.
5619 attrs
= JSPROP_ENUMERATE
;
5622 clasp
= obj
->getClass();
5623 getter
= clasp
->getProperty
;
5624 setter
= clasp
->setProperty
;
5627 /* ES5 8.12.4 [[Put]] step 2. */
5628 if (shape
->isAccessorDescriptor()) {
5629 if (shape
->hasDefaultSetter())
5630 return js_ReportGetterOnlyAssignment(cx
);
5632 JS_ASSERT(shape
->isDataDescriptor());
5634 if (!shape
->writable()) {
5635 PCMETER((defineHow
& JSDNP_CACHE_RESULT
) && JS_PROPERTY_CACHE(cx
).rofills
++);
5637 /* Error in strict mode code, warn with strict option, otherwise do nothing. */
5639 return obj
->reportReadOnly(cx
, id
);
5640 if (cx
->hasStrictOption())
5641 return obj
->reportReadOnly(cx
, id
, JSREPORT_STRICT
| JSREPORT_WARNING
);
5646 attrs
= shape
->attributes();
5649 * We found id in a prototype object: prepare to share or shadow.
5651 if (!shape
->shadowable()) {
5652 if (defineHow
& JSDNP_CACHE_RESULT
)
5653 JS_PROPERTY_CACHE(cx
).fill(cx
, obj
, 0, protoIndex
, pobj
, shape
);
5655 if (shape
->hasDefaultSetter() && !shape
->hasGetterValue())
5658 return shape
->set(cx
, obj
, strict
, vp
);
5662 * Preserve attrs except JSPROP_SHARED, getter, and setter when
5663 * shadowing any property that has no slot (is shared). We must
5664 * clear the shared attribute for the shadowing shape so that the
5665 * property in obj that it defines has a slot to retain the value
5666 * being set, in case the setter simply cannot operate on instances
5667 * of obj's class by storing the value in some class-specific
5670 * A subset of slotless shared properties is the set of properties
5671 * with shortids, which must be preserved too. An old API requires
5672 * that the property's getter and setter receive the shortid, not
5673 * id, when they are called on the shadowing property that we are
5674 * about to create in obj.
5676 if (!shape
->hasSlot()) {
5677 defineHow
&= ~JSDNP_SET_METHOD
;
5678 if (shape
->hasShortID()) {
5679 flags
= Shape::HAS_SHORTID
;
5680 shortid
= shape
->shortid
;
5682 attrs
&= ~JSPROP_SHARED
;
5683 getter
= shape
->getter();
5684 setter
= shape
->setter();
5686 /* Restore attrs to the ECMA default for new properties. */
5687 attrs
= JSPROP_ENUMERATE
;
5691 * Forget we found the proto-property now that we've copied any
5692 * needed member values.
5697 JS_ASSERT_IF(shape
&& shape
->isMethod(), pobj
->hasMethodBarrier());
5698 JS_ASSERT_IF(shape
&& shape
->isMethod(),
5699 &pobj
->getSlot(shape
->slot
).toObject() == &shape
->methodObject());
5700 if (shape
&& (defineHow
& JSDNP_SET_METHOD
)) {
5702 * JSOP_SETMETHOD is assigning to an existing own property. If it
5703 * is an identical method property, do nothing. Otherwise downgrade
5704 * to ordinary assignment. Either way, do not fill the property
5705 * cache, as the interpreter has no fast path for these unusual
5708 bool identical
= shape
->isMethod() && &shape
->methodObject() == &vp
->toObject();
5710 shape
= obj
->methodShapeChange(cx
, *shape
);
5714 JSObject
*funobj
= &vp
->toObject();
5715 JSFunction
*fun
= funobj
->getFunctionPrivate();
5716 if (fun
== funobj
) {
5717 funobj
= CloneFunctionObject(cx
, fun
, fun
->parent
);
5720 vp
->setObject(*funobj
);
5723 return identical
|| js_NativeSet(cx
, obj
, shape
, false, strict
, vp
);
5729 if (!obj
->isExtensible()) {
5730 /* Error in strict mode code, warn with strict option, otherwise do nothing. */
5732 return obj
->reportNotExtensible(cx
);
5733 if (cx
->hasStrictOption())
5734 return obj
->reportNotExtensible(cx
, JSREPORT_STRICT
| JSREPORT_WARNING
);
5739 * Purge the property cache of now-shadowed id in obj's scope chain.
5740 * Do this early, before locking obj to avoid nesting locks.
5742 js_PurgeScopeChain(cx
, obj
, id
);
5744 /* Find or make a property descriptor with the right heritage. */
5745 if (!obj
->ensureClassReservedSlots(cx
))
5749 * Check for Object class here to avoid defining a method on a class
5750 * with magic resolve, addProperty, getProperty, etc. hooks.
5752 if ((defineHow
& JSDNP_SET_METHOD
) && obj
->canHaveMethodBarrier()) {
5753 JS_ASSERT(IsFunctionObject(*vp
));
5754 JS_ASSERT(!(attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)));
5756 JSObject
*funobj
= &vp
->toObject();
5757 JSFunction
*fun
= GET_FUNCTION_PRIVATE(cx
, funobj
);
5758 if (fun
== funobj
) {
5759 flags
|= Shape::METHOD
;
5760 getter
= CastAsPropertyOp(funobj
);
5764 shape
= obj
->putProperty(cx
, id
, getter
, setter
, SHAPE_INVALID_SLOT
,
5765 attrs
, flags
, shortid
);
5769 if (defineHow
& JSDNP_CACHE_RESULT
)
5770 TRACE_1(AddProperty
, obj
);
5773 * Initialize the new property value (passed to setter) to undefined.
5774 * Note that we store before calling addProperty, to match the order
5775 * in js_DefineNativeProperty.
5777 if (obj
->containsSlot(shape
->slot
))
5778 obj
->nativeSetSlot(shape
->slot
, UndefinedValue());
5780 /* XXXbe called with obj locked */
5781 if (!CallAddPropertyHook(cx
, clasp
, obj
, shape
, vp
)) {
5782 obj
->removeProperty(cx
, id
);
5788 if (defineHow
& JSDNP_CACHE_RESULT
)
5789 JS_PROPERTY_CACHE(cx
).fill(cx
, obj
, 0, 0, obj
, shape
, added
);
5791 return js_NativeSet(cx
, obj
, shape
, added
, strict
, vp
);
5794 error
: // TRACE_1 jumps here in case of error.
5800 js_SetProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
, JSBool strict
)
5802 return js_SetPropertyHelper(cx
, obj
, id
, 0, vp
, strict
);
5806 js_GetAttributes(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN
*attrsp
)
5809 if (!js_LookupProperty(cx
, obj
, id
, &obj
, &prop
))
5815 if (!obj
->isNative())
5816 return obj
->getAttributes(cx
, id
, attrsp
);
5818 const Shape
*shape
= (Shape
*)prop
;
5819 *attrsp
= shape
->attributes();
5824 js_SetNativeAttributes(JSContext
*cx
, JSObject
*obj
, Shape
*shape
, uintN attrs
)
5826 JS_ASSERT(obj
->isNative());
5827 return !!js_ChangeNativePropertyAttrs(cx
, obj
, shape
, attrs
, 0,
5828 shape
->getter(), shape
->setter());
5832 js_SetAttributes(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN
*attrsp
)
5835 if (!js_LookupProperty(cx
, obj
, id
, &obj
, &prop
))
5839 return obj
->isNative()
5840 ? js_SetNativeAttributes(cx
, obj
, (Shape
*) prop
, *attrsp
)
5841 : obj
->setAttributes(cx
, id
, attrsp
);
5845 js_DeleteProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*rval
, JSBool strict
)
5851 rval
->setBoolean(true);
5853 /* Convert string indices to integers if appropriate. */
5854 id
= js_CheckForStringIndex(id
);
5856 if (!js_LookupProperty(cx
, obj
, id
, &proto
, &prop
))
5858 if (!prop
|| proto
!= obj
) {
5860 * If the property was found in a native prototype, check whether it's
5861 * shared and permanent. Such a property stands for direct properties
5862 * in all delegating objects, matching ECMA semantics without bloating
5863 * each delegating object.
5865 if (prop
&& proto
->isNative()) {
5866 shape
= (Shape
*)prop
;
5867 if (shape
->isSharedPermanent()) {
5869 return obj
->reportNotConfigurable(cx
, id
);
5870 rval
->setBoolean(false);
5876 * If no property, or the property comes unshared or impermanent from
5877 * a prototype, call the class's delProperty hook, passing rval as the
5880 return CallJSPropertyOp(cx
, obj
->getClass()->delProperty
, obj
, id
, rval
);
5883 shape
= (Shape
*)prop
;
5884 if (!shape
->configurable()) {
5886 return obj
->reportNotConfigurable(cx
, id
);
5887 rval
->setBoolean(false);
5891 if (!CallJSPropertyOp(cx
, obj
->getClass()->delProperty
, obj
, SHAPE_USERID(shape
), rval
))
5894 if (obj
->containsSlot(shape
->slot
)) {
5895 const Value
&v
= obj
->nativeGetSlot(shape
->slot
);
5899 * Delete is rare enough that we can take the hit of checking for an
5900 * active cloned method function object that must be homed to a callee
5901 * slot on the active stack frame before this delete completes, in case
5902 * someone saved the clone and checks it against foo.caller for a foo
5903 * called from the active method.
5905 * We do not check suspended frames. They can't be reached via caller,
5906 * so the only way they could have the method's joined function object
5907 * as callee is through an API abusage. We break any such edge case.
5909 if (obj
->hasMethodBarrier()) {
5912 if (IsFunctionObject(v
, &funobj
)) {
5913 JSFunction
*fun
= GET_FUNCTION_PRIVATE(cx
, funobj
);
5915 if (fun
!= funobj
) {
5916 for (JSStackFrame
*fp
= cx
->maybefp(); fp
; fp
= fp
->prev()) {
5917 if (fp
->isFunctionFrame() &&
5918 &fp
->callee() == &fun
->compiledFunObj() &&
5919 fp
->thisValue().isObject())
5921 JSObject
*tmp
= &fp
->thisValue().toObject();
5924 fp
->calleeValue().setObject(*funobj
);
5927 } while ((tmp
= tmp
->getProto()) != NULL
);
5935 return obj
->removeProperty(cx
, id
) && js_SuppressDeletedProperty(cx
, obj
, id
);
5941 HasNativeMethod(JSObject
*obj
, jsid methodid
, Native native
)
5943 const Shape
*shape
= obj
->nativeLookup(methodid
);
5944 if (!shape
|| !shape
->hasDefaultGetter() || !obj
->containsSlot(shape
->slot
))
5947 const Value
&fval
= obj
->nativeGetSlot(shape
->slot
);
5949 if (!IsFunctionObject(fval
, &funobj
) || funobj
->getFunctionPrivate()->maybeNative() != native
)
5956 DefaultValue(JSContext
*cx
, JSObject
*obj
, JSType hint
, Value
*vp
)
5958 JS_ASSERT(hint
!= JSTYPE_OBJECT
&& hint
!= JSTYPE_FUNCTION
);
5960 Value v
= ObjectValue(*obj
);
5961 if (hint
== JSTYPE_STRING
) {
5962 /* Optimize (new String(...)).toString(). */
5963 if (obj
->getClass() == &js_StringClass
&&
5964 ClassMethodIsNative(cx
, obj
,
5966 ATOM_TO_JSID(cx
->runtime
->atomState
.toStringAtom
),
5968 *vp
= obj
->getPrimitiveThis();
5972 if (!js_TryMethod(cx
, obj
, cx
->runtime
->atomState
.toStringAtom
, 0, NULL
, &v
))
5974 if (!v
.isPrimitive()) {
5975 if (!obj
->getClass()->convert(cx
, obj
, hint
, &v
))
5979 /* Optimize (new String(...)).valueOf(). */
5980 Class
*clasp
= obj
->getClass();
5981 if ((clasp
== &js_StringClass
&&
5982 ClassMethodIsNative(cx
, obj
, &js_StringClass
,
5983 ATOM_TO_JSID(cx
->runtime
->atomState
.valueOfAtom
),
5984 js_str_toString
)) ||
5985 (clasp
== &js_NumberClass
&&
5986 ClassMethodIsNative(cx
, obj
, &js_NumberClass
,
5987 ATOM_TO_JSID(cx
->runtime
->atomState
.valueOfAtom
),
5989 *vp
= obj
->getPrimitiveThis();
5993 if (!obj
->getClass()->convert(cx
, obj
, hint
, &v
))
5996 JS_ASSERT(hint
!= TypeOfValue(cx
, v
));
5997 if (!js_TryMethod(cx
, obj
, cx
->runtime
->atomState
.toStringAtom
, 0, NULL
, &v
))
6002 /* Avoid recursive death when decompiling in js_ReportValueError. */
6004 if (hint
== JSTYPE_STRING
) {
6005 str
= JS_InternString(cx
, obj
->getClass()->name
);
6011 vp
->setObject(*obj
);
6012 js_ReportValueError2(cx
, JSMSG_CANT_CONVERT_TO
,
6013 JSDVG_SEARCH_STACK
, *vp
, str
,
6014 (hint
== JSTYPE_VOID
)
6016 : JS_TYPE_STR(hint
));
6023 } /* namespace js */
6025 JS_FRIEND_API(JSBool
)
6026 js_Enumerate(JSContext
*cx
, JSObject
*obj
, JSIterateOp enum_op
, Value
*statep
, jsid
*idp
)
6028 /* If the class has a custom JSCLASS_NEW_ENUMERATE hook, call it. */
6029 Class
*clasp
= obj
->getClass();
6030 JSEnumerateOp enumerate
= clasp
->enumerate
;
6031 if (clasp
->flags
& JSCLASS_NEW_ENUMERATE
) {
6032 JS_ASSERT(enumerate
!= JS_EnumerateStub
);
6033 return ((NewEnumerateOp
) enumerate
)(cx
, obj
, enum_op
, statep
, idp
);
6036 if (!enumerate(cx
, obj
))
6039 /* Tell InitNativeIterator to treat us like a native object. */
6040 JS_ASSERT(enum_op
== JSENUMERATE_INIT
|| enum_op
== JSENUMERATE_INIT_ALL
);
6041 statep
->setMagic(JS_NATIVE_ENUMERATE
);
6048 CheckAccess(JSContext
*cx
, JSObject
*obj
, jsid id
, JSAccessMode mode
,
6049 Value
*vp
, uintN
*attrsp
)
6056 JSSecurityCallbacks
*callbacks
;
6057 CheckAccessOp check
;
6059 while (JS_UNLIKELY(obj
->getClass() == &js_WithClass
))
6060 obj
= obj
->getProto();
6062 writing
= (mode
& JSACC_WRITE
) != 0;
6063 switch (mode
& JSACC_TYPEMASK
) {
6067 vp
->setObjectOrNull(obj
->getProto());
6068 *attrsp
= JSPROP_PERMANENT
;
6072 JS_ASSERT(!writing
);
6074 vp
->setObject(*obj
->getParent());
6075 *attrsp
= JSPROP_READONLY
| JSPROP_PERMANENT
;
6079 if (!obj
->lookupProperty(cx
, id
, &pobj
, &prop
))
6089 if (!pobj
->isNative()) {
6097 shape
= (Shape
*)prop
;
6098 *attrsp
= shape
->attributes();
6100 if (pobj
->containsSlot(shape
->slot
))
6101 *vp
= pobj
->nativeGetSlot(shape
->slot
);
6108 * If obj's class has a stub (null) checkAccess hook, use the per-runtime
6109 * checkObjectAccess callback, if configured.
6111 * We don't want to require all classes to supply a checkAccess hook; we
6112 * need that hook only for certain classes used when precompiling scripts
6113 * and functions ("brutal sharing"). But for general safety of built-in
6114 * magic properties like __proto__, we route all access checks, even for
6115 * classes that stub out checkAccess, through the global checkObjectAccess
6116 * hook. This covers precompilation-based sharing and (possibly
6117 * unintended) runtime sharing across trust boundaries.
6119 clasp
= pobj
->getClass();
6120 check
= clasp
->checkAccess
;
6122 callbacks
= JS_GetSecurityCallbacks(cx
);
6123 check
= callbacks
? Valueify(callbacks
->checkObjectAccess
) : NULL
;
6125 return !check
|| check(cx
, pobj
, id
, mode
, vp
);
6131 js_TypeOf(JSContext
*cx
, JSObject
*obj
)
6134 * ECMA 262, 11.4.3 says that any native object that implements
6135 * [[Call]] should be of type "function". However, RegExp is of
6136 * type "object", not "function", for Web compatibility.
6138 if (obj
->isCallable()) {
6139 return (obj
->getClass() != &js_RegExpClass
)
6144 return JSTYPE_OBJECT
;
6148 js_IsDelegate(JSContext
*cx
, JSObject
*obj
, const Value
&v
)
6150 if (v
.isPrimitive())
6152 JSObject
*obj2
= &v
.toObject();
6153 while ((obj2
= obj2
->getProto()) != NULL
) {
6161 js::FindClassPrototype(JSContext
*cx
, JSObject
*scopeobj
, JSProtoKey protoKey
,
6162 JSObject
**protop
, Class
*clasp
)
6165 if (!js_FindClassObject(cx
, scopeobj
, protoKey
, &v
, clasp
))
6168 if (IsFunctionObject(v
)) {
6169 JSObject
*ctor
= &v
.toObject();
6170 if (!ctor
->getProperty(cx
, ATOM_TO_JSID(cx
->runtime
->atomState
.classPrototypeAtom
), &v
))
6174 *protop
= v
.isObject() ? &v
.toObject() : NULL
;
6179 * The first part of this function has been hand-expanded and optimized into
6180 * NewBuiltinClassInstance in jsobjinlines.h.
6183 js_GetClassPrototype(JSContext
*cx
, JSObject
*scopeobj
, JSProtoKey protoKey
,
6184 JSObject
**protop
, Class
*clasp
)
6186 VOUCH_DOES_NOT_REQUIRE_STACK();
6187 JS_ASSERT(JSProto_Null
<= protoKey
);
6188 JS_ASSERT(protoKey
< JSProto_LIMIT
);
6190 if (protoKey
!= JSProto_Null
) {
6193 scopeobj
= &cx
->fp()->scopeChain();
6195 scopeobj
= cx
->globalObject
;
6202 scopeobj
= scopeobj
->getGlobal();
6203 if (scopeobj
->isGlobal()) {
6204 const Value
&v
= scopeobj
->getReservedSlot(JSProto_LIMIT
+ protoKey
);
6206 *protop
= &v
.toObject();
6212 return FindClassPrototype(cx
, scopeobj
, protoKey
, protop
, clasp
);
6216 js_SetClassPrototype(JSContext
*cx
, JSObject
*ctor
, JSObject
*proto
, uintN attrs
)
6219 * Use the given attributes for the prototype property of the constructor,
6220 * as user-defined constructors have a DontDelete prototype (which may be
6221 * reset), while native or "system" constructors have DontEnum | ReadOnly |
6224 if (!ctor
->defineProperty(cx
, ATOM_TO_JSID(cx
->runtime
->atomState
.classPrototypeAtom
),
6225 ObjectOrNullValue(proto
), PropertyStub
, StrictPropertyStub
, attrs
)) {
6230 * ECMA says that Object.prototype.constructor, or f.prototype.constructor
6231 * for a user-defined function f, is DontEnum.
6233 return proto
->defineProperty(cx
, ATOM_TO_JSID(cx
->runtime
->atomState
.constructorAtom
),
6234 ObjectOrNullValue(ctor
), PropertyStub
, StrictPropertyStub
, 0);
6238 PrimitiveToObject(JSContext
*cx
, const Value
&v
)
6240 JS_ASSERT(v
.isPrimitive());
6244 clasp
= &js_NumberClass
;
6245 } else if (v
.isString()) {
6246 clasp
= &js_StringClass
;
6248 JS_ASSERT(v
.isBoolean());
6249 clasp
= &js_BooleanClass
;
6252 JSObject
*obj
= NewBuiltinClassInstance(cx
, clasp
);
6256 obj
->setPrimitiveThis(v
);
6261 js_PrimitiveToObject(JSContext
*cx
, Value
*vp
)
6263 JSObject
*obj
= PrimitiveToObject(cx
, *vp
);
6267 vp
->setObject(*obj
);
6272 js_ValueToObjectOrNull(JSContext
*cx
, const Value
&v
, JSObject
**objp
)
6276 if (v
.isObjectOrNull()) {
6277 obj
= v
.toObjectOrNull();
6278 } else if (v
.isUndefined()) {
6281 obj
= PrimitiveToObject(cx
, v
);
6291 /* Callers must handle the already-object case . */
6293 ToObjectSlow(JSContext
*cx
, Value
*vp
)
6295 JS_ASSERT(!vp
->isMagic());
6296 JS_ASSERT(!vp
->isObject());
6298 if (vp
->isNullOrUndefined()) {
6299 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_CANT_CONVERT_TO
,
6300 vp
->isNull() ? "null" : "undefined", "object");
6304 JSObject
*obj
= PrimitiveToObject(cx
, *vp
);
6306 vp
->setObject(*obj
);
6313 js_ValueToNonNullObject(JSContext
*cx
, const Value
&v
)
6317 if (!js_ValueToObjectOrNull(cx
, v
, &obj
))
6320 js_ReportIsNullOrUndefined(cx
, JSDVG_SEARCH_STACK
, v
, NULL
);
6325 js_TryValueOf(JSContext
*cx
, JSObject
*obj
, JSType type
, Value
*rval
)
6329 argv
[0].setString(ATOM_TO_STRING(cx
->runtime
->atomState
.typeAtoms
[type
]));
6330 return js_TryMethod(cx
, obj
, cx
->runtime
->atomState
.valueOfAtom
,
6335 js_TryMethod(JSContext
*cx
, JSObject
*obj
, JSAtom
*atom
,
6336 uintN argc
, Value
*argv
, Value
*rval
)
6338 JS_CHECK_RECURSION(cx
, return JS_FALSE
);
6341 * Report failure only if an appropriate method was found, and calling it
6342 * returned failure. We propagate failure in this case to make exceptions
6345 JSErrorReporter older
= JS_SetErrorReporter(cx
, NULL
);
6346 jsid id
= ATOM_TO_JSID(atom
);
6348 JSBool ok
= js_GetMethod(cx
, obj
, id
, JSGET_NO_METHOD_BARRIER
, &fval
);
6349 JS_SetErrorReporter(cx
, older
);
6353 if (fval
.isPrimitive())
6355 return ExternalInvoke(cx
, ObjectValue(*obj
), fval
, argc
, argv
, rval
);
6361 js_XDRObject(JSXDRState
*xdr
, JSObject
**objp
)
6366 uint32 classId
, classDef
;
6367 JSProtoKey protoKey
;
6372 if (xdr
->mode
== JSXDR_ENCODE
) {
6373 clasp
= (*objp
)->getClass();
6374 classId
= JS_XDRFindClassIdByName(xdr
, clasp
->name
);
6375 classDef
= !classId
;
6377 if (!JS_XDRRegisterClass(xdr
, Jsvalify(clasp
), &classId
))
6379 protoKey
= JSCLASS_CACHED_PROTO_KEY(clasp
);
6380 if (protoKey
!= JSProto_Null
) {
6381 classDef
|= (protoKey
<< 1);
6383 atom
= js_Atomize(cx
, clasp
->name
, strlen(clasp
->name
), 0);
6389 clasp
= NULL
; /* quell GCC overwarning */
6394 * XDR a flag word, which could be 0 for a class use, in which case no
6395 * name follows, only the id in xdr's class registry; 1 for a class def,
6396 * in which case the flag word is followed by the class name transferred
6397 * from or to atom; or a value greater than 1, an odd number that when
6398 * divided by two yields the JSProtoKey for class. In the last case, as
6399 * in the 0 classDef case, no name is transferred via atom.
6401 if (!JS_XDRUint32(xdr
, &classDef
))
6403 if (classDef
== 1 && !js_XDRAtom(xdr
, &atom
))
6406 if (!JS_XDRUint32(xdr
, &classId
))
6409 if (xdr
->mode
== JSXDR_DECODE
) {
6411 /* NB: we know that JSProto_Null is 0 here, for backward compat. */
6412 protoKey
= (JSProtoKey
) (classDef
>> 1);
6413 if (!js_GetClassPrototype(cx
, NULL
, protoKey
, &proto
, clasp
))
6415 clasp
= proto
->getClass();
6416 if (!JS_XDRRegisterClass(xdr
, Jsvalify(clasp
), &classId
))
6419 clasp
= Valueify(JS_XDRFindClassById(xdr
, classId
));
6422 JS_snprintf(numBuf
, sizeof numBuf
, "%ld", (long)classId
);
6423 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
6424 JSMSG_CANT_FIND_CLASS
, numBuf
);
6430 if (!clasp
->xdrObject
) {
6431 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
6432 JSMSG_CANT_XDR_CLASS
, clasp
->name
);
6435 return clasp
->xdrObject(xdr
, objp
);
6438 #endif /* JS_HAS_XDR */
6440 #ifdef JS_DUMP_SCOPE_METERS
6444 JSBasicStats js_entry_count_bs
= JS_INIT_STATIC_BASIC_STATS
;
6447 MeterEntryCount(uintN count
)
6449 JS_BASIC_STATS_ACCUM(&js_entry_count_bs
, count
);
6453 js_DumpScopeMeters(JSRuntime
*rt
)
6457 logfp
= fopen("/tmp/scope.stats", "a");
6462 mean
= JS_MeanAndStdDevBS(&js_entry_count_bs
, &sigma
);
6464 fprintf(logfp
, "scopes %u entries %g mean %g sigma %g max %u",
6465 js_entry_count_bs
.num
, js_entry_count_bs
.sum
, mean
, sigma
,
6466 js_entry_count_bs
.max
);
6469 JS_DumpHistogram(&js_entry_count_bs
, logfp
);
6470 JS_BASIC_STATS_INIT(&js_entry_count_bs
);
6477 js_PrintObjectSlotName(JSTracer
*trc
, char *buf
, size_t bufsize
)
6479 JS_ASSERT(trc
->debugPrinter
== js_PrintObjectSlotName
);
6481 JSObject
*obj
= (JSObject
*)trc
->debugPrintArg
;
6482 uint32 slot
= (uint32
)trc
->debugPrintIndex
;
6485 if (obj
->isNative()) {
6486 shape
= obj
->lastProperty();
6487 while (shape
->previous() && shape
->slot
!= slot
)
6488 shape
= shape
->previous();
6489 if (shape
->slot
!= slot
)
6496 const char *slotname
= NULL
;
6497 if (obj
->isGlobal()) {
6498 #define JS_PROTO(name,code,init) \
6499 if ((code) == slot) { slotname = js_##name##_str; goto found; }
6500 #include "jsproto.tbl"
6505 JS_snprintf(buf
, bufsize
, "CLASS_OBJECT(%s)", slotname
);
6507 JS_snprintf(buf
, bufsize
, "**UNKNOWN SLOT %ld**", (long)slot
);
6509 jsid id
= shape
->id
;
6510 if (JSID_IS_INT(id
)) {
6511 JS_snprintf(buf
, bufsize
, "%ld", (long)JSID_TO_INT(id
));
6512 } else if (JSID_IS_ATOM(id
)) {
6513 PutEscapedString(buf
, bufsize
, JSID_TO_ATOM(id
), 0);
6515 JS_snprintf(buf
, bufsize
, "**FINALIZED ATOM KEY**");
6522 js_TraceObject(JSTracer
*trc
, JSObject
*obj
)
6524 JS_ASSERT(obj
->isNative());
6526 JSContext
*cx
= trc
->context
;
6527 if (obj
->hasSlotsArray() && !obj
->nativeEmpty() && IS_GC_MARKING_TRACER(trc
)) {
6529 * Trim overlong dslots allocations from the GC, to avoid thrashing in
6530 * case of delete-happy code that settles down at a given population.
6531 * The !obj->nativeEmpty() guard above is due to the bug described by
6532 * the FIXME comment below.
6534 size_t slots
= obj
->slotSpan();
6535 if (obj
->numSlots() != slots
)
6536 obj
->shrinkSlots(cx
, slots
);
6539 #ifdef JS_DUMP_SCOPE_METERS
6540 MeterEntryCount(obj
->propertyCount
);
6545 if (!JS_CLIST_IS_EMPTY(&cx
->runtime
->watchPointList
))
6546 js_TraceWatchPoints(trc
, obj
);
6548 /* No one runs while the GC is running, so we can use LOCKED_... here. */
6549 Class
*clasp
= obj
->getClass();
6551 if (clasp
->flags
& JSCLASS_MARK_IS_TRACE
)
6552 ((JSTraceOp
) clasp
->mark
)(trc
, obj
);
6553 else if (IS_GC_MARKING_TRACER(trc
))
6554 (void) clasp
->mark(cx
, obj
, trc
);
6556 if (clasp
->flags
& JSCLASS_IS_GLOBAL
) {
6557 JSCompartment
*compartment
= obj
->getCompartment();
6558 compartment
->mark(trc
);
6562 * NB: clasp->mark could mutate something (which would be a bug, but we are
6563 * defensive), so don't hoist this above calling clasp->mark.
6565 uint32 nslots
= Min(obj
->numSlots(), obj
->slotSpan());
6566 for (uint32 i
= 0; i
!= nslots
; ++i
) {
6567 const Value
&v
= obj
->getSlot(i
);
6568 JS_SET_TRACING_DETAILS(trc
, js_PrintObjectSlotName
, obj
, i
);
6569 MarkValueRaw(trc
, v
);
6574 js_ClearNative(JSContext
*cx
, JSObject
*obj
)
6577 * Clear obj of all obj's properties. FIXME: we do not clear reserved slots
6578 * lying below JSSLOT_FREE(clasp). JS_ClearScope does that.
6580 if (!obj
->nativeEmpty()) {
6581 /* Now that we're done using real properties, clear obj. */
6584 /* Clear slot values since obj->clear reset our shape to empty. */
6585 uint32 freeslot
= JSSLOT_FREE(obj
->getClass());
6586 uint32 n
= obj
->numSlots();
6587 for (uint32 i
= freeslot
; i
< n
; ++i
)
6588 obj
->setSlot(i
, UndefinedValue());
6593 js_GetReservedSlot(JSContext
*cx
, JSObject
*obj
, uint32 slot
, Value
*vp
)
6595 if (!obj
->isNative()) {
6600 if (slot
< obj
->numSlots())
6601 *vp
= obj
->getSlot(slot
);
6608 js_SetReservedSlot(JSContext
*cx
, JSObject
*obj
, uint32 slot
, const Value
&v
)
6610 if (!obj
->isNative())
6613 Class
*clasp
= obj
->getClass();
6615 if (slot
>= obj
->numSlots()) {
6616 uint32 nslots
= JSSLOT_FREE(clasp
);
6617 JS_ASSERT(slot
< nslots
);
6618 if (!obj
->allocSlots(cx
, nslots
))
6622 obj
->setSlot(slot
, v
);
6623 GC_POKE(cx
, JS_NULL
);
6628 JSObject::getGlobal() const
6630 JSObject
*obj
= const_cast<JSObject
*>(this);
6631 while (JSObject
*parent
= obj
->getParent())
6637 js_ReportGetterOnlyAssignment(JSContext
*cx
)
6639 return JS_ReportErrorFlagsAndNumber(cx
,
6640 JSREPORT_WARNING
| JSREPORT_STRICT
|
6641 JSREPORT_STRICT_MODE_ERROR
,
6642 js_GetErrorMessage
, NULL
,
6646 JS_FRIEND_API(JSBool
)
6647 js_GetterOnlyPropertyStub(JSContext
*cx
, JSObject
*obj
, jsid id
, JSBool strict
, jsval
*vp
)
6649 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_GETTER_ONLY
);
6656 * Routines to print out values during debugging. These are FRIEND_API to help
6657 * the debugger find them and to support temporarily hacking js_Dump* calls
6662 dumpChars(const jschar
*s
, size_t n
)
6666 if (n
== (size_t) -1) {
6671 for (i
= 0; i
< n
; i
++) {
6673 fprintf(stderr
, "\\n");
6674 else if (s
[i
] == '\t')
6675 fprintf(stderr
, "\\t");
6676 else if (s
[i
] >= 32 && s
[i
] < 127)
6677 fputc(s
[i
], stderr
);
6678 else if (s
[i
] <= 255)
6679 fprintf(stderr
, "\\x%02x", (unsigned int) s
[i
]);
6681 fprintf(stderr
, "\\u%04x", (unsigned int) s
[i
]);
6687 js_DumpChars(const jschar
*s
, size_t n
)
6689 fprintf(stderr
, "jschar * (%p) = ", (void *) s
);
6691 fputc('\n', stderr
);
6695 dumpString(JSString
*str
)
6697 if (const jschar
*chars
= str
->getChars(NULL
))
6698 dumpChars(chars
, str
->length());
6700 fprintf(stderr
, "(oom in dumpString)");
6704 js_DumpString(JSString
*str
)
6706 if (const jschar
*chars
= str
->getChars(NULL
)) {
6707 fprintf(stderr
, "JSString* (%p) = jschar * (%p) = ",
6708 (void *) str
, (void *) chars
);
6711 fprintf(stderr
, "(oom in JS_DumpString)");
6713 fputc('\n', stderr
);
6717 js_DumpAtom(JSAtom
*atom
)
6719 fprintf(stderr
, "JSAtom* (%p) = ", (void *) atom
);
6720 js_DumpString(ATOM_TO_STRING(atom
));
6724 dumpValue(const Value
&v
)
6727 fprintf(stderr
, "null");
6728 else if (v
.isUndefined())
6729 fprintf(stderr
, "undefined");
6730 else if (v
.isInt32())
6731 fprintf(stderr
, "%d", v
.toInt32());
6732 else if (v
.isDouble())
6733 fprintf(stderr
, "%g", v
.toDouble());
6734 else if (v
.isString())
6735 dumpString(v
.toString());
6736 else if (v
.isObject() && v
.toObject().isFunction()) {
6737 JSObject
*funobj
= &v
.toObject();
6738 JSFunction
*fun
= GET_FUNCTION_PRIVATE(cx
, funobj
);
6740 fputs("<function ", stderr
);
6741 FileEscapedString(stderr
, ATOM_TO_STRING(fun
->atom
), 0);
6743 fputs("<unnamed function", stderr
);
6745 if (fun
->isInterpreted()) {
6746 JSScript
*script
= fun
->script();
6747 fprintf(stderr
, " (%s:%u)",
6748 script
->filename
? script
->filename
: "", script
->lineno
);
6750 fprintf(stderr
, " at %p (JSFunction at %p)>", (void *) funobj
, (void *) fun
);
6751 } else if (v
.isObject()) {
6752 JSObject
*obj
= &v
.toObject();
6753 Class
*clasp
= obj
->getClass();
6754 fprintf(stderr
, "<%s%s at %p>",
6756 (clasp
== &js_ObjectClass
) ? "" : " object",
6758 } else if (v
.isBoolean()) {
6760 fprintf(stderr
, "true");
6762 fprintf(stderr
, "false");
6763 } else if (v
.isMagic()) {
6764 fprintf(stderr
, "<invalid");
6766 switch (v
.whyMagic()) {
6767 case JS_ARRAY_HOLE
: fprintf(stderr
, " array hole"); break;
6768 case JS_ARGS_HOLE
: fprintf(stderr
, " args hole"); break;
6769 case JS_NATIVE_ENUMERATE
: fprintf(stderr
, " native enumeration"); break;
6770 case JS_NO_ITER_VALUE
: fprintf(stderr
, " no iter value"); break;
6771 case JS_GENERATOR_CLOSING
: fprintf(stderr
, " generator closing"); break;
6772 default: fprintf(stderr
, " ?!"); break;
6775 fprintf(stderr
, ">");
6777 fprintf(stderr
, "unexpected value");
6782 js_DumpValue(const Value
&val
)
6785 fputc('\n', stderr
);
6791 fprintf(stderr
, "jsid %p = ", (void *) JSID_BITS(id
));
6792 dumpValue(IdToValue(id
));
6793 fputc('\n', stderr
);
6797 DumpShape(const Shape
&shape
)
6800 uint8 attrs
= shape
.attributes();
6802 fprintf(stderr
, " ");
6803 if (attrs
& JSPROP_ENUMERATE
) fprintf(stderr
, "enumerate ");
6804 if (attrs
& JSPROP_READONLY
) fprintf(stderr
, "readonly ");
6805 if (attrs
& JSPROP_PERMANENT
) fprintf(stderr
, "permanent ");
6806 if (attrs
& JSPROP_GETTER
) fprintf(stderr
, "getter ");
6807 if (attrs
& JSPROP_SETTER
) fprintf(stderr
, "setter ");
6808 if (attrs
& JSPROP_SHARED
) fprintf(stderr
, "shared ");
6809 if (shape
.isAlias()) fprintf(stderr
, "alias ");
6810 if (shape
.isMethod()) fprintf(stderr
, "method(%p) ", (void *) &shape
.methodObject());
6812 if (JSID_IS_ATOM(id
))
6813 dumpString(JSID_TO_STRING(id
));
6814 else if (JSID_IS_INT(id
))
6815 fprintf(stderr
, "%d", (int) JSID_TO_INT(id
));
6817 fprintf(stderr
, "unknown jsid %p", (void *) JSID_BITS(id
));
6818 fprintf(stderr
, ": slot %d", shape
.slot
);
6819 fprintf(stderr
, "\n");
6823 js_DumpObject(JSObject
*obj
)
6825 fprintf(stderr
, "object %p\n", (void *) obj
);
6826 Class
*clasp
= obj
->getClass();
6827 fprintf(stderr
, "class %p %s\n", (void *)clasp
, clasp
->name
);
6829 fprintf(stderr
, "flags:");
6830 uint32 flags
= obj
->flags
;
6831 if (flags
& JSObject::DELEGATE
) fprintf(stderr
, " delegate");
6832 if (flags
& JSObject::SYSTEM
) fprintf(stderr
, " system");
6833 if (flags
& JSObject::NOT_EXTENSIBLE
) fprintf(stderr
, " not extensible");
6834 if (flags
& JSObject::BRANDED
) fprintf(stderr
, " branded");
6835 if (flags
& JSObject::GENERIC
) fprintf(stderr
, " generic");
6836 if (flags
& JSObject::METHOD_BARRIER
) fprintf(stderr
, " method_barrier");
6837 if (flags
& JSObject::INDEXED
) fprintf(stderr
, " indexed");
6838 if (flags
& JSObject::OWN_SHAPE
) fprintf(stderr
, " own_shape");
6839 if (flags
& JSObject::HAS_EQUALITY
) fprintf(stderr
, " has_equality");
6841 bool anyFlags
= flags
!= 0;
6842 if (obj
->isNative()) {
6843 if (obj
->inDictionaryMode()) {
6844 fprintf(stderr
, " inDictionaryMode");
6847 if (obj
->hasPropertyTable()) {
6848 fprintf(stderr
, " hasPropertyTable");
6853 fprintf(stderr
, " none");
6854 fprintf(stderr
, "\n");
6856 if (obj
->isDenseArray()) {
6857 unsigned slots
= JS_MIN(obj
->getArrayLength(), obj
->getDenseArrayCapacity());
6858 fprintf(stderr
, "elements\n");
6859 for (unsigned i
= 0; i
< slots
; i
++) {
6860 fprintf(stderr
, " %3d: ", i
);
6861 dumpValue(obj
->getDenseArrayElement(i
));
6862 fprintf(stderr
, "\n");
6868 if (obj
->isNative()) {
6869 fprintf(stderr
, "properties:\n");
6870 for (Shape::Range r
= obj
->lastProperty()->all(); !r
.empty(); r
.popFront())
6871 DumpShape(r
.front());
6873 if (!obj
->isNative())
6874 fprintf(stderr
, "not native\n");
6877 fprintf(stderr
, "proto ");
6878 dumpValue(ObjectOrNullValue(obj
->getProto()));
6879 fputc('\n', stderr
);
6881 fprintf(stderr
, "parent ");
6882 dumpValue(ObjectOrNullValue(obj
->getParent()));
6883 fputc('\n', stderr
);
6885 if (clasp
->flags
& JSCLASS_HAS_PRIVATE
)
6886 fprintf(stderr
, "private %p\n", obj
->getPrivate());
6888 fprintf(stderr
, "slots:\n");
6889 unsigned reservedEnd
= JSCLASS_RESERVED_SLOTS(clasp
);
6890 unsigned slots
= obj
->slotSpan();
6891 for (unsigned i
= 0; i
< slots
; i
++) {
6892 fprintf(stderr
, " %3d ", i
);
6893 if (i
< reservedEnd
)
6894 fprintf(stderr
, "(reserved) ");
6895 fprintf(stderr
, "= ");
6896 dumpValue(obj
->getSlot(i
));
6897 fputc('\n', stderr
);
6899 fputc('\n', stderr
);
6903 MaybeDumpObject(const char *name
, JSObject
*obj
)
6906 fprintf(stderr
, " %s: ", name
);
6907 dumpValue(ObjectValue(*obj
));
6908 fputc('\n', stderr
);
6913 MaybeDumpValue(const char *name
, const Value
&v
)
6916 fprintf(stderr
, " %s: ", name
);
6918 fputc('\n', stderr
);
6923 js_DumpStackFrame(JSContext
*cx
, JSStackFrame
*start
)
6925 /* This should only called during live debugging. */
6926 VOUCH_DOES_NOT_REQUIRE_STACK();
6929 start
= cx
->maybefp();
6930 FrameRegsIter
i(cx
);
6931 while (!i
.done() && i
.fp() != start
)
6935 fprintf(stderr
, "fp = %p not found in cx = %p\n", (void *)start
, (void *)cx
);
6939 for (; !i
.done(); ++i
) {
6940 JSStackFrame
*const fp
= i
.fp();
6942 fprintf(stderr
, "JSStackFrame at %p\n", (void *) fp
);
6943 if (fp
->isFunctionFrame()) {
6944 fprintf(stderr
, "callee fun: ");
6945 dumpValue(ObjectValue(fp
->callee()));
6947 fprintf(stderr
, "global frame, no callee");
6949 fputc('\n', stderr
);
6951 if (fp
->isScriptFrame()) {
6952 fprintf(stderr
, "file %s line %u\n",
6953 fp
->script()->filename
, (unsigned) fp
->script()->lineno
);
6956 if (jsbytecode
*pc
= i
.pc()) {
6957 if (!fp
->isScriptFrame()) {
6958 fprintf(stderr
, "*** pc && !script, skipping frame\n\n");
6961 if (fp
->hasImacropc()) {
6962 fprintf(stderr
, " pc in imacro at %p\n called from ", pc
);
6963 pc
= fp
->imacropc();
6965 fprintf(stderr
, " ");
6967 fprintf(stderr
, "pc = %p\n", pc
);
6968 fprintf(stderr
, " current op: %s\n", js_CodeName
[*pc
]);
6971 fprintf(stderr
, " slots: %p\n", (void *) fp
->slots());
6972 fprintf(stderr
, " sp: %p = slots + %u\n", (void *) sp
, (unsigned) (sp
- fp
->slots()));
6973 if (sp
- fp
->slots() < 10000) { // sanity
6974 for (Value
*p
= fp
->slots(); p
< sp
; p
++) {
6975 fprintf(stderr
, " %p: ", (void *) p
);
6977 fputc('\n', stderr
);
6980 if (fp
->isFunctionFrame() && !fp
->isEvalFrame()) {
6981 fprintf(stderr
, " actuals: %p (%u) ", (void *) fp
->actualArgs(), (unsigned) fp
->numActualArgs());
6982 fprintf(stderr
, " formals: %p (%u)\n", (void *) fp
->formalArgs(), (unsigned) fp
->numFormalArgs());
6984 MaybeDumpObject("callobj", fp
->maybeCallObj());
6985 MaybeDumpObject("argsobj", fp
->maybeArgsObj());
6986 if (!fp
->isDummyFrame()) {
6987 MaybeDumpValue("this", fp
->thisValue());
6988 fprintf(stderr
, " rval: ");
6989 dumpValue(fp
->returnValue());
6991 fprintf(stderr
, "dummy frame");
6993 fputc('\n', stderr
);
6995 fprintf(stderr
, " flags:");
6996 if (fp
->isConstructing())
6997 fprintf(stderr
, " constructing");
6998 if (fp
->hasOverriddenArgs())
6999 fprintf(stderr
, " overridden_args");
7000 if (fp
->isAssigning())
7001 fprintf(stderr
, " assigning");
7002 if (fp
->isDebuggerFrame())
7003 fprintf(stderr
, " debugger");
7004 if (fp
->isEvalFrame())
7005 fprintf(stderr
, " eval");
7006 if (fp
->isYielding())
7007 fprintf(stderr
, " yielding");
7008 if (fp
->isGeneratorFrame())
7009 fprintf(stderr
, " generator");
7010 fputc('\n', stderr
);
7012 fprintf(stderr
, " scopeChain: (JSObject *) %p\n", (void *) &fp
->scopeChain());
7014 fputc('\n', stderr
);