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"
81 #include "jsinterpinlines.h"
82 #include "jsscopeinlines.h"
83 #include "jsscriptinlines.h"
84 #include "jsobjinlines.h"
90 #if JS_HAS_XML_SUPPORT
99 #include "jsatominlines.h"
100 #include "jsobjinlines.h"
101 #include "jsscriptinlines.h"
103 #include "jsautooplen.h"
106 using namespace js::gc
;
108 JS_FRIEND_DATA(const JSObjectMap
) JSObjectMap::sharedNonNative(JSObjectMap::SHAPELESS
);
110 Class js_ObjectClass
= {
112 JSCLASS_HAS_CACHED_PROTO(JSProto_Object
),
113 PropertyStub
, /* addProperty */
114 PropertyStub
, /* delProperty */
115 PropertyStub
, /* getProperty */
116 PropertyStub
, /* setProperty */
122 JS_FRIEND_API(JSObject
*)
123 js_ObjectToOuterObject(JSContext
*cx
, JSObject
*obj
)
125 OBJ_TO_OUTER_OBJECT(cx
, obj
);
129 #if JS_HAS_OBJ_PROTO_PROP
132 obj_getProto(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
);
135 obj_setProto(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
);
137 static JSPropertySpec object_props
[] = {
138 {js_proto_str
, 0, JSPROP_PERMANENT
|JSPROP_SHARED
, Jsvalify(obj_getProto
), Jsvalify(obj_setProto
)},
143 obj_getProto(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
145 /* Let CheckAccess get the slot's value, based on the access mode. */
147 id
= ATOM_TO_JSID(cx
->runtime
->atomState
.protoAtom
);
148 return CheckAccess(cx
, obj
, id
, JSACC_PROTO
, vp
, &attrs
);
152 obj_setProto(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
154 /* ECMAScript 5 8.6.2 forbids changing [[Prototype]] if not [[Extensible]]. */
155 if (!obj
->isExtensible()) {
156 obj
->reportNotExtensible(cx
);
160 if (!vp
->isObjectOrNull())
163 JSObject
*pobj
= vp
->toObjectOrNull();
166 * Innerize pobj here to avoid sticking unwanted properties on the
167 * outer object. This ensures that any with statements only grant
168 * access to the inner object.
170 OBJ_TO_INNER_OBJECT(cx
, pobj
);
176 id
= ATOM_TO_JSID(cx
->runtime
->atomState
.protoAtom
);
177 if (!CheckAccess(cx
, obj
, id
, JSAccessMode(JSACC_PROTO
|JSACC_WRITE
), vp
, &attrs
))
180 return SetProto(cx
, obj
, pobj
, JS_TRUE
);
183 #else /* !JS_HAS_OBJ_PROTO_PROP */
185 #define object_props NULL
187 #endif /* !JS_HAS_OBJ_PROTO_PROP */
190 js_hash_object(const void *key
)
192 return JSHashNumber(uintptr_t(key
) >> JS_GCTHING_ALIGN
);
196 MarkSharpObjects(JSContext
*cx
, JSObject
*obj
, JSIdArray
**idap
)
198 JSSharpObjectMap
*map
;
201 JSHashEntry
**hep
, *he
;
210 JS_CHECK_RECURSION(cx
, return NULL
);
212 map
= &cx
->sharpObjectMap
;
213 JS_ASSERT(map
->depth
>= 1);
215 hash
= js_hash_object(obj
);
216 hep
= JS_HashTableRawLookup(table
, hash
, obj
);
220 he
= JS_HashTableRawAdd(table
, hep
, hash
, obj
, (void *) sharpid
);
222 JS_ReportOutOfMemory(cx
);
226 ida
= JS_Enumerate(cx
, obj
);
231 for (i
= 0, length
= ida
->length
; i
< length
; i
++) {
233 ok
= obj
->lookupProperty(cx
, id
, &obj2
, &prop
);
238 bool hasGetter
, hasSetter
;
239 AutoValueRooter
v(cx
);
240 AutoValueRooter
setter(cx
);
241 if (obj2
->isNative()) {
242 const Shape
*shape
= (Shape
*) prop
;
243 hasGetter
= shape
->hasGetterValue();
244 hasSetter
= shape
->hasSetterValue();
246 v
.set(shape
->getterValue());
248 setter
.set(shape
->setterValue());
250 hasGetter
= hasSetter
= false;
253 /* Mark the getter, then set val to setter. */
254 if (hasGetter
&& v
.value().isObject()) {
255 ok
= !!MarkSharpObjects(cx
, &v
.value().toObject(), NULL
);
259 v
.set(setter
.value());
260 } else if (!hasGetter
) {
261 ok
= obj
->getProperty(cx
, id
, v
.addr());
265 if (v
.value().isObject() &&
266 !MarkSharpObjects(cx
, &v
.value().toObject(), NULL
)) {
272 JS_DestroyIdArray(cx
, ida
);
276 sharpid
= uintptr_t(he
->value
);
278 sharpid
= ++map
->sharpgen
<< SHARP_ID_SHIFT
;
279 he
->value
= (void *) sharpid
;
289 js_EnterSharpObject(JSContext
*cx
, JSObject
*obj
, JSIdArray
**idap
,
292 JSSharpObjectMap
*map
;
296 JSHashEntry
*he
, **hep
;
301 if (!JS_CHECK_OPERATION_LIMIT(cx
))
304 /* Set to null in case we return an early error. */
306 map
= &cx
->sharpObjectMap
;
309 table
= JS_NewHashTable(8, js_hash_object
, JS_CompareValues
,
310 JS_CompareValues
, NULL
, NULL
);
312 JS_ReportOutOfMemory(cx
);
316 JS_KEEP_ATOMS(cx
->runtime
);
319 /* From this point the control must flow either through out: or bad:. */
321 if (map
->depth
== 0) {
323 * Although MarkSharpObjects tries to avoid invoking getters,
324 * it ends up doing so anyway under some circumstances; for
325 * example, if a wrapped object has getters, the wrapper will
326 * prevent MarkSharpObjects from recognizing them as such.
327 * This could lead to js_LeaveSharpObject being called while
328 * MarkSharpObjects is still working.
330 * Increment map->depth while we call MarkSharpObjects, to
331 * ensure that such a call doesn't free the hash table we're
335 he
= MarkSharpObjects(cx
, obj
, &ida
);
339 JS_ASSERT((uintptr_t(he
->value
) & SHARP_BIT
) == 0);
341 JS_DestroyIdArray(cx
, ida
);
345 hash
= js_hash_object(obj
);
346 hep
= JS_HashTableRawLookup(table
, hash
, obj
);
350 * It's possible that the value of a property has changed from the
351 * first time the object's properties are traversed (when the property
352 * ids are entered into the hash table) to the second (when they are
353 * converted to strings), i.e., the JSObject::getProperty() call is not
357 he
= JS_HashTableRawAdd(table
, hep
, hash
, obj
, NULL
);
359 JS_ReportOutOfMemory(cx
);
367 sharpid
= uintptr_t(he
->value
);
369 len
= JS_snprintf(buf
, sizeof buf
, "#%u%c",
370 sharpid
>> SHARP_ID_SHIFT
,
371 (sharpid
& SHARP_BIT
) ? '#' : '=');
372 *sp
= js_InflateString(cx
, buf
, &len
);
375 JS_DestroyIdArray(cx
, ida
);
382 if ((sharpid
& SHARP_BIT
) == 0) {
384 ida
= JS_Enumerate(cx
, obj
);
401 /* Clean up the sharpObjectMap table on outermost error. */
402 if (map
->depth
== 0) {
403 JS_UNKEEP_ATOMS(cx
->runtime
);
405 JS_HashTableDestroy(map
->table
);
412 js_LeaveSharpObject(JSContext
*cx
, JSIdArray
**idap
)
414 JSSharpObjectMap
*map
;
417 map
= &cx
->sharpObjectMap
;
418 JS_ASSERT(map
->depth
> 0);
419 if (--map
->depth
== 0) {
420 JS_UNKEEP_ATOMS(cx
->runtime
);
422 JS_HashTableDestroy(map
->table
);
428 JS_DestroyIdArray(cx
, ida
);
435 gc_sharp_table_entry_marker(JSHashEntry
*he
, intN i
, void *arg
)
437 MarkObject((JSTracer
*)arg
, *(JSObject
*)he
->key
, "sharp table entry");
438 return JS_DHASH_NEXT
;
442 js_TraceSharpMap(JSTracer
*trc
, JSSharpObjectMap
*map
)
444 JS_ASSERT(map
->depth
> 0);
445 JS_ASSERT(map
->table
);
448 * During recursive calls to MarkSharpObjects a non-native object or
449 * object with a custom getProperty method can potentially return an
450 * unrooted value or even cut from the object graph an argument of one of
451 * MarkSharpObjects recursive invocations. So we must protect map->table
452 * entries against GC.
454 * We can not simply use JSTempValueRooter to mark the obj argument of
455 * MarkSharpObjects during recursion as we have to protect *all* entries
456 * in JSSharpObjectMap including those that contains otherwise unreachable
457 * objects just allocated through custom getProperty. Otherwise newer
458 * allocations can re-use the address of an object stored in the hashtable
459 * confusing js_EnterSharpObject. So to address the problem we simply
460 * mark all objects from map->table.
462 * An alternative "proper" solution is to use JSTempValueRooter in
463 * MarkSharpObjects with code to remove during finalization entries
464 * with otherwise unreachable objects. But this is way too complex
465 * to justify spending efforts.
467 JS_HashTableEnumerateEntries(map
->table
, gc_sharp_table_entry_marker
, trc
);
472 obj_toSource(JSContext
*cx
, uintN argc
, Value
*vp
)
474 JSBool ok
, outermost
;
478 jschar
*chars
, *ochars
, *vsharp
;
479 const jschar
*idstrchars
, *vchars
;
480 size_t nchars
, idstrlength
, gsoplength
, vlength
, vsharplength
, curlen
;
486 JSString
*idstr
, *valstr
, *str
;
488 JS_CHECK_RECURSION(cx
, return JS_FALSE
);
491 PodArrayZero(localroot
);
492 AutoArrayRooter
tvr(cx
, JS_ARRAY_LENGTH(localroot
), localroot
);
494 /* If outermost, we need parentheses to be an expression, not a block. */
495 outermost
= (cx
->sharpObjectMap
.depth
== 0);
496 obj
= ComputeThisFromVp(cx
, vp
);
497 if (!obj
|| !(he
= js_EnterSharpObject(cx
, obj
, &ida
, &chars
))) {
503 * We didn't enter -- obj is already "sharp", meaning we've visited it
504 * already in our depth first search, and therefore chars contains a
505 * string of the form "#n#".
508 #if JS_HAS_SHARP_VARS
509 nchars
= js_strlen(chars
);
522 /* If outermost, allocate 4 + 1 for "({})" and the terminator. */
523 chars
= (jschar
*) cx
->runtime
->malloc(((outermost
? 4 : 2) + 1) * sizeof(jschar
));
528 chars
[nchars
++] = '(';
530 /* js_EnterSharpObject returned a string of the form "#n=" in chars. */
532 nchars
= js_strlen(chars
);
534 js_realloc((ochars
= chars
), (nchars
+ 2 + 1) * sizeof(jschar
));
541 * No need for parentheses around the whole shebang, because #n=
542 * unambiguously begins an object initializer, and never a block
545 outermost
= JS_FALSE
;
549 chars
[nchars
++] = '{';
554 * We have four local roots for cooked and raw value GC safety. Hoist the
555 * "localroot + 2" out of the loop using the val local, which refers to
556 * the raw (unconverted, "uncooked") values.
560 for (jsint i
= 0, length
= ida
->length
; i
< length
; i
++) {
561 /* Get strings for id and value and GC-root them via vp. */
562 jsid id
= ida
->vector
[i
];
564 ok
= obj
->lookupProperty(cx
, id
, &obj2
, &prop
);
569 * Convert id to a value and then to a string. Decide early whether we
570 * prefer get/set or old getter/setter syntax.
572 idstr
= js_ValueToString(cx
, IdToValue(id
));
577 vp
->setString(idstr
); /* local root */
582 if (obj2
->isNative()) {
583 const Shape
*shape
= (Shape
*) prop
;
584 unsigned attrs
= shape
->attributes();
585 if (attrs
& JSPROP_GETTER
) {
587 val
[valcnt
] = shape
->getterValue();
588 gsop
[valcnt
] = ATOM_TO_STRING(cx
->runtime
->atomState
.getAtom
);
591 if (attrs
& JSPROP_SETTER
) {
593 val
[valcnt
] = shape
->setterValue();
594 gsop
[valcnt
] = ATOM_TO_STRING(cx
->runtime
->atomState
.setAtom
);
601 ok
= obj
->getProperty(cx
, id
, &val
[0]);
608 * If id is a string that's not an identifier, or if it's a negative
609 * integer, then it must be quoted.
611 bool idIsLexicalIdentifier
= !!js_IsIdentifier(idstr
);
613 ? !idIsLexicalIdentifier
614 : (!JSID_IS_INT(id
) || JSID_TO_INT(id
) < 0)) {
615 idstr
= js_QuoteString(cx
, idstr
, jschar('\''));
620 vp
->setString(idstr
); /* local root */
622 idstr
->getCharsAndLength(idstrchars
, idstrlength
);
624 for (jsint j
= 0; j
< valcnt
; j
++) {
626 * Censor an accessor descriptor getter or setter part if it's
629 if (gsop
[j
] && val
[j
].isUndefined())
632 /* Convert val[j] to its canonical source form. */
633 valstr
= js_ValueToSource(cx
, val
[j
]);
638 localroot
[j
].setString(valstr
); /* local root */
639 valstr
->getCharsAndLength(vchars
, vlength
);
642 * If val[j] is a non-sharp object, and we're not serializing an
643 * accessor (ECMA syntax can't accommodate sharpened accessors),
644 * consider sharpening it.
648 #if JS_HAS_SHARP_VARS
649 if (!gsop
[j
] && val
[j
].isObject() && vchars
[0] != '#') {
650 he
= js_EnterSharpObject(cx
, &val
[j
].toObject(), NULL
, &vsharp
);
657 vlength
= js_strlen(vchars
);
660 vsharplength
= js_strlen(vsharp
);
663 js_LeaveSharpObject(cx
, NULL
);
669 * Remove '(function ' from the beginning of valstr and ')' from the
670 * end so that we can put "get" in front of the function definition.
672 if (gsop
[j
] && IsFunctionObject(val
[j
])) {
673 const jschar
*start
= vchars
;
674 const jschar
*end
= vchars
+ vlength
;
676 uint8 parenChomp
= 0;
677 if (vchars
[0] == '(') {
682 /* Try to jump "function" keyword. */
684 vchars
= js_strchr_limit(vchars
, ' ', end
);
687 * Jump over the function's name: it can't be encoded as part
688 * of an ECMA getter or setter.
691 vchars
= js_strchr_limit(vchars
, '(', end
);
696 vlength
= end
- vchars
- parenChomp
;
703 #define SAFE_ADD(n) \
714 SAFE_ADD(idstrlength
+ 1);
716 SAFE_ADD(gsop
[j
]->length() + 1);
717 SAFE_ADD(vsharplength
);
719 /* Account for the trailing null. */
720 SAFE_ADD((outermost
? 2 : 1) + 1);
723 if (curlen
> size_t(-1) / sizeof(jschar
))
726 /* Allocate 1 + 1 at end for closing brace and terminating 0. */
727 chars
= (jschar
*) js_realloc((ochars
= chars
), curlen
* sizeof(jschar
));
729 /* Save code space on error: let JS_free ignore null vsharp. */
736 chars
[nchars
++] = comma
[0];
737 chars
[nchars
++] = comma
[1];
742 gsoplength
= gsop
[j
]->length();
743 js_strncpy(&chars
[nchars
], gsop
[j
]->chars(),
745 nchars
+= gsoplength
;
746 chars
[nchars
++] = ' ';
748 js_strncpy(&chars
[nchars
], idstrchars
, idstrlength
);
749 nchars
+= idstrlength
;
750 /* Extraneous space after id here will be extracted later */
751 chars
[nchars
++] = gsop
[j
] ? ' ' : ':';
754 js_strncpy(&chars
[nchars
], vsharp
, vsharplength
);
755 nchars
+= vsharplength
;
757 js_strncpy(&chars
[nchars
], vchars
, vlength
);
765 chars
[nchars
++] = '}';
767 chars
[nchars
++] = ')';
771 js_LeaveSharpObject(cx
, &ida
);
780 JS_ReportOutOfMemory(cx
);
785 str
= js_NewString(cx
, chars
, nchars
);
802 #endif /* JS_HAS_TOSOURCE */
807 obj_toStringHelper(JSContext
*cx
, JSObject
*obj
)
810 return JSProxy::obj_toString(cx
, obj
);
812 const char *clazz
= obj
->getClass()->name
;
813 size_t nchars
= 9 + strlen(clazz
); /* 9 for "[object ]" */
814 jschar
*chars
= (jschar
*) cx
->malloc((nchars
+ 1) * sizeof(jschar
));
818 const char *prefix
= "[object ";
820 while ((chars
[nchars
] = (jschar
)*prefix
) != 0)
822 while ((chars
[nchars
] = (jschar
)*clazz
) != 0)
824 chars
[nchars
++] = ']';
827 JSString
*str
= js_NewString(cx
, chars
, nchars
);
835 /* ES5 15.2.4.2. Note steps 1 and 2 are errata. */
837 obj_toString(JSContext
*cx
, uintN argc
, Value
*vp
)
839 Value
&thisv
= vp
[1];
841 /* ES5 15.2.4.2 step 1. */
842 if (thisv
.isUndefined()) {
843 vp
->setString(ATOM_TO_STRING(cx
->runtime
->atomState
.objectUndefinedAtom
));
847 /* ES5 15.2.4.2 step 2. */
848 if (thisv
.isNull()) {
849 vp
->setString(ATOM_TO_STRING(cx
->runtime
->atomState
.objectNullAtom
));
853 /* ES5 15.2.4.2 step 3. */
854 if (!thisv
.isObject() && !js_PrimitiveToObject(cx
, &thisv
))
857 /* ES5 15.2.4.2 steps 4-5. */
858 JSString
*str
= js::obj_toStringHelper(cx
, &thisv
.toObject());
866 obj_toLocaleString(JSContext
*cx
, uintN argc
, Value
*vp
)
868 if (!ComputeThisFromVp(cx
, vp
))
871 JSString
*str
= js_ValueToString(cx
, vp
[1]);
880 obj_valueOf(JSContext
*cx
, uintN argc
, Value
*vp
)
882 if (!ComputeThisFromVp(cx
, vp
))
889 * Check if CSP allows new Function() or eval() to run in the current
893 js_CheckContentSecurityPolicy(JSContext
*cx
)
895 JSSecurityCallbacks
*callbacks
= JS_GetSecurityCallbacks(cx
);
897 // if there are callbacks, make sure that the CSP callback is installed and
898 // that it permits eval().
899 if (callbacks
&& callbacks
->contentSecurityPolicyAllows
)
900 return callbacks
->contentSecurityPolicyAllows(cx
);
906 * Check whether principals subsumes scopeobj's principals, and return true
907 * if so (or if scopeobj has no principals, for backward compatibility with
908 * the JS API, which does not require principals), and false otherwise.
911 js_CheckPrincipalsAccess(JSContext
*cx
, JSObject
*scopeobj
,
912 JSPrincipals
*principals
, JSAtom
*caller
)
914 JSSecurityCallbacks
*callbacks
;
915 JSPrincipals
*scopePrincipals
;
917 callbacks
= JS_GetSecurityCallbacks(cx
);
918 if (callbacks
&& callbacks
->findObjectPrincipals
) {
919 scopePrincipals
= callbacks
->findObjectPrincipals(cx
, scopeobj
);
920 if (!principals
|| !scopePrincipals
||
921 !principals
->subsume(principals
, scopePrincipals
)) {
922 JSAutoByteString callerstr
;
923 if (!js_AtomToPrintableString(cx
, caller
, &callerstr
))
925 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
926 JSMSG_BAD_INDIRECT_CALL
, callerstr
.ptr());
934 CheckScopeChainValidity(JSContext
*cx
, JSObject
*scopeobj
)
936 JSObject
*inner
= scopeobj
;
937 OBJ_TO_INNER_OBJECT(cx
, inner
);
940 JS_ASSERT(inner
== scopeobj
);
942 /* XXX This is an awful gross hack. */
944 JSObjectOp op
= scopeobj
->getClass()->ext
.innerObject
;
945 if (op
&& op(cx
, scopeobj
) != scopeobj
) {
946 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_BAD_INDIRECT_CALL
,
950 scopeobj
= scopeobj
->getParent();
957 js_ComputeFilename(JSContext
*cx
, JSStackFrame
*caller
,
958 JSPrincipals
*principals
, uintN
*linenop
)
962 JSSecurityCallbacks
*callbacks
= JS_GetSecurityCallbacks(cx
);
965 JS_ASSERT(principals
|| !(callbacks
&& callbacks
->findObjectPrincipals
));
966 flags
= JS_GetScriptFilenameFlags(caller
->script());
967 if ((flags
& JSFILENAME_PROTECTED
) &&
969 strcmp(principals
->codebase
, "[System Principal]")) {
971 return principals
->codebase
;
974 jsbytecode
*pc
= caller
->pc(cx
);
975 if (pc
&& js_GetOpcode(cx
, caller
->script(), pc
) == JSOP_EVAL
) {
976 JS_ASSERT(js_GetOpcode(cx
, caller
->script(), pc
+ JSOP_EVAL_LENGTH
) == JSOP_LINENO
);
977 *linenop
= GET_UINT16(pc
+ JSOP_EVAL_LENGTH
);
979 *linenop
= js_FramePCToLineNumber(cx
, caller
);
981 return caller
->script()->filename
;
984 #ifndef EVAL_CACHE_CHAIN_LIMIT
985 # define EVAL_CACHE_CHAIN_LIMIT 4
988 static inline JSScript
**
989 EvalCacheHash(JSContext
*cx
, JSString
*str
)
995 str
->getCharsAndLength(s
, n
);
998 for (h
= 0; n
; s
++, n
--)
999 h
= JS_ROTATE_LEFT32(h
, 4) ^ *s
;
1001 h
*= JS_GOLDEN_RATIO
;
1002 h
>>= 32 - JS_EVAL_CACHE_SHIFT
;
1003 return &JS_SCRIPTS_TO_GC(cx
)[h
];
1006 static JS_ALWAYS_INLINE JSScript
*
1007 EvalCacheLookup(JSContext
*cx
, JSString
*str
, JSStackFrame
*caller
, uintN staticLevel
,
1008 JSPrincipals
*principals
, JSObject
*scopeobj
, JSScript
**bucket
)
1011 * Cache local eval scripts indexed by source qualified by scope.
1013 * An eval cache entry should never be considered a hit unless its
1014 * strictness matches that of the new eval code. The existing code takes
1015 * care of this, because hits are qualified by the function from which
1016 * eval was called, whose strictness doesn't change. Scripts produced by
1017 * calls to eval from global code are not cached.
1020 JSScript
**scriptp
= bucket
;
1022 EVAL_CACHE_METER(probe
);
1023 JSVersion version
= cx
->findVersion();
1025 while ((script
= *scriptp
) != NULL
) {
1026 if (script
->savedCallerFun
&&
1027 script
->staticLevel
== staticLevel
&&
1028 script
->version
== version
&&
1029 (script
->principals
== principals
||
1030 (principals
->subsume(principals
, script
->principals
) &&
1031 script
->principals
->subsume(script
->principals
, principals
)))) {
1033 * Get the prior (cache-filling) eval's saved caller function.
1034 * See Compiler::compileScript in jsparse.cpp.
1036 JSFunction
*fun
= script
->getFunction(0);
1038 if (fun
== caller
->fun()) {
1040 * Get the source string passed for safekeeping in the
1041 * atom map by the prior eval to Compiler::compileScript.
1043 JSString
*src
= ATOM_TO_STRING(script
->atomMap
.vector
[0]);
1045 if (src
== str
|| js_EqualStrings(src
, str
)) {
1047 * Source matches, qualify by comparing scopeobj to the
1048 * COMPILE_N_GO-memoized parent of the first literal
1049 * function or regexp object if any. If none, then this
1050 * script has no compiled-in dependencies on the prior
1053 JSObjectArray
*objarray
= script
->objects();
1056 if (objarray
->length
== 1) {
1057 if (script
->regexpsOffset
!= 0) {
1058 objarray
= script
->regexps();
1061 EVAL_CACHE_METER(noscope
);
1066 objarray
->vector
[i
]->getParent() == scopeobj
) {
1067 JS_ASSERT(staticLevel
== script
->staticLevel
);
1068 EVAL_CACHE_METER(hit
);
1069 *scriptp
= script
->u
.nextToGC
;
1070 script
->u
.nextToGC
= NULL
;
1077 if (++count
== EVAL_CACHE_CHAIN_LIMIT
)
1079 EVAL_CACHE_METER(step
);
1080 scriptp
= &script
->u
.nextToGC
;
1087 eval(JSContext
*cx
, uintN argc
, Value
*vp
)
1090 * NB: This method handles only indirect eval: direct eval is handled by
1094 JSStackFrame
*caller
= js_GetScriptedCaller(cx
, NULL
);
1096 /* FIXME Bug 602994: This really should be perfectly cromulent. */
1098 /* Eval code needs to inherit principals from the caller. */
1099 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1100 JSMSG_BAD_INDIRECT_CALL
, js_eval_str
);
1104 return EvalKernel(cx
, argc
, vp
, INDIRECT_EVAL
, caller
, vp
[0].toObject().getGlobal());
1110 EvalKernel(JSContext
*cx
, uintN argc
, Value
*vp
, EvalType evalType
, JSStackFrame
*caller
,
1114 * FIXME Bug 602994: Calls with no scripted caller should be permitted and
1115 * should be implemented as indirect calls.
1118 JS_ASSERT(scopeobj
);
1121 * We once supported a second argument to eval to use as the scope chain
1122 * when evaluating the code string. Warn when such uses are seen so that
1123 * authors will know that support for eval(s, o) has been removed.
1125 JSScript
*callerScript
= caller
->script();
1126 if (argc
> 1 && !callerScript
->warnedAboutTwoArgumentEval
) {
1127 static const char TWO_ARGUMENT_WARNING
[] =
1128 "Support for eval(code, scopeObject) has been removed. "
1129 "Use |with (scopeObject) eval(code);| instead.";
1130 if (!JS_ReportWarning(cx
, TWO_ARGUMENT_WARNING
))
1132 callerScript
->warnedAboutTwoArgumentEval
= true;
1136 * CSP check: Is eval() allowed at all?
1137 * Report errors via CSP is done in the script security mgr.
1139 if (!js_CheckContentSecurityPolicy(cx
)) {
1140 JS_ReportError(cx
, "call to eval() blocked by CSP");
1144 /* ES5 15.1.2.1 step 1. */
1149 if (!vp
[2].isString()) {
1153 JSString
*str
= vp
[2].toString();
1155 /* ES5 15.1.2.1 steps 2-8. */
1156 JSObject
*callee
= JSVAL_TO_OBJECT(JS_CALLEE(cx
, Jsvalify(vp
)));
1157 JS_ASSERT(IsBuiltinEvalFunction(callee
->getFunctionPrivate()));
1158 JSPrincipals
*principals
= js_EvalFramePrincipals(cx
, callee
, caller
);
1161 * Per ES5, indirect eval runs in the global scope. (eval is specified this
1162 * way so that the compiler can make assumptions about what bindings may or
1163 * may not exist in the current frame if it doesn't see 'eval'.)
1166 if (evalType
== DIRECT_EVAL
) {
1167 staticLevel
= caller
->script()->staticLevel
+ 1;
1170 jsbytecode
*callerPC
= caller
->pc(cx
);
1171 JS_ASSERT_IF(caller
->isFunctionFrame(), caller
->hasCallObj());
1172 JS_ASSERT(callerPC
&& js_GetOpcode(cx
, caller
->script(), callerPC
) == JSOP_EVAL
);
1175 /* Pretend that we're top level. */
1178 JS_ASSERT(scopeobj
== scopeobj
->getGlobal());
1179 JS_ASSERT(scopeobj
->isGlobal());
1182 /* Ensure we compile this eval with the right object in the scope chain. */
1183 if (!CheckScopeChainValidity(cx
, scopeobj
))
1186 const jschar
*chars
;
1188 str
->getCharsAndLength(chars
, length
);
1191 * If the eval string starts with '(' and ends with ')', it may be JSON.
1192 * Try the JSON parser first because it's much faster. If the eval string
1193 * isn't JSON, JSON parsing will probably fail quickly, so little time
1196 if (length
> 2 && chars
[0] == '(' && chars
[length
- 1] == ')') {
1197 JSONParser
*jp
= js_BeginJSONParse(cx
, vp
, /* suppressErrors = */true);
1199 /* Run JSON-parser on string inside ( and ). */
1200 bool ok
= js_ConsumeJSONText(cx
, jp
, chars
+ 1, length
- 2);
1201 ok
&= js_FinishJSONParse(cx
, jp
, NullValue());
1208 * Direct calls to eval are supposed to see the caller's |this|. If we
1209 * haven't wrapped that yet, do so now, before we make a copy of it for
1210 * the eval code to use.
1212 if (evalType
== DIRECT_EVAL
&& !caller
->computeThis(cx
))
1215 JSScript
*script
= NULL
;
1216 JSScript
**bucket
= EvalCacheHash(cx
, str
);
1217 if (evalType
== DIRECT_EVAL
&& caller
->isFunctionFrame())
1218 script
= EvalCacheLookup(cx
, str
, caller
, staticLevel
, principals
, scopeobj
, bucket
);
1221 * We can't have a callerFrame (down in js::Execute's terms) if we're in
1222 * global code (or if we're an indirect eval).
1224 JSStackFrame
*callerFrame
= (staticLevel
!= 0) ? caller
: NULL
;
1227 const char *filename
= js_ComputeFilename(cx
, caller
, principals
, &lineno
);
1229 uint32 tcflags
= TCF_COMPILE_N_GO
| TCF_NEED_MUTABLE_SCRIPT
| TCF_COMPILE_FOR_EVAL
;
1230 script
= Compiler::compileScript(cx
, scopeobj
, callerFrame
,
1231 principals
, tcflags
,
1233 NULL
, filename
, lineno
, str
, staticLevel
);
1238 assertSameCompartment(cx
, scopeobj
, script
);
1241 * Belt-and-braces: check that the lesser of eval's principals and the
1242 * caller's principals has access to scopeobj.
1244 JSBool ok
= js_CheckPrincipalsAccess(cx
, scopeobj
, principals
,
1245 cx
->runtime
->atomState
.evalAtom
) &&
1246 Execute(cx
, scopeobj
, script
, callerFrame
, JSFRAME_EVAL
, vp
);
1248 script
->u
.nextToGC
= *bucket
;
1250 #ifdef CHECK_SCRIPT_OWNER
1251 script
->owner
= NULL
;
1258 IsBuiltinEvalFunction(JSFunction
*fun
)
1260 return fun
->maybeNative() == eval
;
1265 #if JS_HAS_OBJ_WATCHPOINT
1268 obj_watch_handler(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval old
,
1269 jsval
*nvp
, void *closure
)
1272 JSSecurityCallbacks
*callbacks
;
1273 JSStackFrame
*caller
;
1274 JSPrincipals
*subject
, *watcher
;
1276 JSResolvingEntry
*entry
;
1281 callable
= (JSObject
*) closure
;
1283 callbacks
= JS_GetSecurityCallbacks(cx
);
1284 if (callbacks
&& callbacks
->findObjectPrincipals
) {
1285 /* Skip over any obj_watch_* frames between us and the real subject. */
1286 caller
= js_GetScriptedCaller(cx
, NULL
);
1289 * Only call the watch handler if the watcher is allowed to watch
1290 * the currently executing script.
1292 watcher
= callbacks
->findObjectPrincipals(cx
, callable
);
1293 subject
= js_StackFramePrincipals(cx
, caller
);
1295 if (watcher
&& subject
&& !watcher
->subsume(watcher
, subject
)) {
1296 /* Silently don't call the watch handler. */
1302 /* Avoid recursion on (obj, id) already being watched on cx. */
1305 if (!js_StartResolving(cx
, &key
, JSRESFLAG_WATCH
, &entry
))
1309 generation
= cx
->resolvingTable
->generation
;
1311 argv
[0] = IdToValue(id
);
1312 argv
[1] = Valueify(old
);
1313 argv
[2] = Valueify(*nvp
);
1314 ok
= ExternalInvoke(cx
, obj
, ObjectOrNullValue(callable
), 3, argv
, Valueify(nvp
));
1315 js_StopResolving(cx
, &key
, JSRESFLAG_WATCH
, entry
, generation
);
1320 obj_watch(JSContext
*cx
, uintN argc
, Value
*vp
)
1323 js_ReportMissingArg(cx
, *vp
, 1);
1327 JSObject
*callable
= js_ValueToCallableObject(cx
, &vp
[3], 0);
1331 /* Compute the unique int/atom symbol id needed by js_LookupProperty. */
1333 if (!ValueToId(cx
, vp
[2], &propid
))
1336 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1339 if (!obj
|| !CheckAccess(cx
, obj
, propid
, JSACC_WATCH
, &tmp
, &attrs
))
1344 if (attrs
& JSPROP_READONLY
)
1346 if (obj
->isDenseArray() && !obj
->makeDenseArraySlow(cx
))
1348 return JS_SetWatchPoint(cx
, obj
, propid
, obj_watch_handler
, callable
);
1352 obj_unwatch(JSContext
*cx
, uintN argc
, Value
*vp
)
1354 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1360 if (!ValueToId(cx
, vp
[2], &id
))
1365 return JS_ClearWatchPoint(cx
, obj
, id
, NULL
, NULL
);
1368 #endif /* JS_HAS_OBJ_WATCHPOINT */
1371 * Prototype and property query methods, to complement the 'in' and
1372 * 'instanceof' operators.
1375 /* Proposed ECMA 15.2.4.5. */
1377 obj_hasOwnProperty(JSContext
*cx
, uintN argc
, Value
*vp
)
1379 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1381 js_HasOwnPropertyHelper(cx
, obj
->getOps()->lookupProperty
, argc
, vp
);
1385 js_HasOwnPropertyHelper(JSContext
*cx
, LookupPropOp lookup
, uintN argc
,
1389 if (!ValueToId(cx
, argc
!= 0 ? vp
[2] : UndefinedValue(), &id
))
1392 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1397 if (obj
->isProxy()) {
1399 if (!JSProxy::hasOwn(cx
, obj
, id
, &has
))
1401 vp
->setBoolean(has
);
1404 if (!js_HasOwnProperty(cx
, lookup
, obj
, id
, &obj2
, &prop
))
1406 vp
->setBoolean(!!prop
);
1411 js_HasOwnProperty(JSContext
*cx
, LookupPropOp lookup
, JSObject
*obj
, jsid id
,
1412 JSObject
**objp
, JSProperty
**propp
)
1414 JSAutoResolveFlags
rf(cx
, JSRESOLVE_QUALIFIED
| JSRESOLVE_DETECTING
);
1415 if (!(lookup
? lookup
: js_LookupProperty
)(cx
, obj
, id
, objp
, propp
))
1423 Class
*clasp
= (*objp
)->getClass();
1424 JSObject
*outer
= NULL
;
1425 if (JSObjectOp op
= (*objp
)->getClass()->ext
.outerObject
) {
1426 outer
= op(cx
, *objp
);
1431 if (outer
!= *objp
) {
1432 if ((*objp
)->isNative() && obj
->getClass() == clasp
) {
1434 * The combination of JSPROP_SHARED and JSPROP_PERMANENT in a
1435 * delegated property makes that property appear to be direct in
1436 * all delegating instances of the same native class. This hack
1437 * avoids bloating every function instance with its own 'length'
1438 * (AKA 'arity') property. But it must not extend across class
1439 * boundaries, to avoid making hasOwnProperty lie (bug 320854).
1441 * It's not really a hack, of course: a permanent property can't
1442 * be deleted, and JSPROP_SHARED means "don't allocate a slot in
1443 * any instance, prototype or delegating". Without a slot, and
1444 * without the ability to remove and recreate (with differences)
1445 * the property, there is no way to tell whether it is directly
1446 * owned, or indirectly delegated.
1448 Shape
*shape
= reinterpret_cast<Shape
*>(*propp
);
1449 if (shape
->isSharedPermanent())
1458 /* Proposed ECMA 15.2.4.6. */
1460 obj_isPrototypeOf(JSContext
*cx
, uintN argc
, Value
*vp
)
1462 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1465 const Value
&v
= argc
!= 0 ? vp
[2] : UndefinedValue();
1466 vp
->setBoolean(js_IsDelegate(cx
, obj
, v
));
1470 /* Proposed ECMA 15.2.4.7. */
1472 obj_propertyIsEnumerable(JSContext
*cx
, uintN argc
, Value
*vp
)
1475 if (!ValueToId(cx
, argc
!= 0 ? vp
[2] : UndefinedValue(), &id
))
1478 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1479 return obj
&& js_PropertyIsEnumerable(cx
, obj
, id
, vp
);
1483 js_PropertyIsEnumerable(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
1487 if (!obj
->lookupProperty(cx
, id
, &pobj
, &prop
))
1491 vp
->setBoolean(false);
1496 * XXX ECMA spec error compatible: return false unless hasOwnProperty.
1497 * The ECMA spec really should be fixed so propertyIsEnumerable and the
1498 * for..in loop agree on whether prototype properties are enumerable,
1499 * obviously by fixing this method (not by breaking the for..in loop!).
1501 * We check here for shared permanent prototype properties, which should
1502 * be treated as if they are local to obj. They are an implementation
1503 * technique used to satisfy ECMA requirements; users should not be able
1504 * to distinguish a shared permanent proto-property from a local one.
1508 if (pobj
->isNative()) {
1509 Shape
*shape
= (Shape
*) prop
;
1510 shared
= shape
->isSharedPermanent();
1511 attrs
= shape
->attributes();
1514 if (!pobj
->getAttributes(cx
, id
, &attrs
))
1517 if (pobj
!= obj
&& !shared
) {
1518 vp
->setBoolean(false);
1521 vp
->setBoolean((attrs
& JSPROP_ENUMERATE
) != 0);
1525 #if OLD_GETTER_SETTER_METHODS
1527 const char js_defineGetter_str
[] = "__defineGetter__";
1528 const char js_defineSetter_str
[] = "__defineSetter__";
1529 const char js_lookupGetter_str
[] = "__lookupGetter__";
1530 const char js_lookupSetter_str
[] = "__lookupSetter__";
1532 JS_FRIEND_API(JSBool
)
1533 js_obj_defineGetter(JSContext
*cx
, uintN argc
, Value
*vp
)
1535 if (argc
<= 1 || !js_IsCallable(vp
[3])) {
1536 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1537 JSMSG_BAD_GETTER_OR_SETTER
,
1541 PropertyOp getter
= CastAsPropertyOp(&vp
[3].toObject());
1544 if (!ValueToId(cx
, vp
[2], &id
))
1546 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1547 if (!obj
|| !CheckRedeclaration(cx
, obj
, id
, JSPROP_GETTER
, NULL
, NULL
))
1550 * Getters and setters are just like watchpoints from an access
1551 * control point of view.
1555 if (!CheckAccess(cx
, obj
, id
, JSACC_WATCH
, &junk
, &attrs
))
1558 return obj
->defineProperty(cx
, id
, UndefinedValue(), getter
, PropertyStub
,
1559 JSPROP_ENUMERATE
| JSPROP_GETTER
| JSPROP_SHARED
);
1562 JS_FRIEND_API(JSBool
)
1563 js_obj_defineSetter(JSContext
*cx
, uintN argc
, Value
*vp
)
1565 if (argc
<= 1 || !js_IsCallable(vp
[3])) {
1566 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1567 JSMSG_BAD_GETTER_OR_SETTER
,
1571 PropertyOp setter
= CastAsPropertyOp(&vp
[3].toObject());
1574 if (!ValueToId(cx
, vp
[2], &id
))
1576 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1577 if (!obj
|| !CheckRedeclaration(cx
, obj
, id
, JSPROP_SETTER
, NULL
, NULL
))
1580 * Getters and setters are just like watchpoints from an access
1581 * control point of view.
1585 if (!CheckAccess(cx
, obj
, id
, JSACC_WATCH
, &junk
, &attrs
))
1588 return obj
->defineProperty(cx
, id
, UndefinedValue(), PropertyStub
, setter
,
1589 JSPROP_ENUMERATE
| JSPROP_SETTER
| JSPROP_SHARED
);
1593 obj_lookupGetter(JSContext
*cx
, uintN argc
, Value
*vp
)
1596 if (!ValueToId(cx
, argc
!= 0 ? vp
[2] : UndefinedValue(), &id
))
1598 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1601 if (!obj
|| !obj
->lookupProperty(cx
, id
, &pobj
, &prop
))
1605 if (pobj
->isNative()) {
1606 Shape
*shape
= (Shape
*) prop
;
1607 if (shape
->hasGetterValue())
1608 *vp
= shape
->getterValue();
1615 obj_lookupSetter(JSContext
*cx
, uintN argc
, Value
*vp
)
1618 if (!ValueToId(cx
, argc
!= 0 ? vp
[2] : UndefinedValue(), &id
))
1620 JSObject
*obj
= ComputeThisFromVp(cx
, vp
);
1623 if (!obj
|| !obj
->lookupProperty(cx
, id
, &pobj
, &prop
))
1627 if (pobj
->isNative()) {
1628 Shape
*shape
= (Shape
*) prop
;
1629 if (shape
->hasSetterValue())
1630 *vp
= shape
->setterValue();
1635 #endif /* OLD_GETTER_SETTER_METHODS */
1638 obj_getPrototypeOf(JSContext
*cx
, uintN argc
, Value
*vp
)
1641 js_ReportMissingArg(cx
, *vp
, 0);
1645 if (vp
[2].isPrimitive()) {
1646 char *bytes
= DecompileValueGenerator(cx
, JSDVG_SEARCH_STACK
, vp
[2], NULL
);
1649 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1650 JSMSG_UNEXPECTED_TYPE
, bytes
, "not an object");
1655 JSObject
*obj
= &vp
[2].toObject();
1657 return CheckAccess(cx
, obj
, ATOM_TO_JSID(cx
->runtime
->atomState
.protoAtom
),
1658 JSACC_PROTO
, vp
, &attrs
);
1662 js_NewPropertyDescriptorObject(JSContext
*cx
, jsid id
, uintN attrs
,
1663 const Value
&getter
, const Value
&setter
,
1664 const Value
&value
, Value
*vp
)
1666 /* We have our own property, so start creating the descriptor. */
1667 JSObject
*desc
= NewBuiltinClassInstance(cx
, &js_ObjectClass
);
1670 vp
->setObject(*desc
); /* Root and return. */
1672 const JSAtomState
&atomState
= cx
->runtime
->atomState
;
1673 if (attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)) {
1674 if (!desc
->defineProperty(cx
, ATOM_TO_JSID(atomState
.getAtom
), getter
,
1675 PropertyStub
, PropertyStub
, JSPROP_ENUMERATE
) ||
1676 !desc
->defineProperty(cx
, ATOM_TO_JSID(atomState
.setAtom
), setter
,
1677 PropertyStub
, PropertyStub
, JSPROP_ENUMERATE
)) {
1681 if (!desc
->defineProperty(cx
, ATOM_TO_JSID(atomState
.valueAtom
), value
,
1682 PropertyStub
, PropertyStub
, JSPROP_ENUMERATE
) ||
1683 !desc
->defineProperty(cx
, ATOM_TO_JSID(atomState
.writableAtom
),
1684 BooleanValue((attrs
& JSPROP_READONLY
) == 0),
1685 PropertyStub
, PropertyStub
, JSPROP_ENUMERATE
)) {
1690 return desc
->defineProperty(cx
, ATOM_TO_JSID(atomState
.enumerableAtom
),
1691 BooleanValue((attrs
& JSPROP_ENUMERATE
) != 0),
1692 PropertyStub
, PropertyStub
, JSPROP_ENUMERATE
) &&
1693 desc
->defineProperty(cx
, ATOM_TO_JSID(atomState
.configurableAtom
),
1694 BooleanValue((attrs
& JSPROP_PERMANENT
) == 0),
1695 PropertyStub
, PropertyStub
, JSPROP_ENUMERATE
);
1699 js_GetOwnPropertyDescriptor(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
1702 return JSProxy::getOwnPropertyDescriptor(cx
, obj
, id
, false, vp
);
1706 if (!js_HasOwnProperty(cx
, obj
->getOps()->lookupProperty
, obj
, id
, &pobj
, &prop
))
1713 Value roots
[] = { UndefinedValue(), UndefinedValue(), UndefinedValue() };
1714 AutoArrayRooter
tvr(cx
, JS_ARRAY_LENGTH(roots
), roots
);
1717 if (pobj
->isNative()) {
1718 Shape
*shape
= (Shape
*) prop
;
1719 attrs
= shape
->attributes();
1720 if (attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)) {
1722 if (attrs
& JSPROP_GETTER
)
1723 roots
[0] = shape
->getterValue();
1724 if (attrs
& JSPROP_SETTER
)
1725 roots
[1] = shape
->setterValue();
1728 if (!pobj
->getAttributes(cx
, id
, &attrs
))
1732 if (doGet
&& !obj
->getProperty(cx
, id
, &roots
[2]))
1735 return js_NewPropertyDescriptorObject(cx
, id
,
1737 roots
[0], /* getter */
1738 roots
[1], /* setter */
1739 roots
[2], /* value */
1744 GetFirstArgumentAsObject(JSContext
*cx
, uintN argc
, Value
*vp
, const char *method
, JSObject
**objp
)
1747 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_MORE_ARGS_NEEDED
,
1752 const Value
&v
= vp
[2];
1753 if (!v
.isObject()) {
1754 char *bytes
= DecompileValueGenerator(cx
, JSDVG_SEARCH_STACK
, v
, NULL
);
1757 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_UNEXPECTED_TYPE
,
1758 bytes
, "not an object");
1763 *objp
= &v
.toObject();
1768 obj_getOwnPropertyDescriptor(JSContext
*cx
, uintN argc
, Value
*vp
)
1771 if (!GetFirstArgumentAsObject(cx
, argc
, vp
, "Object.getOwnPropertyDescriptor", &obj
))
1773 AutoIdRooter
nameidr(cx
);
1774 if (!ValueToId(cx
, argc
>= 2 ? vp
[3] : UndefinedValue(), nameidr
.addr()))
1776 return js_GetOwnPropertyDescriptor(cx
, obj
, nameidr
.id(), vp
);
1780 obj_keys(JSContext
*cx
, uintN argc
, Value
*vp
)
1783 if (!GetFirstArgumentAsObject(cx
, argc
, vp
, "Object.keys", &obj
))
1786 AutoIdVector
props(cx
);
1787 if (!GetPropertyNames(cx
, obj
, JSITER_OWNONLY
, &props
))
1790 AutoValueVector
vals(cx
);
1791 if (!vals
.reserve(props
.length()))
1793 for (size_t i
= 0, len
= props
.length(); i
< len
; i
++) {
1795 if (JSID_IS_STRING(id
)) {
1796 JS_ALWAYS_TRUE(vals
.append(StringValue(JSID_TO_STRING(id
))));
1797 } else if (JSID_IS_INT(id
)) {
1798 JSString
*str
= js_IntToString(cx
, JSID_TO_INT(id
));
1801 JS_ALWAYS_TRUE(vals
.append(StringValue(str
)));
1803 JS_ASSERT(JSID_IS_OBJECT(id
));
1807 JS_ASSERT(props
.length() <= UINT32_MAX
);
1808 JSObject
*aobj
= js_NewArrayObject(cx
, jsuint(vals
.length()), vals
.begin());
1811 vp
->setObject(*aobj
);
1817 HasProperty(JSContext
* cx
, JSObject
* obj
, jsid id
, Value
* vp
, bool *foundp
)
1819 if (!obj
->hasProperty(cx
, id
, foundp
, JSRESOLVE_QUALIFIED
| JSRESOLVE_DETECTING
))
1827 * We must go through the method read barrier in case id is 'get' or 'set'.
1828 * There is no obvious way to defer cloning a joined function object whose
1829 * identity will be used by DefinePropertyOnObject, e.g., or reflected via
1830 * js_GetOwnPropertyDescriptor, as the getter or setter callable object.
1832 return !!obj
->getProperty(cx
, id
, vp
);
1835 PropDesc::PropDesc()
1836 : pd(UndefinedValue()),
1838 value(UndefinedValue()),
1839 get(UndefinedValue()),
1840 set(UndefinedValue()),
1846 hasEnumerable(false),
1847 hasConfigurable(false)
1852 PropDesc::initialize(JSContext
* cx
, jsid id
, const Value
&origval
)
1858 if (v
.isPrimitive()) {
1859 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_NOT_NONNULL_OBJECT
);
1862 JSObject
* desc
= &v
.toObject();
1864 /* Make a copy of the descriptor. We might need it later. */
1867 /* Start with the proper defaults. */
1868 attrs
= JSPROP_PERMANENT
| JSPROP_READONLY
;
1873 if (!HasProperty(cx
, desc
, ATOM_TO_JSID(cx
->runtime
->atomState
.enumerableAtom
), &v
, &found
))
1876 hasEnumerable
= JS_TRUE
;
1877 if (js_ValueToBoolean(v
))
1878 attrs
|= JSPROP_ENUMERATE
;
1882 if (!HasProperty(cx
, desc
, ATOM_TO_JSID(cx
->runtime
->atomState
.configurableAtom
), &v
, &found
))
1885 hasConfigurable
= JS_TRUE
;
1886 if (js_ValueToBoolean(v
))
1887 attrs
&= ~JSPROP_PERMANENT
;
1891 if (!HasProperty(cx
, desc
, ATOM_TO_JSID(cx
->runtime
->atomState
.valueAtom
), &v
, &found
))
1899 if (!HasProperty(cx
, desc
, ATOM_TO_JSID(cx
->runtime
->atomState
.writableAtom
), &v
, &found
))
1902 hasWritable
= JS_TRUE
;
1903 if (js_ValueToBoolean(v
))
1904 attrs
&= ~JSPROP_READONLY
;
1908 if (!HasProperty(cx
, desc
, ATOM_TO_JSID(cx
->runtime
->atomState
.getAtom
), &v
, &found
))
1911 if ((v
.isPrimitive() || !js_IsCallable(v
)) && !v
.isUndefined()) {
1912 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_BAD_GET_SET_FIELD
,
1918 attrs
|= JSPROP_GETTER
| JSPROP_SHARED
;
1922 if (!HasProperty(cx
, desc
, ATOM_TO_JSID(cx
->runtime
->atomState
.setAtom
), &v
, &found
))
1925 if ((v
.isPrimitive() || !js_IsCallable(v
)) && !v
.isUndefined()) {
1926 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_BAD_GET_SET_FIELD
,
1932 attrs
|= JSPROP_SETTER
| JSPROP_SHARED
;
1936 if ((hasGet
|| hasSet
) && (hasValue
|| hasWritable
)) {
1937 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_INVALID_DESCRIPTOR
);
1945 Reject(JSContext
*cx
, uintN errorNumber
, bool throwError
, jsid id
, bool *rval
)
1949 if (!js_ValueToStringId(cx
, IdToValue(id
), &idstr
))
1951 JSAutoByteString
bytes(cx
, JSID_TO_STRING(idstr
));
1954 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, errorNumber
, bytes
.ptr());
1963 Reject(JSContext
*cx
, JSObject
*obj
, uintN errorNumber
, bool throwError
, bool *rval
)
1966 if (js_ErrorFormatString
[errorNumber
].argCount
== 1) {
1967 js_ReportValueErrorFlags(cx
, JSREPORT_ERROR
, errorNumber
,
1968 JSDVG_IGNORE_STACK
, ObjectValue(*obj
),
1971 JS_ASSERT(js_ErrorFormatString
[errorNumber
].argCount
== 0);
1972 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, errorNumber
);
1982 DefinePropertyOnObject(JSContext
*cx
, JSObject
*obj
, const PropDesc
&desc
,
1983 bool throwError
, bool *rval
)
1985 /* 8.12.9 step 1. */
1986 JSProperty
*current
;
1988 JS_ASSERT(!obj
->getOps()->lookupProperty
);
1989 if (!js_HasOwnProperty(cx
, NULL
, obj
, desc
.id
, &obj2
, ¤t
))
1992 JS_ASSERT(!obj
->getOps()->defineProperty
);
1994 /* 8.12.9 steps 2-4. */
1996 if (!obj
->isExtensible())
1997 return Reject(cx
, obj
, JSMSG_OBJECT_NOT_EXTENSIBLE
, throwError
, rval
);
2001 if (desc
.isGenericDescriptor() || desc
.isDataDescriptor()) {
2002 JS_ASSERT(!obj
->getOps()->defineProperty
);
2003 return js_DefineProperty(cx
, obj
, desc
.id
, &desc
.value
,
2004 PropertyStub
, PropertyStub
, desc
.attrs
);
2007 JS_ASSERT(desc
.isAccessorDescriptor());
2010 * Getters and setters are just like watchpoints from an access
2011 * control point of view.
2015 if (!CheckAccess(cx
, obj
, desc
.id
, JSACC_WATCH
, &dummy
, &dummyAttrs
))
2018 Value tmp
= UndefinedValue();
2019 return js_DefineProperty(cx
, obj
, desc
.id
, &tmp
,
2020 desc
.getter(), desc
.setter(), desc
.attrs
);
2023 /* 8.12.9 steps 5-6 (note 5 is merely a special case of 6). */
2024 Value v
= UndefinedValue();
2027 * In the special case of shared permanent properties, the "own" property
2028 * can be found on a different object. In that case the returned property
2029 * might not be native, except: the shared permanent property optimization
2030 * is not applied if the objects have different classes (bug 320854), as
2031 * must be enforced by js_HasOwnProperty for the Shape cast below to be
2034 JS_ASSERT(obj
->getClass() == obj2
->getClass());
2036 const Shape
*shape
= reinterpret_cast<Shape
*>(current
);
2038 if (desc
.isAccessorDescriptor()) {
2039 if (!shape
->isAccessorDescriptor())
2043 !SameValue(desc
.getterValue(), shape
->getterOrUndefined(), cx
)) {
2048 !SameValue(desc
.setterValue(), shape
->setterOrUndefined(), cx
)) {
2053 * Determine the current value of the property once, if the current
2054 * value might actually need to be used or preserved later. NB: we
2055 * guard on whether the current property is a data descriptor to
2056 * avoid calling a getter; we won't need the value if it's not a
2059 if (shape
->isDataDescriptor()) {
2061 * Non-standard: if the property is non-configurable and is
2062 * represented by a native getter or setter, don't permit
2063 * redefinition. We expose properties with native getter/setter
2064 * as though they were data properties, for the most part, but
2065 * in this particular case we must worry about integrity
2066 * concerns for JSAPI users who expected that
2067 * permanent+getter/setter means precisely controlled behavior.
2068 * If we permitted such redefinitions, such a property could be
2069 * "fixed" to some specific previous value, no longer varying
2070 * according to the intent of the native getter/setter for the
2073 * Other engines expose properties of this nature using ECMA
2074 * getter/setter pairs, but we can't because we use them even
2075 * for properties which ECMA specifies as being true data
2076 * descriptors ([].length, Function.length, /regex/.lastIndex,
2077 * &c.). Longer-term perhaps we should convert such properties
2078 * to use data descriptors (at which point representing a
2079 * descriptor with native getter/setter as an accessor
2080 * descriptor would be fine) and take a small memory hit, but
2081 * for now we'll simply forbid their redefinition.
2083 if (!shape
->configurable() &&
2084 (!shape
->hasDefaultGetter() || !shape
->hasDefaultSetter())) {
2085 return Reject(cx
, JSMSG_CANT_REDEFINE_PROP
, throwError
, desc
.id
, rval
);
2088 if (!js_NativeGet(cx
, obj
, obj2
, shape
, JSGET_NO_METHOD_BARRIER
, &v
)) {
2089 /* current was dropped when the failure occurred. */
2094 if (desc
.isDataDescriptor()) {
2095 if (!shape
->isDataDescriptor())
2098 if (desc
.hasValue
&& !SameValue(desc
.value
, v
, cx
))
2100 if (desc
.hasWritable
&& desc
.writable() != shape
->writable())
2103 /* The only fields in desc will be handled below. */
2104 JS_ASSERT(desc
.isGenericDescriptor());
2108 if (desc
.hasConfigurable
&& desc
.configurable() != shape
->configurable())
2110 if (desc
.hasEnumerable
&& desc
.enumerable() != shape
->enumerable())
2113 /* The conditions imposed by step 5 or step 6 apply. */
2118 /* 8.12.9 step 7. */
2119 if (!shape
->configurable()) {
2121 * Since [[Configurable]] defaults to false, we don't need to check
2122 * whether it was specified. We can't do likewise for [[Enumerable]]
2123 * because its putative value is used in a comparison -- a comparison
2124 * whose result must always be false per spec if the [[Enumerable]]
2125 * field is not present. Perfectly pellucid logic, eh?
2127 JS_ASSERT_IF(!desc
.hasConfigurable
, !desc
.configurable());
2128 if (desc
.configurable() ||
2129 (desc
.hasEnumerable
&& desc
.enumerable() != shape
->enumerable())) {
2130 return Reject(cx
, JSMSG_CANT_REDEFINE_PROP
, throwError
, desc
.id
, rval
);
2134 bool callDelProperty
= false;
2136 if (desc
.isGenericDescriptor()) {
2137 /* 8.12.9 step 8, no validation required */
2138 } else if (desc
.isDataDescriptor() != shape
->isDataDescriptor()) {
2139 /* 8.12.9 step 9. */
2140 if (!shape
->configurable())
2141 return Reject(cx
, JSMSG_CANT_REDEFINE_PROP
, throwError
, desc
.id
, rval
);
2142 } else if (desc
.isDataDescriptor()) {
2143 /* 8.12.9 step 10. */
2144 JS_ASSERT(shape
->isDataDescriptor());
2145 if (!shape
->configurable() && !shape
->writable()) {
2146 if ((desc
.hasWritable
&& desc
.writable()) ||
2147 (desc
.hasValue
&& !SameValue(desc
.value
, v
, cx
))) {
2148 return Reject(cx
, JSMSG_CANT_REDEFINE_PROP
, throwError
, desc
.id
, rval
);
2152 callDelProperty
= !shape
->hasDefaultGetter() || !shape
->hasDefaultSetter();
2154 /* 8.12.9 step 11. */
2155 JS_ASSERT(desc
.isAccessorDescriptor() && shape
->isAccessorDescriptor());
2156 if (!shape
->configurable()) {
2158 !SameValue(desc
.setterValue(), shape
->setterOrUndefined(), cx
)) ||
2160 !SameValue(desc
.getterValue(), shape
->getterOrUndefined(), cx
))) {
2161 return Reject(cx
, JSMSG_CANT_REDEFINE_PROP
, throwError
, desc
.id
, rval
);
2166 /* 8.12.9 step 12. */
2168 PropertyOp getter
, setter
;
2169 if (desc
.isGenericDescriptor()) {
2171 if (desc
.hasConfigurable
)
2172 changed
|= JSPROP_PERMANENT
;
2173 if (desc
.hasEnumerable
)
2174 changed
|= JSPROP_ENUMERATE
;
2176 attrs
= (shape
->attributes() & ~changed
) | (desc
.attrs
& changed
);
2177 if (shape
->isMethod()) {
2178 JS_ASSERT(!(attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)));
2179 getter
= setter
= PropertyStub
;
2181 getter
= shape
->getter();
2182 setter
= shape
->setter();
2184 } else if (desc
.isDataDescriptor()) {
2185 uintN unchanged
= 0;
2186 if (!desc
.hasConfigurable
)
2187 unchanged
|= JSPROP_PERMANENT
;
2188 if (!desc
.hasEnumerable
)
2189 unchanged
|= JSPROP_ENUMERATE
;
2190 if (!desc
.hasWritable
)
2191 unchanged
|= JSPROP_READONLY
;
2195 attrs
= (desc
.attrs
& ~unchanged
) | (shape
->attributes() & unchanged
);
2196 getter
= setter
= PropertyStub
;
2198 JS_ASSERT(desc
.isAccessorDescriptor());
2201 * Getters and setters are just like watchpoints from an access
2202 * control point of view.
2205 if (!CheckAccess(cx
, obj2
, desc
.id
, JSACC_WATCH
, &dummy
, &attrs
))
2208 JS_ASSERT_IF(shape
->isMethod(), !(attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)));
2210 /* 8.12.9 step 12. */
2212 if (desc
.hasConfigurable
)
2213 changed
|= JSPROP_PERMANENT
;
2214 if (desc
.hasEnumerable
)
2215 changed
|= JSPROP_ENUMERATE
;
2217 changed
|= JSPROP_GETTER
| JSPROP_SHARED
;
2219 changed
|= JSPROP_SETTER
| JSPROP_SHARED
;
2221 attrs
= (desc
.attrs
& changed
) | (shape
->attributes() & ~changed
);
2223 getter
= desc
.getter();
2225 getter
= (shape
->isMethod() || (shape
->hasDefaultGetter() && !shape
->hasGetterValue()))
2230 setter
= desc
.setter();
2232 setter
= (shape
->hasDefaultSetter() && !shape
->hasSetterValue())
2241 * Since "data" properties implemented using native C functions may rely on
2242 * side effects during setting, we must make them aware that they have been
2243 * "assigned"; deleting the property before redefining it does the trick.
2244 * See bug 539766, where we ran into problems when we redefined
2245 * arguments.length without making the property aware that its value had
2246 * been changed (which would have happened if we had deleted it before
2247 * redefining it or we had invoked its setter to change its value).
2249 if (callDelProperty
) {
2251 if (!CallJSPropertyOp(cx
, obj2
->getClass()->delProperty
, obj2
, desc
.id
, &dummy
))
2255 return js_DefineProperty(cx
, obj
, desc
.id
, &v
, getter
, setter
, attrs
);
2259 DefinePropertyOnArray(JSContext
*cx
, JSObject
*obj
, const PropDesc
&desc
,
2260 bool throwError
, bool *rval
)
2263 * We probably should optimize dense array property definitions where
2264 * the descriptor describes a traditional array property (enumerable,
2265 * configurable, writable, numeric index or length without altering its
2266 * attributes). Such definitions are probably unlikely, so we don't bother
2269 if (obj
->isDenseArray() && !obj
->makeDenseArraySlow(cx
))
2272 jsuint oldLen
= obj
->getArrayLength();
2274 if (JSID_IS_ATOM(desc
.id
, cx
->runtime
->atomState
.lengthAtom
)) {
2276 * Our optimization of storage of the length property of arrays makes
2277 * it very difficult to properly implement defining the property. For
2278 * now simply throw an exception (NB: not merely Reject) on any attempt
2279 * to define the "length" property, rather than attempting to implement
2280 * some difficult-for-authors-to-grasp subset of that functionality.
2282 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_CANT_DEFINE_ARRAY_LENGTH
);
2287 if (js_IdIsIndex(desc
.id
, &index
)) {
2289 // Disabled until we support defining "length":
2290 if (index >= oldLen && lengthPropertyNotWritable())
2291 return ThrowTypeError(cx, JSMSG_CANT_APPEND_TO_ARRAY);
2293 if (!DefinePropertyOnObject(cx
, obj
, desc
, false, rval
))
2296 return Reject(cx
, obj
, JSMSG_CANT_DEFINE_ARRAY_INDEX
, throwError
, rval
);
2298 if (index
>= oldLen
) {
2299 JS_ASSERT(index
!= UINT32_MAX
);
2300 obj
->setArrayLength(index
+ 1);
2307 return DefinePropertyOnObject(cx
, obj
, desc
, throwError
, rval
);
2311 DefineProperty(JSContext
*cx
, JSObject
*obj
, const PropDesc
&desc
, bool throwError
,
2315 return DefinePropertyOnArray(cx
, obj
, desc
, throwError
, rval
);
2317 if (obj
->getOps()->lookupProperty
) {
2319 return JSProxy::defineProperty(cx
, obj
, desc
.id
, desc
.pd
);
2320 return Reject(cx
, obj
, JSMSG_OBJECT_NOT_EXTENSIBLE
, throwError
, rval
);
2323 return DefinePropertyOnObject(cx
, obj
, desc
, throwError
, rval
);
2327 js_DefineOwnProperty(JSContext
*cx
, JSObject
*obj
, jsid id
,
2328 const Value
&descriptor
, JSBool
*bp
)
2330 AutoPropDescArrayRooter
descs(cx
);
2331 PropDesc
*desc
= descs
.append();
2332 if (!desc
|| !desc
->initialize(cx
, id
, descriptor
))
2336 if (!DefineProperty(cx
, obj
, *desc
, true, &rval
))
2342 /* ES5 15.2.3.6: Object.defineProperty(O, P, Attributes) */
2344 obj_defineProperty(JSContext
* cx
, uintN argc
, Value
* vp
)
2346 /* 15.2.3.6 steps 1 and 5. */
2348 if (!GetFirstArgumentAsObject(cx
, argc
, vp
, "Object.defineProperty", &obj
))
2350 vp
->setObject(*obj
);
2352 /* 15.2.3.6 step 2. */
2353 AutoIdRooter
nameidr(cx
);
2354 if (!ValueToId(cx
, argc
>= 2 ? vp
[3] : UndefinedValue(), nameidr
.addr()))
2357 /* 15.2.3.6 step 3. */
2358 const Value
&descval
= argc
>= 3 ? vp
[4] : UndefinedValue();
2360 /* 15.2.3.6 step 4 */
2362 return js_DefineOwnProperty(cx
, obj
, nameidr
.id(), descval
, &junk
);
2366 DefineProperties(JSContext
*cx
, JSObject
*obj
, JSObject
*props
)
2368 AutoIdArray
ida(cx
, JS_Enumerate(cx
, props
));
2372 AutoPropDescArrayRooter
descs(cx
);
2373 size_t len
= ida
.length();
2374 for (size_t i
= 0; i
< len
; i
++) {
2376 PropDesc
* desc
= descs
.append();
2377 AutoValueRooter
tvr(cx
);
2379 !JS_GetPropertyById(cx
, props
, id
, tvr
.jsval_addr()) ||
2380 !desc
->initialize(cx
, id
, tvr
.value())) {
2386 for (size_t i
= 0; i
< len
; i
++) {
2387 if (!DefineProperty(cx
, obj
, descs
[i
], true, &dummy
))
2395 js_PopulateObject(JSContext
*cx
, JSObject
*newborn
, JSObject
*props
)
2397 return DefineProperties(cx
, newborn
, props
);
2400 /* ES5 15.2.3.7: Object.defineProperties(O, Properties) */
2402 obj_defineProperties(JSContext
* cx
, uintN argc
, Value
* vp
)
2404 /* 15.2.3.6 steps 1 and 5. */
2406 if (!GetFirstArgumentAsObject(cx
, argc
, vp
, "Object.defineProperties", &obj
))
2408 vp
->setObject(*obj
);
2411 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_MORE_ARGS_NEEDED
,
2412 "Object.defineProperties", "0", "s");
2416 JSObject
* props
= js_ValueToNonNullObject(cx
, vp
[3]);
2419 vp
[3].setObject(*props
);
2421 return DefineProperties(cx
, obj
, props
);
2424 /* ES5 15.2.3.5: Object.create(O [, Properties]) */
2426 obj_create(JSContext
*cx
, uintN argc
, Value
*vp
)
2429 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_MORE_ARGS_NEEDED
,
2430 "Object.create", "0", "s");
2434 const Value
&v
= vp
[2];
2435 if (!v
.isObjectOrNull()) {
2436 char *bytes
= DecompileValueGenerator(cx
, JSDVG_SEARCH_STACK
, v
, NULL
);
2439 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_UNEXPECTED_TYPE
,
2440 bytes
, "not an object or null");
2446 * Use the callee's global as the parent of the new object to avoid dynamic
2447 * scoping (i.e., using the caller's global).
2449 JSObject
*obj
= NewNonFunction
<WithProto::Given
>(cx
, &js_ObjectClass
, v
.toObjectOrNull(),
2450 vp
->toObject().getGlobal());
2453 vp
->setObject(*obj
); /* Root and prepare for eventual return. */
2455 /* 15.2.3.5 step 4. */
2456 if (argc
> 1 && !vp
[3].isUndefined()) {
2457 if (vp
[3].isPrimitive()) {
2458 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_NOT_NONNULL_OBJECT
);
2462 JSObject
*props
= &vp
[3].toObject();
2463 AutoIdArray
ida(cx
, JS_Enumerate(cx
, props
));
2467 AutoPropDescArrayRooter
descs(cx
);
2468 size_t len
= ida
.length();
2469 for (size_t i
= 0; i
< len
; i
++) {
2471 PropDesc
*desc
= descs
.append();
2472 if (!desc
|| !JS_GetPropertyById(cx
, props
, id
, Jsvalify(&vp
[1])) ||
2473 !desc
->initialize(cx
, id
, vp
[1])) {
2479 for (size_t i
= 0; i
< len
; i
++) {
2480 if (!DefineProperty(cx
, obj
, descs
[i
], true, &dummy
))
2485 /* 5. Return obj. */
2490 obj_getOwnPropertyNames(JSContext
*cx
, uintN argc
, Value
*vp
)
2493 if (!GetFirstArgumentAsObject(cx
, argc
, vp
, "Object.getOwnPropertyNames", &obj
))
2496 AutoIdVector
keys(cx
);
2497 if (!GetPropertyNames(cx
, obj
, JSITER_OWNONLY
| JSITER_HIDDEN
, &keys
))
2500 AutoValueVector
vals(cx
);
2501 if (!vals
.resize(keys
.length()))
2504 for (size_t i
= 0, len
= keys
.length(); i
< len
; i
++) {
2506 if (JSID_IS_INT(id
)) {
2507 JSString
*str
= js_ValueToString(cx
, Int32Value(JSID_TO_INT(id
)));
2510 vals
[i
].setString(str
);
2511 } else if (JSID_IS_ATOM(id
)) {
2512 vals
[i
].setString(JSID_TO_STRING(id
));
2514 vals
[i
].setObject(*JSID_TO_OBJECT(id
));
2518 JSObject
*aobj
= js_NewArrayObject(cx
, vals
.length(), vals
.begin());
2522 vp
->setObject(*aobj
);
2527 obj_isExtensible(JSContext
*cx
, uintN argc
, Value
*vp
)
2530 if (!GetFirstArgumentAsObject(cx
, argc
, vp
, "Object.isExtensible", &obj
))
2533 vp
->setBoolean(obj
->isExtensible());
2538 obj_preventExtensions(JSContext
*cx
, uintN argc
, Value
*vp
)
2541 if (!GetFirstArgumentAsObject(cx
, argc
, vp
, "Object.preventExtensions", &obj
))
2544 vp
->setObject(*obj
);
2545 if (!obj
->isExtensible())
2548 AutoIdVector
props(cx
);
2549 return obj
->preventExtensions(cx
, &props
);
2553 JSObject::sealOrFreeze(JSContext
*cx
, ImmutabilityType it
)
2555 assertSameCompartment(cx
, this);
2556 JS_ASSERT(it
== SEAL
|| it
== FREEZE
);
2558 AutoIdVector
props(cx
);
2559 if (isExtensible()) {
2560 if (!preventExtensions(cx
, &props
))
2563 if (!GetPropertyNames(cx
, this, JSITER_HIDDEN
| JSITER_OWNONLY
, &props
))
2567 /* preventExtensions must slowify dense arrays, so we can assign to holes without checks. */
2568 JS_ASSERT(!isDenseArray());
2570 for (size_t i
= 0, len
= props
.length(); i
< len
; i
++) {
2574 if (!getAttributes(cx
, id
, &attrs
))
2577 /* Make all attributes permanent; if freezing, make data attributes read-only. */
2579 if (it
== FREEZE
&& !(attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)))
2580 new_attrs
= JSPROP_PERMANENT
| JSPROP_READONLY
;
2582 new_attrs
= JSPROP_PERMANENT
;
2584 /* If we already have the attributes we need, skip the setAttributes call. */
2585 if ((attrs
| new_attrs
) == attrs
)
2589 if (!setAttributes(cx
, id
, &attrs
))
2597 obj_freeze(JSContext
*cx
, uintN argc
, Value
*vp
)
2600 if (!GetFirstArgumentAsObject(cx
, argc
, vp
, "Object.freeze", &obj
))
2603 vp
->setObject(*obj
);
2605 return obj
->freeze(cx
);
2609 obj_isFrozen(JSContext
*cx
, uintN argc
, Value
*vp
)
2612 if (!GetFirstArgumentAsObject(cx
, argc
, vp
, "Object.preventExtensions", &obj
))
2615 vp
->setBoolean(false);
2617 if (obj
->isExtensible())
2618 return true; /* The JavaScript value returned is false. */
2620 AutoIdVector
props(cx
);
2621 if (!GetPropertyNames(cx
, obj
, JSITER_HIDDEN
| JSITER_OWNONLY
, &props
))
2624 for (size_t i
= 0, len
= props
.length(); i
< len
; i
++) {
2628 if (!obj
->getAttributes(cx
, id
, &attrs
))
2631 /* The property must be non-configurable and either read-only or an accessor. */
2632 if (!(attrs
& JSPROP_PERMANENT
) ||
2633 !(attrs
& (JSPROP_READONLY
| JSPROP_GETTER
| JSPROP_SETTER
)))
2634 return true; /* The JavaScript value returned is false. */
2637 /* It really was sealed, so return true. */
2638 vp
->setBoolean(true);
2643 obj_seal(JSContext
*cx
, uintN argc
, Value
*vp
)
2646 if (!GetFirstArgumentAsObject(cx
, argc
, vp
, "Object.seal", &obj
))
2649 vp
->setObject(*obj
);
2651 return obj
->seal(cx
);
2655 obj_isSealed(JSContext
*cx
, uintN argc
, Value
*vp
)
2658 if (!GetFirstArgumentAsObject(cx
, argc
, vp
, "Object.isSealed", &obj
))
2661 /* Assume not sealed until proven otherwise. */
2662 vp
->setBoolean(false);
2664 if (obj
->isExtensible())
2665 return true; /* The JavaScript value returned is false. */
2667 AutoIdVector
props(cx
);
2668 if (!GetPropertyNames(cx
, obj
, JSITER_HIDDEN
| JSITER_OWNONLY
, &props
))
2671 for (size_t i
= 0, len
= props
.length(); i
< len
; i
++) {
2675 if (!obj
->getAttributes(cx
, id
, &attrs
))
2678 if (!(attrs
& JSPROP_PERMANENT
))
2679 return true; /* The JavaScript value returned is false. */
2682 /* It really was sealed, so return true. */
2683 vp
->setBoolean(true);
2687 #if JS_HAS_OBJ_WATCHPOINT
2688 const char js_watch_str
[] = "watch";
2689 const char js_unwatch_str
[] = "unwatch";
2691 const char js_hasOwnProperty_str
[] = "hasOwnProperty";
2692 const char js_isPrototypeOf_str
[] = "isPrototypeOf";
2693 const char js_propertyIsEnumerable_str
[] = "propertyIsEnumerable";
2695 static JSFunctionSpec object_methods
[] = {
2697 JS_FN(js_toSource_str
, obj_toSource
, 0,0),
2699 JS_FN(js_toString_str
, obj_toString
, 0,JSFUN_PRIMITIVE_THIS
),
2700 JS_FN(js_toLocaleString_str
, obj_toLocaleString
, 0,0),
2701 JS_FN(js_valueOf_str
, obj_valueOf
, 0,0),
2702 #if JS_HAS_OBJ_WATCHPOINT
2703 JS_FN(js_watch_str
, obj_watch
, 2,0),
2704 JS_FN(js_unwatch_str
, obj_unwatch
, 1,0),
2706 JS_FN(js_hasOwnProperty_str
, obj_hasOwnProperty
, 1,0),
2707 JS_FN(js_isPrototypeOf_str
, obj_isPrototypeOf
, 1,0),
2708 JS_FN(js_propertyIsEnumerable_str
, obj_propertyIsEnumerable
, 1,0),
2709 #if OLD_GETTER_SETTER_METHODS
2710 JS_FN(js_defineGetter_str
, js_obj_defineGetter
, 2,0),
2711 JS_FN(js_defineSetter_str
, js_obj_defineSetter
, 2,0),
2712 JS_FN(js_lookupGetter_str
, obj_lookupGetter
, 1,0),
2713 JS_FN(js_lookupSetter_str
, obj_lookupSetter
, 1,0),
2718 static JSFunctionSpec object_static_methods
[] = {
2719 JS_FN("getPrototypeOf", obj_getPrototypeOf
, 1,0),
2720 JS_FN("getOwnPropertyDescriptor", obj_getOwnPropertyDescriptor
,2,0),
2721 JS_FN("keys", obj_keys
, 1,0),
2722 JS_FN("defineProperty", obj_defineProperty
, 3,0),
2723 JS_FN("defineProperties", obj_defineProperties
, 2,0),
2724 JS_FN("create", obj_create
, 2,0),
2725 JS_FN("getOwnPropertyNames", obj_getOwnPropertyNames
, 1,0),
2726 JS_FN("isExtensible", obj_isExtensible
, 1,0),
2727 JS_FN("preventExtensions", obj_preventExtensions
, 1,0),
2728 JS_FN("freeze", obj_freeze
, 1,0),
2729 JS_FN("isFrozen", obj_isFrozen
, 1,0),
2730 JS_FN("seal", obj_seal
, 1,0),
2731 JS_FN("isSealed", obj_isSealed
, 1,0),
2736 js_Object(JSContext
*cx
, uintN argc
, Value
*vp
)
2740 /* Trigger logic below to construct a blank object. */
2743 /* If argv[0] is null or undefined, obj comes back null. */
2744 if (!js_ValueToObjectOrNull(cx
, vp
[2], &obj
))
2748 /* Make an object whether this was called with 'new' or not. */
2749 JS_ASSERT(!argc
|| vp
[2].isNull() || vp
[2].isUndefined());
2750 gc::FinalizeKind kind
= NewObjectGCKind(cx
, &js_ObjectClass
);
2751 obj
= NewBuiltinClassInstance(cx
, &js_ObjectClass
, kind
);
2755 vp
->setObject(*obj
);
2760 js_CreateThis(JSContext
*cx
, JSObject
*callee
)
2762 Class
*clasp
= callee
->getClass();
2764 Class
*newclasp
= &js_ObjectClass
;
2765 if (clasp
== &js_FunctionClass
) {
2766 JSFunction
*fun
= callee
->getFunctionPrivate();
2767 if (fun
->isNative() && fun
->u
.n
.clasp
)
2768 newclasp
= fun
->u
.n
.clasp
;
2772 if (!callee
->getProperty(cx
, ATOM_TO_JSID(cx
->runtime
->atomState
.classPrototypeAtom
), &protov
))
2775 JSObject
*proto
= protov
.isObjectOrNull() ? protov
.toObjectOrNull() : NULL
;
2776 JSObject
*parent
= callee
->getParent();
2777 gc::FinalizeKind kind
= NewObjectGCKind(cx
, newclasp
);
2778 JSObject
*obj
= NewObject
<WithProto::Class
>(cx
, newclasp
, proto
, parent
, kind
);
2780 obj
->syncSpecialEquality();
2785 js_CreateThisForFunctionWithProto(JSContext
*cx
, JSObject
*callee
, JSObject
*proto
)
2787 gc::FinalizeKind kind
= NewObjectGCKind(cx
, &js_ObjectClass
);
2788 return NewNonFunction
<WithProto::Class
>(cx
, &js_ObjectClass
, proto
, callee
->getParent(), kind
);
2792 js_CreateThisForFunction(JSContext
*cx
, JSObject
*callee
)
2795 if (!callee
->getProperty(cx
,
2796 ATOM_TO_JSID(cx
->runtime
->atomState
.classPrototypeAtom
),
2800 JSObject
*proto
= protov
.isObject() ? &protov
.toObject() : NULL
;
2801 return js_CreateThisForFunctionWithProto(cx
, callee
, proto
);
2806 static JS_ALWAYS_INLINE JSObject
*
2807 NewObjectWithClassProto(JSContext
*cx
, Class
*clasp
, JSObject
*proto
,
2808 /*gc::FinalizeKind*/ unsigned _kind
)
2810 JS_ASSERT(clasp
->isNative());
2811 gc::FinalizeKind kind
= gc::FinalizeKind(_kind
);
2813 JSObject
* obj
= js_NewGCObject(cx
, kind
);
2817 if (!obj
->initSharingEmptyShape(cx
, clasp
, proto
, proto
->getParent(), NULL
, kind
))
2823 js_Object_tn(JSContext
* cx
, JSObject
* proto
)
2825 JS_ASSERT(!(js_ObjectClass
.flags
& JSCLASS_HAS_PRIVATE
));
2826 return NewObjectWithClassProto(cx
, &js_ObjectClass
, proto
, FINALIZE_OBJECT8
);
2829 JS_DEFINE_TRCINFO_1(js_Object
,
2830 (2, (extern, CONSTRUCTOR_RETRY
, js_Object_tn
, CONTEXT
, CALLEE_PROTOTYPE
, 0,
2831 nanojit::ACCSET_STORE_ANY
)))
2834 js_InitializerObject(JSContext
* cx
, JSObject
*proto
, JSObject
*baseobj
)
2837 gc::FinalizeKind kind
= GuessObjectGCKind(0, false);
2838 return NewObjectWithClassProto(cx
, &js_ObjectClass
, proto
, kind
);
2841 return CopyInitializerObject(cx
, baseobj
);
2844 JS_DEFINE_CALLINFO_3(extern, OBJECT
, js_InitializerObject
, CONTEXT
, OBJECT
, OBJECT
,
2845 0, nanojit::ACCSET_STORE_ANY
)
2848 js_String_tn(JSContext
* cx
, JSObject
* proto
, JSString
* str
)
2850 JS_ASSERT(JS_ON_TRACE(cx
));
2851 JSObject
*obj
= NewObjectWithClassProto(cx
, &js_StringClass
, proto
, FINALIZE_OBJECT2
);
2854 obj
->setPrimitiveThis(StringValue(str
));
2857 JS_DEFINE_CALLINFO_3(extern, OBJECT
, js_String_tn
, CONTEXT
, CALLEE_PROTOTYPE
, STRING
, 0,
2858 nanojit::ACCSET_STORE_ANY
)
2861 js_CreateThisFromTrace(JSContext
*cx
, Class
*clasp
, JSObject
*ctor
)
2863 JS_ASSERT(JS_ON_TRACE(cx
));
2864 JS_ASSERT(ctor
->isFunction());
2866 if (!ctor
->ensureClassReservedSlots(cx
))
2869 jsid classPrototypeId
= ATOM_TO_JSID(cx
->runtime
->atomState
.classPrototypeAtom
);
2870 const Shape
*shape
= ctor
->nativeLookup(classPrototypeId
);
2871 Value pval
= shape
? ctor
->getSlot(shape
->slot
) : MagicValue(JS_GENERIC_MAGIC
);
2873 JSObject
*parent
= ctor
->getParent();
2875 if (pval
.isObject()) {
2876 /* An object in ctor.prototype, let's use it as the new instance's proto. */
2877 proto
= &pval
.toObject();
2879 /* A hole or a primitive: either way, we need to get Object.prototype. */
2880 if (!js_GetClassPrototype(cx
, parent
, JSProto_Object
, &proto
))
2883 if (pval
.isMagic(JS_GENERIC_MAGIC
)) {
2885 * No ctor.prototype was set, so we inline-expand and optimize
2886 * fun_resolve's prototype creation code.
2888 proto
= NewNativeClassInstance(cx
, clasp
, proto
, parent
);
2891 if (!js_SetClassPrototype(cx
, ctor
, proto
, JSPROP_ENUMERATE
| JSPROP_PERMANENT
))
2895 * A primitive value in .prototype means to use Object.prototype
2896 * for proto. See ES5 13.2.2 step 7.
2902 * FIXME: 561785 at least. Quasi-natives including XML objects prevent us
2903 * from easily or unconditionally calling NewNativeClassInstance here.
2905 gc::FinalizeKind kind
= NewObjectGCKind(cx
, clasp
);
2906 return NewNonFunction
<WithProto::Given
>(cx
, clasp
, proto
, parent
, kind
);
2909 JS_DEFINE_CALLINFO_3(extern, CONSTRUCTOR_RETRY
, js_CreateThisFromTrace
, CONTEXT
, CLASS
, OBJECT
, 0,
2910 nanojit::ACCSET_STORE_ANY
)
2912 #else /* !JS_TRACER */
2914 # define js_Object_trcinfo NULL
2916 #endif /* !JS_TRACER */
2919 * Given pc pointing after a property accessing bytecode, return true if the
2920 * access is "object-detecting" in the sense used by web scripts, e.g., when
2921 * checking whether document.all is defined.
2923 JS_REQUIRES_STACK JSBool
2924 Detecting(JSContext
*cx
, jsbytecode
*pc
)
2931 script
= cx
->fp()->script();
2932 endpc
= script
->code
+ script
->length
;
2933 for (;; pc
+= js_CodeSpec
[op
].length
) {
2934 JS_ASSERT_IF(!cx
->fp()->hasImacropc(), script
->code
<= pc
&& pc
< endpc
);
2936 /* General case: a branch or equality op follows the access. */
2937 op
= js_GetOpcode(cx
, script
, pc
);
2938 if (js_CodeSpec
[op
].format
& JOF_DETECTING
)
2944 * Special case #1: handle (document.all == null). Don't sweat
2945 * about JS1.2's revision of the equality operators here.
2948 op
= js_GetOpcode(cx
, script
, pc
);
2949 return *pc
== JSOP_EQ
|| *pc
== JSOP_NE
;
2956 * Special case #2: handle (document.all == undefined). Don't
2957 * worry about someone redefining undefined, which was added by
2958 * Edition 3, so is read/write for backward compatibility.
2960 GET_ATOM_FROM_BYTECODE(script
, pc
, 0, atom
);
2961 if (atom
== cx
->runtime
->atomState
.typeAtoms
[JSTYPE_VOID
] &&
2962 (pc
+= js_CodeSpec
[op
].length
) < endpc
) {
2963 op
= js_GetOpcode(cx
, script
, pc
);
2964 return op
== JSOP_EQ
|| op
== JSOP_NE
||
2965 op
== JSOP_STRICTEQ
|| op
== JSOP_STRICTNE
;
2971 * At this point, anything but an extended atom index prefix means
2972 * we're not detecting.
2974 if (!(js_CodeSpec
[op
].format
& JOF_INDEXBASE
))
2982 * Infer lookup flags from the currently executing bytecode. This does
2983 * not attempt to infer JSRESOLVE_WITH, because the current bytecode
2984 * does not indicate whether we are in a with statement. Return defaultFlags
2985 * if a currently executing bytecode cannot be determined.
2988 js_InferFlags(JSContext
*cx
, uintN defaultFlags
)
2991 if (JS_ON_TRACE(cx
))
2992 return cx
->bailExit
->lookupFlags
;
2995 JS_ASSERT_NOT_ON_TRACE(cx
);
2998 const JSCodeSpec
*cs
;
3002 JSStackFrame
*const fp
= js_GetTopStackFrame(cx
);
3003 if (!fp
|| !(pc
= cx
->regs
->pc
))
3004 return defaultFlags
;
3005 cs
= &js_CodeSpec
[js_GetOpcode(cx
, fp
->script(), pc
)];
3006 format
= cs
->format
;
3007 if (JOF_MODE(format
) != JOF_NAME
)
3008 flags
|= JSRESOLVE_QUALIFIED
;
3009 if ((format
& (JOF_SET
| JOF_FOR
)) || fp
->isAssigning()) {
3010 flags
|= JSRESOLVE_ASSIGNING
;
3011 } else if (cs
->length
>= 0) {
3013 JSScript
*script
= cx
->fp()->script();
3014 if (pc
< script
->code
+ script
->length
&& Detecting(cx
, pc
))
3015 flags
|= JSRESOLVE_DETECTING
;
3017 if (format
& JOF_DECLARING
)
3018 flags
|= JSRESOLVE_DECLARING
;
3023 * ObjectOps and Class for with-statement stack objects.
3026 with_LookupProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, JSObject
**objp
,
3029 /* Fixes bug 463997 */
3030 uintN flags
= cx
->resolveFlags
;
3031 if (flags
== JSRESOLVE_INFER
)
3032 flags
= js_InferFlags(cx
, flags
);
3033 flags
|= JSRESOLVE_WITH
;
3034 JSAutoResolveFlags
rf(cx
, flags
);
3035 return obj
->getProto()->lookupProperty(cx
, id
, objp
, propp
);
3039 with_GetProperty(JSContext
*cx
, JSObject
*obj
, JSObject
*receiver
, jsid id
, Value
*vp
)
3041 return obj
->getProto()->getProperty(cx
, id
, vp
);
3045 with_SetProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
, JSBool strict
)
3047 return obj
->getProto()->setProperty(cx
, id
, vp
, strict
);
3051 with_GetAttributes(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN
*attrsp
)
3053 return obj
->getProto()->getAttributes(cx
, id
, attrsp
);
3057 with_SetAttributes(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN
*attrsp
)
3059 return obj
->getProto()->setAttributes(cx
, id
, attrsp
);
3063 with_DeleteProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*rval
, JSBool strict
)
3065 return obj
->getProto()->deleteProperty(cx
, id
, rval
, strict
);
3069 with_Enumerate(JSContext
*cx
, JSObject
*obj
, JSIterateOp enum_op
,
3070 Value
*statep
, jsid
*idp
)
3072 return obj
->getProto()->enumerate(cx
, enum_op
, statep
, idp
);
3076 with_TypeOf(JSContext
*cx
, JSObject
*obj
)
3078 return JSTYPE_OBJECT
;
3082 with_ThisObject(JSContext
*cx
, JSObject
*obj
)
3084 return obj
->getWithThis();
3087 Class js_WithClass
= {
3089 JSCLASS_HAS_PRIVATE
| JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS
,
3090 PropertyStub
, /* addProperty */
3091 PropertyStub
, /* delProperty */
3092 PropertyStub
, /* getProperty */
3093 PropertyStub
, /* setProperty */
3097 NULL
, /* finalize */
3098 NULL
, /* reserved */
3099 NULL
, /* checkAccess */
3101 NULL
, /* construct */
3102 NULL
, /* xdrObject */
3103 NULL
, /* hasInstance */
3107 with_LookupProperty
,
3108 NULL
, /* defineProperty */
3113 with_DeleteProperty
,
3123 JS_REQUIRES_STACK JSObject
*
3124 js_NewWithObject(JSContext
*cx
, JSObject
*proto
, JSObject
*parent
, jsint depth
)
3128 obj
= js_NewGCObject(cx
, FINALIZE_OBJECT2
);
3132 JSStackFrame
*priv
= js_FloatingFrameIfGenerator(cx
, cx
->fp());
3134 obj
->init(cx
, &js_WithClass
, proto
, parent
, priv
, false);
3135 obj
->setMap(cx
->runtime
->emptyWithShape
);
3136 OBJ_SET_BLOCK_DEPTH(cx
, obj
, depth
);
3138 AutoObjectRooter
tvr(cx
, obj
);
3139 JSObject
*thisp
= proto
->thisObject(cx
);
3143 assertSameCompartment(cx
, obj
, thisp
);
3145 obj
->setWithThis(thisp
);
3150 js_NewBlockObject(JSContext
*cx
)
3153 * Null obj's proto slot so that Object.prototype.* does not pollute block
3154 * scopes and to give the block object its own scope.
3156 JSObject
*blockObj
= js_NewGCObject(cx
, FINALIZE_OBJECT2
);
3160 blockObj
->init(cx
, &js_BlockClass
, NULL
, NULL
, NULL
, false);
3161 blockObj
->setMap(cx
->runtime
->emptyBlockShape
);
3166 js_CloneBlockObject(JSContext
*cx
, JSObject
*proto
, JSStackFrame
*fp
)
3168 JS_ASSERT(proto
->isStaticBlock());
3170 size_t count
= OBJ_BLOCK_COUNT(cx
, proto
);
3171 gc::FinalizeKind kind
= gc::GetGCObjectKind(count
+ 1);
3173 JSObject
*clone
= js_NewGCObject(cx
, kind
);
3177 JSStackFrame
*priv
= js_FloatingFrameIfGenerator(cx
, fp
);
3179 /* The caller sets parent on its own. */
3180 clone
->init(cx
, &js_BlockClass
, proto
, NULL
, priv
, false);
3182 clone
->setMap(proto
->map
);
3183 if (!clone
->ensureInstanceReservedSlots(cx
, count
+ 1))
3186 clone
->setSlot(JSSLOT_BLOCK_DEPTH
, proto
->getSlot(JSSLOT_BLOCK_DEPTH
));
3188 JS_ASSERT(clone
->isClonedBlock());
3192 JS_REQUIRES_STACK JSBool
3193 js_PutBlockObject(JSContext
*cx
, JSBool normalUnwind
)
3195 JSStackFrame
*const fp
= cx
->fp();
3196 JSObject
*obj
= &fp
->scopeChain();
3197 JS_ASSERT(obj
->isClonedBlock());
3198 JS_ASSERT(obj
->getPrivate() == js_FloatingFrameIfGenerator(cx
, cx
->fp()));
3200 /* Block objects should have all reserved slots allocated early. */
3201 uintN count
= OBJ_BLOCK_COUNT(cx
, obj
);
3202 JS_ASSERT(obj
->numSlots() >= JSSLOT_BLOCK_DEPTH
+ 1 + count
);
3204 /* The block and its locals must be on the current stack for GC safety. */
3205 uintN depth
= OBJ_BLOCK_DEPTH(cx
, obj
);
3206 JS_ASSERT(depth
<= size_t(cx
->regs
->sp
- fp
->base()));
3207 JS_ASSERT(count
<= size_t(cx
->regs
->sp
- fp
->base() - depth
));
3209 /* See comments in CheckDestructuring from jsparse.cpp. */
3210 JS_ASSERT(count
>= 1);
3213 uintN slot
= JSSLOT_BLOCK_FIRST_FREE_SLOT
;
3214 depth
+= fp
->numFixed();
3215 memcpy(obj
->getSlots() + slot
, fp
->slots() + depth
, count
* sizeof(Value
));
3218 /* We must clear the private slot even with errors. */
3219 obj
->setPrivate(NULL
);
3220 fp
->setScopeChainNoCallObj(*obj
->getParent());
3221 return normalUnwind
;
3225 block_getProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
3228 * Block objects are never exposed to script, and the engine handles them
3229 * with care. So unlike other getters, this one can assert (rather than
3230 * check) certain invariants about obj.
3232 JS_ASSERT(obj
->isClonedBlock());
3233 uintN index
= (uintN
) JSID_TO_INT(id
);
3234 JS_ASSERT(index
< OBJ_BLOCK_COUNT(cx
, obj
));
3236 JSStackFrame
*fp
= (JSStackFrame
*) obj
->getPrivate();
3238 fp
= js_LiveFrameIfGenerator(fp
);
3239 index
+= fp
->numFixed() + OBJ_BLOCK_DEPTH(cx
, obj
);
3240 JS_ASSERT(index
< fp
->numSlots());
3241 *vp
= fp
->slots()[index
];
3245 /* Values are in slots immediately following the class-reserved ones. */
3246 JS_ASSERT(obj
->getSlot(JSSLOT_FREE(&js_BlockClass
) + index
) == *vp
);
3251 block_setProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
)
3253 JS_ASSERT(obj
->isClonedBlock());
3254 uintN index
= (uintN
) JSID_TO_INT(id
);
3255 JS_ASSERT(index
< OBJ_BLOCK_COUNT(cx
, obj
));
3257 JSStackFrame
*fp
= (JSStackFrame
*) obj
->getPrivate();
3259 fp
= js_LiveFrameIfGenerator(fp
);
3260 index
+= fp
->numFixed() + OBJ_BLOCK_DEPTH(cx
, obj
);
3261 JS_ASSERT(index
< fp
->numSlots());
3262 fp
->slots()[index
] = *vp
;
3267 * The value in *vp will be written back to the slot in obj that was
3268 * allocated when this let binding was defined.
3274 JSObject::defineBlockVariable(JSContext
*cx
, jsid id
, intN index
)
3276 JS_ASSERT(isStaticBlock());
3278 /* Use JSPROP_ENUMERATE to aid the disassembler. */
3279 uint32 slot
= JSSLOT_FREE(&js_BlockClass
) + index
;
3280 const Shape
*shape
= addProperty(cx
, id
,
3281 block_getProperty
, block_setProperty
,
3282 slot
, JSPROP_ENUMERATE
| JSPROP_PERMANENT
,
3283 Shape::HAS_SHORTID
, index
);
3286 if (slot
>= numSlots() && !growSlots(cx
, slot
+ 1))
3292 GetObjectSize(JSObject
*obj
)
3294 return (obj
->isFunction() && !obj
->getPrivate())
3295 ? sizeof(JSFunction
)
3296 : sizeof(JSObject
) + sizeof(js::Value
) * obj
->numFixedSlots();
3300 * Use this method with extreme caution. It trades the guts of two objects and updates
3301 * scope ownership. This operation is not thread-safe, just as fast array to slow array
3302 * transitions are inherently not thread-safe. Don't perform a swap operation on objects
3303 * shared across threads or, or bad things will happen. You have been warned.
3306 JSObject::swap(JSContext
*cx
, JSObject
*other
)
3308 size_t size
= GetObjectSize(this);
3310 if (size
!= GetObjectSize(other
)) {
3312 * Objects with different numbers of fixed slots can be swapped only if they
3313 * are both shapeless non-natives, to preserve the invariant that objects with the
3314 * same shape have the same number of fixed slots. Use a dynamic array for both.
3316 JS_ASSERT(!isNative());
3317 JS_ASSERT(!other
->isNative());
3318 size
= sizeof(JSObject
);
3319 if (!hasSlotsArray()) {
3320 if (!allocSlots(cx
, numSlots()))
3323 if (!other
->hasSlotsArray()) {
3324 if (!other
->allocSlots(cx
, other
->numSlots()))
3329 bool thisInline
= !hasSlotsArray();
3330 bool otherInline
= !other
->hasSlotsArray();
3332 JS_STATIC_ASSERT(FINALIZE_OBJECT_LAST
== FINALIZE_OBJECT16
);
3334 char tmp
[tl::Max
<sizeof(JSFunction
), sizeof(JSObject_Slots16
)>::result
];
3335 JS_ASSERT(size
<= sizeof(tmp
));
3337 /* Trade the guts of the objects. */
3338 memcpy(tmp
, this, size
);
3339 memcpy(this, other
, size
);
3340 memcpy(other
, tmp
, size
);
3342 /* Fixup pointers for inline slots on the objects. */
3344 other
->slots
= other
->fixedSlots();
3346 this->slots
= this->fixedSlots();
3353 #define NO_PARENT_INDEX ((uint32)-1)
3356 FindObjectIndex(JSObjectArray
*array
, JSObject
*obj
)
3364 if (array
->vector
[--i
] == obj
)
3369 return NO_PARENT_INDEX
;
3373 js_XDRBlockObject(JSXDRState
*xdr
, JSObject
**objp
)
3377 JSObject
*obj
, *parent
;
3379 uint32 depthAndCount
;
3384 obj
= NULL
; /* quell GCC overwarning */
3387 if (xdr
->mode
== JSXDR_ENCODE
) {
3389 parent
= obj
->getParent();
3390 parentId
= (xdr
->script
->objectsOffset
== 0)
3392 : FindObjectIndex(xdr
->script
->objects(), parent
);
3393 depth
= (uint16
)OBJ_BLOCK_DEPTH(cx
, obj
);
3394 count
= (uint16
)OBJ_BLOCK_COUNT(cx
, obj
);
3395 depthAndCount
= (uint32
)(depth
<< 16) | count
;
3397 #ifdef __GNUC__ /* suppress bogus gcc warnings */
3401 /* First, XDR the parent atomid. */
3402 if (!JS_XDRUint32(xdr
, &parentId
))
3405 if (xdr
->mode
== JSXDR_DECODE
) {
3406 obj
= js_NewBlockObject(cx
);
3412 * If there's a parent id, then get the parent out of our script's
3413 * object array. We know that we XDR block object in outer-to-inner
3414 * order, which means that getting the parent now will work.
3416 if (parentId
== NO_PARENT_INDEX
)
3419 parent
= xdr
->script
->getObject(parentId
);
3420 obj
->setParent(parent
);
3423 AutoObjectRooter
tvr(cx
, obj
);
3425 if (!JS_XDRUint32(xdr
, &depthAndCount
))
3428 if (xdr
->mode
== JSXDR_DECODE
) {
3429 depth
= (uint16
)(depthAndCount
>> 16);
3430 count
= (uint16
)depthAndCount
;
3431 obj
->setSlot(JSSLOT_BLOCK_DEPTH
, Value(Int32Value(depth
)));
3434 * XDR the block object's properties. We know that there are 'count'
3435 * properties to XDR, stored as id/shortid pairs.
3437 for (uintN i
= 0; i
< count
; i
++) {
3441 /* XDR the real id, then the shortid. */
3442 if (!js_XDRAtom(xdr
, &atom
) || !JS_XDRUint16(xdr
, &shortid
))
3445 if (!obj
->defineBlockVariable(cx
, ATOM_TO_JSID(atom
), shortid
))
3449 Vector
<const Shape
*, 8> shapes(cx
);
3450 shapes
.growByUninitialized(count
);
3452 for (Shape::Range
r(obj
->lastProperty()); !r
.empty(); r
.popFront()) {
3454 shapes
[shape
->shortid
] = shape
;
3458 * XDR the block object's properties. We know that there are 'count'
3459 * properties to XDR, stored as id/shortid pairs.
3461 for (uintN i
= 0; i
< count
; i
++) {
3463 JS_ASSERT(shape
->getter() == block_getProperty
);
3465 jsid propid
= shape
->id
;
3466 JS_ASSERT(JSID_IS_ATOM(propid
));
3467 JSAtom
*atom
= JSID_TO_ATOM(propid
);
3469 uint16 shortid
= uint16(shape
->shortid
);
3470 JS_ASSERT(shortid
== i
);
3472 /* XDR the real id, then the shortid. */
3473 if (!js_XDRAtom(xdr
, &atom
) || !JS_XDRUint16(xdr
, &shortid
))
3482 Class js_BlockClass
= {
3484 JSCLASS_HAS_PRIVATE
| JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_IS_ANONYMOUS
,
3485 PropertyStub
, /* addProperty */
3486 PropertyStub
, /* delProperty */
3487 PropertyStub
, /* getProperty */
3488 PropertyStub
, /* setProperty */
3495 js_InitObjectClass(JSContext
*cx
, JSObject
*obj
)
3497 JSObject
*proto
= js_InitClass(cx
, obj
, NULL
, &js_ObjectClass
, js_Object
, 1,
3498 object_props
, object_methods
, NULL
, object_static_methods
);
3502 /* ECMA (15.1.2.1) says 'eval' is a property of the global object. */
3503 jsid id
= ATOM_TO_JSID(cx
->runtime
->atomState
.evalAtom
);
3504 if (!js_DefineFunction(cx
, obj
, id
, eval
, 1, JSFUN_STUB_GSOPS
))
3511 DefineStandardSlot(JSContext
*cx
, JSObject
*obj
, JSProtoKey key
, JSAtom
*atom
,
3512 const Value
&v
, uint32 attrs
, bool &named
)
3514 jsid id
= ATOM_TO_JSID(atom
);
3516 if (key
!= JSProto_Null
) {
3518 * Initializing an actual standard class on a global object. If the
3519 * property is not yet present, force it into a new one bound to a
3520 * reserved slot. Otherwise, go through the normal property path.
3522 JS_ASSERT(obj
->getClass()->flags
& JSCLASS_IS_GLOBAL
);
3523 JS_ASSERT(obj
->isNative());
3525 if (!obj
->ensureClassReservedSlots(cx
))
3528 const Shape
*shape
= obj
->nativeLookup(id
);
3530 uint32 slot
= 2 * JSProto_LIMIT
+ key
;
3531 if (!js_SetReservedSlot(cx
, obj
, slot
, v
))
3533 if (!obj
->addProperty(cx
, id
, PropertyStub
, PropertyStub
, slot
, attrs
, 0, 0))
3541 named
= obj
->defineProperty(cx
, id
, v
, PropertyStub
, PropertyStub
, attrs
);
3546 js_InitClass(JSContext
*cx
, JSObject
*obj
, JSObject
*parent_proto
,
3547 Class
*clasp
, Native constructor
, uintN nargs
,
3548 JSPropertySpec
*ps
, JSFunctionSpec
*fs
,
3549 JSPropertySpec
*static_ps
, JSFunctionSpec
*static_fs
)
3556 atom
= js_Atomize(cx
, clasp
->name
, strlen(clasp
->name
), 0);
3561 * When initializing a standard class, if no parent_proto (grand-proto of
3562 * instances of the class, parent-proto of the class's prototype object)
3563 * is given, we must use Object.prototype if it is available. Otherwise,
3564 * we could look up the wrong binding for a class name in obj. Example:
3567 * print("hi there".join);
3569 * should print undefined, not Array.prototype.join. This is required by
3570 * ECMA-262, alas. It might have been better to make String readonly and
3571 * permanent in the global object, instead -- but that's too big a change
3572 * to swallow at this point.
3574 key
= JSCLASS_CACHED_PROTO_KEY(clasp
);
3575 if (key
!= JSProto_Null
&&
3577 !js_GetClassPrototype(cx
, obj
, JSProto_Object
, &parent_proto
)) {
3582 * Create a prototype object for this class.
3584 * FIXME: lazy standard (built-in) class initialization and even older
3585 * eager boostrapping code rely on all of these properties:
3587 * 1. NewObject attempting to compute a default prototype object when
3588 * passed null for proto; and
3590 * 2. NewObject tolerating no default prototype (null proto slot value)
3591 * due to this js_InitClass call coming from js_InitFunctionClass on an
3592 * otherwise-uninitialized global.
3594 * 3. NewObject allocating a JSFunction-sized GC-thing when clasp is
3595 * &js_FunctionClass, not a JSObject-sized (smaller) GC-thing.
3597 * The JS_NewObjectForGivenProto and JS_NewObject APIs also allow clasp to
3598 * be &js_FunctionClass (we could break compatibility easily). But fixing
3599 * (3) is not enough without addressing the bootstrapping dependency on (1)
3602 JSObject
*proto
= NewObject
<WithProto::Class
>(cx
, clasp
, parent_proto
, obj
);
3606 proto
->syncSpecialEquality();
3608 /* After this point, control must exit via label bad or out. */
3609 AutoObjectRooter
tvr(cx
, proto
);
3614 * Lacking a constructor, name the prototype (e.g., Math) unless this
3615 * class (a) is anonymous, i.e. for internal use only; (b) the class
3616 * of obj (the global object) is has a reserved slot indexed by key;
3617 * and (c) key is not the null key.
3619 if (!(clasp
->flags
& JSCLASS_IS_ANONYMOUS
) ||
3620 !(obj
->getClass()->flags
& JSCLASS_IS_GLOBAL
) ||
3621 key
== JSProto_Null
)
3623 uint32 attrs
= (clasp
->flags
& JSCLASS_IS_ANONYMOUS
)
3624 ? JSPROP_READONLY
| JSPROP_PERMANENT
3626 if (!DefineStandardSlot(cx
, obj
, key
, atom
, ObjectValue(*proto
), attrs
, named
))
3632 fun
= js_NewFunction(cx
, NULL
, constructor
, nargs
, JSFUN_CONSTRUCTOR
, obj
, atom
);
3636 AutoValueRooter
tvr2(cx
, ObjectValue(*fun
));
3637 if (!DefineStandardSlot(cx
, obj
, key
, atom
, tvr2
.value(), 0, named
))
3641 * Remember the class this function is a constructor for so that
3642 * we know to create an object of this class when we call the
3645 FUN_CLASP(fun
) = clasp
;
3648 * Optionally construct the prototype object, before the class has
3649 * been fully initialized. Allow the ctor to replace proto with a
3650 * different object, as is done for operator new -- and as at least
3651 * XML support requires.
3653 ctor
= FUN_OBJECT(fun
);
3654 if (clasp
->flags
& JSCLASS_CONSTRUCT_PROTOTYPE
) {
3656 if (!InvokeConstructorWithGivenThis(cx
, proto
, ObjectOrNullValue(ctor
),
3660 if (rval
.isObject() && &rval
.toObject() != proto
)
3661 proto
= &rval
.toObject();
3664 /* Connect constructor and prototype by named properties. */
3665 if (!js_SetClassPrototype(cx
, ctor
, proto
,
3666 JSPROP_READONLY
| JSPROP_PERMANENT
)) {
3670 /* Bootstrap Function.prototype (see also JS_InitStandardClasses). */
3671 if (ctor
->getClass() == clasp
)
3672 ctor
->setProto(proto
);
3675 /* Add properties and methods to the prototype and the constructor. */
3676 if ((ps
&& !JS_DefineProperties(cx
, proto
, ps
)) ||
3677 (fs
&& !JS_DefineFunctions(cx
, proto
, fs
)) ||
3678 (static_ps
&& !JS_DefineProperties(cx
, ctor
, static_ps
)) ||
3679 (static_fs
&& !JS_DefineFunctions(cx
, ctor
, static_fs
))) {
3684 * Pre-brand the prototype and constructor if they have built-in methods.
3685 * This avoids extra shape guard branch exits in the tracejitted code.
3687 if (fs
&& !proto
->brand(cx
))
3689 if (ctor
!= proto
&& static_fs
&& !ctor
->brand(cx
))
3693 * Make sure proto's emptyShape is available to be shared by objects of
3694 * this class. JSObject::emptyShape is a one-slot cache. If we omit this,
3695 * some other class could snap it up. (The risk is particularly great for
3696 * Object.prototype.)
3698 * All callers of JSObject::initSharingEmptyShape depend on this.
3700 * FIXME: bug 592296 -- js_InitArrayClass should pass &js_SlowArrayClass
3701 * and make the Array.prototype slow from the start.
3703 JS_ASSERT_IF(proto
->clasp
!= clasp
,
3704 clasp
== &js_ArrayClass
&& proto
->clasp
== &js_SlowArrayClass
);
3705 if (!proto
->getEmptyShape(cx
, proto
->clasp
, FINALIZE_OBJECT0
))
3708 if (clasp
->flags
& (JSCLASS_FREEZE_PROTO
|JSCLASS_FREEZE_CTOR
)) {
3709 JS_ASSERT_IF(ctor
== proto
, !(clasp
->flags
& JSCLASS_FREEZE_CTOR
));
3710 if (proto
&& (clasp
->flags
& JSCLASS_FREEZE_PROTO
) && !proto
->freeze(cx
))
3712 if (ctor
&& (clasp
->flags
& JSCLASS_FREEZE_CTOR
) && !ctor
->freeze(cx
))
3716 /* If this is a standard class, cache its prototype. */
3717 if (key
!= JSProto_Null
&& !js_SetClassObject(cx
, obj
, key
, ctor
, proto
))
3725 obj
->deleteProperty(cx
, ATOM_TO_JSID(atom
), &rval
, false);
3731 JSObject::allocSlots(JSContext
*cx
, size_t newcap
)
3733 uint32 oldcap
= numSlots();
3735 JS_ASSERT(newcap
>= oldcap
&& !hasSlotsArray());
3737 if (newcap
> NSLOTS_LIMIT
) {
3738 if (!JS_ON_TRACE(cx
))
3739 js_ReportAllocationOverflow(cx
);
3743 Value
*tmpslots
= (Value
*) cx
->malloc(newcap
* sizeof(Value
));
3745 return false; /* Leave slots at inline buffer. */
3749 /* Copy over anything from the inline buffer. */
3750 memcpy(slots
, fixedSlots(), oldcap
* sizeof(Value
));
3751 ClearValueRange(slots
+ oldcap
, newcap
- oldcap
, isDenseArray());
3756 JSObject::growSlots(JSContext
*cx
, size_t newcap
)
3759 * When an object with CAPACITY_DOUBLING_MAX or fewer slots needs to
3760 * grow, double its capacity, to add N elements in amortized O(N) time.
3762 * Above this limit, grow by 12.5% each time. Speed is still amortized
3763 * O(N), with a higher constant factor, and we waste less space.
3765 static const size_t CAPACITY_DOUBLING_MAX
= 1024 * 1024;
3766 static const size_t CAPACITY_CHUNK
= CAPACITY_DOUBLING_MAX
/ sizeof(Value
);
3768 uint32 oldcap
= numSlots();
3769 JS_ASSERT(oldcap
< newcap
);
3771 uint32 nextsize
= (oldcap
<= CAPACITY_DOUBLING_MAX
)
3773 : oldcap
+ (oldcap
>> 3);
3775 uint32 actualCapacity
= JS_MAX(newcap
, nextsize
);
3776 if (actualCapacity
>= CAPACITY_CHUNK
)
3777 actualCapacity
= JS_ROUNDUP(actualCapacity
, CAPACITY_CHUNK
);
3778 else if (actualCapacity
< SLOT_CAPACITY_MIN
)
3779 actualCapacity
= SLOT_CAPACITY_MIN
;
3781 /* Don't let nslots get close to wrapping around uint32. */
3782 if (actualCapacity
>= NSLOTS_LIMIT
) {
3783 JS_ReportOutOfMemory(cx
);
3787 /* If nothing was allocated yet, treat it as initial allocation. */
3788 if (!hasSlotsArray())
3789 return allocSlots(cx
, actualCapacity
);
3791 Value
*tmpslots
= (Value
*) cx
->realloc(slots
, actualCapacity
* sizeof(Value
));
3793 return false; /* Leave dslots as its old size. */
3795 capacity
= actualCapacity
;
3797 /* Initialize the additional slots we added. */
3798 ClearValueRange(slots
+ oldcap
, actualCapacity
- oldcap
, isDenseArray());
3803 JSObject::shrinkSlots(JSContext
*cx
, size_t newcap
)
3805 uint32 oldcap
= numSlots();
3806 JS_ASSERT(newcap
<= oldcap
);
3807 JS_ASSERT(newcap
>= slotSpan());
3809 if (oldcap
<= SLOT_CAPACITY_MIN
|| !hasSlotsArray()) {
3810 /* We won't shrink the slots any more. Clear excess holes. */
3811 ClearValueRange(slots
+ newcap
, oldcap
- newcap
, isDenseArray());
3815 uint32 fill
= newcap
;
3816 if (newcap
< SLOT_CAPACITY_MIN
)
3817 newcap
= SLOT_CAPACITY_MIN
;
3818 if (newcap
< numFixedSlots())
3819 newcap
= numFixedSlots();
3821 Value
*tmpslots
= (Value
*) cx
->realloc(slots
, newcap
* sizeof(Value
));
3823 return; /* Leave slots at its old size. */
3827 if (fill
< newcap
) {
3828 /* Clear any excess holes if we tried to shrink below SLOT_CAPACITY_MIN. */
3829 ClearValueRange(slots
+ fill
, newcap
- fill
, isDenseArray());
3834 JSObject::ensureInstanceReservedSlots(JSContext
*cx
, size_t nreserved
)
3836 JS_ASSERT_IF(isNative(),
3837 isBlock() || isCall() || (isFunction() && isBoundFunction()));
3839 uintN nslots
= JSSLOT_FREE(clasp
) + nreserved
;
3840 return nslots
<= numSlots() || allocSlots(cx
, nslots
);
3844 js_InitNullClass(JSContext
*cx
, JSObject
*obj
)
3850 #define JS_PROTO(name,code,init) extern JSObject *init(JSContext *, JSObject *);
3851 #include "jsproto.tbl"
3854 static JSObjectOp lazy_prototype_init
[JSProto_LIMIT
] = {
3855 #define JS_PROTO(name,code,init) init,
3856 #include "jsproto.tbl"
3863 SetProto(JSContext
*cx
, JSObject
*obj
, JSObject
*proto
, bool checkForCycles
)
3865 JS_ASSERT_IF(!checkForCycles
, obj
!= proto
);
3866 JS_ASSERT(obj
->isExtensible());
3868 if (obj
->isNative()) {
3869 if (!obj
->ensureClassReservedSlots(cx
))
3874 * Regenerate property cache shape ids for all of the scopes along the
3875 * old prototype chain to invalidate their property cache entries, in
3876 * case any entries were filled by looking up through obj.
3878 JSObject
*oldproto
= obj
;
3879 while (oldproto
&& oldproto
->isNative()) {
3880 oldproto
->protoShapeChange(cx
);
3881 oldproto
= oldproto
->getProto();
3884 if (!proto
|| !checkForCycles
) {
3885 obj
->setProto(proto
);
3886 } else if (!SetProtoCheckingForCycles(cx
, obj
, proto
)) {
3887 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_CYCLIC_VALUE
, js_proto_str
);
3896 js_GetClassObject(JSContext
*cx
, JSObject
*obj
, JSProtoKey key
,
3899 JSObject
*tmp
, *cobj
;
3900 JSResolvingKey rkey
;
3901 JSResolvingEntry
*rentry
;
3906 while ((tmp
= obj
->getParent()) != NULL
)
3908 if (!(obj
->getClass()->flags
& JSCLASS_IS_GLOBAL
)) {
3913 v
= obj
->getReservedSlot(key
);
3915 *objp
= &v
.toObject();
3920 rkey
.id
= ATOM_TO_JSID(cx
->runtime
->atomState
.classAtoms
[key
]);
3921 if (!js_StartResolving(cx
, &rkey
, JSRESFLAG_LOOKUP
, &rentry
))
3924 /* Already caching key in obj -- suppress recursion. */
3928 generation
= cx
->resolvingTable
->generation
;
3932 init
= lazy_prototype_init
[key
];
3934 if (!init(cx
, obj
)) {
3937 v
= obj
->getReservedSlot(key
);
3939 cobj
= &v
.toObject();
3943 js_StopResolving(cx
, &rkey
, JSRESFLAG_LOOKUP
, rentry
, generation
);
3949 js_SetClassObject(JSContext
*cx
, JSObject
*obj
, JSProtoKey key
, JSObject
*cobj
, JSObject
*proto
)
3951 JS_ASSERT(!obj
->getParent());
3952 if (!(obj
->getClass()->flags
& JSCLASS_IS_GLOBAL
))
3955 return js_SetReservedSlot(cx
, obj
, key
, ObjectOrNullValue(cobj
)) &&
3956 js_SetReservedSlot(cx
, obj
, JSProto_LIMIT
+ key
, ObjectOrNullValue(proto
));
3960 js_FindClassObject(JSContext
*cx
, JSObject
*start
, JSProtoKey protoKey
,
3961 Value
*vp
, Class
*clasp
)
3964 JSObject
*obj
, *cobj
, *pobj
;
3970 * Find the global object. Use cx->fp() directly to avoid falling off
3971 * trace; all JIT-elided stack frames have the same global object as
3974 VOUCH_DOES_NOT_REQUIRE_STACK();
3975 if (!start
&& (fp
= cx
->maybefp()) != NULL
)
3976 start
= &fp
->scopeChain();
3979 /* Find the topmost object in the scope chain. */
3982 start
= obj
->getParent();
3985 obj
= cx
->globalObject
;
3992 OBJ_TO_INNER_OBJECT(cx
, obj
);
3996 if (protoKey
!= JSProto_Null
) {
3997 JS_ASSERT(JSProto_Null
< protoKey
);
3998 JS_ASSERT(protoKey
< JSProto_LIMIT
);
3999 if (!js_GetClassObject(cx
, obj
, protoKey
, &cobj
))
4002 vp
->setObject(*cobj
);
4005 id
= ATOM_TO_JSID(cx
->runtime
->atomState
.classAtoms
[protoKey
]);
4007 JSAtom
*atom
= js_Atomize(cx
, clasp
->name
, strlen(clasp
->name
), 0);
4010 id
= ATOM_TO_JSID(atom
);
4013 JS_ASSERT(obj
->isNative());
4014 if (js_LookupPropertyWithFlags(cx
, obj
, id
, JSRESOLVE_CLASSNAME
,
4015 &pobj
, &prop
) < 0) {
4018 Value v
= UndefinedValue();
4019 if (prop
&& pobj
->isNative()) {
4020 shape
= (Shape
*) prop
;
4021 if (pobj
->containsSlot(shape
->slot
)) {
4022 v
= pobj
->nativeGetSlot(shape
->slot
);
4023 if (v
.isPrimitive())
4032 js_ConstructObject(JSContext
*cx
, Class
*clasp
, JSObject
*proto
, JSObject
*parent
,
4033 uintN argc
, Value
*argv
)
4035 AutoArrayRooter
argtvr(cx
, argc
, argv
);
4037 JSProtoKey protoKey
= GetClassProtoKey(clasp
);
4039 /* Protect constructor in case a crazy getter for .prototype uproots it. */
4040 AutoValueRooter
tvr(cx
);
4041 if (!js_FindClassObject(cx
, parent
, protoKey
, tvr
.addr(), clasp
))
4044 const Value
&cval
= tvr
.value();
4045 if (tvr
.value().isPrimitive()) {
4046 js_ReportIsNotFunction(cx
, tvr
.addr(), JSV2F_CONSTRUCT
| JSV2F_SEARCH_STACK
);
4051 * If proto is NULL, set it to Constructor.prototype, just like JSOP_NEW
4052 * does, likewise for the new object's parent.
4054 JSObject
*ctor
= &cval
.toObject();
4056 parent
= ctor
->getParent();
4059 if (!ctor
->getProperty(cx
, ATOM_TO_JSID(cx
->runtime
->atomState
.classPrototypeAtom
),
4063 if (rval
.isObjectOrNull())
4064 proto
= rval
.toObjectOrNull();
4067 JSObject
*obj
= NewObject
<WithProto::Class
>(cx
, clasp
, proto
, parent
);
4071 obj
->syncSpecialEquality();
4074 if (!InvokeConstructorWithGivenThis(cx
, obj
, cval
, argc
, argv
, &rval
))
4077 if (rval
.isPrimitive())
4081 * If the instance's class differs from what was requested, throw a type
4082 * error. If the given class has both the JSCLASS_HAS_PRIVATE and the
4083 * JSCLASS_CONSTRUCT_PROTOTYPE flags, and the instance does not have its
4084 * private data set at this point, then the constructor was replaced and
4085 * we should throw a type error.
4087 obj
= &rval
.toObject();
4088 if (obj
->getClass() != clasp
||
4089 (!(~clasp
->flags
& (JSCLASS_HAS_PRIVATE
|
4090 JSCLASS_CONSTRUCT_PROTOTYPE
)) &&
4091 !obj
->getPrivate())) {
4092 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
4093 JSMSG_WRONG_CONSTRUCTOR
, clasp
->name
);
4100 JSObject::allocSlot(JSContext
*cx
, uint32
*slotp
)
4102 uint32 slot
= slotSpan();
4103 JS_ASSERT(slot
>= JSSLOT_FREE(clasp
));
4106 * If this object is in dictionary mode and it has a property table, try to
4107 * pull a free slot from the property table's slot-number freelist.
4109 if (inDictionaryMode() && lastProp
->table
) {
4110 uint32
&last
= lastProp
->table
->freelist
;
4111 if (last
!= SHAPE_INVALID_SLOT
) {
4113 JS_ASSERT(last
< slot
);
4114 uint32 next
= getSlot(last
).toPrivateUint32();
4115 JS_ASSERT_IF(next
!= SHAPE_INVALID_SLOT
, next
< slot
);
4120 Value
&vref
= getSlotRef(last
);
4121 last
= vref
.toPrivateUint32();
4122 vref
.setUndefined();
4127 if (slot
>= numSlots() && !growSlots(cx
, slot
+ 1))
4130 /* JSObject::growSlots or JSObject::freeSlot should set the free slots to void. */
4131 JS_ASSERT(getSlot(slot
).isUndefined());
4137 JSObject::freeSlot(JSContext
*cx
, uint32 slot
)
4139 uint32 limit
= slotSpan();
4140 JS_ASSERT(slot
< limit
);
4142 Value
&vref
= getSlotRef(slot
);
4143 if (inDictionaryMode() && lastProp
->table
) {
4144 uint32
&last
= lastProp
->table
->freelist
;
4146 /* Can't afford to check the whole freelist, but let's check the head. */
4147 JS_ASSERT_IF(last
!= SHAPE_INVALID_SLOT
, last
< limit
&& last
!= slot
);
4150 * Freeing a slot other than the last one mapped by this object's
4151 * shape (and not a reserved slot; see bug 595230): push the slot onto
4152 * the dictionary property table's freelist. We want to let the last
4153 * slot be freed by shrinking the dslots vector; see js_TraceObject.
4155 if (JSSLOT_FREE(clasp
) <= slot
&& slot
+ 1 < limit
) {
4156 JS_ASSERT_IF(last
!= SHAPE_INVALID_SLOT
, last
< slotSpan());
4157 vref
.setPrivateUint32(last
);
4162 vref
.setUndefined();
4165 /* JSBOXEDWORD_INT_MAX as a string */
4166 #define JSBOXEDWORD_INT_MAX_STRING "1073741823"
4169 * Convert string indexes that convert to int jsvals as ints to save memory.
4170 * Care must be taken to use this macro every time a property name is used, or
4171 * else double-sets, incorrect property cache misses, or other mistakes could
4175 js_CheckForStringIndex(jsid id
)
4177 if (!JSID_IS_ATOM(id
))
4180 JSAtom
*atom
= JSID_TO_ATOM(id
);
4181 JSString
*str
= ATOM_TO_STRING(atom
);
4182 const jschar
*s
= str
->flatChars();
4185 JSBool negative
= (ch
== '-');
4192 size_t n
= str
->flatLength() - negative
;
4193 if (n
> sizeof(JSBOXEDWORD_INT_MAX_STRING
) - 1)
4196 const jschar
*cp
= s
;
4197 const jschar
*end
= s
+ n
;
4199 jsuint index
= JS7_UNDEC(*cp
++);
4200 jsuint oldIndex
= 0;
4204 while (JS7_ISDEC(*cp
)) {
4207 index
= 10 * index
+ c
;
4213 * Non-integer indexes can't be represented as integers. Also, distinguish
4214 * index "-0" from "0", because JSBOXEDWORD_INT cannot.
4216 if (cp
!= end
|| (negative
&& index
== 0))
4220 if (oldIndex
< -(JSID_INT_MIN
/ 10) ||
4221 (oldIndex
== -(JSID_INT_MIN
/ 10) && c
<= (-JSID_INT_MIN
% 10)))
4223 id
= INT_TO_JSID(-jsint(index
));
4226 if (oldIndex
< JSID_INT_MAX
/ 10 ||
4227 (oldIndex
== JSID_INT_MAX
/ 10 && c
<= (JSID_INT_MAX
% 10)))
4229 id
= INT_TO_JSID(jsint(index
));
4237 PurgeProtoChain(JSContext
*cx
, JSObject
*obj
, jsid id
)
4242 if (!obj
->isNative()) {
4243 obj
= obj
->getProto();
4246 shape
= obj
->nativeLookup(id
);
4248 PCMETER(JS_PROPERTY_CACHE(cx
).pcpurges
++);
4249 obj
->shadowingShapeChange(cx
, *shape
);
4251 if (!obj
->getParent()) {
4253 * All scope chains end in a global object, so this will change
4254 * the global shape. jstracer.cpp assumes that the global shape
4255 * never changes on trace, so we must deep-bail here.
4261 obj
= obj
->getProto();
4267 js_PurgeScopeChainHelper(JSContext
*cx
, JSObject
*obj
, jsid id
)
4269 JS_ASSERT(obj
->isDelegate());
4270 PurgeProtoChain(cx
, obj
->getProto(), id
);
4273 * We must purge the scope chain only for Call objects as they are the only
4274 * kind of cacheable non-global object that can gain properties after outer
4275 * properties with the same names have been cached or traced. Call objects
4276 * may gain such properties via eval introducing new vars; see bug 490364.
4278 if (obj
->isCall()) {
4279 while ((obj
= obj
->getParent()) != NULL
) {
4280 if (PurgeProtoChain(cx
, obj
, id
))
4287 js_AddNativeProperty(JSContext
*cx
, JSObject
*obj
, jsid id
,
4288 PropertyOp getter
, PropertyOp setter
, uint32 slot
,
4289 uintN attrs
, uintN flags
, intN shortid
)
4291 JS_ASSERT(!(flags
& Shape::METHOD
));
4294 * Purge the property cache of now-shadowed id in obj's scope chain. Do
4295 * this optimistically (assuming no failure below) before locking obj, so
4296 * we can lock the shadowed scope.
4298 js_PurgeScopeChain(cx
, obj
, id
);
4300 if (!obj
->ensureClassReservedSlots(cx
))
4303 /* Convert string indices to integers if appropriate. */
4304 id
= js_CheckForStringIndex(id
);
4305 return obj
->putProperty(cx
, id
, getter
, setter
, slot
, attrs
, flags
, shortid
);
4309 js_ChangeNativePropertyAttrs(JSContext
*cx
, JSObject
*obj
,
4310 const Shape
*shape
, uintN attrs
, uintN mask
,
4311 PropertyOp getter
, PropertyOp setter
)
4313 if (!obj
->ensureClassReservedSlots(cx
))
4315 return obj
->changeProperty(cx
, shape
, attrs
, mask
, getter
, setter
);
4319 js_DefineProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, const Value
*value
,
4320 PropertyOp getter
, PropertyOp setter
, uintN attrs
)
4322 return js_DefineNativeProperty(cx
, obj
, id
, *value
, getter
, setter
, attrs
,
4327 * Backward compatibility requires allowing addProperty hooks to mutate the
4328 * nominal initial value of a slotful property, while GC safety wants that
4329 * value to be stored before the call-out through the hook. Optimize to do
4330 * both while saving cycles for classes that stub their addProperty hook.
4333 CallAddPropertyHook(JSContext
*cx
, Class
*clasp
, JSObject
*obj
, const Shape
*shape
, Value
*vp
)
4335 if (clasp
->addProperty
!= PropertyStub
) {
4336 Value nominal
= *vp
;
4338 if (!CallJSPropertyOp(cx
, clasp
->addProperty
, obj
, SHAPE_USERID(shape
), vp
))
4340 if (*vp
!= nominal
) {
4341 if (obj
->containsSlot(shape
->slot
))
4342 obj
->nativeSetSlot(shape
->slot
, *vp
);
4349 js_DefineNativeProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, const Value
&value
,
4350 PropertyOp getter
, PropertyOp setter
, uintN attrs
,
4351 uintN flags
, intN shortid
, JSProperty
**propp
,
4352 uintN defineHow
/* = 0 */)
4359 JS_ASSERT((defineHow
& ~(JSDNP_CACHE_RESULT
| JSDNP_DONT_PURGE
| JSDNP_SET_METHOD
)) == 0);
4360 LeaveTraceIfGlobalObject(cx
, obj
);
4362 /* Convert string indices to integers if appropriate. */
4363 id
= js_CheckForStringIndex(id
);
4366 * If defining a getter or setter, we must check for its counterpart and
4367 * update the attributes and property ops. A getter or setter is really
4368 * only half of a property.
4371 if (attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)) {
4376 * If JS_THREADSAFE and id is found, js_LookupProperty returns with
4377 * shape non-null and pobj locked. If pobj == obj, the property is
4378 * already in obj and obj has its own (mutable) scope. So if we are
4379 * defining a getter whose setter was already defined, or vice versa,
4380 * finish the job via obj->changeProperty, and refresh the property
4381 * cache line for (obj, id) to map shape.
4383 if (!js_LookupProperty(cx
, obj
, id
, &pobj
, &prop
))
4385 shape
= (Shape
*) prop
;
4386 if (shape
&& pobj
== obj
&& shape
->isAccessorDescriptor()) {
4387 shape
= obj
->changeProperty(cx
, shape
, attrs
,
4388 JSPROP_GETTER
| JSPROP_SETTER
,
4389 (attrs
& JSPROP_GETTER
)
4392 (attrs
& JSPROP_SETTER
)
4405 * Purge the property cache of any properties named by id that are about
4406 * to be shadowed in obj's scope chain unless it is known a priori that it
4407 * is not possible. We do this before locking obj to avoid nesting locks.
4409 if (!(defineHow
& JSDNP_DONT_PURGE
))
4410 js_PurgeScopeChain(cx
, obj
, id
);
4413 * Check whether a readonly property or setter is being defined on a known
4414 * prototype object. See the comment in jscntxt.h before protoHazardShape's
4415 * member declaration.
4417 if (obj
->isDelegate() && (attrs
& (JSPROP_READONLY
| JSPROP_SETTER
)))
4418 cx
->runtime
->protoHazardShape
= js_GenerateShape(cx
, false);
4420 /* Use the object's class getter and setter by default. */
4421 clasp
= obj
->getClass();
4422 if (!(defineHow
& JSDNP_SET_METHOD
)) {
4423 if (!getter
&& !(attrs
& JSPROP_GETTER
))
4424 getter
= clasp
->getProperty
;
4425 if (!setter
&& !(attrs
& JSPROP_SETTER
))
4426 setter
= clasp
->setProperty
;
4429 /* Get obj's own scope if it has one, or create a new one for obj. */
4430 if (!obj
->ensureClassReservedSlots(cx
))
4435 /* Add a new property, or replace an existing one of the same id. */
4436 if (defineHow
& JSDNP_SET_METHOD
) {
4437 JS_ASSERT(clasp
== &js_ObjectClass
);
4438 JS_ASSERT(IsFunctionObject(value
));
4439 JS_ASSERT(!(attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)));
4440 JS_ASSERT(!getter
&& !setter
);
4442 JSObject
*funobj
= &value
.toObject();
4443 if (FUN_OBJECT(GET_FUNCTION_PRIVATE(cx
, funobj
)) == funobj
) {
4444 flags
|= Shape::METHOD
;
4445 getter
= CastAsPropertyOp(funobj
);
4449 added
= !obj
->nativeContains(id
);
4450 uint32 oldShape
= obj
->shape();
4451 shape
= obj
->putProperty(cx
, id
, getter
, setter
, SHAPE_INVALID_SLOT
,
4452 attrs
, flags
, shortid
);
4457 * If shape is a method, the above call to putProperty suffices to
4458 * update the shape if necessary. But if scope->branded(), the shape
4459 * may not have changed and we may be overwriting a function-valued
4460 * property. See bug 560998.
4462 if (obj
->shape() == oldShape
&& obj
->branded() && shape
->slot
!= SHAPE_INVALID_SLOT
)
4463 obj
->methodWriteBarrier(cx
, shape
->slot
, value
);
4466 /* Store value before calling addProperty, in case the latter GC's. */
4467 if (obj
->containsSlot(shape
->slot
))
4468 obj
->nativeSetSlot(shape
->slot
, value
);
4470 /* XXXbe called with lock held */
4472 if (!CallAddPropertyHook(cx
, clasp
, obj
, shape
, &valueCopy
)) {
4473 obj
->removeProperty(cx
, id
);
4477 if (defineHow
& JSDNP_CACHE_RESULT
) {
4479 JS_ASSERT_NOT_ON_TRACE(cx
);
4480 PropertyCacheEntry
*entry
=
4482 JS_PROPERTY_CACHE(cx
).fill(cx
, obj
, 0, 0, obj
, shape
, added
);
4483 TRACE_2(SetPropHit
, entry
, shape
);
4486 *propp
= (JSProperty
*) shape
;
4490 error
: // TRACE_2 jumps here on error.
4495 #define SCOPE_DEPTH_ACCUM(bs,val) \
4496 JS_SCOPE_DEPTH_METERING(JS_BASIC_STATS_ACCUM(bs, val))
4499 * Call obj's resolve hook. obj is a native object and the caller holds its
4502 * cx, start, id, and flags are the parameters initially passed to the ongoing
4503 * lookup; objp and propp are its out parameters. obj is an object along
4504 * start's prototype chain.
4506 * There are four possible outcomes:
4508 * - On failure, report an error or exception, unlock obj, and return false.
4510 * - If we are alrady resolving a property of *curobjp, set *recursedp = true,
4511 * unlock obj, and return true.
4513 * - If the resolve hook finds or defines the sought property, set *objp and
4514 * *propp appropriately, set *recursedp = false, and return true with *objp's
4517 * - Otherwise no property was resolved. Set *propp = NULL and *recursedp = false
4521 CallResolveOp(JSContext
*cx
, JSObject
*start
, JSObject
*obj
, jsid id
, uintN flags
,
4522 JSObject
**objp
, JSProperty
**propp
, bool *recursedp
)
4524 Class
*clasp
= obj
->getClass();
4525 JSResolveOp resolve
= clasp
->resolve
;
4528 * Avoid recursion on (obj, id) already being resolved on cx.
4530 * Once we have successfully added an entry for (obj, key) to
4531 * cx->resolvingTable, control must go through cleanup: before
4532 * returning. But note that JS_DHASH_ADD may find an existing
4533 * entry, in which case we bail to suppress runaway recursion.
4535 JSResolvingKey key
= {obj
, id
};
4536 JSResolvingEntry
*entry
;
4537 if (!js_StartResolving(cx
, &key
, JSRESFLAG_LOOKUP
, &entry
))
4540 /* Already resolving id in obj -- suppress recursion. */
4544 uint32 generation
= cx
->resolvingTable
->generation
;
4550 const Shape
*shape
= NULL
;
4551 if (clasp
->flags
& JSCLASS_NEW_RESOLVE
) {
4552 JSNewResolveOp newresolve
= (JSNewResolveOp
)resolve
;
4553 if (flags
== JSRESOLVE_INFER
)
4554 flags
= js_InferFlags(cx
, 0);
4555 JSObject
*obj2
= (clasp
->flags
& JSCLASS_NEW_RESOLVE_GETS_START
) ? start
: NULL
;
4558 /* Protect id and all atoms from a GC nested in resolve. */
4559 AutoKeepAtoms
keep(cx
->runtime
);
4560 ok
= newresolve(cx
, obj
, id
, flags
, &obj2
);
4566 /* Resolved: lookup id again for backward compatibility. */
4567 if (!obj2
->isNative()) {
4568 /* Whoops, newresolve handed back a foreign obj2. */
4569 JS_ASSERT(obj2
!= obj
);
4570 ok
= obj2
->lookupProperty(cx
, id
, objp
, propp
);
4575 * Require that obj2 not be empty now, as we do for old-style
4576 * resolve. If it doesn't, then id was not truly resolved, and
4577 * we'll find it in the proto chain, or miss it if obj2's proto
4578 * is not on obj's proto chain. That last case is a "too bad!"
4581 if (!obj2
->nativeEmpty())
4582 shape
= obj2
->nativeLookup(id
);
4585 JS_ASSERT(!obj2
->nativeEmpty());
4591 * Old resolve always requires id re-lookup if obj is not empty after
4594 ok
= resolve(cx
, obj
, id
);
4597 JS_ASSERT(obj
->isNative());
4598 if (!obj
->nativeEmpty())
4599 shape
= obj
->nativeLookup(id
);
4605 *propp
= (JSProperty
*) shape
;
4607 js_StopResolving(cx
, &key
, JSRESFLAG_LOOKUP
, entry
, generation
);
4611 static JS_ALWAYS_INLINE
int
4612 js_LookupPropertyWithFlagsInline(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN flags
,
4613 JSObject
**objp
, JSProperty
**propp
)
4615 /* We should not get string indices which aren't already integers here. */
4616 JS_ASSERT(id
== js_CheckForStringIndex(id
));
4618 /* Search scopes starting with obj and following the prototype link. */
4619 JSObject
*start
= obj
;
4621 for (protoIndex
= 0; ; protoIndex
++) {
4622 const Shape
*shape
= obj
->nativeLookup(id
);
4624 SCOPE_DEPTH_ACCUM(&cx
->runtime
->protoLookupDepthStats
, protoIndex
);
4626 *propp
= (JSProperty
*) shape
;
4630 /* Try obj's class resolve hook if id was not found in obj's scope. */
4631 if (!shape
&& obj
->getClass()->resolve
!= JS_ResolveStub
) {
4633 if (!CallResolveOp(cx
, start
, obj
, id
, flags
, objp
, propp
, &recursed
))
4638 /* Recalculate protoIndex in case it was resolved on some other object. */
4640 for (JSObject
*proto
= start
; proto
&& proto
!= *objp
; proto
= proto
->getProto())
4642 SCOPE_DEPTH_ACCUM(&cx
->runtime
->protoLookupDepthStats
, protoIndex
);
4647 JSObject
*proto
= obj
->getProto();
4650 if (!proto
->isNative()) {
4651 if (!proto
->lookupProperty(cx
, id
, objp
, propp
))
4653 return protoIndex
+ 1;
4664 JS_FRIEND_API(JSBool
)
4665 js_LookupProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, JSObject
**objp
,
4668 /* Convert string indices to integers if appropriate. */
4669 id
= js_CheckForStringIndex(id
);
4671 return js_LookupPropertyWithFlagsInline(cx
, obj
, id
, cx
->resolveFlags
, objp
, propp
) >= 0;
4675 js_LookupPropertyWithFlags(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN flags
,
4676 JSObject
**objp
, JSProperty
**propp
)
4678 /* Convert string indices to integers if appropriate. */
4679 id
= js_CheckForStringIndex(id
);
4681 return js_LookupPropertyWithFlagsInline(cx
, obj
, id
, flags
, objp
, propp
);
4684 PropertyCacheEntry
*
4685 js_FindPropertyHelper(JSContext
*cx
, jsid id
, JSBool cacheResult
,
4686 JSObject
**objp
, JSObject
**pobjp
, JSProperty
**propp
)
4688 JSObject
*scopeChain
, *obj
, *parent
, *pobj
;
4689 PropertyCacheEntry
*entry
;
4690 int scopeIndex
, protoIndex
;
4693 JS_ASSERT_IF(cacheResult
, !JS_ON_TRACE(cx
));
4694 scopeChain
= &js_GetTopStackFrame(cx
)->scopeChain();
4696 /* Scan entries on the scope chain that we can cache across. */
4697 entry
= JS_NO_PROP_CACHE_FILL
;
4699 parent
= obj
->getParent();
4700 for (scopeIndex
= 0;
4702 ? js_IsCacheableNonGlobalScope(obj
)
4703 : !obj
->getOps()->lookupProperty
;
4706 js_LookupPropertyWithFlags(cx
, obj
, id
, cx
->resolveFlags
,
4714 Class
*clasp
= obj
->getClass();
4715 JS_ASSERT(pobj
->isNative());
4716 JS_ASSERT(pobj
->getClass() == clasp
);
4717 if (clasp
== &js_BlockClass
) {
4719 * A block instance on the scope chain is immutable and it
4720 * shares its shapes with its compile-time prototype.
4722 JS_ASSERT(pobj
== obj
);
4723 JS_ASSERT(pobj
->isClonedBlock());
4724 JS_ASSERT(protoIndex
== 0);
4726 /* Call and DeclEnvClass objects have no prototypes. */
4727 JS_ASSERT(!obj
->getProto());
4728 JS_ASSERT(protoIndex
== 0);
4731 JS_ASSERT(obj
->isNative());
4735 * We must check if pobj is native as a global object can have
4736 * non-native prototype.
4738 if (cacheResult
&& pobj
->isNative()) {
4739 entry
= JS_PROPERTY_CACHE(cx
).fill(cx
, scopeChain
, scopeIndex
,
4743 SCOPE_DEPTH_ACCUM(&cx
->runtime
->scopeSearchDepthStats
, scopeIndex
);
4752 parent
= obj
->getParent();
4756 if (!obj
->lookupProperty(cx
, id
, &pobj
, &prop
))
4759 PCMETER(JS_PROPERTY_CACHE(cx
).nofills
++);
4764 * We conservatively assume that a resolve hook could mutate the scope
4765 * chain during JSObject::lookupProperty. So we read parent here again.
4767 parent
= obj
->getParent();
4776 JS_ASSERT(!!pobj
== !!prop
);
4784 * On return, if |*pobjp| is a native object, then |*propp| is a |Shape *|.
4785 * Otherwise, its type and meaning depends on the host object's implementation.
4787 JS_FRIEND_API(JSBool
)
4788 js_FindProperty(JSContext
*cx
, jsid id
, JSObject
**objp
, JSObject
**pobjp
,
4791 return !!js_FindPropertyHelper(cx
, id
, false, objp
, pobjp
, propp
);
4795 js_FindIdentifierBase(JSContext
*cx
, JSObject
*scopeChain
, jsid id
)
4798 * This function should not be called for a global object or from the
4799 * trace and should have a valid cache entry for native scopeChain.
4801 JS_ASSERT(scopeChain
->getParent());
4802 JS_ASSERT(!JS_ON_TRACE(cx
));
4804 JSObject
*obj
= scopeChain
;
4807 * Loop over cacheable objects on the scope chain until we find a
4808 * property. We also stop when we reach the global object skipping any
4809 * farther checks or lookups. For details see the JSOP_BINDNAME case of
4812 * The test order here matters because js_IsCacheableNonGlobalScope
4813 * must not be passed a global object (i.e. one with null parent).
4815 for (int scopeIndex
= 0;
4816 !obj
->getParent() || js_IsCacheableNonGlobalScope(obj
);
4820 int protoIndex
= js_LookupPropertyWithFlags(cx
, obj
, id
,
4826 if (!pobj
->isNative()) {
4827 JS_ASSERT(!obj
->getParent());
4830 JS_ASSERT_IF(obj
->getParent(), pobj
->getClass() == obj
->getClass());
4832 PropertyCacheEntry
*entry
=
4834 JS_PROPERTY_CACHE(cx
).fill(cx
, scopeChain
, scopeIndex
, protoIndex
, pobj
,
4840 JSObject
*parent
= obj
->getParent();
4846 /* Loop until we find a property or reach the global object. */
4850 if (!obj
->lookupProperty(cx
, id
, &pobj
, &prop
))
4856 * We conservatively assume that a resolve hook could mutate the scope
4857 * chain during JSObject::lookupProperty. So we must check if parent is
4858 * not null here even if it wasn't before the lookup.
4860 JSObject
*parent
= obj
->getParent();
4864 } while (obj
->getParent());
4868 static JS_ALWAYS_INLINE JSBool
4869 js_NativeGetInline(JSContext
*cx
, JSObject
*receiver
, JSObject
*obj
, JSObject
*pobj
,
4870 const Shape
*shape
, uintN getHow
, Value
*vp
)
4872 LeaveTraceIfGlobalObject(cx
, pobj
);
4877 JS_ASSERT(pobj
->isNative());
4880 if (slot
!= SHAPE_INVALID_SLOT
) {
4881 *vp
= pobj
->nativeGetSlot(slot
);
4882 JS_ASSERT(!vp
->isMagic());
4886 if (shape
->hasDefaultGetter())
4889 if (JS_UNLIKELY(shape
->isMethod()) && (getHow
& JSGET_NO_METHOD_BARRIER
)) {
4890 JS_ASSERT(&shape
->methodObject() == &vp
->toObject());
4894 sample
= cx
->runtime
->propertyRemovals
;
4896 AutoShapeRooter
tvr(cx
, shape
);
4897 AutoObjectRooter
tvr2(cx
, pobj
);
4898 if (!shape
->get(cx
, receiver
, obj
, pobj
, vp
))
4902 if (pobj
->containsSlot(slot
) &&
4903 (JS_LIKELY(cx
->runtime
->propertyRemovals
== sample
) ||
4904 pobj
->nativeContains(*shape
))) {
4905 if (!pobj
->methodWriteBarrier(cx
, *shape
, *vp
))
4907 pobj
->nativeSetSlot(slot
, *vp
);
4914 js_NativeGet(JSContext
*cx
, JSObject
*obj
, JSObject
*pobj
, const Shape
*shape
, uintN getHow
,
4917 return js_NativeGetInline(cx
, obj
, obj
, pobj
, shape
, getHow
, vp
);
4921 js_NativeSet(JSContext
*cx
, JSObject
*obj
, const Shape
*shape
, bool added
, Value
*vp
)
4923 LeaveTraceIfGlobalObject(cx
, obj
);
4928 JS_ASSERT(obj
->isNative());
4931 if (slot
!= SHAPE_INVALID_SLOT
) {
4932 JS_ASSERT(obj
->containsSlot(slot
));
4934 /* If shape has a stub setter, keep obj locked and just store *vp. */
4935 if (shape
->hasDefaultSetter()) {
4936 if (!added
&& !obj
->methodWriteBarrier(cx
, *shape
, *vp
))
4938 obj
->nativeSetSlot(slot
, *vp
);
4943 * Allow API consumers to create shared properties with stub setters.
4944 * Such properties effectively function as data descriptors which are
4945 * not writable, so attempting to set such a property should do nothing
4946 * or throw if we're in strict mode.
4948 if (!shape
->hasGetterValue() && shape
->hasDefaultSetter())
4949 return js_ReportGetterOnlyAssignment(cx
);
4952 sample
= cx
->runtime
->propertyRemovals
;
4954 AutoShapeRooter
tvr(cx
, shape
);
4955 if (!shape
->set(cx
, obj
, vp
))
4959 if (obj
->containsSlot(slot
) &&
4960 (JS_LIKELY(cx
->runtime
->propertyRemovals
== sample
) ||
4961 obj
->nativeContains(*shape
))) {
4962 if (!added
&& !obj
->methodWriteBarrier(cx
, *shape
, *vp
))
4964 obj
->setSlot(slot
, *vp
);
4970 static JS_ALWAYS_INLINE
bool
4971 js_GetPropertyHelperWithShapeInline(JSContext
*cx
, JSObject
*obj
, JSObject
*receiver
, jsid id
,
4972 uintN getHow
, Value
*vp
,
4973 const Shape
**shapeOut
, JSObject
**holderOut
)
4975 JSObject
*aobj
, *obj2
;
4980 JS_ASSERT_IF(getHow
& JSGET_CACHE_RESULT
, !JS_ON_TRACE(cx
));
4984 /* Convert string indices to integers if appropriate. */
4985 id
= js_CheckForStringIndex(id
);
4987 aobj
= js_GetProtoIfDenseArray(obj
);
4988 /* This call site is hot -- use the always-inlined variant of js_LookupPropertyWithFlags(). */
4989 protoIndex
= js_LookupPropertyWithFlagsInline(cx
, aobj
, id
, cx
->resolveFlags
,
4999 if (!CallJSPropertyOp(cx
, obj
->getClass()->getProperty
, obj
, id
, vp
))
5002 PCMETER(getHow
& JSGET_CACHE_RESULT
&& JS_PROPERTY_CACHE(cx
).nofills
++);
5005 * Give a strict warning if foo.bar is evaluated by a script for an
5006 * object foo with no property named 'bar'.
5009 if (vp
->isUndefined() && ((pc
= js_GetCurrentBytecodePC(cx
)) != NULL
)) {
5014 if (op
== JSOP_TRAP
) {
5015 JS_ASSERT_NOT_ON_TRACE(cx
);
5016 op
= JS_GetTrapOpcode(cx
, cx
->fp()->script(), pc
);
5018 if (op
== JSOP_GETXPROP
) {
5019 flags
= JSREPORT_ERROR
;
5021 if (!JS_HAS_STRICT_OPTION(cx
) ||
5022 (op
!= JSOP_GETPROP
&& op
!= JSOP_GETELEM
) ||
5023 js_CurrentPCIsInImacro(cx
)) {
5028 * XXX do not warn about missing __iterator__ as the function
5029 * may be called from JS_GetMethodById. See bug 355145.
5031 if (JSID_IS_ATOM(id
, cx
->runtime
->atomState
.iteratorAtom
))
5034 /* Do not warn about tests like (obj[prop] == undefined). */
5035 if (cx
->resolveFlags
== JSRESOLVE_INFER
) {
5037 pc
+= js_CodeSpec
[op
].length
;
5038 if (Detecting(cx
, pc
))
5040 } else if (cx
->resolveFlags
& JSRESOLVE_DETECTING
) {
5044 flags
= JSREPORT_WARNING
| JSREPORT_STRICT
;
5047 /* Ok, bad undefined property reference: whine about it. */
5048 if (!js_ReportValueErrorFlags(cx
, flags
, JSMSG_UNDEFINED_PROP
,
5049 JSDVG_IGNORE_STACK
, IdToValue(id
),
5050 NULL
, NULL
, NULL
)) {
5057 if (!obj2
->isNative()) {
5058 return obj2
->isProxy()
5059 ? JSProxy::get(cx
, obj2
, receiver
, id
, vp
)
5060 : obj2
->getProperty(cx
, id
, vp
);
5063 shape
= (Shape
*) prop
;
5066 if (getHow
& JSGET_CACHE_RESULT
) {
5067 JS_ASSERT_NOT_ON_TRACE(cx
);
5068 JS_PROPERTY_CACHE(cx
).fill(cx
, aobj
, 0, protoIndex
, obj2
, shape
);
5071 /* This call site is hot -- use the always-inlined variant of js_NativeGet(). */
5072 if (!js_NativeGetInline(cx
, receiver
, obj
, obj2
, shape
, getHow
, vp
))
5079 js_GetPropertyHelperWithShape(JSContext
*cx
, JSObject
*obj
, JSObject
*receiver
, jsid id
,
5080 uint32 getHow
, Value
*vp
,
5081 const Shape
**shapeOut
, JSObject
**holderOut
)
5083 return js_GetPropertyHelperWithShapeInline(cx
, obj
, receiver
, id
, getHow
, vp
,
5084 shapeOut
, holderOut
);
5087 static JS_ALWAYS_INLINE JSBool
5088 js_GetPropertyHelperInline(JSContext
*cx
, JSObject
*obj
, JSObject
*receiver
, jsid id
,
5089 uint32 getHow
, Value
*vp
)
5093 return js_GetPropertyHelperWithShapeInline(cx
, obj
, receiver
, id
, getHow
, vp
, &shape
, &holder
);
5097 js_GetPropertyHelper(JSContext
*cx
, JSObject
*obj
, jsid id
, uint32 getHow
, Value
*vp
)
5099 return js_GetPropertyHelperInline(cx
, obj
, obj
, id
, getHow
, vp
);
5103 js_GetProperty(JSContext
*cx
, JSObject
*obj
, JSObject
*receiver
, jsid id
, Value
*vp
)
5105 /* This call site is hot -- use the always-inlined variant of js_GetPropertyHelper(). */
5106 return js_GetPropertyHelperInline(cx
, obj
, receiver
, id
, JSGET_METHOD_BARRIER
, vp
);
5110 js::GetPropertyDefault(JSContext
*cx
, JSObject
*obj
, jsid id
, const Value
&def
, Value
*vp
)
5114 if (js_LookupPropertyWithFlags(cx
, obj
, id
, JSRESOLVE_QUALIFIED
, &obj2
, &prop
) < 0)
5122 return js_GetProperty(cx
, obj2
, id
, vp
);
5126 js_GetMethod(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN getHow
, Value
*vp
)
5128 JSAutoResolveFlags
rf(cx
, JSRESOLVE_QUALIFIED
);
5130 PropertyIdOp op
= obj
->getOps()->getProperty
;
5132 #if JS_HAS_XML_SUPPORT
5133 JS_ASSERT(!obj
->isXML());
5135 return js_GetPropertyHelper(cx
, obj
, id
, getHow
, vp
);
5137 JS_ASSERT_IF(getHow
& JSGET_CACHE_RESULT
, obj
->isDenseArray());
5138 #if JS_HAS_XML_SUPPORT
5140 return js_GetXMLMethod(cx
, obj
, id
, vp
);
5142 return op(cx
, obj
, obj
, id
, vp
);
5146 js_CheckUndeclaredVarAssignment(JSContext
*cx
, JSString
*propname
)
5148 JSStackFrame
*const fp
= js_GetTopStackFrame(cx
);
5152 /* If neither cx nor the code is strict, then no check is needed. */
5153 if (!(fp
->isScriptFrame() && fp
->script()->strictModeCode
) &&
5154 !JS_HAS_STRICT_OPTION(cx
)) {
5158 JSAutoByteString
bytes(cx
, propname
);
5160 JS_ReportErrorFlagsAndNumber(cx
,
5161 (JSREPORT_WARNING
| JSREPORT_STRICT
5162 | JSREPORT_STRICT_MODE_ERROR
),
5163 js_GetErrorMessage
, NULL
,
5164 JSMSG_UNDECLARED_VAR
, bytes
.ptr());
5168 JSObject::reportReadOnly(JSContext
* cx
, jsid id
, uintN report
)
5170 return js_ReportValueErrorFlags(cx
, report
, JSMSG_READ_ONLY
,
5171 JSDVG_IGNORE_STACK
, IdToValue(id
), NULL
,
5176 JSObject::reportNotConfigurable(JSContext
* cx
, jsid id
, uintN report
)
5178 return js_ReportValueErrorFlags(cx
, report
, JSMSG_CANT_DELETE
,
5179 JSDVG_IGNORE_STACK
, IdToValue(id
), NULL
,
5184 JSObject::reportNotExtensible(JSContext
*cx
, uintN report
)
5186 return js_ReportValueErrorFlags(cx
, report
, JSMSG_OBJECT_NOT_EXTENSIBLE
,
5187 JSDVG_IGNORE_STACK
, ObjectValue(*this),
5192 * Note: all non-error exits in this function must notify the tracer using
5193 * SetPropHit when called from the interpreter, which is detected by testing
5194 * (defineHow & JSDNP_CACHE_RESULT).
5197 js_SetPropertyHelper(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN defineHow
,
5198 Value
*vp
, JSBool strict
)
5207 PropertyOp getter
, setter
;
5210 JS_ASSERT((defineHow
&
5211 ~(JSDNP_CACHE_RESULT
| JSDNP_SET_METHOD
| JSDNP_UNQUALIFIED
)) == 0);
5212 if (defineHow
& JSDNP_CACHE_RESULT
)
5213 JS_ASSERT_NOT_ON_TRACE(cx
);
5215 /* Convert string indices to integers if appropriate. */
5216 id
= js_CheckForStringIndex(id
);
5218 protoIndex
= js_LookupPropertyWithFlags(cx
, obj
, id
, cx
->resolveFlags
,
5223 if (!pobj
->isNative()) {
5224 if (pobj
->isProxy()) {
5225 AutoPropertyDescriptorRooter
pd(cx
);
5226 if (!pobj
->getProxyHandler()->getPropertyDescriptor(cx
, pobj
, id
, true, &pd
))
5229 if (pd
.attrs
& JSPROP_SHARED
)
5230 return CallSetter(cx
, obj
, id
, pd
.setter
, pd
.attrs
, pd
.shortid
, vp
);
5232 if (pd
.attrs
& JSPROP_READONLY
) {
5234 return obj
->reportReadOnly(cx
, id
);
5235 if (JS_HAS_STRICT_OPTION(cx
))
5236 return obj
->reportReadOnly(cx
, id
, JSREPORT_STRICT
| JSREPORT_WARNING
);
5244 /* We should never add properties to lexical blocks. */
5245 JS_ASSERT(!obj
->isBlock());
5247 if (!obj
->getParent() &&
5248 (defineHow
& JSDNP_UNQUALIFIED
) &&
5249 !js_CheckUndeclaredVarAssignment(cx
, JSID_TO_STRING(id
))) {
5253 shape
= (Shape
*) prop
;
5256 * Now either shape is null, meaning id was not found in obj or one of its
5257 * prototypes; or shape is non-null, meaning id was found directly in pobj.
5259 attrs
= JSPROP_ENUMERATE
;
5262 clasp
= obj
->getClass();
5263 getter
= clasp
->getProperty
;
5264 setter
= clasp
->setProperty
;
5267 /* ES5 8.12.4 [[Put]] step 2. */
5268 if (shape
->isAccessorDescriptor()) {
5269 if (shape
->hasDefaultSetter()) {
5270 if (defineHow
& JSDNP_CACHE_RESULT
)
5271 TRACE_2(SetPropHit
, JS_NO_PROP_CACHE_FILL
, shape
);
5272 return js_ReportGetterOnlyAssignment(cx
);
5275 JS_ASSERT(shape
->isDataDescriptor());
5277 if (!shape
->writable()) {
5278 PCMETER((defineHow
& JSDNP_CACHE_RESULT
) && JS_PROPERTY_CACHE(cx
).rofills
++);
5279 if (defineHow
& JSDNP_CACHE_RESULT
) {
5280 JS_ASSERT_NOT_ON_TRACE(cx
);
5281 TRACE_2(SetPropHit
, JS_NO_PROP_CACHE_FILL
, shape
);
5284 /* Error in strict mode code, warn with strict option, otherwise do nothing. */
5286 return obj
->reportReadOnly(cx
, id
);
5287 if (JS_HAS_STRICT_OPTION(cx
))
5288 return obj
->reportReadOnly(cx
, id
, JSREPORT_STRICT
| JSREPORT_WARNING
);
5292 error
: // TRACE_2 jumps here in case of error.
5298 attrs
= shape
->attributes();
5301 * We found id in a prototype object: prepare to share or shadow.
5303 * Don't clone a prototype property that doesn't have a slot.
5305 if (!shape
->hasSlot()) {
5306 if (defineHow
& JSDNP_CACHE_RESULT
) {
5308 JS_ASSERT_NOT_ON_TRACE(cx
);
5309 PropertyCacheEntry
*entry
=
5311 JS_PROPERTY_CACHE(cx
).fill(cx
, obj
, 0, protoIndex
, pobj
, shape
);
5312 TRACE_2(SetPropHit
, entry
, shape
);
5315 if (shape
->hasDefaultSetter() && !shape
->hasGetterValue())
5318 return shape
->set(cx
, obj
, vp
);
5321 /* Restore attrs to the ECMA default for new properties. */
5322 attrs
= JSPROP_ENUMERATE
;
5325 * Preserve the shortid, getter, and setter when shadowing any
5326 * property that has a shortid. An old API convention requires
5327 * that the property's getter and setter functions receive the
5328 * shortid, not id, when they are called on the shadow we are
5329 * about to create in obj.
5331 if (shape
->hasShortID()) {
5332 flags
= Shape::HAS_SHORTID
;
5333 shortid
= shape
->shortid
;
5334 getter
= shape
->getter();
5335 setter
= shape
->setter();
5339 * Forget we found the proto-property now that we've copied any
5340 * needed member values.
5346 if (shape
->isMethod()) {
5347 JS_ASSERT(pobj
->hasMethodBarrier());
5348 } else if ((defineHow
& JSDNP_SET_METHOD
) && obj
->canHaveMethodBarrier()) {
5349 JS_ASSERT(IsFunctionObject(*vp
));
5350 JS_ASSERT(!(attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)));
5352 JSObject
*funobj
= &vp
->toObject();
5353 JSFunction
*fun
= GET_FUNCTION_PRIVATE(cx
, funobj
);
5354 if (fun
== funobj
) {
5355 funobj
= CloneFunctionObject(cx
, fun
, fun
->parent
);
5358 vp
->setObject(*funobj
);
5366 if (!obj
->isExtensible()) {
5367 if (defineHow
& JSDNP_CACHE_RESULT
) {
5368 JS_ASSERT_NOT_ON_TRACE(cx
);
5369 TRACE_2(SetPropHit
, JS_NO_PROP_CACHE_FILL
, shape
);
5372 /* Error in strict mode code, warn with strict option, otherwise do nothing. */
5374 return obj
->reportNotExtensible(cx
);
5375 if (JS_HAS_STRICT_OPTION(cx
))
5376 return obj
->reportNotExtensible(cx
, JSREPORT_STRICT
| JSREPORT_WARNING
);
5381 * Purge the property cache of now-shadowed id in obj's scope chain.
5382 * Do this early, before locking obj to avoid nesting locks.
5384 js_PurgeScopeChain(cx
, obj
, id
);
5386 /* Find or make a property descriptor with the right heritage. */
5387 if (!obj
->ensureClassReservedSlots(cx
))
5391 * Check for Object class here to avoid defining a method on a class
5392 * with magic resolve, addProperty, getProperty, etc. hooks.
5394 if ((defineHow
& JSDNP_SET_METHOD
) && obj
->canHaveMethodBarrier()) {
5395 JS_ASSERT(IsFunctionObject(*vp
));
5396 JS_ASSERT(!(attrs
& (JSPROP_GETTER
| JSPROP_SETTER
)));
5398 JSObject
*funobj
= &vp
->toObject();
5399 JSFunction
*fun
= GET_FUNCTION_PRIVATE(cx
, funobj
);
5400 if (fun
== funobj
) {
5401 flags
|= Shape::METHOD
;
5402 getter
= CastAsPropertyOp(funobj
);
5406 shape
= obj
->putProperty(cx
, id
, getter
, setter
, SHAPE_INVALID_SLOT
,
5407 attrs
, flags
, shortid
);
5412 * Initialize the new property value (passed to setter) to undefined.
5413 * Note that we store before calling addProperty, to match the order
5414 * in js_DefineNativeProperty.
5416 if (obj
->containsSlot(shape
->slot
))
5417 obj
->nativeSetSlot(shape
->slot
, UndefinedValue());
5419 /* XXXbe called with obj locked */
5420 if (!CallAddPropertyHook(cx
, clasp
, obj
, shape
, vp
)) {
5421 obj
->removeProperty(cx
, id
);
5427 if (defineHow
& JSDNP_CACHE_RESULT
) {
5429 JS_ASSERT_NOT_ON_TRACE(cx
);
5430 PropertyCacheEntry
*entry
=
5432 JS_PROPERTY_CACHE(cx
).fill(cx
, obj
, 0, 0, obj
, shape
, added
);
5433 TRACE_2(SetPropHit
, entry
, shape
);
5436 return js_NativeSet(cx
, obj
, shape
, added
, vp
);
5440 js_SetProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*vp
, JSBool strict
)
5442 return js_SetPropertyHelper(cx
, obj
, id
, 0, vp
, strict
);
5446 js_GetAttributes(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN
*attrsp
)
5449 if (!js_LookupProperty(cx
, obj
, id
, &obj
, &prop
))
5455 if (!obj
->isNative())
5456 return obj
->getAttributes(cx
, id
, attrsp
);
5458 const Shape
*shape
= (Shape
*)prop
;
5459 *attrsp
= shape
->attributes();
5464 js_SetNativeAttributes(JSContext
*cx
, JSObject
*obj
, Shape
*shape
, uintN attrs
)
5466 JS_ASSERT(obj
->isNative());
5467 return !!js_ChangeNativePropertyAttrs(cx
, obj
, shape
, attrs
, 0,
5468 shape
->getter(), shape
->setter());
5472 js_SetAttributes(JSContext
*cx
, JSObject
*obj
, jsid id
, uintN
*attrsp
)
5475 if (!js_LookupProperty(cx
, obj
, id
, &obj
, &prop
))
5479 return obj
->isNative()
5480 ? js_SetNativeAttributes(cx
, obj
, (Shape
*) prop
, *attrsp
)
5481 : obj
->setAttributes(cx
, id
, attrsp
);
5485 js_DeleteProperty(JSContext
*cx
, JSObject
*obj
, jsid id
, Value
*rval
, JSBool strict
)
5491 rval
->setBoolean(true);
5493 /* Convert string indices to integers if appropriate. */
5494 id
= js_CheckForStringIndex(id
);
5496 if (!js_LookupProperty(cx
, obj
, id
, &proto
, &prop
))
5498 if (!prop
|| proto
!= obj
) {
5500 * If the property was found in a native prototype, check whether it's
5501 * shared and permanent. Such a property stands for direct properties
5502 * in all delegating objects, matching ECMA semantics without bloating
5503 * each delegating object.
5505 if (prop
&& proto
->isNative()) {
5506 shape
= (Shape
*)prop
;
5507 if (shape
->isSharedPermanent()) {
5509 return obj
->reportNotConfigurable(cx
, id
);
5510 rval
->setBoolean(false);
5516 * If no property, or the property comes unshared or impermanent from
5517 * a prototype, call the class's delProperty hook, passing rval as the
5520 return CallJSPropertyOp(cx
, obj
->getClass()->delProperty
, obj
, id
, rval
);
5523 shape
= (Shape
*)prop
;
5524 if (!shape
->configurable()) {
5526 return obj
->reportNotConfigurable(cx
, id
);
5527 rval
->setBoolean(false);
5531 if (!CallJSPropertyOp(cx
, obj
->getClass()->delProperty
, obj
, SHAPE_USERID(shape
), rval
))
5534 if (obj
->containsSlot(shape
->slot
)) {
5535 const Value
&v
= obj
->nativeGetSlot(shape
->slot
);
5539 * Delete is rare enough that we can take the hit of checking for an
5540 * active cloned method function object that must be homed to a callee
5541 * slot on the active stack frame before this delete completes, in case
5542 * someone saved the clone and checks it against foo.caller for a foo
5543 * called from the active method.
5545 * We do not check suspended frames. They can't be reached via caller,
5546 * so the only way they could have the method's joined function object
5547 * as callee is through an API abusage. We break any such edge case.
5549 if (obj
->hasMethodBarrier()) {
5552 if (IsFunctionObject(v
, &funobj
)) {
5553 JSFunction
*fun
= GET_FUNCTION_PRIVATE(cx
, funobj
);
5555 if (fun
!= funobj
) {
5556 for (JSStackFrame
*fp
= cx
->maybefp(); fp
; fp
= fp
->prev()) {
5557 if (fp
->isFunctionFrame() &&
5558 &fp
->callee() == &fun
->compiledFunObj() &&
5559 fp
->thisValue().isObject() &&
5560 &fp
->thisValue().toObject() == obj
) {
5561 fp
->calleeValue().setObject(*funobj
);
5569 return obj
->removeProperty(cx
, id
) && js_SuppressDeletedProperty(cx
, obj
, id
);
5575 HasNativeMethod(JSObject
*obj
, jsid methodid
, Native native
)
5577 const Shape
*shape
= obj
->nativeLookup(methodid
);
5578 if (!shape
|| !shape
->hasDefaultGetter() || !obj
->containsSlot(shape
->slot
))
5581 const Value
&fval
= obj
->nativeGetSlot(shape
->slot
);
5583 if (!IsFunctionObject(fval
, &funobj
) || funobj
->getFunctionPrivate()->maybeNative() != native
)
5590 * When we have an object of a builtin class, we don't quite know what its
5591 * valueOf/toString methods are, since these methods may have been overwritten
5592 * or shadowed. However, we can still do better than js_TryMethod by
5593 * hard-coding the necessary properties for us to find the native we expect.
5595 * TODO: a per-thread shape-based cache would be faster and simpler.
5597 static JS_ALWAYS_INLINE
bool
5598 ClassMethodIsNative(JSContext
*cx
, JSObject
*obj
, Class
*clasp
, jsid methodid
,
5601 JS_ASSERT(obj
->getClass() == clasp
);
5603 if (HasNativeMethod(obj
, methodid
, native
))
5606 JSObject
*pobj
= obj
->getProto();
5607 return pobj
&& pobj
->getClass() == clasp
&&
5608 HasNativeMethod(pobj
, methodid
, native
);
5612 DefaultValue(JSContext
*cx
, JSObject
*obj
, JSType hint
, Value
*vp
)
5614 JS_ASSERT(hint
!= JSTYPE_OBJECT
&& hint
!= JSTYPE_FUNCTION
);
5616 Value v
= ObjectValue(*obj
);
5617 if (hint
== JSTYPE_STRING
) {
5618 /* Optimize (new String(...)).toString(). */
5619 if (obj
->getClass() == &js_StringClass
&&
5620 ClassMethodIsNative(cx
, obj
,
5622 ATOM_TO_JSID(cx
->runtime
->atomState
.toStringAtom
),
5624 *vp
= obj
->getPrimitiveThis();
5628 if (!js_TryMethod(cx
, obj
, cx
->runtime
->atomState
.toStringAtom
, 0, NULL
, &v
))
5630 if (!v
.isPrimitive()) {
5631 if (!obj
->getClass()->convert(cx
, obj
, hint
, &v
))
5635 /* Optimize (new String(...)).valueOf(). */
5636 Class
*clasp
= obj
->getClass();
5637 if ((clasp
== &js_StringClass
&&
5638 ClassMethodIsNative(cx
, obj
, &js_StringClass
,
5639 ATOM_TO_JSID(cx
->runtime
->atomState
.valueOfAtom
),
5640 js_str_toString
)) ||
5641 (clasp
== &js_NumberClass
&&
5642 ClassMethodIsNative(cx
, obj
, &js_NumberClass
,
5643 ATOM_TO_JSID(cx
->runtime
->atomState
.valueOfAtom
),
5645 *vp
= obj
->getPrimitiveThis();
5649 if (!obj
->getClass()->convert(cx
, obj
, hint
, &v
))
5652 JS_ASSERT(hint
!= TypeOfValue(cx
, v
));
5653 if (!js_TryMethod(cx
, obj
, cx
->runtime
->atomState
.toStringAtom
, 0, NULL
, &v
))
5658 /* Avoid recursive death when decompiling in js_ReportValueError. */
5660 if (hint
== JSTYPE_STRING
) {
5661 str
= JS_InternString(cx
, obj
->getClass()->name
);
5667 vp
->setObject(*obj
);
5668 js_ReportValueError2(cx
, JSMSG_CANT_CONVERT_TO
,
5669 JSDVG_SEARCH_STACK
, *vp
, str
,
5670 (hint
== JSTYPE_VOID
)
5672 : JS_TYPE_STR(hint
));
5679 } /* namespace js */
5681 JS_FRIEND_API(JSBool
)
5682 js_Enumerate(JSContext
*cx
, JSObject
*obj
, JSIterateOp enum_op
, Value
*statep
, jsid
*idp
)
5684 /* If the class has a custom JSCLASS_NEW_ENUMERATE hook, call it. */
5685 Class
*clasp
= obj
->getClass();
5686 JSEnumerateOp enumerate
= clasp
->enumerate
;
5687 if (clasp
->flags
& JSCLASS_NEW_ENUMERATE
) {
5688 JS_ASSERT(enumerate
!= JS_EnumerateStub
);
5689 return ((NewEnumerateOp
) enumerate
)(cx
, obj
, enum_op
, statep
, idp
);
5692 if (!enumerate(cx
, obj
))
5695 /* Tell InitNativeIterator to treat us like a native object. */
5696 JS_ASSERT(enum_op
== JSENUMERATE_INIT
|| enum_op
== JSENUMERATE_INIT_ALL
);
5697 statep
->setMagic(JS_NATIVE_ENUMERATE
);
5704 CheckAccess(JSContext
*cx
, JSObject
*obj
, jsid id
, JSAccessMode mode
,
5705 Value
*vp
, uintN
*attrsp
)
5712 JSSecurityCallbacks
*callbacks
;
5713 CheckAccessOp check
;
5715 while (JS_UNLIKELY(obj
->getClass() == &js_WithClass
))
5716 obj
= obj
->getProto();
5718 writing
= (mode
& JSACC_WRITE
) != 0;
5719 switch (mode
& JSACC_TYPEMASK
) {
5723 vp
->setObjectOrNull(obj
->getProto());
5724 *attrsp
= JSPROP_PERMANENT
;
5728 JS_ASSERT(!writing
);
5730 vp
->setObject(*obj
->getParent());
5731 *attrsp
= JSPROP_READONLY
| JSPROP_PERMANENT
;
5735 if (!obj
->lookupProperty(cx
, id
, &pobj
, &prop
))
5745 if (!pobj
->isNative()) {
5753 shape
= (Shape
*)prop
;
5754 *attrsp
= shape
->attributes();
5756 if (pobj
->containsSlot(shape
->slot
))
5757 *vp
= pobj
->nativeGetSlot(shape
->slot
);
5764 * If obj's class has a stub (null) checkAccess hook, use the per-runtime
5765 * checkObjectAccess callback, if configured.
5767 * We don't want to require all classes to supply a checkAccess hook; we
5768 * need that hook only for certain classes used when precompiling scripts
5769 * and functions ("brutal sharing"). But for general safety of built-in
5770 * magic properties like __proto__, we route all access checks, even for
5771 * classes that stub out checkAccess, through the global checkObjectAccess
5772 * hook. This covers precompilation-based sharing and (possibly
5773 * unintended) runtime sharing across trust boundaries.
5775 clasp
= pobj
->getClass();
5776 check
= clasp
->checkAccess
;
5778 callbacks
= JS_GetSecurityCallbacks(cx
);
5779 check
= callbacks
? Valueify(callbacks
->checkObjectAccess
) : NULL
;
5781 return !check
|| check(cx
, pobj
, id
, mode
, vp
);
5787 js_TypeOf(JSContext
*cx
, JSObject
*obj
)
5790 * ECMA 262, 11.4.3 says that any native object that implements
5791 * [[Call]] should be of type "function". However, RegExp is of
5792 * type "object", not "function", for Web compatibility.
5794 if (obj
->isCallable()) {
5795 return (obj
->getClass() != &js_RegExpClass
)
5800 return JSTYPE_OBJECT
;
5804 js_IsDelegate(JSContext
*cx
, JSObject
*obj
, const Value
&v
)
5806 if (v
.isPrimitive())
5808 JSObject
*obj2
= &v
.toObject();
5809 while ((obj2
= obj2
->getProto()) != NULL
) {
5817 js::FindClassPrototype(JSContext
*cx
, JSObject
*scopeobj
, JSProtoKey protoKey
,
5818 JSObject
**protop
, Class
*clasp
)
5821 if (!js_FindClassObject(cx
, scopeobj
, protoKey
, &v
, clasp
))
5824 if (IsFunctionObject(v
)) {
5825 JSObject
*ctor
= &v
.toObject();
5826 if (!ctor
->getProperty(cx
, ATOM_TO_JSID(cx
->runtime
->atomState
.classPrototypeAtom
), &v
))
5830 *protop
= v
.isObject() ? &v
.toObject() : NULL
;
5835 * The first part of this function has been hand-expanded and optimized into
5836 * NewBuiltinClassInstance in jsobjinlines.h.
5839 js_GetClassPrototype(JSContext
*cx
, JSObject
*scopeobj
, JSProtoKey protoKey
,
5840 JSObject
**protop
, Class
*clasp
)
5842 VOUCH_DOES_NOT_REQUIRE_STACK();
5843 JS_ASSERT(JSProto_Null
<= protoKey
);
5844 JS_ASSERT(protoKey
< JSProto_LIMIT
);
5846 if (protoKey
!= JSProto_Null
) {
5849 scopeobj
= &cx
->fp()->scopeChain();
5851 scopeobj
= cx
->globalObject
;
5858 scopeobj
= scopeobj
->getGlobal();
5859 if (scopeobj
->getClass()->flags
& JSCLASS_IS_GLOBAL
) {
5860 const Value
&v
= scopeobj
->getReservedSlot(JSProto_LIMIT
+ protoKey
);
5862 *protop
= &v
.toObject();
5868 return FindClassPrototype(cx
, scopeobj
, protoKey
, protop
, clasp
);
5872 js_SetClassPrototype(JSContext
*cx
, JSObject
*ctor
, JSObject
*proto
, uintN attrs
)
5875 * Use the given attributes for the prototype property of the constructor,
5876 * as user-defined constructors have a DontDelete prototype (which may be
5877 * reset), while native or "system" constructors have DontEnum | ReadOnly |
5880 if (!ctor
->defineProperty(cx
, ATOM_TO_JSID(cx
->runtime
->atomState
.classPrototypeAtom
),
5881 ObjectOrNullValue(proto
), PropertyStub
, PropertyStub
, attrs
)) {
5886 * ECMA says that Object.prototype.constructor, or f.prototype.constructor
5887 * for a user-defined function f, is DontEnum.
5889 return proto
->defineProperty(cx
, ATOM_TO_JSID(cx
->runtime
->atomState
.constructorAtom
),
5890 ObjectOrNullValue(ctor
), PropertyStub
, PropertyStub
, 0);
5894 js_PrimitiveToObject(JSContext
*cx
, Value
*vp
)
5897 JS_ASSERT(v
.isPrimitive());
5901 clasp
= &js_NumberClass
;
5902 } else if (v
.isString()) {
5903 clasp
= &js_StringClass
;
5905 JS_ASSERT(v
.isBoolean());
5906 clasp
= &js_BooleanClass
;
5909 JSObject
*obj
= NewBuiltinClassInstance(cx
, clasp
);
5913 obj
->setPrimitiveThis(v
);
5914 vp
->setObject(*obj
);
5919 js_ValueToObjectOrNull(JSContext
*cx
, const Value
&v
, JSObject
**objp
)
5923 if (v
.isObjectOrNull()) {
5924 obj
= v
.toObjectOrNull();
5925 } else if (v
.isUndefined()) {
5929 if (!js_PrimitiveToObject(cx
, &tmp
))
5931 obj
= &tmp
.toObject();
5938 js_ValueToNonNullObject(JSContext
*cx
, const Value
&v
)
5942 if (!js_ValueToObjectOrNull(cx
, v
, &obj
))
5945 js_ReportIsNullOrUndefined(cx
, JSDVG_SEARCH_STACK
, v
, NULL
);
5950 js_TryValueOf(JSContext
*cx
, JSObject
*obj
, JSType type
, Value
*rval
)
5954 argv
[0].setString(ATOM_TO_STRING(cx
->runtime
->atomState
.typeAtoms
[type
]));
5955 return js_TryMethod(cx
, obj
, cx
->runtime
->atomState
.valueOfAtom
,
5960 js_TryMethod(JSContext
*cx
, JSObject
*obj
, JSAtom
*atom
,
5961 uintN argc
, Value
*argv
, Value
*rval
)
5963 JS_CHECK_RECURSION(cx
, return JS_FALSE
);
5966 * Report failure only if an appropriate method was found, and calling it
5967 * returned failure. We propagate failure in this case to make exceptions
5970 JSErrorReporter older
= JS_SetErrorReporter(cx
, NULL
);
5971 jsid id
= ATOM_TO_JSID(atom
);
5973 JSBool ok
= js_GetMethod(cx
, obj
, id
, JSGET_NO_METHOD_BARRIER
, &fval
);
5974 JS_SetErrorReporter(cx
, older
);
5978 if (fval
.isPrimitive())
5980 return ExternalInvoke(cx
, obj
, fval
, argc
, argv
, rval
);
5986 js_XDRObject(JSXDRState
*xdr
, JSObject
**objp
)
5991 uint32 classId
, classDef
;
5992 JSProtoKey protoKey
;
5997 if (xdr
->mode
== JSXDR_ENCODE
) {
5998 clasp
= (*objp
)->getClass();
5999 classId
= JS_XDRFindClassIdByName(xdr
, clasp
->name
);
6000 classDef
= !classId
;
6002 if (!JS_XDRRegisterClass(xdr
, Jsvalify(clasp
), &classId
))
6004 protoKey
= JSCLASS_CACHED_PROTO_KEY(clasp
);
6005 if (protoKey
!= JSProto_Null
) {
6006 classDef
|= (protoKey
<< 1);
6008 atom
= js_Atomize(cx
, clasp
->name
, strlen(clasp
->name
), 0);
6014 clasp
= NULL
; /* quell GCC overwarning */
6019 * XDR a flag word, which could be 0 for a class use, in which case no
6020 * name follows, only the id in xdr's class registry; 1 for a class def,
6021 * in which case the flag word is followed by the class name transferred
6022 * from or to atom; or a value greater than 1, an odd number that when
6023 * divided by two yields the JSProtoKey for class. In the last case, as
6024 * in the 0 classDef case, no name is transferred via atom.
6026 if (!JS_XDRUint32(xdr
, &classDef
))
6028 if (classDef
== 1 && !js_XDRAtom(xdr
, &atom
))
6031 if (!JS_XDRUint32(xdr
, &classId
))
6034 if (xdr
->mode
== JSXDR_DECODE
) {
6036 /* NB: we know that JSProto_Null is 0 here, for backward compat. */
6037 protoKey
= (JSProtoKey
) (classDef
>> 1);
6038 if (!js_GetClassPrototype(cx
, NULL
, protoKey
, &proto
, clasp
))
6040 clasp
= proto
->getClass();
6041 if (!JS_XDRRegisterClass(xdr
, Jsvalify(clasp
), &classId
))
6044 clasp
= Valueify(JS_XDRFindClassById(xdr
, classId
));
6047 JS_snprintf(numBuf
, sizeof numBuf
, "%ld", (long)classId
);
6048 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
6049 JSMSG_CANT_FIND_CLASS
, numBuf
);
6055 if (!clasp
->xdrObject
) {
6056 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
6057 JSMSG_CANT_XDR_CLASS
, clasp
->name
);
6060 return clasp
->xdrObject(xdr
, objp
);
6063 #endif /* JS_HAS_XDR */
6065 #ifdef JS_DUMP_SCOPE_METERS
6069 JSBasicStats js_entry_count_bs
= JS_INIT_STATIC_BASIC_STATS
;
6072 MeterEntryCount(uintN count
)
6074 JS_BASIC_STATS_ACCUM(&js_entry_count_bs
, count
);
6078 js_DumpScopeMeters(JSRuntime
*rt
)
6082 logfp
= fopen("/tmp/scope.stats", "a");
6087 mean
= JS_MeanAndStdDevBS(&js_entry_count_bs
, &sigma
);
6089 fprintf(logfp
, "scopes %u entries %g mean %g sigma %g max %u",
6090 js_entry_count_bs
.num
, js_entry_count_bs
.sum
, mean
, sigma
,
6091 js_entry_count_bs
.max
);
6094 JS_DumpHistogram(&js_entry_count_bs
, logfp
);
6095 JS_BASIC_STATS_INIT(&js_entry_count_bs
);
6102 js_PrintObjectSlotName(JSTracer
*trc
, char *buf
, size_t bufsize
)
6104 JS_ASSERT(trc
->debugPrinter
== js_PrintObjectSlotName
);
6106 JSObject
*obj
= (JSObject
*)trc
->debugPrintArg
;
6107 uint32 slot
= (uint32
)trc
->debugPrintIndex
;
6110 if (obj
->isNative()) {
6111 shape
= obj
->lastProperty();
6112 while (shape
->previous() && shape
->slot
!= slot
)
6113 shape
= shape
->previous();
6114 if (shape
->slot
!= slot
)
6121 const char *slotname
= NULL
;
6122 Class
*clasp
= obj
->getClass();
6123 if (clasp
->flags
& JSCLASS_IS_GLOBAL
) {
6124 #define JS_PROTO(name,code,init) \
6125 if ((code) == slot) { slotname = js_##name##_str; goto found; }
6126 #include "jsproto.tbl"
6131 JS_snprintf(buf
, bufsize
, "CLASS_OBJECT(%s)", slotname
);
6133 JS_snprintf(buf
, bufsize
, "**UNKNOWN SLOT %ld**", (long)slot
);
6135 jsid id
= shape
->id
;
6136 if (JSID_IS_INT(id
)) {
6137 JS_snprintf(buf
, bufsize
, "%ld", (long)JSID_TO_INT(id
));
6138 } else if (JSID_IS_ATOM(id
)) {
6139 PutEscapedString(buf
, bufsize
, JSID_TO_STRING(id
), 0);
6141 JS_snprintf(buf
, bufsize
, "**FINALIZED ATOM KEY**");
6148 js_TraceObject(JSTracer
*trc
, JSObject
*obj
)
6150 JS_ASSERT(obj
->isNative());
6152 JSContext
*cx
= trc
->context
;
6153 if (obj
->hasSlotsArray() && !obj
->nativeEmpty() && IS_GC_MARKING_TRACER(trc
)) {
6155 * Trim overlong dslots allocations from the GC, to avoid thrashing in
6156 * case of delete-happy code that settles down at a given population.
6157 * The !obj->nativeEmpty() guard above is due to the bug described by
6158 * the FIXME comment below.
6160 size_t slots
= obj
->slotSpan();
6161 if (obj
->numSlots() != slots
)
6162 obj
->shrinkSlots(cx
, slots
);
6165 #ifdef JS_DUMP_SCOPE_METERS
6166 MeterEntryCount(obj
->propertyCount
);
6171 if (!JS_CLIST_IS_EMPTY(&cx
->runtime
->watchPointList
))
6172 js_TraceWatchPoints(trc
, obj
);
6174 /* No one runs while the GC is running, so we can use LOCKED_... here. */
6175 Class
*clasp
= obj
->getClass();
6177 if (clasp
->flags
& JSCLASS_MARK_IS_TRACE
)
6178 ((JSTraceOp
) clasp
->mark
)(trc
, obj
);
6179 else if (IS_GC_MARKING_TRACER(trc
))
6180 (void) clasp
->mark(cx
, obj
, trc
);
6182 if (clasp
->flags
& JSCLASS_IS_GLOBAL
) {
6183 JSCompartment
*compartment
= obj
->getCompartment();
6184 compartment
->marked
= true;
6188 * NB: In case clasp->mark mutates something (which would be a bug, but we
6189 * want to be defensive), leave this code here -- don't move it up and
6190 * unify it with the |if (!traceScope)| section above.
6192 * FIXME: We minimize nslots against obj->slotSpan because native objects
6193 * such as Date instances may have failed to advance slotSpan to cover all
6194 * reserved slots (this Date issue may be a bug in JSObject::growSlots, but
6195 * the general problem occurs in other built-in class implementations).
6197 uint32 nslots
= obj
->numSlots();
6198 if (!obj
->nativeEmpty() && obj
->slotSpan() < nslots
)
6199 nslots
= obj
->slotSpan();
6201 for (uint32 i
= 0; i
!= nslots
; ++i
) {
6202 const Value
&v
= obj
->getSlot(i
);
6203 JS_SET_TRACING_DETAILS(trc
, js_PrintObjectSlotName
, obj
, i
);
6204 MarkValueRaw(trc
, v
);
6209 js_ClearNative(JSContext
*cx
, JSObject
*obj
)
6212 * Clear obj of all obj's properties. FIXME: we do not clear reserved slots
6213 * lying below JSSLOT_FREE(clasp). JS_ClearScope does that.
6215 if (!obj
->nativeEmpty()) {
6216 /* Now that we're done using real properties, clear obj. */
6219 /* Clear slot values since obj->clear reset our shape to empty. */
6220 uint32 freeslot
= JSSLOT_FREE(obj
->getClass());
6221 uint32 n
= obj
->numSlots();
6222 for (uint32 i
= freeslot
; i
< n
; ++i
)
6223 obj
->setSlot(i
, UndefinedValue());
6228 js_GetReservedSlot(JSContext
*cx
, JSObject
*obj
, uint32 slot
, Value
*vp
)
6230 if (!obj
->isNative()) {
6235 if (slot
< obj
->numSlots())
6236 *vp
= obj
->getSlot(slot
);
6243 js_SetReservedSlot(JSContext
*cx
, JSObject
*obj
, uint32 slot
, const Value
&v
)
6245 if (!obj
->isNative())
6248 Class
*clasp
= obj
->getClass();
6250 if (slot
>= obj
->numSlots()) {
6251 uint32 nslots
= JSSLOT_FREE(clasp
);
6252 JS_ASSERT(slot
< nslots
);
6253 if (!obj
->allocSlots(cx
, nslots
))
6257 obj
->setSlot(slot
, v
);
6258 GC_POKE(cx
, JS_NULL
);
6263 JSObject::getGlobal() const
6265 JSObject
*obj
= const_cast<JSObject
*>(this);
6266 while (JSObject
*parent
= obj
->getParent())
6272 js_ReportGetterOnlyAssignment(JSContext
*cx
)
6274 return JS_ReportErrorFlagsAndNumber(cx
,
6275 JSREPORT_WARNING
| JSREPORT_STRICT
|
6276 JSREPORT_STRICT_MODE_ERROR
,
6277 js_GetErrorMessage
, NULL
,
6281 JS_FRIEND_API(JSBool
)
6282 js_GetterOnlyPropertyStub(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
6284 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
, JSMSG_GETTER_ONLY
);
6291 * Routines to print out values during debugging. These are FRIEND_API to help
6292 * the debugger find them and to support temporarily hacking js_Dump* calls
6297 dumpChars(const jschar
*s
, size_t n
)
6301 if (n
== (size_t) -1) {
6306 for (i
= 0; i
< n
; i
++) {
6308 fprintf(stderr
, "\\n");
6309 else if (s
[i
] == '\t')
6310 fprintf(stderr
, "\\t");
6311 else if (s
[i
] >= 32 && s
[i
] < 127)
6312 fputc(s
[i
], stderr
);
6313 else if (s
[i
] <= 255)
6314 fprintf(stderr
, "\\x%02x", (unsigned int) s
[i
]);
6316 fprintf(stderr
, "\\u%04x", (unsigned int) s
[i
]);
6322 js_DumpChars(const jschar
*s
, size_t n
)
6324 fprintf(stderr
, "jschar * (%p) = ", (void *) s
);
6326 fputc('\n', stderr
);
6330 dumpString(JSString
*str
)
6332 dumpChars(str
->chars(), str
->length());
6336 js_DumpString(JSString
*str
)
6338 fprintf(stderr
, "JSString* (%p) = jschar * (%p) = ",
6339 (void *) str
, (void *) str
->chars());
6341 fputc('\n', stderr
);
6345 js_DumpAtom(JSAtom
*atom
)
6347 fprintf(stderr
, "JSAtom* (%p) = ", (void *) atom
);
6348 js_DumpString(ATOM_TO_STRING(atom
));
6352 dumpValue(const Value
&v
)
6355 fprintf(stderr
, "null");
6356 else if (v
.isUndefined())
6357 fprintf(stderr
, "undefined");
6358 else if (v
.isInt32())
6359 fprintf(stderr
, "%d", v
.toInt32());
6360 else if (v
.isDouble())
6361 fprintf(stderr
, "%g", v
.toDouble());
6362 else if (v
.isString())
6363 dumpString(v
.toString());
6364 else if (v
.isObject() && v
.toObject().isFunction()) {
6365 JSObject
*funobj
= &v
.toObject();
6366 JSFunction
*fun
= GET_FUNCTION_PRIVATE(cx
, funobj
);
6368 fputs("<function ", stderr
);
6369 FileEscapedString(stderr
, ATOM_TO_STRING(fun
->atom
), 0);
6371 fputs("<unnamed function", stderr
);
6373 fprintf(stderr
, " at %p (JSFunction at %p)>", (void *) funobj
, (void *) fun
);
6374 } else if (v
.isObject()) {
6375 JSObject
*obj
= &v
.toObject();
6376 Class
*clasp
= obj
->getClass();
6377 fprintf(stderr
, "<%s%s at %p>",
6379 (clasp
== &js_ObjectClass
) ? "" : " object",
6381 } else if (v
.isBoolean()) {
6383 fprintf(stderr
, "true");
6385 fprintf(stderr
, "false");
6386 } else if (v
.isMagic()) {
6387 fprintf(stderr
, "<invalid");
6389 switch (v
.whyMagic()) {
6390 case JS_ARRAY_HOLE
: fprintf(stderr
, " array hole"); break;
6391 case JS_ARGS_HOLE
: fprintf(stderr
, " args hole"); break;
6392 case JS_NATIVE_ENUMERATE
: fprintf(stderr
, " native enumeration"); break;
6393 case JS_NO_ITER_VALUE
: fprintf(stderr
, " no iter value"); break;
6394 case JS_GENERATOR_CLOSING
: fprintf(stderr
, " generator closing"); break;
6395 default: fprintf(stderr
, " ?!"); break;
6398 fprintf(stderr
, ">");
6400 fprintf(stderr
, "unexpected value");
6405 js_DumpValue(const Value
&val
)
6408 fputc('\n', stderr
);
6414 fprintf(stderr
, "jsid %p = ", (void *) JSID_BITS(id
));
6415 dumpValue(IdToValue(id
));
6416 fputc('\n', stderr
);
6420 DumpShape(const Shape
&shape
)
6423 uint8 attrs
= shape
.attributes();
6425 fprintf(stderr
, " ");
6426 if (attrs
& JSPROP_ENUMERATE
) fprintf(stderr
, "enumerate ");
6427 if (attrs
& JSPROP_READONLY
) fprintf(stderr
, "readonly ");
6428 if (attrs
& JSPROP_PERMANENT
) fprintf(stderr
, "permanent ");
6429 if (attrs
& JSPROP_GETTER
) fprintf(stderr
, "getter ");
6430 if (attrs
& JSPROP_SETTER
) fprintf(stderr
, "setter ");
6431 if (attrs
& JSPROP_SHARED
) fprintf(stderr
, "shared ");
6432 if (shape
.isAlias()) fprintf(stderr
, "alias ");
6433 if (JSID_IS_ATOM(id
))
6434 dumpString(JSID_TO_STRING(id
));
6435 else if (JSID_IS_INT(id
))
6436 fprintf(stderr
, "%d", (int) JSID_TO_INT(id
));
6438 fprintf(stderr
, "unknown jsid %p", (void *) JSID_BITS(id
));
6439 fprintf(stderr
, ": slot %d", shape
.slot
);
6440 fprintf(stderr
, "\n");
6444 js_DumpObject(JSObject
*obj
)
6446 fprintf(stderr
, "object %p\n", (void *) obj
);
6447 Class
*clasp
= obj
->getClass();
6448 fprintf(stderr
, "class %p %s\n", (void *)clasp
, clasp
->name
);
6450 fprintf(stderr
, "flags:");
6451 uint32 flags
= obj
->flags
;
6452 if (flags
& JSObject::DELEGATE
) fprintf(stderr
, " delegate");
6453 if (flags
& JSObject::SYSTEM
) fprintf(stderr
, " system");
6454 if (flags
& JSObject::NOT_EXTENSIBLE
) fprintf(stderr
, " not extensible");
6455 if (flags
& JSObject::BRANDED
) fprintf(stderr
, " branded");
6456 if (flags
& JSObject::GENERIC
) fprintf(stderr
, " generic");
6457 if (flags
& JSObject::METHOD_BARRIER
) fprintf(stderr
, " method_barrier");
6458 if (flags
& JSObject::INDEXED
) fprintf(stderr
, " indexed");
6459 if (flags
& JSObject::OWN_SHAPE
) fprintf(stderr
, " own_shape");
6460 if (flags
& JSObject::HAS_EQUALITY
) fprintf(stderr
, " has_equality");
6462 bool anyFlags
= flags
!= 0;
6463 if (obj
->isNative()) {
6464 if (obj
->inDictionaryMode()) {
6465 fprintf(stderr
, " inDictionaryMode");
6468 if (obj
->hasPropertyTable()) {
6469 fprintf(stderr
, " hasPropertyTable");
6474 fprintf(stderr
, " none");
6475 fprintf(stderr
, "\n");
6477 if (obj
->isDenseArray()) {
6478 unsigned slots
= JS_MIN(obj
->getArrayLength(), obj
->getDenseArrayCapacity());
6479 fprintf(stderr
, "elements\n");
6480 for (unsigned i
= 0; i
< slots
; i
++) {
6481 fprintf(stderr
, " %3d: ", i
);
6482 dumpValue(obj
->getDenseArrayElement(i
));
6483 fprintf(stderr
, "\n");
6489 if (obj
->isNative()) {
6490 fprintf(stderr
, "properties:\n");
6491 for (Shape::Range r
= obj
->lastProperty()->all(); !r
.empty(); r
.popFront())
6492 DumpShape(r
.front());
6494 if (!obj
->isNative())
6495 fprintf(stderr
, "not native\n");
6498 fprintf(stderr
, "proto ");
6499 dumpValue(ObjectOrNullValue(obj
->getProto()));
6500 fputc('\n', stderr
);
6502 fprintf(stderr
, "parent ");
6503 dumpValue(ObjectOrNullValue(obj
->getParent()));
6504 fputc('\n', stderr
);
6506 if (clasp
->flags
& JSCLASS_HAS_PRIVATE
)
6507 fprintf(stderr
, "private %p\n", obj
->getPrivate());
6509 fprintf(stderr
, "slots:\n");
6510 unsigned reservedEnd
= JSCLASS_RESERVED_SLOTS(clasp
);
6511 unsigned slots
= obj
->slotSpan();
6512 for (unsigned i
= 0; i
< slots
; i
++) {
6513 fprintf(stderr
, " %3d ", i
);
6514 if (i
< reservedEnd
)
6515 fprintf(stderr
, "(reserved) ");
6516 fprintf(stderr
, "= ");
6517 dumpValue(obj
->getSlot(i
));
6518 fputc('\n', stderr
);
6520 fputc('\n', stderr
);
6524 MaybeDumpObject(const char *name
, JSObject
*obj
)
6527 fprintf(stderr
, " %s: ", name
);
6528 dumpValue(ObjectValue(*obj
));
6529 fputc('\n', stderr
);
6534 MaybeDumpValue(const char *name
, const Value
&v
)
6537 fprintf(stderr
, " %s: ", name
);
6539 fputc('\n', stderr
);
6544 js_DumpStackFrame(JSContext
*cx
, JSStackFrame
*start
)
6546 /* This should only called during live debugging. */
6547 VOUCH_DOES_NOT_REQUIRE_STACK();
6550 start
= cx
->maybefp();
6551 FrameRegsIter
i(cx
);
6552 while (!i
.done() && i
.fp() != start
)
6556 fprintf(stderr
, "fp = %p not found in cx = %p\n", (void *)start
, (void *)cx
);
6560 for (; !i
.done(); ++i
) {
6561 JSStackFrame
*const fp
= i
.fp();
6563 fprintf(stderr
, "JSStackFrame at %p\n", (void *) fp
);
6564 if (fp
->isFunctionFrame()) {
6565 fprintf(stderr
, "callee fun: ");
6566 dumpValue(ObjectValue(fp
->callee()));
6568 fprintf(stderr
, "global frame, no callee");
6570 fputc('\n', stderr
);
6572 if (fp
->isScriptFrame()) {
6573 fprintf(stderr
, "file %s line %u\n",
6574 fp
->script()->filename
, (unsigned) fp
->script()->lineno
);
6577 if (jsbytecode
*pc
= i
.pc()) {
6578 if (!fp
->isScriptFrame()) {
6579 fprintf(stderr
, "*** pc && !script, skipping frame\n\n");
6582 if (fp
->hasImacropc()) {
6583 fprintf(stderr
, " pc in imacro at %p\n called from ", pc
);
6584 pc
= fp
->imacropc();
6586 fprintf(stderr
, " ");
6588 fprintf(stderr
, "pc = %p\n", pc
);
6589 fprintf(stderr
, " current op: %s\n", js_CodeName
[*pc
]);
6592 fprintf(stderr
, " slots: %p\n", (void *) fp
->slots());
6593 fprintf(stderr
, " sp: %p = slots + %u\n", (void *) sp
, (unsigned) (sp
- fp
->slots()));
6594 if (sp
- fp
->slots() < 10000) { // sanity
6595 for (Value
*p
= fp
->slots(); p
< sp
; p
++) {
6596 fprintf(stderr
, " %p: ", (void *) p
);
6598 fputc('\n', stderr
);
6601 if (fp
->isFunctionFrame() && !fp
->isEvalFrame()) {
6602 fprintf(stderr
, " actuals: %p (%u) ", (void *) fp
->actualArgs(), (unsigned) fp
->numActualArgs());
6603 fprintf(stderr
, " formals: %p (%u)\n", (void *) fp
->formalArgs(), (unsigned) fp
->numFormalArgs());
6605 MaybeDumpObject("callobj", fp
->maybeCallObj());
6606 MaybeDumpObject("argsobj", fp
->maybeArgsObj());
6607 if (!fp
->isDummyFrame()) {
6608 MaybeDumpValue("this", fp
->thisValue());
6609 fprintf(stderr
, " rval: ");
6610 dumpValue(fp
->returnValue());
6612 fprintf(stderr
, "dummy frame");
6614 fputc('\n', stderr
);
6616 fprintf(stderr
, " flags:");
6617 if (fp
->isConstructing())
6618 fprintf(stderr
, " constructing");
6619 if (fp
->hasOverriddenArgs())
6620 fprintf(stderr
, " overridden_args");
6621 if (fp
->isAssigning())
6622 fprintf(stderr
, " assigning");
6623 if (fp
->isDebuggerFrame())
6624 fprintf(stderr
, " debugger");
6625 if (fp
->isEvalFrame())
6626 fprintf(stderr
, " eval");
6627 if (fp
->isYielding())
6628 fprintf(stderr
, " yielding");
6629 if (fp
->isGeneratorFrame())
6630 fprintf(stderr
, " generator");
6631 fputc('\n', stderr
);
6633 fprintf(stderr
, " scopeChain: (JSObject *) %p\n", (void *) &fp
->scopeChain());
6635 fputc('\n', stderr
);
6641 IsSaneThisObject(JSObject
&obj
)
6643 Class
*clasp
= obj
.getClass();
6644 return clasp
!= &js_CallClass
&&
6645 clasp
!= &js_BlockClass
&&
6646 clasp
!= &js_DeclEnvClass
&&
6647 clasp
!= &js_WithClass
;