Bug 586358: make imacpc flagged. (r=lw)
[mozilla-central.git] / js / src / jsobj.cpp
blob4120d74e36741311a9e86c397cb8a05980359728
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
15 * License.
17 * The Original Code is Mozilla Communicator client code, released
18 * March 31, 1998.
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.
25 * Contributor(s):
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.
44 #include <stdlib.h>
45 #include <string.h>
46 #include "jstypes.h"
47 #include "jsstdint.h"
48 #include "jsarena.h" /* Added by JSIFY */
49 #include "jsbit.h"
50 #include "jsutil.h" /* Added by JSIFY */
51 #include "jshash.h" /* Added by JSIFY */
52 #include "jsdhash.h"
53 #include "jsprf.h"
54 #include "jsapi.h"
55 #include "jsarray.h"
56 #include "jsatom.h"
57 #include "jsbool.h"
58 #include "jsbuiltins.h"
59 #include "jscntxt.h"
60 #include "jsversion.h"
61 #include "jsemit.h"
62 #include "jsfun.h"
63 #include "jsgc.h"
64 #include "jsinterp.h"
65 #include "jsiter.h"
66 #include "jslock.h"
67 #include "jsnum.h"
68 #include "jsobj.h"
69 #include "jsopcode.h"
70 #include "jsparse.h"
71 #include "jsproxy.h"
72 #include "jsscope.h"
73 #include "jsscript.h"
74 #include "jsstaticcheck.h"
75 #include "jsstdint.h"
76 #include "jsstr.h"
77 #include "jstracer.h"
78 #include "jsdbgapi.h"
80 #include "jsscopeinlines.h"
81 #include "jsscriptinlines.h"
82 #include "jsobjinlines.h"
84 #if JS_HAS_GENERATORS
85 #include "jsiter.h"
86 #endif
88 #if JS_HAS_XML_SUPPORT
89 #include "jsxml.h"
90 #endif
92 #if JS_HAS_XDR
93 #include "jsxdrapi.h"
94 #endif
96 #include "jsdtracef.h"
97 #include "jsatominlines.h"
98 #include "jsobjinlines.h"
99 #include "jsscriptinlines.h"
101 #include "jsautooplen.h"
103 using namespace js;
105 JS_FRIEND_DATA(const JSObjectMap) JSObjectMap::sharedNonNative(JSObjectMap::SHAPELESS);
107 Class js_ObjectClass = {
108 js_Object_str,
109 JSCLASS_HAS_CACHED_PROTO(JSProto_Object) |
110 JSCLASS_FAST_CONSTRUCTOR,
111 PropertyStub, /* addProperty */
112 PropertyStub, /* delProperty */
113 PropertyStub, /* getProperty */
114 PropertyStub, /* setProperty */
115 EnumerateStub,
116 ResolveStub,
117 ConvertStub
120 JS_FRIEND_API(JSObject *)
121 js_ObjectToOuterObject(JSContext *cx, JSObject *obj)
123 OBJ_TO_OUTER_OBJECT(cx, obj);
124 return obj;
127 #if JS_HAS_OBJ_PROTO_PROP
129 static JSBool
130 obj_getProto(JSContext *cx, JSObject *obj, jsid id, Value *vp);
132 static JSBool
133 obj_setProto(JSContext *cx, JSObject *obj, jsid id, Value *vp);
135 static JSPropertySpec object_props[] = {
136 {js_proto_str, 0, JSPROP_PERMANENT|JSPROP_SHARED, Jsvalify(obj_getProto), Jsvalify(obj_setProto)},
137 {0,0,0,0,0}
140 static JSBool
141 obj_getProto(JSContext *cx, JSObject *obj, jsid id, Value *vp)
143 /* Let CheckAccess get the slot's value, based on the access mode. */
144 uintN attrs;
145 id = ATOM_TO_JSID(cx->runtime->atomState.protoAtom);
146 return CheckAccess(cx, obj, id, JSACC_PROTO, vp, &attrs);
149 static JSBool
150 obj_setProto(JSContext *cx, JSObject *obj, jsid id, Value *vp)
152 if (!vp->isObjectOrNull())
153 return JS_TRUE;
155 JSObject *pobj = vp->toObjectOrNull();
156 if (pobj) {
158 * Innerize pobj here to avoid sticking unwanted properties on the
159 * outer object. This ensures that any with statements only grant
160 * access to the inner object.
162 OBJ_TO_INNER_OBJECT(cx, pobj);
163 if (!pobj)
164 return JS_FALSE;
167 uintN attrs;
168 id = ATOM_TO_JSID(cx->runtime->atomState.protoAtom);
169 if (!CheckAccess(cx, obj, id, JSAccessMode(JSACC_PROTO|JSACC_WRITE), vp, &attrs))
170 return JS_FALSE;
172 return SetProto(cx, obj, pobj, JS_TRUE);
175 #else /* !JS_HAS_OBJ_PROTO_PROP */
177 #define object_props NULL
179 #endif /* !JS_HAS_OBJ_PROTO_PROP */
181 static JSHashNumber
182 js_hash_object(const void *key)
184 return JSHashNumber(uintptr_t(key) >> JS_GCTHING_ALIGN);
187 static JSHashEntry *
188 MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap)
190 JSSharpObjectMap *map;
191 JSHashTable *table;
192 JSHashNumber hash;
193 JSHashEntry **hep, *he;
194 jsatomid sharpid;
195 JSIdArray *ida;
196 JSBool ok;
197 jsint i, length;
198 jsid id;
199 JSObject *obj2;
200 JSProperty *prop;
202 JS_CHECK_RECURSION(cx, return NULL);
204 map = &cx->sharpObjectMap;
205 JS_ASSERT(map->depth >= 1);
206 table = map->table;
207 hash = js_hash_object(obj);
208 hep = JS_HashTableRawLookup(table, hash, obj);
209 he = *hep;
210 if (!he) {
211 sharpid = 0;
212 he = JS_HashTableRawAdd(table, hep, hash, obj, (void *) sharpid);
213 if (!he) {
214 JS_ReportOutOfMemory(cx);
215 return NULL;
218 ida = JS_Enumerate(cx, obj);
219 if (!ida)
220 return NULL;
222 ok = JS_TRUE;
223 for (i = 0, length = ida->length; i < length; i++) {
224 id = ida->vector[i];
225 ok = obj->lookupProperty(cx, id, &obj2, &prop);
226 if (!ok)
227 break;
228 if (!prop)
229 continue;
230 bool hasGetter, hasSetter;
231 AutoValueRooter v(cx);
232 AutoValueRooter setter(cx);
233 if (obj2->isNative()) {
234 JSScopeProperty *sprop = (JSScopeProperty *) prop;
235 hasGetter = sprop->hasGetterValue();
236 hasSetter = sprop->hasSetterValue();
237 if (hasGetter)
238 v.set(sprop->getterValue());
239 if (hasSetter)
240 setter.set(sprop->setterValue());
241 JS_UNLOCK_OBJ(cx, obj2);
242 } else {
243 hasGetter = hasSetter = false;
245 if (hasSetter) {
246 /* Mark the getter, then set val to setter. */
247 if (hasGetter && v.value().isObject()) {
248 ok = !!MarkSharpObjects(cx, &v.value().toObject(), NULL);
249 if (!ok)
250 break;
252 v.set(setter.value());
253 } else if (!hasGetter) {
254 ok = obj->getProperty(cx, id, v.addr());
255 if (!ok)
256 break;
258 if (v.value().isObject() &&
259 !MarkSharpObjects(cx, &v.value().toObject(), NULL)) {
260 ok = JS_FALSE;
261 break;
264 if (!ok || !idap)
265 JS_DestroyIdArray(cx, ida);
266 if (!ok)
267 return NULL;
268 } else {
269 sharpid = uintptr_t(he->value);
270 if (sharpid == 0) {
271 sharpid = ++map->sharpgen << SHARP_ID_SHIFT;
272 he->value = (void *) sharpid;
274 ida = NULL;
276 if (idap)
277 *idap = ida;
278 return he;
281 JSHashEntry *
282 js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap,
283 jschar **sp)
285 JSSharpObjectMap *map;
286 JSHashTable *table;
287 JSIdArray *ida;
288 JSHashNumber hash;
289 JSHashEntry *he, **hep;
290 jsatomid sharpid;
291 char buf[20];
292 size_t len;
294 if (!JS_CHECK_OPERATION_LIMIT(cx))
295 return NULL;
297 /* Set to null in case we return an early error. */
298 *sp = NULL;
299 map = &cx->sharpObjectMap;
300 table = map->table;
301 if (!table) {
302 table = JS_NewHashTable(8, js_hash_object, JS_CompareValues,
303 JS_CompareValues, NULL, NULL);
304 if (!table) {
305 JS_ReportOutOfMemory(cx);
306 return NULL;
308 map->table = table;
309 JS_KEEP_ATOMS(cx->runtime);
312 /* From this point the control must flow either through out: or bad:. */
313 ida = NULL;
314 if (map->depth == 0) {
316 * Although MarkSharpObjects tries to avoid invoking getters,
317 * it ends up doing so anyway under some circumstances; for
318 * example, if a wrapped object has getters, the wrapper will
319 * prevent MarkSharpObjects from recognizing them as such.
320 * This could lead to js_LeaveSharpObject being called while
321 * MarkSharpObjects is still working.
323 * Increment map->depth while we call MarkSharpObjects, to
324 * ensure that such a call doesn't free the hash table we're
325 * still using.
327 ++map->depth;
328 he = MarkSharpObjects(cx, obj, &ida);
329 --map->depth;
330 if (!he)
331 goto bad;
332 JS_ASSERT((uintptr_t(he->value) & SHARP_BIT) == 0);
333 if (!idap) {
334 JS_DestroyIdArray(cx, ida);
335 ida = NULL;
337 } else {
338 hash = js_hash_object(obj);
339 hep = JS_HashTableRawLookup(table, hash, obj);
340 he = *hep;
343 * It's possible that the value of a property has changed from the
344 * first time the object's properties are traversed (when the property
345 * ids are entered into the hash table) to the second (when they are
346 * converted to strings), i.e., the JSObject::getProperty() call is not
347 * idempotent.
349 if (!he) {
350 he = JS_HashTableRawAdd(table, hep, hash, obj, NULL);
351 if (!he) {
352 JS_ReportOutOfMemory(cx);
353 goto bad;
355 sharpid = 0;
356 goto out;
360 sharpid = uintptr_t(he->value);
361 if (sharpid != 0) {
362 len = JS_snprintf(buf, sizeof buf, "#%u%c",
363 sharpid >> SHARP_ID_SHIFT,
364 (sharpid & SHARP_BIT) ? '#' : '=');
365 *sp = js_InflateString(cx, buf, &len);
366 if (!*sp) {
367 if (ida)
368 JS_DestroyIdArray(cx, ida);
369 goto bad;
373 out:
374 JS_ASSERT(he);
375 if ((sharpid & SHARP_BIT) == 0) {
376 if (idap && !ida) {
377 ida = JS_Enumerate(cx, obj);
378 if (!ida) {
379 if (*sp) {
380 cx->free(*sp);
381 *sp = NULL;
383 goto bad;
386 map->depth++;
389 if (idap)
390 *idap = ida;
391 return he;
393 bad:
394 /* Clean up the sharpObjectMap table on outermost error. */
395 if (map->depth == 0) {
396 JS_UNKEEP_ATOMS(cx->runtime);
397 map->sharpgen = 0;
398 JS_HashTableDestroy(map->table);
399 map->table = NULL;
401 return NULL;
404 void
405 js_LeaveSharpObject(JSContext *cx, JSIdArray **idap)
407 JSSharpObjectMap *map;
408 JSIdArray *ida;
410 map = &cx->sharpObjectMap;
411 JS_ASSERT(map->depth > 0);
412 if (--map->depth == 0) {
413 JS_UNKEEP_ATOMS(cx->runtime);
414 map->sharpgen = 0;
415 JS_HashTableDestroy(map->table);
416 map->table = NULL;
418 if (idap) {
419 ida = *idap;
420 if (ida) {
421 JS_DestroyIdArray(cx, ida);
422 *idap = NULL;
427 static intN
428 gc_sharp_table_entry_marker(JSHashEntry *he, intN i, void *arg)
430 JS_CALL_OBJECT_TRACER((JSTracer *)arg, (JSObject *)he->key,
431 "sharp table entry");
432 return JS_DHASH_NEXT;
435 void
436 js_TraceSharpMap(JSTracer *trc, JSSharpObjectMap *map)
438 JS_ASSERT(map->depth > 0);
439 JS_ASSERT(map->table);
442 * During recursive calls to MarkSharpObjects a non-native object or
443 * object with a custom getProperty method can potentially return an
444 * unrooted value or even cut from the object graph an argument of one of
445 * MarkSharpObjects recursive invocations. So we must protect map->table
446 * entries against GC.
448 * We can not simply use JSTempValueRooter to mark the obj argument of
449 * MarkSharpObjects during recursion as we have to protect *all* entries
450 * in JSSharpObjectMap including those that contains otherwise unreachable
451 * objects just allocated through custom getProperty. Otherwise newer
452 * allocations can re-use the address of an object stored in the hashtable
453 * confusing js_EnterSharpObject. So to address the problem we simply
454 * mark all objects from map->table.
456 * An alternative "proper" solution is to use JSTempValueRooter in
457 * MarkSharpObjects with code to remove during finalization entries
458 * with otherwise unreachable objects. But this is way too complex
459 * to justify spending efforts.
461 JS_HashTableEnumerateEntries(map->table, gc_sharp_table_entry_marker, trc);
464 #if JS_HAS_TOSOURCE
465 static JSBool
466 obj_toSource(JSContext *cx, uintN argc, Value *vp)
468 JSBool ok, outermost;
469 JSObject *obj;
470 JSHashEntry *he;
471 JSIdArray *ida;
472 jschar *chars, *ochars, *vsharp;
473 const jschar *idstrchars, *vchars;
474 size_t nchars, idstrlength, gsoplength, vlength, vsharplength, curlen;
475 const char *comma;
476 JSObject *obj2;
477 JSProperty *prop;
478 Value *val;
479 JSString *gsop[2];
480 JSString *idstr, *valstr, *str;
482 JS_CHECK_RECURSION(cx, return JS_FALSE);
484 Value localroot[4];
485 PodArrayZero(localroot);
486 AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(localroot), localroot);
488 /* If outermost, we need parentheses to be an expression, not a block. */
489 outermost = (cx->sharpObjectMap.depth == 0);
490 obj = ComputeThisFromVp(cx, vp);
491 if (!obj || !(he = js_EnterSharpObject(cx, obj, &ida, &chars))) {
492 ok = JS_FALSE;
493 goto out;
495 if (IS_SHARP(he)) {
497 * We didn't enter -- obj is already "sharp", meaning we've visited it
498 * already in our depth first search, and therefore chars contains a
499 * string of the form "#n#".
501 JS_ASSERT(!ida);
502 #if JS_HAS_SHARP_VARS
503 nchars = js_strlen(chars);
504 #else
505 chars[0] = '{';
506 chars[1] = '}';
507 chars[2] = 0;
508 nchars = 2;
509 #endif
510 goto make_string;
512 JS_ASSERT(ida);
513 ok = JS_TRUE;
515 if (!chars) {
516 /* If outermost, allocate 4 + 1 for "({})" and the terminator. */
517 chars = (jschar *) js_malloc(((outermost ? 4 : 2) + 1) * sizeof(jschar));
518 nchars = 0;
519 if (!chars)
520 goto error;
521 if (outermost)
522 chars[nchars++] = '(';
523 } else {
524 /* js_EnterSharpObject returned a string of the form "#n=" in chars. */
525 MAKE_SHARP(he);
526 nchars = js_strlen(chars);
527 chars = (jschar *)
528 js_realloc((ochars = chars), (nchars + 2 + 1) * sizeof(jschar));
529 if (!chars) {
530 js_free(ochars);
531 goto error;
533 if (outermost) {
535 * No need for parentheses around the whole shebang, because #n=
536 * unambiguously begins an object initializer, and never a block
537 * statement.
539 outermost = JS_FALSE;
543 chars[nchars++] = '{';
545 comma = NULL;
548 * We have four local roots for cooked and raw value GC safety. Hoist the
549 * "localroot + 2" out of the loop using the val local, which refers to
550 * the raw (unconverted, "uncooked") values.
552 val = localroot + 2;
554 for (jsint i = 0, length = ida->length; i < length; i++) {
555 /* Get strings for id and value and GC-root them via vp. */
556 jsid id = ida->vector[i];
558 ok = obj->lookupProperty(cx, id, &obj2, &prop);
559 if (!ok)
560 goto error;
563 * Convert id to a value and then to a string. Decide early whether we
564 * prefer get/set or old getter/setter syntax.
566 idstr = js_ValueToString(cx, IdToValue(id));
567 if (!idstr) {
568 ok = JS_FALSE;
569 obj2->dropProperty(cx, prop);
570 goto error;
572 vp->setString(idstr); /* local root */
574 jsint valcnt = 0;
575 if (prop) {
576 bool doGet = true;
577 if (obj2->isNative()) {
578 JSScopeProperty *sprop = (JSScopeProperty *) prop;
579 unsigned attrs = sprop->attributes();
580 if (attrs & JSPROP_GETTER) {
581 doGet = false;
582 val[valcnt] = sprop->getterValue();
583 gsop[valcnt] = ATOM_TO_STRING(cx->runtime->atomState.getAtom);
584 valcnt++;
586 if (attrs & JSPROP_SETTER) {
587 doGet = false;
588 val[valcnt] = sprop->setterValue();
589 gsop[valcnt] = ATOM_TO_STRING(cx->runtime->atomState.setAtom);
590 valcnt++;
592 JS_UNLOCK_OBJ(cx, obj2);
594 if (doGet) {
595 valcnt = 1;
596 gsop[0] = NULL;
597 ok = obj->getProperty(cx, id, &val[0]);
598 if (!ok)
599 goto error;
604 * If id is a string that's not an identifier, or if it's a negative
605 * integer, then it must be quoted.
607 bool idIsLexicalIdentifier = !!js_IsIdentifier(idstr);
608 if (JSID_IS_ATOM(id)
609 ? !idIsLexicalIdentifier
610 : (!JSID_IS_INT(id) || JSID_TO_INT(id) < 0)) {
611 idstr = js_QuoteString(cx, idstr, jschar('\''));
612 if (!idstr) {
613 ok = JS_FALSE;
614 goto error;
616 vp->setString(idstr); /* local root */
618 idstr->getCharsAndLength(idstrchars, idstrlength);
620 for (jsint j = 0; j < valcnt; j++) {
622 * Censor an accessor descriptor getter or setter part if it's
623 * undefined.
625 if (gsop[j] && val[j].isUndefined())
626 continue;
628 /* Convert val[j] to its canonical source form. */
629 valstr = js_ValueToSource(cx, val[j]);
630 if (!valstr) {
631 ok = JS_FALSE;
632 goto error;
634 localroot[j].setString(valstr); /* local root */
635 valstr->getCharsAndLength(vchars, vlength);
638 * If val[j] is a non-sharp object, and we're not serializing an
639 * accessor (ECMA syntax can't accommodate sharpened accessors),
640 * consider sharpening it.
642 vsharp = NULL;
643 vsharplength = 0;
644 #if JS_HAS_SHARP_VARS
645 if (!gsop[j] && val[j].isObject() && vchars[0] != '#') {
646 he = js_EnterSharpObject(cx, &val[j].toObject(), NULL, &vsharp);
647 if (!he) {
648 ok = JS_FALSE;
649 goto error;
651 if (IS_SHARP(he)) {
652 vchars = vsharp;
653 vlength = js_strlen(vchars);
654 } else {
655 if (vsharp) {
656 vsharplength = js_strlen(vsharp);
657 MAKE_SHARP(he);
659 js_LeaveSharpObject(cx, NULL);
662 #endif
665 * Remove '(function ' from the beginning of valstr and ')' from the
666 * end so that we can put "get" in front of the function definition.
668 if (gsop[j] && IsFunctionObject(val[j])) {
669 const jschar *start = vchars;
670 const jschar *end = vchars + vlength;
672 uint8 parenChomp = 0;
673 if (vchars[0] == '(') {
674 vchars++;
675 parenChomp = 1;
678 /* Try to jump "function" keyword. */
679 if (vchars)
680 vchars = js_strchr_limit(vchars, ' ', end);
683 * Jump over the function's name: it can't be encoded as part
684 * of an ECMA getter or setter.
686 if (vchars)
687 vchars = js_strchr_limit(vchars, '(', end);
689 if (vchars) {
690 if (*vchars == ' ')
691 vchars++;
692 vlength = end - vchars - parenChomp;
693 } else {
694 gsop[j] = NULL;
695 vchars = start;
699 #define SAFE_ADD(n) \
700 JS_BEGIN_MACRO \
701 size_t n_ = (n); \
702 curlen += n_; \
703 if (curlen < n_) \
704 goto overflow; \
705 JS_END_MACRO
707 curlen = nchars;
708 if (comma)
709 SAFE_ADD(2);
710 SAFE_ADD(idstrlength + 1);
711 if (gsop[j])
712 SAFE_ADD(gsop[j]->length() + 1);
713 SAFE_ADD(vsharplength);
714 SAFE_ADD(vlength);
715 /* Account for the trailing null. */
716 SAFE_ADD((outermost ? 2 : 1) + 1);
717 #undef SAFE_ADD
719 if (curlen > size_t(-1) / sizeof(jschar))
720 goto overflow;
722 /* Allocate 1 + 1 at end for closing brace and terminating 0. */
723 chars = (jschar *) js_realloc((ochars = chars), curlen * sizeof(jschar));
724 if (!chars) {
725 /* Save code space on error: let JS_free ignore null vsharp. */
726 cx->free(vsharp);
727 js_free(ochars);
728 goto error;
731 if (comma) {
732 chars[nchars++] = comma[0];
733 chars[nchars++] = comma[1];
735 comma = ", ";
737 if (gsop[j]) {
738 gsoplength = gsop[j]->length();
739 js_strncpy(&chars[nchars], gsop[j]->chars(),
740 gsoplength);
741 nchars += gsoplength;
742 chars[nchars++] = ' ';
744 js_strncpy(&chars[nchars], idstrchars, idstrlength);
745 nchars += idstrlength;
746 /* Extraneous space after id here will be extracted later */
747 chars[nchars++] = gsop[j] ? ' ' : ':';
749 if (vsharplength) {
750 js_strncpy(&chars[nchars], vsharp, vsharplength);
751 nchars += vsharplength;
753 js_strncpy(&chars[nchars], vchars, vlength);
754 nchars += vlength;
756 if (vsharp)
757 cx->free(vsharp);
761 chars[nchars++] = '}';
762 if (outermost)
763 chars[nchars++] = ')';
764 chars[nchars] = 0;
766 error:
767 js_LeaveSharpObject(cx, &ida);
769 if (!ok) {
770 if (chars)
771 js_free(chars);
772 goto out;
775 if (!chars) {
776 JS_ReportOutOfMemory(cx);
777 ok = JS_FALSE;
778 goto out;
780 make_string:
781 str = js_NewString(cx, chars, nchars);
782 if (!str) {
783 js_free(chars);
784 ok = JS_FALSE;
785 goto out;
787 vp->setString(str);
788 ok = JS_TRUE;
789 out:
790 return ok;
792 overflow:
793 cx->free(vsharp);
794 js_free(chars);
795 chars = NULL;
796 goto error;
798 #endif /* JS_HAS_TOSOURCE */
800 namespace js {
802 JSString *
803 obj_toStringHelper(JSContext *cx, JSObject *obj)
805 if (obj->isProxy())
806 return JSProxy::obj_toString(cx, obj);
808 const char *clazz = obj->wrappedObject(cx)->getClass()->name;
809 size_t nchars = 9 + strlen(clazz); /* 9 for "[object ]" */
810 jschar *chars = (jschar *) cx->malloc((nchars + 1) * sizeof(jschar));
811 if (!chars)
812 return NULL;
814 const char *prefix = "[object ";
815 nchars = 0;
816 while ((chars[nchars] = (jschar)*prefix) != 0)
817 nchars++, prefix++;
818 while ((chars[nchars] = (jschar)*clazz) != 0)
819 nchars++, clazz++;
820 chars[nchars++] = ']';
821 chars[nchars] = 0;
823 JSString *str = js_NewString(cx, chars, nchars);
824 if (!str)
825 cx->free(chars);
826 return str;
831 static JSBool
832 obj_toString(JSContext *cx, uintN argc, Value *vp)
834 JSObject *obj = ComputeThisFromVp(cx, vp);
835 if (!obj)
836 return false;
838 JSString *str = js::obj_toStringHelper(cx, obj);
839 if (!str)
840 return false;
842 vp->setString(str);
843 return true;
846 static JSBool
847 obj_toLocaleString(JSContext *cx, uintN argc, Value *vp)
849 if (!ComputeThisFromVp(cx, vp))
850 return JS_FALSE;
852 JSString *str = js_ValueToString(cx, vp[1]);
853 if (!str)
854 return JS_FALSE;
856 vp->setString(str);
857 return JS_TRUE;
860 static JSBool
861 obj_valueOf(JSContext *cx, uintN argc, Value *vp)
863 if (!ComputeThisFromVp(cx, vp))
864 return JS_FALSE;
865 *vp = vp[1];
866 return JS_TRUE;
870 * Check if CSP allows new Function() or eval() to run in the current
871 * principals.
873 JSBool
874 js_CheckContentSecurityPolicy(JSContext *cx)
876 JSSecurityCallbacks *callbacks = JS_GetSecurityCallbacks(cx);
878 // if there are callbacks, make sure that the CSP callback is installed and
879 // that it permits eval().
880 if (callbacks && callbacks->contentSecurityPolicyAllows)
881 return callbacks->contentSecurityPolicyAllows(cx);
883 return JS_TRUE;
887 * Check whether principals subsumes scopeobj's principals, and return true
888 * if so (or if scopeobj has no principals, for backward compatibility with
889 * the JS API, which does not require principals), and false otherwise.
891 JSBool
892 js_CheckPrincipalsAccess(JSContext *cx, JSObject *scopeobj,
893 JSPrincipals *principals, JSAtom *caller)
895 JSSecurityCallbacks *callbacks;
896 JSPrincipals *scopePrincipals;
897 const char *callerstr;
899 callbacks = JS_GetSecurityCallbacks(cx);
900 if (callbacks && callbacks->findObjectPrincipals) {
901 scopePrincipals = callbacks->findObjectPrincipals(cx, scopeobj);
902 if (!principals || !scopePrincipals ||
903 !principals->subsume(principals, scopePrincipals)) {
904 callerstr = js_AtomToPrintableString(cx, caller);
905 if (!callerstr)
906 return JS_FALSE;
907 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
908 JSMSG_BAD_INDIRECT_CALL, callerstr);
909 return JS_FALSE;
912 return JS_TRUE;
915 static JSObject *
916 CheckScopeChainValidity(JSContext *cx, JSObject *scopeobj, const char *caller)
918 JSObject *inner;
920 if (!scopeobj)
921 goto bad;
923 OBJ_TO_INNER_OBJECT(cx, scopeobj);
924 if (!scopeobj)
925 return NULL;
927 /* XXX This is an awful gross hack. */
928 inner = scopeobj;
929 while (scopeobj) {
930 JSObjectOp op = scopeobj->getClass()->ext.innerObject;
931 if (op && op(cx, scopeobj) != scopeobj)
932 goto bad;
933 scopeobj = scopeobj->getParent();
936 return inner;
938 bad:
939 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
940 JSMSG_BAD_INDIRECT_CALL, caller);
941 return NULL;
944 const char *
945 js_ComputeFilename(JSContext *cx, JSStackFrame *caller,
946 JSPrincipals *principals, uintN *linenop)
948 uint32 flags;
949 #ifdef DEBUG
950 JSSecurityCallbacks *callbacks = JS_GetSecurityCallbacks(cx);
951 #endif
953 JS_ASSERT(principals || !(callbacks && callbacks->findObjectPrincipals));
954 flags = JS_GetScriptFilenameFlags(caller->script);
955 if ((flags & JSFILENAME_PROTECTED) &&
956 principals &&
957 strcmp(principals->codebase, "[System Principal]")) {
958 *linenop = 0;
959 return principals->codebase;
962 jsbytecode *pc = caller->pc(cx);
963 if (pc && js_GetOpcode(cx, caller->script, pc) == JSOP_EVAL) {
964 JS_ASSERT(js_GetOpcode(cx, caller->script, pc + JSOP_EVAL_LENGTH) == JSOP_LINENO);
965 *linenop = GET_UINT16(pc + JSOP_EVAL_LENGTH);
966 } else {
967 *linenop = js_FramePCToLineNumber(cx, caller);
969 return caller->script->filename;
972 #ifndef EVAL_CACHE_CHAIN_LIMIT
973 # define EVAL_CACHE_CHAIN_LIMIT 4
974 #endif
976 static inline JSScript **
977 EvalCacheHash(JSContext *cx, JSString *str)
979 const jschar *s;
980 size_t n;
981 uint32 h;
983 str->getCharsAndLength(s, n);
984 if (n > 100)
985 n = 100;
986 for (h = 0; n; s++, n--)
987 h = JS_ROTATE_LEFT32(h, 4) ^ *s;
989 h *= JS_GOLDEN_RATIO;
990 h >>= 32 - JS_EVAL_CACHE_SHIFT;
991 return &JS_SCRIPTS_TO_GC(cx)[h];
994 static JSBool
995 obj_eval(JSContext *cx, uintN argc, Value *vp)
997 if (argc < 1) {
998 vp->setUndefined();
999 return JS_TRUE;
1002 JSStackFrame *caller = js_GetScriptedCaller(cx, NULL);
1003 if (!caller) {
1004 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1005 JSMSG_BAD_INDIRECT_CALL, js_eval_str);
1006 return JS_FALSE;
1009 jsbytecode *callerPC = caller->pc(cx);
1010 bool indirectCall = (callerPC && *callerPC != JSOP_EVAL);
1013 * This call to JSObject::wrappedObject is safe because of the security
1014 * checks we do below. However, the control flow below is confusing, so we
1015 * double check. There are two cases:
1016 * - Direct call: This object is never used. So unwrapping can't hurt.
1017 * - Indirect call: If this object isn't already the scope chain (which
1018 * we're guaranteed to be allowed to access) then we do a security
1019 * check.
1021 Value *argv = JS_ARGV(cx, vp);
1022 JSObject *obj = ComputeThisFromVp(cx, vp);
1023 if (!obj)
1024 return JS_FALSE;
1025 obj = obj->wrappedObject(cx);
1028 * Ban all indirect uses of eval (global.foo = eval; global.foo(...)) and
1029 * calls that attempt to use a non-global object as the "with" object in
1030 * the former indirect case.
1033 JSObject *parent = obj->getParent();
1034 if (indirectCall || parent) {
1035 uintN flags = parent
1036 ? JSREPORT_ERROR
1037 : JSREPORT_STRICT | JSREPORT_WARNING;
1038 if (!JS_ReportErrorFlagsAndNumber(cx, flags, js_GetErrorMessage, NULL,
1039 JSMSG_BAD_INDIRECT_CALL,
1040 js_eval_str)) {
1041 return JS_FALSE;
1046 if (!argv[0].isString()) {
1047 *vp = argv[0];
1048 return JS_TRUE;
1052 * We once supported a second argument to eval to use as the scope chain
1053 * when evaluating the code string. Warn when such uses are seen so that
1054 * authors will know that support for eval(s, o) has been removed.
1056 if (argc > 1 && !caller->script->warnedAboutTwoArgumentEval) {
1057 static const char TWO_ARGUMENT_WARNING[] =
1058 "Support for eval(code, scopeObject) has been removed. "
1059 "Use |with (scopeObject) eval(code);| instead.";
1060 if (!JS_ReportWarning(cx, TWO_ARGUMENT_WARNING))
1061 return JS_FALSE;
1062 caller->script->warnedAboutTwoArgumentEval = true;
1065 /* From here on, control must exit through label out with ok set. */
1066 MUST_FLOW_THROUGH("out");
1067 uintN staticLevel = caller->script->staticLevel + 1;
1070 * Bring fp->scopeChain up to date. We're either going to use
1071 * it (direct call) or save it and restore it (indirect call).
1073 JSObject *callerScopeChain = js_GetScopeChain(cx, caller);
1074 if (!callerScopeChain)
1075 return JS_FALSE;
1077 JSObject *scopeobj = NULL;
1079 #if JS_HAS_EVAL_THIS_SCOPE
1081 * If we see an indirect call, then run eval in the global scope. We do
1082 * this so the compiler can make assumptions about what bindings may or
1083 * may not exist in the current frame if it doesn't see 'eval'.
1085 if (indirectCall) {
1086 /* Pretend that we're top level. */
1087 staticLevel = 0;
1089 OBJ_TO_INNER_OBJECT(cx, obj);
1090 if (!obj)
1091 return JS_FALSE;
1093 if (!js_CheckPrincipalsAccess(cx, obj,
1094 JS_StackFramePrincipals(cx, caller),
1095 cx->runtime->atomState.evalAtom)) {
1096 return JS_FALSE;
1099 /* NB: We know inner is a global object here. */
1100 JS_ASSERT(!obj->getParent());
1101 scopeobj = obj;
1102 } else {
1104 * Compile using the caller's current scope object.
1106 * NB: This means that the C API must not be used to call eval.
1108 JS_ASSERT_IF(caller->argv, caller->hasCallObj());
1109 scopeobj = callerScopeChain;
1111 #endif
1113 /* Ensure we compile this eval with the right object in the scope chain. */
1114 JSObject *result = CheckScopeChainValidity(cx, scopeobj, js_eval_str);
1115 JS_ASSERT_IF(result, result == scopeobj);
1116 if (!result)
1117 return JS_FALSE;
1119 // CSP check: is eval() allowed at all?
1120 // report errors via CSP is done in the script security mgr.
1121 if (!js_CheckContentSecurityPolicy(cx)) {
1122 JS_ReportError(cx, "call to eval() blocked by CSP");
1123 return JS_FALSE;
1126 JSObject *callee = &vp[0].toObject();
1127 JSPrincipals *principals = js_EvalFramePrincipals(cx, callee, caller);
1128 uintN line;
1129 const char *file = js_ComputeFilename(cx, caller, principals, &line);
1131 JSString *str = argv[0].toString();
1132 JSScript *script = NULL;
1135 * Cache local eval scripts indexed by source qualified by scope.
1137 * An eval cache entry should never be considered a hit unless its
1138 * strictness matches that of the new eval code. The existing code takes
1139 * care of this, because hits are qualified by the function from which
1140 * eval was called, whose strictness doesn't change. Scripts produced by
1141 * calls to eval from global code are not cached.
1143 JSScript **bucket = EvalCacheHash(cx, str);
1144 if (!indirectCall && caller->fun) {
1145 uintN count = 0;
1146 JSScript **scriptp = bucket;
1148 EVAL_CACHE_METER(probe);
1149 while ((script = *scriptp) != NULL) {
1150 if (script->savedCallerFun &&
1151 script->staticLevel == staticLevel &&
1152 script->version == cx->version &&
1153 (script->principals == principals ||
1154 (principals->subsume(principals, script->principals) &&
1155 script->principals->subsume(script->principals, principals)))) {
1157 * Get the prior (cache-filling) eval's saved caller function.
1158 * See Compiler::compileScript in jsparse.cpp.
1160 JSFunction *fun = script->getFunction(0);
1162 if (fun == caller->fun) {
1164 * Get the source string passed for safekeeping in the
1165 * atom map by the prior eval to Compiler::compileScript.
1167 JSString *src = ATOM_TO_STRING(script->atomMap.vector[0]);
1169 if (src == str || js_EqualStrings(src, str)) {
1171 * Source matches, qualify by comparing scopeobj to the
1172 * COMPILE_N_GO-memoized parent of the first literal
1173 * function or regexp object if any. If none, then this
1174 * script has no compiled-in dependencies on the prior
1175 * eval's scopeobj.
1177 JSObjectArray *objarray = script->objects();
1178 int i = 1;
1180 if (objarray->length == 1) {
1181 if (script->regexpsOffset != 0) {
1182 objarray = script->regexps();
1183 i = 0;
1184 } else {
1185 EVAL_CACHE_METER(noscope);
1186 i = -1;
1189 if (i < 0 ||
1190 objarray->vector[i]->getParent() == scopeobj) {
1191 JS_ASSERT(staticLevel == script->staticLevel);
1192 EVAL_CACHE_METER(hit);
1193 *scriptp = script->u.nextToGC;
1194 script->u.nextToGC = NULL;
1195 break;
1201 if (++count == EVAL_CACHE_CHAIN_LIMIT) {
1202 script = NULL;
1203 break;
1205 EVAL_CACHE_METER(step);
1206 scriptp = &script->u.nextToGC;
1211 * We can't have a callerFrame (down in js_Execute's terms) if we're in
1212 * global code. This includes indirect eval and direct eval called with a
1213 * scope object parameter.
1215 JSStackFrame *callerFrame = (staticLevel != 0) ? caller : NULL;
1216 if (!script) {
1217 script = Compiler::compileScript(cx, scopeobj, callerFrame,
1218 principals,
1219 TCF_COMPILE_N_GO | TCF_NEED_MUTABLE_SCRIPT,
1220 str->chars(), str->length(),
1221 NULL, file, line, str, staticLevel);
1222 if (!script)
1223 return JS_FALSE;
1227 * Belt-and-braces: check that the lesser of eval's principals and the
1228 * caller's principals has access to scopeobj.
1230 JSBool ok = js_CheckPrincipalsAccess(cx, scopeobj, principals,
1231 cx->runtime->atomState.evalAtom) &&
1232 Execute(cx, scopeobj, script, callerFrame, JSFRAME_EVAL, vp);
1234 script->u.nextToGC = *bucket;
1235 *bucket = script;
1236 #ifdef CHECK_SCRIPT_OWNER
1237 script->owner = NULL;
1238 #endif
1240 return ok;
1243 #if JS_HAS_OBJ_WATCHPOINT
1245 static JSBool
1246 obj_watch_handler(JSContext *cx, JSObject *obj, jsid id, jsval old,
1247 jsval *nvp, void *closure)
1249 JSObject *callable;
1250 JSSecurityCallbacks *callbacks;
1251 JSStackFrame *caller;
1252 JSPrincipals *subject, *watcher;
1253 JSResolvingKey key;
1254 JSResolvingEntry *entry;
1255 uint32 generation;
1256 Value argv[3];
1257 JSBool ok;
1259 callable = (JSObject *) closure;
1261 callbacks = JS_GetSecurityCallbacks(cx);
1262 if (callbacks && callbacks->findObjectPrincipals) {
1263 /* Skip over any obj_watch_* frames between us and the real subject. */
1264 caller = js_GetScriptedCaller(cx, NULL);
1265 if (caller) {
1267 * Only call the watch handler if the watcher is allowed to watch
1268 * the currently executing script.
1270 watcher = callbacks->findObjectPrincipals(cx, callable);
1271 subject = JS_StackFramePrincipals(cx, caller);
1273 if (watcher && subject && !watcher->subsume(watcher, subject)) {
1274 /* Silently don't call the watch handler. */
1275 return JS_TRUE;
1280 /* Avoid recursion on (obj, id) already being watched on cx. */
1281 key.obj = obj;
1282 key.id = id;
1283 if (!js_StartResolving(cx, &key, JSRESFLAG_WATCH, &entry))
1284 return JS_FALSE;
1285 if (!entry)
1286 return JS_TRUE;
1287 generation = cx->resolvingTable->generation;
1289 argv[0] = IdToValue(id);
1290 argv[1] = Valueify(old);
1291 argv[2] = Valueify(*nvp);
1292 ok = InternalCall(cx, obj, ObjectOrNullValue(callable), 3, argv, Valueify(nvp));
1293 js_StopResolving(cx, &key, JSRESFLAG_WATCH, entry, generation);
1294 return ok;
1297 static JSBool
1298 obj_watch(JSContext *cx, uintN argc, Value *vp)
1300 if (argc <= 1) {
1301 js_ReportMissingArg(cx, *vp, 1);
1302 return JS_FALSE;
1305 JSObject *callable = js_ValueToCallableObject(cx, &vp[3], 0);
1306 if (!callable)
1307 return JS_FALSE;
1309 /* Compute the unique int/atom symbol id needed by js_LookupProperty. */
1310 jsid propid;
1311 if (!ValueToId(cx, vp[2], &propid))
1312 return JS_FALSE;
1314 JSObject *obj = ComputeThisFromVp(cx, vp);
1315 Value tmp;
1316 uintN attrs;
1317 if (!obj || !CheckAccess(cx, obj, propid, JSACC_WATCH, &tmp, &attrs))
1318 return JS_FALSE;
1320 vp->setUndefined();
1322 if (attrs & JSPROP_READONLY)
1323 return JS_TRUE;
1324 if (obj->isDenseArray() && !obj->makeDenseArraySlow(cx))
1325 return JS_FALSE;
1326 return JS_SetWatchPoint(cx, obj, propid, obj_watch_handler, callable);
1329 static JSBool
1330 obj_unwatch(JSContext *cx, uintN argc, Value *vp)
1332 JSObject *obj = ComputeThisFromVp(cx, vp);
1333 if (!obj)
1334 return JS_FALSE;
1335 vp->setUndefined();
1336 jsid id;
1337 if (argc != 0) {
1338 if (!ValueToId(cx, vp[2], &id))
1339 return JS_FALSE;
1340 } else {
1341 id = JSID_VOID;
1343 return JS_ClearWatchPoint(cx, obj, id, NULL, NULL);
1346 #endif /* JS_HAS_OBJ_WATCHPOINT */
1349 * Prototype and property query methods, to complement the 'in' and
1350 * 'instanceof' operators.
1353 /* Proposed ECMA 15.2.4.5. */
1354 static JSBool
1355 obj_hasOwnProperty(JSContext *cx, uintN argc, Value *vp)
1357 JSObject *obj = ComputeThisFromVp(cx, vp);
1358 return obj &&
1359 js_HasOwnPropertyHelper(cx, obj->getOps()->lookupProperty, argc, vp);
1362 JSBool
1363 js_HasOwnPropertyHelper(JSContext *cx, JSLookupPropOp lookup, uintN argc,
1364 Value *vp)
1366 jsid id;
1367 if (!ValueToId(cx, argc != 0 ? vp[2] : UndefinedValue(), &id))
1368 return JS_FALSE;
1370 JSObject *obj = ComputeThisFromVp(cx, vp);
1371 JSObject *obj2;
1372 JSProperty *prop;
1373 if (!obj)
1374 return false;
1375 if (obj->isProxy()) {
1376 bool has;
1377 if (!JSProxy::hasOwn(cx, obj, id, &has))
1378 return false;
1379 vp->setBoolean(has);
1380 return true;
1382 if (!js_HasOwnProperty(cx, lookup, obj, id, &obj2, &prop))
1383 return JS_FALSE;
1384 if (prop) {
1385 vp->setBoolean(true);
1386 obj2->dropProperty(cx, prop);
1387 } else {
1388 vp->setBoolean(false);
1390 return JS_TRUE;
1393 JSBool
1394 js_HasOwnProperty(JSContext *cx, JSLookupPropOp lookup, JSObject *obj, jsid id,
1395 JSObject **objp, JSProperty **propp)
1397 JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_DETECTING);
1398 if (!(lookup ? lookup : js_LookupProperty)(cx, obj, id, objp, propp))
1399 return false;
1400 if (!*propp)
1401 return true;
1403 if (*objp == obj)
1404 return true;
1406 Class *clasp = (*objp)->getClass();
1407 JSObject *outer = NULL;
1408 if (JSObjectOp op = (*objp)->getClass()->ext.outerObject) {
1409 outer = op(cx, *objp);
1410 if (!outer)
1411 return false;
1414 if (outer != *objp) {
1415 if ((*objp)->isNative() && obj->getClass() == clasp) {
1417 * The combination of JSPROP_SHARED and JSPROP_PERMANENT in a
1418 * delegated property makes that property appear to be direct in
1419 * all delegating instances of the same native class. This hack
1420 * avoids bloating every function instance with its own 'length'
1421 * (AKA 'arity') property. But it must not extend across class
1422 * boundaries, to avoid making hasOwnProperty lie (bug 320854).
1424 * It's not really a hack, of course: a permanent property can't
1425 * be deleted, and JSPROP_SHARED means "don't allocate a slot in
1426 * any instance, prototype or delegating". Without a slot, and
1427 * without the ability to remove and recreate (with differences)
1428 * the property, there is no way to tell whether it is directly
1429 * owned, or indirectly delegated.
1431 JSScopeProperty *sprop = reinterpret_cast<JSScopeProperty *>(*propp);
1432 if (sprop->isSharedPermanent())
1433 return true;
1436 (*objp)->dropProperty(cx, *propp);
1437 *propp = NULL;
1439 return true;
1442 /* Proposed ECMA 15.2.4.6. */
1443 static JSBool
1444 obj_isPrototypeOf(JSContext *cx, uintN argc, Value *vp)
1446 JSObject *obj = ComputeThisFromVp(cx, vp);
1447 if (!obj)
1448 return JS_FALSE;
1449 const Value &v = argc != 0 ? vp[2] : UndefinedValue();
1450 vp->setBoolean(js_IsDelegate(cx, obj, v));
1451 return JS_TRUE;
1454 /* Proposed ECMA 15.2.4.7. */
1455 static JSBool
1456 obj_propertyIsEnumerable(JSContext *cx, uintN argc, Value *vp)
1458 jsid id;
1459 if (!ValueToId(cx, argc != 0 ? vp[2] : UndefinedValue(), &id))
1460 return JS_FALSE;
1462 JSObject *obj = ComputeThisFromVp(cx, vp);
1463 return obj && js_PropertyIsEnumerable(cx, obj, id, vp);
1466 JSBool
1467 js_PropertyIsEnumerable(JSContext *cx, JSObject *obj, jsid id, Value *vp)
1469 JSObject *pobj;
1470 JSProperty *prop;
1471 if (!obj->lookupProperty(cx, id, &pobj, &prop))
1472 return JS_FALSE;
1474 if (!prop) {
1475 vp->setBoolean(false);
1476 return JS_TRUE;
1480 * XXX ECMA spec error compatible: return false unless hasOwnProperty.
1481 * The ECMA spec really should be fixed so propertyIsEnumerable and the
1482 * for..in loop agree on whether prototype properties are enumerable,
1483 * obviously by fixing this method (not by breaking the for..in loop!).
1485 * We check here for shared permanent prototype properties, which should
1486 * be treated as if they are local to obj. They are an implementation
1487 * technique used to satisfy ECMA requirements; users should not be able
1488 * to distinguish a shared permanent proto-property from a local one.
1490 bool shared;
1491 uintN attrs;
1492 if (pobj->isNative()) {
1493 JSScopeProperty *sprop = (JSScopeProperty *) prop;
1494 shared = sprop->isSharedPermanent();
1495 attrs = sprop->attributes();
1496 JS_UNLOCK_OBJ(cx, pobj);
1497 } else {
1498 shared = false;
1499 if (!pobj->getAttributes(cx, id, &attrs))
1500 return false;
1502 if (pobj != obj && !shared) {
1503 vp->setBoolean(false);
1504 return true;
1506 vp->setBoolean((attrs & JSPROP_ENUMERATE) != 0);
1507 return true;
1510 #if OLD_GETTER_SETTER_METHODS
1512 const char js_defineGetter_str[] = "__defineGetter__";
1513 const char js_defineSetter_str[] = "__defineSetter__";
1514 const char js_lookupGetter_str[] = "__lookupGetter__";
1515 const char js_lookupSetter_str[] = "__lookupSetter__";
1517 JS_FRIEND_API(JSBool)
1518 js_obj_defineGetter(JSContext *cx, uintN argc, Value *vp)
1520 if (argc <= 1 || !js_IsCallable(vp[3])) {
1521 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1522 JSMSG_BAD_GETTER_OR_SETTER,
1523 js_getter_str);
1524 return JS_FALSE;
1526 PropertyOp getter = CastAsPropertyOp(&vp[3].toObject());
1528 jsid id;
1529 if (!ValueToId(cx, vp[2], &id))
1530 return JS_FALSE;
1531 JSObject *obj = ComputeThisFromVp(cx, vp);
1532 if (!obj || !CheckRedeclaration(cx, obj, id, JSPROP_GETTER, NULL, NULL))
1533 return JS_FALSE;
1535 * Getters and setters are just like watchpoints from an access
1536 * control point of view.
1538 Value junk;
1539 uintN attrs;
1540 if (!CheckAccess(cx, obj, id, JSACC_WATCH, &junk, &attrs))
1541 return JS_FALSE;
1542 vp->setUndefined();
1543 return obj->defineProperty(cx, id, UndefinedValue(), getter, PropertyStub,
1544 JSPROP_ENUMERATE | JSPROP_GETTER | JSPROP_SHARED);
1547 JS_FRIEND_API(JSBool)
1548 js_obj_defineSetter(JSContext *cx, uintN argc, Value *vp)
1550 if (argc <= 1 || !js_IsCallable(vp[3])) {
1551 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1552 JSMSG_BAD_GETTER_OR_SETTER,
1553 js_setter_str);
1554 return JS_FALSE;
1556 PropertyOp setter = CastAsPropertyOp(&vp[3].toObject());
1558 jsid id;
1559 if (!ValueToId(cx, vp[2], &id))
1560 return JS_FALSE;
1561 JSObject *obj = ComputeThisFromVp(cx, vp);
1562 if (!obj || !CheckRedeclaration(cx, obj, id, JSPROP_SETTER, NULL, NULL))
1563 return JS_FALSE;
1565 * Getters and setters are just like watchpoints from an access
1566 * control point of view.
1568 Value junk;
1569 uintN attrs;
1570 if (!CheckAccess(cx, obj, id, JSACC_WATCH, &junk, &attrs))
1571 return JS_FALSE;
1572 vp->setUndefined();
1573 return obj->defineProperty(cx, id, UndefinedValue(), PropertyStub, setter,
1574 JSPROP_ENUMERATE | JSPROP_SETTER | JSPROP_SHARED);
1577 static JSBool
1578 obj_lookupGetter(JSContext *cx, uintN argc, Value *vp)
1580 jsid id;
1581 if (!ValueToId(cx, argc != 0 ? vp[2] : UndefinedValue(), &id))
1582 return JS_FALSE;
1583 JSObject *obj = ComputeThisFromVp(cx, vp);
1584 JSObject *pobj;
1585 JSProperty *prop;
1586 if (!obj || !obj->lookupProperty(cx, id, &pobj, &prop))
1587 return JS_FALSE;
1588 vp->setUndefined();
1589 if (prop) {
1590 if (pobj->isNative()) {
1591 JSScopeProperty *sprop = (JSScopeProperty *) prop;
1592 if (sprop->hasGetterValue())
1593 *vp = sprop->getterValue();
1594 JS_UNLOCK_OBJ(cx, pobj);
1597 return JS_TRUE;
1600 static JSBool
1601 obj_lookupSetter(JSContext *cx, uintN argc, Value *vp)
1603 jsid id;
1604 if (!ValueToId(cx, argc != 0 ? vp[2] : UndefinedValue(), &id))
1605 return JS_FALSE;
1606 JSObject *obj = ComputeThisFromVp(cx, vp);
1607 JSObject *pobj;
1608 JSProperty *prop;
1609 if (!obj || !obj->lookupProperty(cx, id, &pobj, &prop))
1610 return JS_FALSE;
1611 vp->setUndefined();
1612 if (prop) {
1613 if (pobj->isNative()) {
1614 JSScopeProperty *sprop = (JSScopeProperty *) prop;
1615 if (sprop->hasSetterValue())
1616 *vp = sprop->setterValue();
1617 JS_UNLOCK_OBJ(cx, pobj);
1620 return JS_TRUE;
1622 #endif /* OLD_GETTER_SETTER_METHODS */
1624 JSBool
1625 obj_getPrototypeOf(JSContext *cx, uintN argc, Value *vp)
1627 if (argc == 0) {
1628 js_ReportMissingArg(cx, *vp, 0);
1629 return JS_FALSE;
1632 if (vp[2].isPrimitive()) {
1633 char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, vp[2], NULL);
1634 if (!bytes)
1635 return JS_FALSE;
1636 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1637 JSMSG_UNEXPECTED_TYPE, bytes, "not an object");
1638 JS_free(cx, bytes);
1639 return JS_FALSE;
1642 JSObject *obj = &vp[2].toObject();
1643 uintN attrs;
1644 return CheckAccess(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.protoAtom),
1645 JSACC_PROTO, vp, &attrs);
1648 extern JSBool
1649 js_NewPropertyDescriptorObject(JSContext *cx, jsid id, uintN attrs,
1650 const Value &getter, const Value &setter,
1651 const Value &value, Value *vp)
1653 /* We have our own property, so start creating the descriptor. */
1654 JSObject *desc = NewBuiltinClassInstance(cx, &js_ObjectClass);
1655 if (!desc)
1656 return false;
1657 vp->setObject(*desc); /* Root and return. */
1659 const JSAtomState &atomState = cx->runtime->atomState;
1660 if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
1661 if (!desc->defineProperty(cx, ATOM_TO_JSID(atomState.getAtom), getter,
1662 PropertyStub, PropertyStub, JSPROP_ENUMERATE) ||
1663 !desc->defineProperty(cx, ATOM_TO_JSID(atomState.setAtom), setter,
1664 PropertyStub, PropertyStub, JSPROP_ENUMERATE)) {
1665 return false;
1667 } else {
1668 if (!desc->defineProperty(cx, ATOM_TO_JSID(atomState.valueAtom), value,
1669 PropertyStub, PropertyStub, JSPROP_ENUMERATE) ||
1670 !desc->defineProperty(cx, ATOM_TO_JSID(atomState.writableAtom),
1671 BooleanValue((attrs & JSPROP_READONLY) == 0),
1672 PropertyStub, PropertyStub, JSPROP_ENUMERATE)) {
1673 return false;
1677 return desc->defineProperty(cx, ATOM_TO_JSID(atomState.enumerableAtom),
1678 BooleanValue((attrs & JSPROP_ENUMERATE) != 0),
1679 PropertyStub, PropertyStub, JSPROP_ENUMERATE) &&
1680 desc->defineProperty(cx, ATOM_TO_JSID(atomState.configurableAtom),
1681 BooleanValue((attrs & JSPROP_PERMANENT) == 0),
1682 PropertyStub, PropertyStub, JSPROP_ENUMERATE);
1685 JSBool
1686 js_GetOwnPropertyDescriptor(JSContext *cx, JSObject *obj, jsid id, Value *vp)
1688 if (obj->isProxy()) {
1689 if (!JSProxy::getOwnPropertyDescriptor(cx, obj, id, vp))
1690 return false;
1693 JSObject *pobj;
1694 JSProperty *prop;
1695 if (!js_HasOwnProperty(cx, obj->getOps()->lookupProperty, obj, id, &pobj, &prop))
1696 return false;
1697 if (!prop) {
1698 vp->setUndefined();
1699 return true;
1702 Value roots[] = { UndefinedValue(), UndefinedValue(), UndefinedValue() };
1703 AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(roots), roots);
1704 unsigned attrs;
1705 bool doGet = true;
1706 if (pobj->isNative()) {
1707 JSScopeProperty *sprop = (JSScopeProperty *) prop;
1708 attrs = sprop->attributes();
1709 if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
1710 doGet = false;
1711 if (attrs & JSPROP_GETTER)
1712 roots[0] = sprop->getterValue();
1713 if (attrs & JSPROP_SETTER)
1714 roots[1] = sprop->setterValue();
1716 JS_UNLOCK_OBJ(cx, pobj);
1717 } else if (!pobj->getAttributes(cx, id, &attrs)) {
1718 return false;
1721 if (doGet && !obj->getProperty(cx, id, &roots[2]))
1722 return false;
1724 return js_NewPropertyDescriptorObject(cx, id,
1725 attrs,
1726 roots[0], /* getter */
1727 roots[1], /* setter */
1728 roots[2], /* value */
1729 vp);
1732 static bool
1733 GetFirstArgumentAsObject(JSContext *cx, uintN argc, Value *vp, const char *method, JSObject **objp)
1735 if (argc == 0) {
1736 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
1737 method, "0", "s");
1738 return false;
1741 const Value &v = vp[2];
1742 if (v.isPrimitive()) {
1743 char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NULL);
1744 if (!bytes)
1745 return false;
1746 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_UNEXPECTED_TYPE,
1747 bytes, "not an object");
1748 JS_free(cx, bytes);
1749 return false;
1752 *objp = &v.toObject();
1753 return true;
1756 static JSBool
1757 obj_getOwnPropertyDescriptor(JSContext *cx, uintN argc, Value *vp)
1759 JSObject *obj;
1760 if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.getOwnPropertyDescriptor", &obj))
1761 return JS_FALSE;
1762 AutoIdRooter nameidr(cx);
1763 if (!ValueToId(cx, argc >= 2 ? vp[3] : UndefinedValue(), nameidr.addr()))
1764 return JS_FALSE;
1765 return js_GetOwnPropertyDescriptor(cx, obj, nameidr.id(), vp);
1768 static JSBool
1769 obj_keys(JSContext *cx, uintN argc, Value *vp)
1771 JSObject *obj;
1772 if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.keys", &obj))
1773 return JS_FALSE;
1775 AutoIdVector props(cx);
1776 if (!GetPropertyNames(cx, obj, JSITER_OWNONLY, props))
1777 return JS_FALSE;
1779 AutoValueVector vals(cx);
1780 vals.resize(props.length());
1781 for (size_t i = 0, len = props.length(); i < len; i++) {
1782 jsid id = props[i];
1783 if (JSID_IS_STRING(id)) {
1784 vals[i].setString(JSID_TO_STRING(id));
1785 } else {
1786 JS_ASSERT(JSID_IS_INT(id));
1787 JSString *str = js_IntToString(cx, JSID_TO_INT(id));
1788 if (!str)
1789 return JS_FALSE;
1790 vals[i].setString(str);
1794 JS_ASSERT(props.length() <= UINT32_MAX);
1795 JSObject *aobj = js_NewArrayObject(cx, jsuint(vals.length()), vals.begin());
1796 if (!aobj)
1797 return JS_FALSE;
1798 vp->setObject(*aobj);
1800 return JS_TRUE;
1803 static JSBool
1804 HasProperty(JSContext* cx, JSObject* obj, jsid id, Value* vp, JSBool* answerp)
1806 if (!JS_HasPropertyById(cx, obj, id, answerp))
1807 return JS_FALSE;
1808 if (!*answerp) {
1809 vp->setUndefined();
1810 return JS_TRUE;
1812 return JS_GetPropertyById(cx, obj, id, Jsvalify(vp));
1815 PropDesc::PropDesc()
1816 : pd(UndefinedValue()),
1817 id(INT_TO_JSID(0)),
1818 value(UndefinedValue()),
1819 get(UndefinedValue()),
1820 set(UndefinedValue()),
1821 attrs(0),
1822 hasGet(false),
1823 hasSet(false),
1824 hasValue(false),
1825 hasWritable(false),
1826 hasEnumerable(false),
1827 hasConfigurable(false)
1831 bool
1832 PropDesc::initialize(JSContext* cx, jsid id, const Value &origval)
1834 Value v = origval;
1835 this->id = id;
1837 /* 8.10.5 step 1 */
1838 if (v.isPrimitive()) {
1839 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_NONNULL_OBJECT);
1840 return false;
1842 JSObject* desc = &v.toObject();
1844 /* Make a copy of the descriptor. We might need it later. */
1845 pd = v;
1847 /* Start with the proper defaults. */
1848 attrs = JSPROP_PERMANENT | JSPROP_READONLY;
1850 JSBool hasProperty;
1852 /* 8.10.5 step 3 */
1853 if (!HasProperty(cx, desc, ATOM_TO_JSID(cx->runtime->atomState.enumerableAtom), &v,
1854 &hasProperty)) {
1855 return false;
1857 if (hasProperty) {
1858 hasEnumerable = JS_TRUE;
1859 if (js_ValueToBoolean(v))
1860 attrs |= JSPROP_ENUMERATE;
1863 /* 8.10.5 step 4 */
1864 if (!HasProperty(cx, desc, ATOM_TO_JSID(cx->runtime->atomState.configurableAtom), &v,
1865 &hasProperty)) {
1866 return false;
1868 if (hasProperty) {
1869 hasConfigurable = JS_TRUE;
1870 if (js_ValueToBoolean(v))
1871 attrs &= ~JSPROP_PERMANENT;
1874 /* 8.10.5 step 5 */
1875 if (!HasProperty(cx, desc, ATOM_TO_JSID(cx->runtime->atomState.valueAtom), &v, &hasProperty))
1876 return false;
1877 if (hasProperty) {
1878 hasValue = true;
1879 value = v;
1882 /* 8.10.6 step 6 */
1883 if (!HasProperty(cx, desc, ATOM_TO_JSID(cx->runtime->atomState.writableAtom), &v, &hasProperty))
1884 return false;
1885 if (hasProperty) {
1886 hasWritable = JS_TRUE;
1887 if (js_ValueToBoolean(v))
1888 attrs &= ~JSPROP_READONLY;
1891 /* 8.10.7 step 7 */
1892 if (!HasProperty(cx, desc, ATOM_TO_JSID(cx->runtime->atomState.getAtom), &v, &hasProperty))
1893 return false;
1894 if (hasProperty) {
1895 if ((v.isPrimitive() || !js_IsCallable(v)) && !v.isUndefined()) {
1896 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_GET_SET_FIELD,
1897 js_getter_str);
1898 return false;
1900 hasGet = true;
1901 get = v;
1902 attrs |= JSPROP_GETTER | JSPROP_SHARED;
1905 /* 8.10.7 step 8 */
1906 if (!HasProperty(cx, desc, ATOM_TO_JSID(cx->runtime->atomState.setAtom), &v, &hasProperty))
1907 return false;
1908 if (hasProperty) {
1909 if ((v.isPrimitive() || !js_IsCallable(v)) && !v.isUndefined()) {
1910 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_GET_SET_FIELD,
1911 js_setter_str);
1912 return false;
1914 hasSet = true;
1915 set = v;
1916 attrs |= JSPROP_SETTER | JSPROP_SHARED;
1919 /* 8.10.7 step 9 */
1920 if ((hasGet || hasSet) && (hasValue || hasWritable)) {
1921 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INVALID_DESCRIPTOR);
1922 return false;
1925 return true;
1928 static JSBool
1929 Reject(JSContext *cx, uintN errorNumber, bool throwError, jsid id, bool *rval)
1931 if (throwError) {
1932 jsid idstr;
1933 if (!js_ValueToStringId(cx, IdToValue(id), &idstr))
1934 return JS_FALSE;
1935 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, errorNumber,
1936 JS_GetStringBytes(JSID_TO_STRING(idstr)));
1937 return JS_FALSE;
1940 *rval = false;
1941 return JS_TRUE;
1944 static JSBool
1945 Reject(JSContext *cx, uintN errorNumber, bool throwError, bool *rval)
1947 if (throwError) {
1948 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, errorNumber);
1949 return JS_FALSE;
1952 *rval = false;
1953 return JS_TRUE;
1956 static JSBool
1957 Reject(JSContext *cx, JSObject *obj, JSProperty *prop, uintN errorNumber, bool throwError,
1958 jsid id, bool *rval)
1960 obj->dropProperty(cx, prop);
1961 return Reject(cx, errorNumber, throwError, id, rval);
1964 static JSBool
1965 DefinePropertyOnObject(JSContext *cx, JSObject *obj, const PropDesc &desc,
1966 bool throwError, bool *rval)
1968 /* 8.12.9 step 1. */
1969 JSProperty *current;
1970 JSObject *obj2;
1971 JS_ASSERT(!obj->getOps()->lookupProperty);
1972 if (!js_HasOwnProperty(cx, NULL, obj, desc.id, &obj2, &current))
1973 return JS_FALSE;
1975 JS_ASSERT(!obj->getOps()->defineProperty);
1977 /* 8.12.9 steps 2-4. */
1978 JSScope *scope = obj->scope();
1979 if (!current) {
1980 if (scope->sealed())
1981 return Reject(cx, JSMSG_OBJECT_NOT_EXTENSIBLE, throwError, rval);
1983 *rval = true;
1985 if (desc.isGenericDescriptor() || desc.isDataDescriptor()) {
1986 JS_ASSERT(!obj->getOps()->defineProperty);
1987 return js_DefineProperty(cx, obj, desc.id, &desc.value,
1988 PropertyStub, PropertyStub, desc.attrs);
1991 JS_ASSERT(desc.isAccessorDescriptor());
1994 * Getters and setters are just like watchpoints from an access
1995 * control point of view.
1997 Value dummy;
1998 uintN dummyAttrs;
1999 if (!CheckAccess(cx, obj, desc.id, JSACC_WATCH, &dummy, &dummyAttrs))
2000 return JS_FALSE;
2002 Value tmp = UndefinedValue();
2003 return js_DefineProperty(cx, obj, desc.id, &tmp,
2004 desc.getter(), desc.setter(), desc.attrs);
2007 /* 8.12.9 steps 5-6 (note 5 is merely a special case of 6). */
2008 Value v = UndefinedValue();
2011 * In the special case of shared permanent properties, the "own" property
2012 * can be found on a different object. In that case the returned property
2013 * might not be native, except: the shared permanent property optimization
2014 * is not applied if the objects have different classes (bug 320854), as
2015 * must be enforced by js_HasOwnProperty for the JSScopeProperty cast below
2016 * to be safe.
2018 JS_ASSERT(obj->getClass() == obj2->getClass());
2020 JSScopeProperty *sprop = reinterpret_cast<JSScopeProperty *>(current);
2021 do {
2022 if (desc.isAccessorDescriptor()) {
2023 if (!sprop->isAccessorDescriptor())
2024 break;
2026 if (desc.hasGet &&
2027 !SameValue(desc.getterValue(), sprop->getterOrUndefined(), cx)) {
2028 break;
2031 if (desc.hasSet &&
2032 !SameValue(desc.setterValue(), sprop->setterOrUndefined(), cx)) {
2033 break;
2035 } else {
2037 * Determine the current value of the property once, if the current
2038 * value might actually need to be used or preserved later. NB: we
2039 * guard on whether the current property is a data descriptor to
2040 * avoid calling a getter; we won't need the value if it's not a
2041 * data descriptor.
2043 if (sprop->isDataDescriptor()) {
2045 * Non-standard: if the property is non-configurable and is
2046 * represented by a native getter or setter, don't permit
2047 * redefinition. We expose properties with native getter/setter
2048 * as though they were data properties, for the most part, but
2049 * in this particular case we must worry about integrity
2050 * concerns for JSAPI users who expected that
2051 * permanent+getter/setter means precisely controlled behavior.
2052 * If we permitted such redefinitions, such a property could be
2053 * "fixed" to some specific previous value, no longer varying
2054 * according to the intent of the native getter/setter for the
2055 * property.
2057 * Other engines expose properties of this nature using ECMA
2058 * getter/setter pairs, but we can't because we use them even
2059 * for properties which ECMA specifies as being true data
2060 * descriptors ([].length, Function.length, /regex/.lastIndex,
2061 * &c.). Longer-term perhaps we should convert such properties
2062 * to use data descriptors (at which point representing a
2063 * descriptor with native getter/setter as an accessor
2064 * descriptor would be fine) and take a small memory hit, but
2065 * for now we'll simply forbid their redefinition.
2067 if (!sprop->configurable() &&
2068 (!sprop->hasDefaultGetter() || !sprop->hasDefaultSetter())) {
2069 return Reject(cx, obj2, current, JSMSG_CANT_REDEFINE_UNCONFIGURABLE_PROP,
2070 throwError, desc.id, rval);
2073 if (!js_NativeGet(cx, obj, obj2, sprop, JSGET_NO_METHOD_BARRIER, &v)) {
2074 /* current was dropped when the failure occurred. */
2075 return JS_FALSE;
2079 if (desc.isDataDescriptor()) {
2080 if (!sprop->isDataDescriptor())
2081 break;
2083 if (desc.hasValue && !SameValue(desc.value, v, cx))
2084 break;
2085 if (desc.hasWritable && desc.writable() != sprop->writable())
2086 break;
2087 } else {
2088 /* The only fields in desc will be handled below. */
2089 JS_ASSERT(desc.isGenericDescriptor());
2093 if (desc.hasConfigurable && desc.configurable() != sprop->configurable())
2094 break;
2095 if (desc.hasEnumerable && desc.enumerable() != sprop->enumerable())
2096 break;
2098 /* The conditions imposed by step 5 or step 6 apply. */
2099 obj2->dropProperty(cx, current);
2100 *rval = true;
2101 return JS_TRUE;
2102 } while (0);
2104 /* 8.12.9 step 7. */
2105 if (!sprop->configurable()) {
2107 * Since [[Configurable]] defaults to false, we don't need to check
2108 * whether it was specified. We can't do likewise for [[Enumerable]]
2109 * because its putative value is used in a comparison -- a comparison
2110 * whose result must always be false per spec if the [[Enumerable]]
2111 * field is not present. Perfectly pellucid logic, eh?
2113 JS_ASSERT_IF(!desc.hasConfigurable, !desc.configurable());
2114 if (desc.configurable() ||
2115 (desc.hasEnumerable && desc.enumerable() != sprop->enumerable())) {
2116 return Reject(cx, obj2, current, JSMSG_CANT_REDEFINE_UNCONFIGURABLE_PROP, throwError,
2117 desc.id, rval);
2121 if (desc.isGenericDescriptor()) {
2122 /* 8.12.9 step 8, no validation required */
2123 } else if (desc.isDataDescriptor() != sprop->isDataDescriptor()) {
2124 /* 8.12.9 step 9. */
2125 if (!sprop->configurable()) {
2126 return Reject(cx, obj2, current, JSMSG_CANT_REDEFINE_UNCONFIGURABLE_PROP,
2127 throwError, desc.id, rval);
2129 } else if (desc.isDataDescriptor()) {
2130 /* 8.12.9 step 10. */
2131 JS_ASSERT(sprop->isDataDescriptor());
2132 if (!sprop->configurable() && !sprop->writable()) {
2133 if ((desc.hasWritable && desc.writable()) ||
2134 (desc.hasValue && !SameValue(desc.value, v, cx))) {
2135 return Reject(cx, obj2, current, JSMSG_CANT_REDEFINE_UNCONFIGURABLE_PROP,
2136 throwError, desc.id, rval);
2139 } else {
2140 /* 8.12.9 step 11. */
2141 JS_ASSERT(desc.isAccessorDescriptor() && sprop->isAccessorDescriptor());
2142 if (!sprop->configurable()) {
2143 if ((desc.hasSet &&
2144 !SameValue(desc.setterValue(), sprop->setterOrUndefined(), cx)) ||
2145 (desc.hasGet &&
2146 !SameValue(desc.getterValue(), sprop->getterOrUndefined(), cx))) {
2147 return Reject(cx, obj2, current, JSMSG_CANT_REDEFINE_UNCONFIGURABLE_PROP,
2148 throwError, desc.id, rval);
2153 /* 8.12.9 step 12. */
2154 uintN attrs;
2155 PropertyOp getter, setter;
2156 if (desc.isGenericDescriptor()) {
2157 uintN changed = 0;
2158 if (desc.hasConfigurable)
2159 changed |= JSPROP_PERMANENT;
2160 if (desc.hasEnumerable)
2161 changed |= JSPROP_ENUMERATE;
2163 attrs = (sprop->attributes() & ~changed) | (desc.attrs & changed);
2164 if (sprop->isMethod()) {
2165 JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
2166 getter = setter = PropertyStub;
2167 } else {
2168 getter = sprop->getter();
2169 setter = sprop->setter();
2171 } else if (desc.isDataDescriptor()) {
2172 uintN unchanged = 0;
2173 if (!desc.hasConfigurable)
2174 unchanged |= JSPROP_PERMANENT;
2175 if (!desc.hasEnumerable)
2176 unchanged |= JSPROP_ENUMERATE;
2177 if (!desc.hasWritable)
2178 unchanged |= JSPROP_READONLY;
2180 if (desc.hasValue)
2181 v = desc.value;
2182 attrs = (desc.attrs & ~unchanged) | (sprop->attributes() & unchanged);
2183 getter = setter = PropertyStub;
2184 } else {
2185 JS_ASSERT(desc.isAccessorDescriptor());
2188 * Getters and setters are just like watchpoints from an access
2189 * control point of view.
2191 Value dummy;
2192 if (!CheckAccess(cx, obj2, desc.id, JSACC_WATCH, &dummy, &attrs)) {
2193 obj2->dropProperty(cx, current);
2194 return JS_FALSE;
2197 JS_ASSERT_IF(sprop->isMethod(), !(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
2199 /* 8.12.9 step 12. */
2200 uintN changed = 0;
2201 if (desc.hasConfigurable)
2202 changed |= JSPROP_PERMANENT;
2203 if (desc.hasEnumerable)
2204 changed |= JSPROP_ENUMERATE;
2205 if (desc.hasGet)
2206 changed |= JSPROP_GETTER | JSPROP_SHARED;
2207 if (desc.hasSet)
2208 changed |= JSPROP_SETTER | JSPROP_SHARED;
2210 attrs = (desc.attrs & changed) | (sprop->attributes() & ~changed);
2211 if (desc.hasGet) {
2212 getter = desc.getter();
2213 } else {
2214 getter = (sprop->isMethod() || (sprop->hasDefaultGetter() && !sprop->hasGetterValue()))
2215 ? PropertyStub
2216 : sprop->getter();
2218 if (desc.hasSet) {
2219 setter = desc.setter();
2220 } else {
2221 setter = (sprop->hasDefaultSetter() && !sprop->hasSetterValue())
2222 ? PropertyStub
2223 : sprop->setter();
2227 *rval = true;
2228 obj2->dropProperty(cx, current);
2229 return js_DefineProperty(cx, obj, desc.id, &v, getter, setter, attrs);
2232 static JSBool
2233 DefinePropertyOnArray(JSContext *cx, JSObject *obj, const PropDesc &desc,
2234 bool throwError, bool *rval)
2237 * We probably should optimize dense array property definitions where
2238 * the descriptor describes a traditional array property (enumerable,
2239 * configurable, writable, numeric index or length without altering its
2240 * attributes). Such definitions are probably unlikely, so we don't bother
2241 * for now.
2243 if (obj->isDenseArray() && !obj->makeDenseArraySlow(cx))
2244 return JS_FALSE;
2246 jsuint oldLen = obj->getArrayLength();
2248 if (JSID_IS_ATOM(desc.id, cx->runtime->atomState.lengthAtom)) {
2250 * Our optimization of storage of the length property of arrays makes
2251 * it very difficult to properly implement defining the property. For
2252 * now simply throw an exception (NB: not merely Reject) on any attempt
2253 * to define the "length" property, rather than attempting to implement
2254 * some difficult-for-authors-to-grasp subset of that functionality.
2256 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_DEFINE_ARRAY_LENGTH_UNSUPPORTED);
2257 return JS_FALSE;
2260 uint32 index;
2261 if (js_IdIsIndex(desc.id, &index)) {
2263 // Disabled until we support defining "length":
2264 if (index >= oldLen && lengthPropertyNotWritable())
2265 return ThrowTypeError(cx, JSMSG_CANT_APPEND_PROPERTIES_TO_UNWRITABLE_LENGTH_ARRAY);
2267 if (!DefinePropertyOnObject(cx, obj, desc, false, rval))
2268 return JS_FALSE;
2269 if (!*rval)
2270 return Reject(cx, JSMSG_CANT_DEFINE_ARRAY_INDEX, throwError, rval);
2272 if (index >= oldLen) {
2273 JS_ASSERT(index != UINT32_MAX);
2274 obj->setArrayLength(index + 1);
2277 *rval = true;
2278 return JS_TRUE;
2281 return DefinePropertyOnObject(cx, obj, desc, throwError, rval);
2284 static JSBool
2285 DefineProperty(JSContext *cx, JSObject *obj, const PropDesc &desc, bool throwError,
2286 bool *rval)
2288 if (obj->isArray())
2289 return DefinePropertyOnArray(cx, obj, desc, throwError, rval);
2291 if (obj->getOps()->lookupProperty) {
2292 if (obj->isProxy())
2293 return JSProxy::defineProperty(cx, obj, desc.id, desc.pd);
2294 return Reject(cx, JSMSG_OBJECT_NOT_EXTENSIBLE, throwError, rval);
2297 return DefinePropertyOnObject(cx, obj, desc, throwError, rval);
2300 JSBool
2301 js_DefineOwnProperty(JSContext *cx, JSObject *obj, jsid id,
2302 const Value &descriptor, JSBool *bp)
2304 AutoPropDescArrayRooter descs(cx);
2305 PropDesc *desc = descs.append();
2306 if (!desc || !desc->initialize(cx, id, descriptor))
2307 return false;
2309 bool rval;
2310 if (!DefineProperty(cx, obj, *desc, true, &rval))
2311 return false;
2312 *bp = !!rval;
2313 return true;
2316 /* ES5 15.2.3.6: Object.defineProperty(O, P, Attributes) */
2317 static JSBool
2318 obj_defineProperty(JSContext* cx, uintN argc, Value* vp)
2320 /* 15.2.3.6 steps 1 and 5. */
2321 JSObject *obj;
2322 if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.defineProperty", &obj))
2323 return JS_FALSE;
2324 vp->setObject(*obj);
2326 /* 15.2.3.6 step 2. */
2327 AutoIdRooter nameidr(cx);
2328 if (!ValueToId(cx, argc >= 2 ? vp[3] : UndefinedValue(), nameidr.addr()))
2329 return JS_FALSE;
2331 /* 15.2.3.6 step 3. */
2332 const Value &descval = argc >= 3 ? vp[4] : UndefinedValue();
2334 /* 15.2.3.6 step 4 */
2335 JSBool junk;
2336 return js_DefineOwnProperty(cx, obj, nameidr.id(), descval, &junk);
2339 static bool
2340 DefineProperties(JSContext *cx, JSObject *obj, JSObject *props)
2342 AutoIdArray ida(cx, JS_Enumerate(cx, props));
2343 if (!ida)
2344 return false;
2346 AutoPropDescArrayRooter descs(cx);
2347 size_t len = ida.length();
2348 for (size_t i = 0; i < len; i++) {
2349 jsid id = ida[i];
2350 PropDesc* desc = descs.append();
2351 AutoValueRooter tvr(cx);
2352 if (!desc ||
2353 !JS_GetPropertyById(cx, props, id, tvr.jsval_addr()) ||
2354 !desc->initialize(cx, id, tvr.value())) {
2355 return false;
2359 bool dummy;
2360 for (size_t i = 0; i < len; i++) {
2361 if (!DefineProperty(cx, obj, descs[i], true, &dummy))
2362 return false;
2365 return true;
2368 extern JSBool
2369 js_PopulateObject(JSContext *cx, JSObject *newborn, JSObject *props)
2371 return DefineProperties(cx, newborn, props);
2374 /* ES5 15.2.3.7: Object.defineProperties(O, Properties) */
2375 static JSBool
2376 obj_defineProperties(JSContext* cx, uintN argc, Value* vp)
2378 /* 15.2.3.6 steps 1 and 5. */
2379 JSObject *obj;
2380 if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.defineProperties", &obj))
2381 return false;
2382 vp->setObject(*obj);
2384 if (argc < 2) {
2385 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
2386 "Object.defineProperties", "0", "s");
2387 return false;
2390 JSObject* props = js_ValueToNonNullObject(cx, vp[3]);
2391 if (!props)
2392 return false;
2393 vp[3].setObject(*props);
2395 return DefineProperties(cx, obj, props);
2398 /* ES5 15.2.3.5: Object.create(O [, Properties]) */
2399 static JSBool
2400 obj_create(JSContext *cx, uintN argc, Value *vp)
2402 if (argc == 0) {
2403 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
2404 "Object.create", "0", "s");
2405 return JS_FALSE;
2408 const Value &v = vp[2];
2409 if (!v.isObjectOrNull()) {
2410 char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NULL);
2411 if (!bytes)
2412 return JS_FALSE;
2413 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_UNEXPECTED_TYPE,
2414 bytes, "not an object or null");
2415 JS_free(cx, bytes);
2416 return JS_FALSE;
2420 * Use the callee's global as the parent of the new object to avoid dynamic
2421 * scoping (i.e., using the caller's global).
2423 JSObject *obj = NewNonFunction<WithProto::Given>(cx, &js_ObjectClass, v.toObjectOrNull(),
2424 vp->toObject().getGlobal());
2425 if (!obj)
2426 return JS_FALSE;
2427 vp->setObject(*obj); /* Root and prepare for eventual return. */
2429 /* 15.2.3.5 step 4. */
2430 if (argc > 1 && !vp[3].isUndefined()) {
2431 if (vp[3].isPrimitive()) {
2432 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_NONNULL_OBJECT);
2433 return JS_FALSE;
2436 JSObject *props = &vp[3].toObject();
2437 AutoIdArray ida(cx, JS_Enumerate(cx, props));
2438 if (!ida)
2439 return JS_FALSE;
2441 AutoPropDescArrayRooter descs(cx);
2442 size_t len = ida.length();
2443 for (size_t i = 0; i < len; i++) {
2444 jsid id = ida[i];
2445 PropDesc *desc = descs.append();
2446 if (!desc || !JS_GetPropertyById(cx, props, id, Jsvalify(&vp[1])) ||
2447 !desc->initialize(cx, id, vp[1])) {
2448 return JS_FALSE;
2452 bool dummy;
2453 for (size_t i = 0; i < len; i++) {
2454 if (!DefineProperty(cx, obj, descs[i], true, &dummy))
2455 return JS_FALSE;
2459 /* 5. Return obj. */
2460 return JS_TRUE;
2463 static JSBool
2464 obj_getOwnPropertyNames(JSContext *cx, uintN argc, Value *vp)
2466 JSObject *obj;
2467 if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.getOwnPropertyNames", &obj))
2468 return false;
2470 AutoIdVector keys(cx);
2471 if (!GetPropertyNames(cx, obj, JSITER_OWNONLY | JSITER_HIDDEN, keys))
2472 return false;
2474 AutoValueVector vals(cx);
2475 if (!vals.resize(keys.length()))
2476 return false;
2478 for (size_t i = 0, len = keys.length(); i < len; i++) {
2479 jsid id = keys[i];
2480 if (JSID_IS_INT(id)) {
2481 JSString *str = js_ValueToString(cx, Int32Value(JSID_TO_INT(id)));
2482 if (!str)
2483 return false;
2484 vals[i].setString(str);
2485 } else if (JSID_IS_ATOM(id)) {
2486 vals[i].setString(JSID_TO_STRING(id));
2487 } else {
2488 vals[i].setObject(*JSID_TO_OBJECT(id));
2492 JSObject *aobj = js_NewArrayObject(cx, vals.length(), vals.begin());
2493 if (!aobj)
2494 return false;
2496 vp->setObject(*aobj);
2497 return true;
2501 #if JS_HAS_OBJ_WATCHPOINT
2502 const char js_watch_str[] = "watch";
2503 const char js_unwatch_str[] = "unwatch";
2504 #endif
2505 const char js_hasOwnProperty_str[] = "hasOwnProperty";
2506 const char js_isPrototypeOf_str[] = "isPrototypeOf";
2507 const char js_propertyIsEnumerable_str[] = "propertyIsEnumerable";
2509 static JSFunctionSpec object_methods[] = {
2510 #if JS_HAS_TOSOURCE
2511 JS_FN(js_toSource_str, obj_toSource, 0,0),
2512 #endif
2513 JS_FN(js_toString_str, obj_toString, 0,0),
2514 JS_FN(js_toLocaleString_str, obj_toLocaleString, 0,0),
2515 JS_FN(js_valueOf_str, obj_valueOf, 0,0),
2516 #if JS_HAS_OBJ_WATCHPOINT
2517 JS_FN(js_watch_str, obj_watch, 2,0),
2518 JS_FN(js_unwatch_str, obj_unwatch, 1,0),
2519 #endif
2520 JS_FN(js_hasOwnProperty_str, obj_hasOwnProperty, 1,0),
2521 JS_FN(js_isPrototypeOf_str, obj_isPrototypeOf, 1,0),
2522 JS_FN(js_propertyIsEnumerable_str, obj_propertyIsEnumerable, 1,0),
2523 #if OLD_GETTER_SETTER_METHODS
2524 JS_FN(js_defineGetter_str, js_obj_defineGetter, 2,0),
2525 JS_FN(js_defineSetter_str, js_obj_defineSetter, 2,0),
2526 JS_FN(js_lookupGetter_str, obj_lookupGetter, 1,0),
2527 JS_FN(js_lookupSetter_str, obj_lookupSetter, 1,0),
2528 #endif
2529 JS_FS_END
2532 static JSFunctionSpec object_static_methods[] = {
2533 JS_FN("getPrototypeOf", obj_getPrototypeOf, 1,0),
2534 JS_FN("getOwnPropertyDescriptor", obj_getOwnPropertyDescriptor,2,0),
2535 JS_FN("keys", obj_keys, 1,0),
2536 JS_FN("defineProperty", obj_defineProperty, 3,0),
2537 JS_FN("defineProperties", obj_defineProperties, 2,0),
2538 JS_FN("create", obj_create, 2,0),
2539 JS_FN("getOwnPropertyNames", obj_getOwnPropertyNames, 1,0),
2540 JS_FS_END
2543 JSBool
2544 js_Object(JSContext *cx, uintN argc, Value *vp)
2546 JSObject *obj;
2547 if (argc == 0) {
2548 /* Trigger logic below to construct a blank object. */
2549 obj = NULL;
2550 } else {
2551 /* If argv[0] is null or undefined, obj comes back null. */
2552 if (!js_ValueToObjectOrNull(cx, vp[2], &obj))
2553 return JS_FALSE;
2555 if (!obj) {
2556 /* Make an object whether this was called with 'new' or not. */
2557 JS_ASSERT(!argc || vp[2].isNull() || vp[2].isUndefined());
2558 obj = NewBuiltinClassInstance(cx, &js_ObjectClass);
2559 if (!obj)
2560 return JS_FALSE;
2562 vp->setObject(*obj);
2563 return JS_TRUE;
2566 #ifdef JS_TRACER
2568 JSObject*
2569 js_NewObjectWithClassProto(JSContext *cx, Class *clasp, JSObject *proto,
2570 const Value &privateSlotValue)
2572 JS_ASSERT(clasp->isNative());
2574 JSObject* obj = js_NewGCObject(cx);
2575 if (!obj)
2576 return NULL;
2578 obj->initSharingEmptyScope(clasp, proto, proto->getParent(), privateSlotValue);
2579 return obj;
2582 JSObject* FASTCALL
2583 js_Object_tn(JSContext* cx, JSObject* proto)
2585 JS_ASSERT(!(js_ObjectClass.flags & JSCLASS_HAS_PRIVATE));
2586 return js_NewObjectWithClassProto(cx, &js_ObjectClass, proto, UndefinedValue());
2589 JS_DEFINE_TRCINFO_1(js_Object,
2590 (2, (extern, CONSTRUCTOR_RETRY, js_Object_tn, CONTEXT, CALLEE_PROTOTYPE, 0,
2591 nanojit::ACCSET_STORE_ANY)))
2593 JSObject* FASTCALL
2594 js_NonEmptyObject(JSContext* cx, JSObject* proto)
2596 JS_ASSERT(!(js_ObjectClass.flags & JSCLASS_HAS_PRIVATE));
2597 JSObject *obj = js_NewObjectWithClassProto(cx, &js_ObjectClass, proto, UndefinedValue());
2598 if (!obj)
2599 return NULL;
2600 JS_LOCK_OBJ(cx, obj);
2601 JSScope *scope = js_GetMutableScope(cx, obj);
2602 if (!scope) {
2603 JS_UNLOCK_OBJ(cx, obj);
2604 return NULL;
2608 * See comments in the JSOP_NEWINIT case of jsinterp.cpp why we cannot
2609 * assume that cx owns the scope and skip the unlock call.
2611 JS_UNLOCK_SCOPE(cx, scope);
2612 return obj;
2615 JS_DEFINE_CALLINFO_2(extern, CONSTRUCTOR_RETRY, js_NonEmptyObject, CONTEXT, CALLEE_PROTOTYPE, 0,
2616 nanojit::ACCSET_STORE_ANY)
2618 JSObject* FASTCALL
2619 js_NewInstance(JSContext *cx, Class *clasp, JSObject *ctor)
2621 JS_ASSERT(JS_ON_TRACE(cx));
2622 JS_ASSERT(ctor->isFunction());
2624 JSAtom *atom = cx->runtime->atomState.classPrototypeAtom;
2626 JSScope *scope = ctor->scope();
2627 #ifdef JS_THREADSAFE
2628 if (scope->title.ownercx != cx)
2629 return NULL;
2630 #endif
2631 if (scope->isSharedEmpty()) {
2632 scope = js_GetMutableScope(cx, ctor);
2633 if (!scope)
2634 return NULL;
2637 JSScopeProperty *sprop = scope->lookup(ATOM_TO_JSID(atom));
2638 Value pval = sprop ? ctor->getSlot(sprop->slot) : MagicValue(JS_GENERIC_MAGIC);
2640 JSObject *parent = ctor->getParent();
2641 JSObject *proto;
2642 if (pval.isObject()) {
2643 /* An object in ctor.prototype, let's use it as the new instance's proto. */
2644 proto = &pval.toObject();
2645 } else {
2646 /* A hole or a primitive: either way, we need to get Object.prototype. */
2647 if (!js_GetClassPrototype(cx, parent, JSProto_Object, &proto))
2648 return NULL;
2650 if (pval.isMagic(JS_GENERIC_MAGIC)) {
2652 * No ctor.prototype was set, so we inline-expand and optimize
2653 * fun_resolve's prototype creation code.
2655 proto = NewNativeClassInstance(cx, clasp, proto, parent);
2656 if (!proto)
2657 return NULL;
2658 if (!js_SetClassPrototype(cx, ctor, proto, JSPROP_ENUMERATE | JSPROP_PERMANENT))
2659 return NULL;
2660 } else {
2662 * A primitive value in .prototype means to use Object.prototype
2663 * for proto. See ES5 13.2.2 step 7.
2669 * FIXME: 561785 at least. Quasi-natives including XML objects prevent us
2670 * from easily or unconditionally calling NewNativeClassInstance here.
2672 return NewNonFunction<WithProto::Given>(cx, clasp, proto, parent);
2675 JS_DEFINE_CALLINFO_3(extern, CONSTRUCTOR_RETRY, js_NewInstance, CONTEXT, CLASS, OBJECT, 0,
2676 nanojit::ACCSET_STORE_ANY)
2678 #else /* !JS_TRACER */
2680 # define js_Object_trcinfo NULL
2682 #endif /* !JS_TRACER */
2685 * Given pc pointing after a property accessing bytecode, return true if the
2686 * access is "object-detecting" in the sense used by web scripts, e.g., when
2687 * checking whether document.all is defined.
2689 JS_REQUIRES_STACK JSBool
2690 Detecting(JSContext *cx, jsbytecode *pc)
2692 JSScript *script;
2693 jsbytecode *endpc;
2694 JSOp op;
2695 JSAtom *atom;
2697 script = cx->fp->script;
2698 endpc = script->code + script->length;
2699 for (;; pc += js_CodeSpec[op].length) {
2700 JS_ASSERT_IF(!cx->fp->hasIMacroPC(), script->code <= pc && pc < endpc);
2702 /* General case: a branch or equality op follows the access. */
2703 op = js_GetOpcode(cx, script, pc);
2704 if (js_CodeSpec[op].format & JOF_DETECTING)
2705 return JS_TRUE;
2707 switch (op) {
2708 case JSOP_NULL:
2710 * Special case #1: handle (document.all == null). Don't sweat
2711 * about JS1.2's revision of the equality operators here.
2713 if (++pc < endpc) {
2714 op = js_GetOpcode(cx, script, pc);
2715 return *pc == JSOP_EQ || *pc == JSOP_NE;
2717 return JS_FALSE;
2719 case JSOP_NAME:
2721 * Special case #2: handle (document.all == undefined). Don't
2722 * worry about someone redefining undefined, which was added by
2723 * Edition 3, so is read/write for backward compatibility.
2725 GET_ATOM_FROM_BYTECODE(script, pc, 0, atom);
2726 if (atom == cx->runtime->atomState.typeAtoms[JSTYPE_VOID] &&
2727 (pc += js_CodeSpec[op].length) < endpc) {
2728 op = js_GetOpcode(cx, script, pc);
2729 return op == JSOP_EQ || op == JSOP_NE ||
2730 op == JSOP_STRICTEQ || op == JSOP_STRICTNE;
2732 return JS_FALSE;
2734 default:
2736 * At this point, anything but an extended atom index prefix means
2737 * we're not detecting.
2739 if (!(js_CodeSpec[op].format & JOF_INDEXBASE))
2740 return JS_FALSE;
2741 break;
2747 * Infer lookup flags from the currently executing bytecode. This does
2748 * not attempt to infer JSRESOLVE_WITH, because the current bytecode
2749 * does not indicate whether we are in a with statement. Return defaultFlags
2750 * if a currently executing bytecode cannot be determined.
2752 uintN
2753 js_InferFlags(JSContext *cx, uintN defaultFlags)
2755 #ifdef JS_TRACER
2756 if (JS_ON_TRACE(cx))
2757 return cx->bailExit->lookupFlags;
2758 #endif
2760 JS_ASSERT_NOT_ON_TRACE(cx);
2762 jsbytecode *pc;
2763 const JSCodeSpec *cs;
2764 uint32 format;
2765 uintN flags = 0;
2767 JSStackFrame *const fp = js_GetTopStackFrame(cx);
2768 if (!fp || !(pc = cx->regs->pc))
2769 return defaultFlags;
2770 cs = &js_CodeSpec[js_GetOpcode(cx, fp->script, pc)];
2771 format = cs->format;
2772 if (JOF_MODE(format) != JOF_NAME)
2773 flags |= JSRESOLVE_QUALIFIED;
2774 if ((format & (JOF_SET | JOF_FOR)) ||
2775 (fp->flags & JSFRAME_ASSIGNING)) {
2776 flags |= JSRESOLVE_ASSIGNING;
2777 } else if (cs->length >= 0) {
2778 pc += cs->length;
2779 if (pc < cx->fp->script->code + cx->fp->script->length && Detecting(cx, pc))
2780 flags |= JSRESOLVE_DETECTING;
2782 if (format & JOF_DECLARING)
2783 flags |= JSRESOLVE_DECLARING;
2784 return flags;
2788 * ObjectOps and Class for with-statement stack objects.
2790 static JSBool
2791 with_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
2792 JSProperty **propp)
2794 /* Fixes bug 463997 */
2795 uintN flags = cx->resolveFlags;
2796 if (flags == JSRESOLVE_INFER)
2797 flags = js_InferFlags(cx, flags);
2798 flags |= JSRESOLVE_WITH;
2799 JSAutoResolveFlags rf(cx, flags);
2800 return obj->getProto()->lookupProperty(cx, id, objp, propp);
2803 static JSBool
2804 with_GetProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
2806 return obj->getProto()->getProperty(cx, id, vp);
2809 static JSBool
2810 with_SetProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
2812 return obj->getProto()->setProperty(cx, id, vp);
2815 static JSBool
2816 with_GetAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp)
2818 return obj->getProto()->getAttributes(cx, id, attrsp);
2821 static JSBool
2822 with_SetAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp)
2824 return obj->getProto()->setAttributes(cx, id, attrsp);
2827 static JSBool
2828 with_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, Value *rval)
2830 return obj->getProto()->deleteProperty(cx, id, rval);
2833 static JSBool
2834 with_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
2835 Value *statep, jsid *idp)
2837 return obj->getProto()->enumerate(cx, enum_op, statep, idp);
2840 static JSType
2841 with_TypeOf(JSContext *cx, JSObject *obj)
2843 return JSTYPE_OBJECT;
2846 static JSObject *
2847 with_ThisObject(JSContext *cx, JSObject *obj)
2849 return obj->getWithThis();
2852 Class js_WithClass = {
2853 "With",
2854 JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS,
2855 PropertyStub, /* addProperty */
2856 PropertyStub, /* delProperty */
2857 PropertyStub, /* getProperty */
2858 PropertyStub, /* setProperty */
2859 EnumerateStub,
2860 ResolveStub,
2861 ConvertStub,
2862 NULL, /* finalize */
2863 NULL, /* reserved */
2864 NULL, /* checkAccess */
2865 NULL, /* call */
2866 NULL, /* construct */
2867 NULL, /* xdrObject */
2868 NULL, /* hasInstance */
2869 NULL, /* mark */
2870 JS_NULL_CLASS_EXT,
2872 with_LookupProperty,
2873 NULL, /* defineProperty */
2874 with_GetProperty,
2875 with_SetProperty,
2876 with_GetAttributes,
2877 with_SetAttributes,
2878 with_DeleteProperty,
2879 with_Enumerate,
2880 with_TypeOf,
2881 NULL, /* trace */
2882 with_ThisObject,
2883 NULL, /* clear */
2887 JS_REQUIRES_STACK JSObject *
2888 js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth)
2890 JSObject *obj;
2892 obj = js_NewGCObject(cx);
2893 if (!obj)
2894 return NULL;
2895 obj->init(&js_WithClass, proto, parent,
2896 PrivateValue(js_FloatingFrameIfGenerator(cx, cx->fp)));
2897 OBJ_SET_BLOCK_DEPTH(cx, obj, depth);
2898 obj->map = cx->runtime->emptyWithScope->hold();
2900 AutoObjectRooter tvr(cx, obj);
2901 JSObject *thisp = proto->thisObject(cx);
2902 if (!thisp)
2903 return NULL;
2904 obj->setWithThis(thisp);
2906 return obj;
2909 JSObject *
2910 js_NewBlockObject(JSContext *cx)
2913 * Null obj's proto slot so that Object.prototype.* does not pollute block
2914 * scopes and to give the block object its own scope.
2916 JSObject *blockObj = js_NewGCObject(cx);
2917 if (!blockObj)
2918 return NULL;
2919 blockObj->init(&js_BlockClass, NULL, NULL, NullValue());
2920 blockObj->map = cx->runtime->emptyBlockScope->hold();
2921 return blockObj;
2924 JSObject *
2925 js_CloneBlockObject(JSContext *cx, JSObject *proto, JSStackFrame *fp)
2927 JS_ASSERT(!OBJ_IS_CLONED_BLOCK(proto));
2928 JS_ASSERT(proto->getClass() == &js_BlockClass);
2930 JSObject *clone = js_NewGCObject(cx);
2931 if (!clone)
2932 return NULL;
2934 Value privateValue = PrivateValue(js_FloatingFrameIfGenerator(cx, fp));
2936 /* The caller sets parent on its own. */
2937 clone->init(&js_BlockClass, proto, NULL, privateValue);
2938 clone->fslots[JSSLOT_BLOCK_DEPTH] = proto->fslots[JSSLOT_BLOCK_DEPTH];
2940 JS_ASSERT(cx->runtime->emptyBlockScope->freeslot == JSSLOT_BLOCK_DEPTH + 1);
2941 clone->map = cx->runtime->emptyBlockScope->hold();
2942 JS_ASSERT(OBJ_IS_CLONED_BLOCK(clone));
2944 if (!js_EnsureReservedSlots(cx, clone, OBJ_BLOCK_COUNT(cx, proto)))
2945 return NULL;
2946 return clone;
2949 JS_REQUIRES_STACK JSBool
2950 js_PutBlockObject(JSContext *cx, JSBool normalUnwind)
2952 /* Blocks have one fixed slot available for the first local.*/
2953 JS_STATIC_ASSERT(JS_INITIAL_NSLOTS == JSSLOT_BLOCK_DEPTH + 2);
2955 JSStackFrame *const fp = cx->fp;
2956 JSObject *obj = fp->getScopeChain();
2957 JS_ASSERT(obj->getClass() == &js_BlockClass);
2958 JS_ASSERT(obj->getPrivate() == js_FloatingFrameIfGenerator(cx, cx->fp));
2959 JS_ASSERT(OBJ_IS_CLONED_BLOCK(obj));
2962 * Block objects should never be exposed to scripts. Thus the clone should
2963 * not own the property map and rather always share it with the prototype
2964 * object. This allows us to skip updating obj->scope()->freeslot after
2965 * we copy the stack slots into reserved slots.
2967 JS_ASSERT(obj->scope()->object != obj);
2969 /* Block objects should have all reserved slots allocated early. */
2970 uintN count = OBJ_BLOCK_COUNT(cx, obj);
2971 JS_ASSERT(obj->numSlots() == JSSLOT_BLOCK_DEPTH + 1 + count);
2973 /* The block and its locals must be on the current stack for GC safety. */
2974 uintN depth = OBJ_BLOCK_DEPTH(cx, obj);
2975 JS_ASSERT(depth <= (size_t) (cx->regs->sp - fp->base()));
2976 JS_ASSERT(count <= (size_t) (cx->regs->sp - fp->base() - depth));
2978 /* See comments in CheckDestructuring from jsparse.cpp. */
2979 JS_ASSERT(count >= 1);
2981 depth += fp->script->nfixed;
2982 obj->fslots[JSSLOT_BLOCK_DEPTH + 1] = fp->slots()[depth];
2983 if (normalUnwind && count > 1) {
2984 --count;
2985 memcpy(obj->dslots, fp->slots() + depth + 1, count * sizeof(Value));
2988 /* We must clear the private slot even with errors. */
2989 obj->setPrivate(NULL);
2990 fp->setScopeChain(obj->getParent());
2991 return normalUnwind;
2994 static JSBool
2995 block_getProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
2998 * Block objects are never exposed to script, and the engine handles them
2999 * with care. So unlike other getters, this one can assert (rather than
3000 * check) certain invariants about obj.
3002 JS_ASSERT(obj->getClass() == &js_BlockClass);
3003 JS_ASSERT(OBJ_IS_CLONED_BLOCK(obj));
3004 uintN index = (uintN) JSID_TO_INT(id);
3005 JS_ASSERT(index < OBJ_BLOCK_COUNT(cx, obj));
3007 JSStackFrame *fp = (JSStackFrame *) obj->getPrivate();
3008 if (fp) {
3009 fp = js_LiveFrameIfGenerator(fp);
3010 index += fp->script->nfixed + OBJ_BLOCK_DEPTH(cx, obj);
3011 JS_ASSERT(index < fp->script->nslots);
3012 *vp = fp->slots()[index];
3013 return true;
3016 /* Values are in reserved slots immediately following DEPTH. */
3017 uint32 slot = JSSLOT_BLOCK_DEPTH + 1 + index;
3018 JS_LOCK_OBJ(cx, obj);
3019 JS_ASSERT(slot < obj->numSlots());
3020 *vp = obj->getSlot(slot);
3021 JS_UNLOCK_OBJ(cx, obj);
3022 return true;
3025 static JSBool
3026 block_setProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
3028 JS_ASSERT(obj->getClass() == &js_BlockClass);
3029 JS_ASSERT(OBJ_IS_CLONED_BLOCK(obj));
3030 uintN index = (uintN) JSID_TO_INT(id);
3031 JS_ASSERT(index < OBJ_BLOCK_COUNT(cx, obj));
3033 JSStackFrame *fp = (JSStackFrame *) obj->getPrivate();
3034 if (fp) {
3035 fp = js_LiveFrameIfGenerator(fp);
3036 index += fp->script->nfixed + OBJ_BLOCK_DEPTH(cx, obj);
3037 JS_ASSERT(index < fp->script->nslots);
3038 fp->slots()[index] = *vp;
3039 return true;
3042 /* Values are in reserved slots immediately following DEPTH. */
3043 uint32 slot = JSSLOT_BLOCK_DEPTH + 1 + index;
3044 JS_LOCK_OBJ(cx, obj);
3045 JS_ASSERT(slot < obj->numSlots());
3046 obj->setSlot(slot, *vp);
3047 JS_UNLOCK_OBJ(cx, obj);
3048 return true;
3051 JSBool
3052 js_DefineBlockVariable(JSContext *cx, JSObject *obj, jsid id, intN index)
3054 JS_ASSERT(obj->getClass() == &js_BlockClass);
3055 JS_ASSERT(!OBJ_IS_CLONED_BLOCK(obj));
3057 /* Use JSPROP_ENUMERATE to aid the disassembler. */
3058 return js_DefineNativeProperty(cx, obj, id, UndefinedValue(),
3059 block_getProperty,
3060 block_setProperty,
3061 JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_SHARED,
3062 JSScopeProperty::HAS_SHORTID, index, NULL);
3065 static size_t
3066 GetObjectSize(JSObject *obj)
3068 return (obj->isFunction() && !obj->getPrivate())
3069 ? sizeof(JSFunction)
3070 : sizeof(JSObject);
3074 * Use this method with extreme caution. It trades the guts of two objects and updates
3075 * scope ownership. This operation is not thread-safe, just as fast array to slow array
3076 * transitions are inherently not thread-safe. Don't perform a swap operation on objects
3077 * shared across threads or, or bad things will happen. You have been warned.
3079 void
3080 JSObject::swap(JSObject *other)
3082 /* For both objects determine whether they own their respective scopes. */
3083 bool thisOwns = this->isNative() && scope()->object == this;
3084 bool otherOwns = other->isNative() && other->scope()->object == other;
3086 size_t size = GetObjectSize(this);
3087 JS_ASSERT(size == GetObjectSize(other));
3089 /* Trade the guts of the objects. */
3090 char tmp[tl::Max<sizeof(JSFunction), sizeof(JSObject)>::result];
3091 memcpy(tmp, this, size);
3092 memcpy(this, other, size);
3093 memcpy(other, tmp, size);
3095 /* Fixup scope ownerships. */
3096 if (otherOwns)
3097 scope()->object = this;
3098 if (thisOwns)
3099 other->scope()->object = other;
3102 #if JS_HAS_XDR
3104 #define NO_PARENT_INDEX ((uint32)-1)
3106 uint32
3107 FindObjectIndex(JSObjectArray *array, JSObject *obj)
3109 size_t i;
3111 if (array) {
3112 i = array->length;
3113 do {
3115 if (array->vector[--i] == obj)
3116 return i;
3117 } while (i != 0);
3120 return NO_PARENT_INDEX;
3123 JSBool
3124 js_XDRBlockObject(JSXDRState *xdr, JSObject **objp)
3126 JSContext *cx;
3127 uint32 parentId;
3128 JSObject *obj, *parent;
3129 uint16 depth, count, i;
3130 uint32 tmp;
3131 JSScopeProperty *sprop;
3132 jsid propid;
3133 JSAtom *atom;
3134 int16 shortid;
3135 JSBool ok;
3137 cx = xdr->cx;
3138 #ifdef __GNUC__
3139 obj = NULL; /* quell GCC overwarning */
3140 #endif
3142 if (xdr->mode == JSXDR_ENCODE) {
3143 obj = *objp;
3144 parent = obj->getParent();
3145 parentId = (xdr->script->objectsOffset == 0)
3146 ? NO_PARENT_INDEX
3147 : FindObjectIndex(xdr->script->objects(), parent);
3148 depth = (uint16)OBJ_BLOCK_DEPTH(cx, obj);
3149 count = (uint16)OBJ_BLOCK_COUNT(cx, obj);
3150 tmp = (uint32)(depth << 16) | count;
3152 #ifdef __GNUC__ /* suppress bogus gcc warnings */
3153 else count = 0;
3154 #endif
3156 /* First, XDR the parent atomid. */
3157 if (!JS_XDRUint32(xdr, &parentId))
3158 return JS_FALSE;
3160 if (xdr->mode == JSXDR_DECODE) {
3161 obj = js_NewBlockObject(cx);
3162 if (!obj)
3163 return JS_FALSE;
3164 *objp = obj;
3167 * If there's a parent id, then get the parent out of our script's
3168 * object array. We know that we XDR block object in outer-to-inner
3169 * order, which means that getting the parent now will work.
3171 if (parentId == NO_PARENT_INDEX)
3172 parent = NULL;
3173 else
3174 parent = xdr->script->getObject(parentId);
3175 obj->setParent(parent);
3178 AutoObjectRooter tvr(cx, obj);
3180 if (!JS_XDRUint32(xdr, &tmp))
3181 return false;
3183 if (xdr->mode == JSXDR_DECODE) {
3184 depth = (uint16)(tmp >> 16);
3185 count = (uint16)tmp;
3186 obj->setSlot(JSSLOT_BLOCK_DEPTH, Value(Int32Value(depth)));
3190 * XDR the block object's properties. We know that there are 'count'
3191 * properties to XDR, stored as id/shortid pairs. We do not XDR any
3192 * non-native properties, only those that the compiler created.
3194 sprop = NULL;
3195 ok = JS_TRUE;
3196 for (i = 0; i < count; i++) {
3197 if (xdr->mode == JSXDR_ENCODE) {
3198 /* Find a property to XDR. */
3199 do {
3200 /* If sprop is NULL, this is the first property. */
3201 sprop = sprop ? sprop->parent : obj->scope()->lastProperty();
3202 } while (!sprop->hasShortID());
3204 JS_ASSERT(sprop->getter() == block_getProperty);
3205 propid = sprop->id;
3206 JS_ASSERT(JSID_IS_ATOM(propid));
3207 atom = JSID_TO_ATOM(propid);
3208 shortid = sprop->shortid;
3209 JS_ASSERT(shortid >= 0);
3212 /* XDR the real id, then the shortid. */
3213 if (!js_XDRAtom(xdr, &atom) ||
3214 !JS_XDRUint16(xdr, (uint16 *)&shortid)) {
3215 return false;
3218 if (xdr->mode == JSXDR_DECODE) {
3219 if (!js_DefineBlockVariable(cx, obj, ATOM_TO_JSID(atom), shortid))
3220 return false;
3223 return true;
3226 #endif
3228 Class js_BlockClass = {
3229 "Block",
3230 JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_IS_ANONYMOUS,
3231 PropertyStub, /* addProperty */
3232 PropertyStub, /* delProperty */
3233 PropertyStub, /* getProperty */
3234 PropertyStub, /* setProperty */
3235 EnumerateStub,
3236 ResolveStub,
3237 ConvertStub
3240 JSObject *
3241 js_InitObjectClass(JSContext *cx, JSObject *obj)
3243 JSObject *proto = js_InitClass(cx, obj, NULL, &js_ObjectClass, (Native) js_Object, 1,
3244 object_props, object_methods, NULL, object_static_methods);
3245 if (!proto)
3246 return NULL;
3248 /* ECMA (15.1.2.1) says 'eval' is a property of the global object. */
3249 if (!js_DefineFunction(cx, obj, cx->runtime->atomState.evalAtom,
3250 (Native)obj_eval, 1,
3251 JSFUN_FAST_NATIVE | JSFUN_STUB_GSOPS)) {
3252 return NULL;
3255 return proto;
3258 static bool
3259 DefineStandardSlot(JSContext *cx, JSObject *obj, JSProtoKey key, JSAtom *atom,
3260 const Value &v, uint32 attrs, bool &named)
3262 jsid id = ATOM_TO_JSID(atom);
3264 if (key != JSProto_Null) {
3266 * Initializing an actual standard class on a global object. If the
3267 * property is not yet present, force it into a new one bound to a
3268 * reserved slot. Otherwise, go through the normal property path.
3270 JS_ASSERT(obj->getClass()->flags & JSCLASS_IS_GLOBAL);
3271 JS_ASSERT(obj->isNative());
3273 JS_LOCK_OBJ(cx, obj);
3275 JSScope *scope = js_GetMutableScope(cx, obj);
3276 if (!scope) {
3277 JS_UNLOCK_OBJ(cx, obj);
3278 return false;
3281 JSScopeProperty *sprop = scope->lookup(id);
3282 if (!sprop) {
3283 uint32 index = 2 * JSProto_LIMIT + key;
3284 if (!js_SetReservedSlot(cx, obj, index, v)) {
3285 JS_UNLOCK_SCOPE(cx, scope);
3286 return false;
3289 uint32 slot = JSSLOT_START(obj->getClass()) + index;
3290 sprop = scope->addProperty(cx, id, PropertyStub, PropertyStub,
3291 slot, attrs, 0, 0);
3293 JS_UNLOCK_SCOPE(cx, scope);
3294 if (!sprop)
3295 return false;
3297 named = true;
3298 return true;
3300 JS_UNLOCK_SCOPE(cx, scope);
3303 named = obj->defineProperty(cx, id, v, PropertyStub, PropertyStub, attrs);
3304 return named;
3307 JSObject *
3308 js_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto,
3309 Class *clasp, Native constructor, uintN nargs,
3310 JSPropertySpec *ps, JSFunctionSpec *fs,
3311 JSPropertySpec *static_ps, JSFunctionSpec *static_fs)
3313 JSAtom *atom;
3314 JSProtoKey key;
3315 JSFunction *fun;
3316 bool named = false;
3318 atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0);
3319 if (!atom)
3320 return NULL;
3323 * When initializing a standard class, if no parent_proto (grand-proto of
3324 * instances of the class, parent-proto of the class's prototype object)
3325 * is given, we must use Object.prototype if it is available. Otherwise,
3326 * we could look up the wrong binding for a class name in obj. Example:
3328 * String = Array;
3329 * print("hi there".join);
3331 * should print undefined, not Array.prototype.join. This is required by
3332 * ECMA-262, alas. It might have been better to make String readonly and
3333 * permanent in the global object, instead -- but that's too big a change
3334 * to swallow at this point.
3336 key = JSCLASS_CACHED_PROTO_KEY(clasp);
3337 if (key != JSProto_Null &&
3338 !parent_proto &&
3339 !js_GetClassPrototype(cx, obj, JSProto_Object, &parent_proto)) {
3340 return NULL;
3344 * Create a prototype object for this class.
3346 * FIXME: lazy standard (built-in) class initialization and even older
3347 * eager boostrapping code rely on all of these properties:
3349 * 1. NewObject attempting to compute a default prototype object when
3350 * passed null for proto; and
3352 * 2. NewObject tolerating no default prototype (null proto slot value)
3353 * due to this js_InitClass call coming from js_InitFunctionClass on an
3354 * otherwise-uninitialized global.
3356 * 3. NewObject allocating a JSFunction-sized GC-thing when clasp is
3357 * &js_FunctionClass, not a JSObject-sized (smaller) GC-thing.
3359 * The JS_NewObjectForGivenProto and JS_NewObject APIs also allow clasp to
3360 * be &js_FunctionClass (we could break compatibility easily). But fixing
3361 * (3) is not enough without addressing the bootstrapping dependency on (1)
3362 * and (2).
3364 JSObject *proto = NewObject<WithProto::Class>(cx, clasp, parent_proto, obj);
3365 if (!proto)
3366 return NULL;
3368 /* After this point, control must exit via label bad or out. */
3369 AutoObjectRooter tvr(cx, proto);
3371 JSObject *ctor;
3372 if (!constructor) {
3374 * Lacking a constructor, name the prototype (e.g., Math) unless this
3375 * class (a) is anonymous, i.e. for internal use only; (b) the class
3376 * of obj (the global object) is has a reserved slot indexed by key;
3377 * and (c) key is not the null key.
3379 if (!(clasp->flags & JSCLASS_IS_ANONYMOUS) ||
3380 !(obj->getClass()->flags & JSCLASS_IS_GLOBAL) ||
3381 key == JSProto_Null)
3383 uint32 attrs = (clasp->flags & JSCLASS_IS_ANONYMOUS)
3384 ? JSPROP_READONLY | JSPROP_PERMANENT
3385 : 0;
3386 if (!DefineStandardSlot(cx, obj, key, atom, ObjectValue(*proto), attrs, named))
3387 goto bad;
3390 ctor = proto;
3391 } else {
3392 uint16 flags = 0;
3393 if (clasp->flags & JSCLASS_FAST_CONSTRUCTOR)
3394 flags |= JSFUN_FAST_NATIVE | JSFUN_FAST_NATIVE_CTOR;
3396 fun = js_NewFunction(cx, NULL, constructor, nargs, flags, obj, atom);
3397 if (!fun)
3398 goto bad;
3400 AutoValueRooter tvr2(cx, ObjectValue(*fun));
3401 if (!DefineStandardSlot(cx, obj, key, atom, tvr2.value(), 0, named))
3402 goto bad;
3405 * Remember the class this function is a constructor for so that
3406 * we know to create an object of this class when we call the
3407 * constructor.
3409 FUN_CLASP(fun) = clasp;
3412 * Optionally construct the prototype object, before the class has
3413 * been fully initialized. Allow the ctor to replace proto with a
3414 * different object, as is done for operator new -- and as at least
3415 * XML support requires.
3417 ctor = FUN_OBJECT(fun);
3418 if (clasp->flags & JSCLASS_CONSTRUCT_PROTOTYPE) {
3419 Value rval;
3420 if (!InternalConstruct(cx, proto, ObjectOrNullValue(ctor), 0, NULL, &rval))
3421 goto bad;
3422 if (rval.isObject() && &rval.toObject() != proto)
3423 proto = &rval.toObject();
3426 /* Connect constructor and prototype by named properties. */
3427 if (!js_SetClassPrototype(cx, ctor, proto,
3428 JSPROP_READONLY | JSPROP_PERMANENT)) {
3429 goto bad;
3432 /* Bootstrap Function.prototype (see also JS_InitStandardClasses). */
3433 if (ctor->getClass() == clasp)
3434 ctor->setProto(proto);
3437 /* Add properties and methods to the prototype and the constructor. */
3438 if ((ps && !JS_DefineProperties(cx, proto, ps)) ||
3439 (fs && !JS_DefineFunctions(cx, proto, fs)) ||
3440 (static_ps && !JS_DefineProperties(cx, ctor, static_ps)) ||
3441 (static_fs && !JS_DefineFunctions(cx, ctor, static_fs))) {
3442 goto bad;
3446 * Make sure proto's scope's emptyScope is available to be shared by
3447 * objects of this class. JSScope::emptyScope is a one-slot cache. If we
3448 * omit this, some other class could snap it up. (The risk is particularly
3449 * great for Object.prototype.)
3451 * All callers of JSObject::initSharingEmptyScope depend on this.
3453 JSScope *scope;
3454 bool ok;
3455 JS_LOCK_OBJ(cx, proto);
3456 scope = js_GetMutableScope(cx, proto);
3457 ok = scope && scope->ensureEmptyScope(cx, clasp);
3458 JS_UNLOCK_OBJ(cx, proto);
3459 if (!ok)
3460 goto bad;
3462 /* If this is a standard class, cache its prototype. */
3463 if (key != JSProto_Null && !js_SetClassObject(cx, obj, key, ctor, proto))
3464 goto bad;
3466 return proto;
3468 bad:
3469 if (named) {
3470 Value rval;
3471 obj->deleteProperty(cx, ATOM_TO_JSID(atom), &rval);
3473 return NULL;
3476 bool
3477 JSObject::allocSlots(JSContext *cx, size_t nslots)
3479 JS_ASSERT(!dslots);
3480 JS_ASSERT(nslots > JS_INITIAL_NSLOTS);
3482 size_t nwords = slotsToDynamicWords(nslots);
3483 dslots = (Value*) cx->malloc(nwords * sizeof(Value));
3484 if (!dslots)
3485 return false;
3487 dslots++;
3488 dslots[-1].setPrivateUint32(nslots);
3489 SetValueRangeToUndefined(dslots, nslots - JS_INITIAL_NSLOTS);
3490 return true;
3493 bool
3494 JSObject::growSlots(JSContext *cx, size_t nslots)
3497 * Minimal number of dynamic slots to allocate.
3499 const size_t MIN_DYNAMIC_WORDS = 4;
3502 * The limit to switch to linear allocation strategy from the power of 2
3503 * growth no to waste too much memory.
3505 const size_t LINEAR_GROWTH_STEP = JS_BIT(16);
3507 /* If we are allocating fslots, there is nothing to do. */
3508 if (nslots <= JS_INITIAL_NSLOTS)
3509 return JS_TRUE;
3511 size_t nwords = slotsToDynamicWords(nslots);
3514 * Round up nslots so the number of bytes in dslots array is power
3515 * of 2 to ensure exponential grouth.
3517 uintN log;
3518 if (nwords <= MIN_DYNAMIC_WORDS) {
3519 nwords = MIN_DYNAMIC_WORDS;
3520 } else if (nwords < LINEAR_GROWTH_STEP) {
3521 JS_CEILING_LOG2(log, nwords);
3522 nwords = JS_BIT(log);
3523 } else {
3524 nwords = JS_ROUNDUP(nwords, LINEAR_GROWTH_STEP);
3526 nslots = dynamicWordsToSlots(nwords);
3529 * If nothing was allocated yet, treat it as initial allocation (but with
3530 * the exponential growth algorithm applied).
3532 if (!dslots)
3533 return allocSlots(cx, nslots);
3535 size_t oldnslots = dslots[-1].toPrivateUint32();
3537 Value *tmpdslots = (Value*) cx->realloc(dslots - 1, nwords * sizeof(Value));
3538 if (!tmpdslots)
3539 return false; /* Leave dslots at its old size. */
3540 dslots = tmpdslots;
3542 dslots++;
3543 dslots[-1].setPrivateUint32(nslots);
3545 /* Initialize the additional slots we added. */
3546 JS_ASSERT(nslots > oldnslots);
3547 Value *beg = dslots + (oldnslots - JS_INITIAL_NSLOTS);
3548 Value *end = dslots + (nslots - JS_INITIAL_NSLOTS);
3549 SetValueRangeToUndefined(beg, end);
3551 return true;
3554 void
3555 JSObject::shrinkSlots(JSContext *cx, size_t nslots)
3557 /* Nothing to shrink? */
3558 if (!dslots)
3559 return;
3561 JS_ASSERT(dslots[-1].toPrivateUint32() > JS_INITIAL_NSLOTS);
3562 JS_ASSERT(nslots <= dslots[-1].toPrivateUint32());
3564 if (nslots <= JS_INITIAL_NSLOTS) {
3565 freeSlotsArray(cx);
3566 dslots = NULL;
3567 } else {
3568 size_t nwords = slotsToDynamicWords(nslots);
3569 Value *tmpdslots = (Value*) cx->realloc(dslots - 1, nwords * sizeof(Value));
3570 if (!tmpdslots)
3571 return; /* Leave dslots at its old size. */
3572 dslots = tmpdslots;
3574 dslots++;
3575 dslots[-1].setPrivateUint32(nslots);
3579 bool
3580 js_EnsureReservedSlots(JSContext *cx, JSObject *obj, size_t nreserved)
3582 uintN nslots = JSSLOT_FREE(obj->getClass()) + nreserved;
3583 if (nslots > obj->numSlots() && !obj->allocSlots(cx, nslots))
3584 return false;
3586 if (obj->isNative()) {
3587 JSScope *scope = obj->scope();
3588 if (!scope->isSharedEmpty()) {
3589 #ifdef JS_THREADSAFE
3590 JS_ASSERT(scope->title.ownercx->thread == cx->thread);
3591 #endif
3592 JS_ASSERT(scope->freeslot == JSSLOT_FREE(obj->getClass()));
3593 if (scope->freeslot < nslots)
3594 scope->freeslot = nslots;
3597 return true;
3600 static JSObject *
3601 js_InitNullClass(JSContext *cx, JSObject *obj)
3603 JS_ASSERT(0);
3604 return NULL;
3607 #define JS_PROTO(name,code,init) extern JSObject *init(JSContext *, JSObject *);
3608 #include "jsproto.tbl"
3609 #undef JS_PROTO
3611 static JSObjectOp lazy_prototype_init[JSProto_LIMIT] = {
3612 #define JS_PROTO(name,code,init) init,
3613 #include "jsproto.tbl"
3614 #undef JS_PROTO
3617 namespace js {
3619 bool
3620 SetProto(JSContext *cx, JSObject *obj, JSObject *proto, bool checkForCycles)
3622 JS_ASSERT_IF(!checkForCycles, obj != proto);
3624 if (obj->isNative()) {
3625 JS_LOCK_OBJ(cx, obj);
3626 bool ok = !!js_GetMutableScope(cx, obj);
3627 JS_UNLOCK_OBJ(cx, obj);
3628 if (!ok)
3629 return false;
3633 * Regenerate property cache shape ids for all of the scopes along the
3634 * old prototype chain to invalidate their property cache entries, in
3635 * case any entries were filled by looking up through obj.
3637 JSObject *oldproto = obj;
3638 while (oldproto && oldproto->isNative()) {
3639 JS_LOCK_OBJ(cx, oldproto);
3640 JSScope *scope = oldproto->scope();
3641 scope->protoShapeChange(cx);
3642 JSObject *tmp = oldproto->getProto();
3643 JS_UNLOCK_OBJ(cx, oldproto);
3644 oldproto = tmp;
3647 if (!proto || !checkForCycles) {
3648 obj->setProto(proto);
3649 } else if (!SetProtoCheckingForCycles(cx, obj, proto)) {
3650 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CYCLIC_VALUE, js_proto_str);
3651 return false;
3653 return true;
3658 JSBool
3659 js_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key,
3660 JSObject **objp)
3662 JSObject *tmp, *cobj;
3663 JSResolvingKey rkey;
3664 JSResolvingEntry *rentry;
3665 uint32 generation;
3666 JSObjectOp init;
3667 Value v;
3669 while ((tmp = obj->getParent()) != NULL)
3670 obj = tmp;
3671 if (!(obj->getClass()->flags & JSCLASS_IS_GLOBAL)) {
3672 *objp = NULL;
3673 return JS_TRUE;
3676 v = obj->getReservedSlot(key);
3677 if (v.isObject()) {
3678 *objp = &v.toObject();
3679 return JS_TRUE;
3682 rkey.obj = obj;
3683 rkey.id = ATOM_TO_JSID(cx->runtime->atomState.classAtoms[key]);
3684 if (!js_StartResolving(cx, &rkey, JSRESFLAG_LOOKUP, &rentry))
3685 return JS_FALSE;
3686 if (!rentry) {
3687 /* Already caching key in obj -- suppress recursion. */
3688 *objp = NULL;
3689 return JS_TRUE;
3691 generation = cx->resolvingTable->generation;
3693 JSBool ok = true;
3694 cobj = NULL;
3695 init = lazy_prototype_init[key];
3696 if (init) {
3697 if (!init(cx, obj)) {
3698 ok = JS_FALSE;
3699 } else {
3700 v = obj->getReservedSlot(key);
3701 if (v.isObject())
3702 cobj = &v.toObject();
3706 js_StopResolving(cx, &rkey, JSRESFLAG_LOOKUP, rentry, generation);
3707 *objp = cobj;
3708 return ok;
3711 JSBool
3712 js_SetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, JSObject *cobj, JSObject *proto)
3714 JS_ASSERT(!obj->getParent());
3715 if (!(obj->getClass()->flags & JSCLASS_IS_GLOBAL))
3716 return JS_TRUE;
3718 return js_SetReservedSlot(cx, obj, key, ObjectOrNullValue(cobj)) &&
3719 js_SetReservedSlot(cx, obj, JSProto_LIMIT + key, ObjectOrNullValue(proto));
3722 JSBool
3723 js_FindClassObject(JSContext *cx, JSObject *start, JSProtoKey protoKey,
3724 Value *vp, Class *clasp)
3726 JSStackFrame *fp;
3727 JSObject *obj, *cobj, *pobj;
3728 jsid id;
3729 JSProperty *prop;
3730 JSScopeProperty *sprop;
3733 * Find the global object. Use cx->fp directly to avoid falling off
3734 * trace; all JIT-elided stack frames have the same global object as
3735 * cx->fp.
3737 VOUCH_DOES_NOT_REQUIRE_STACK();
3738 if (!start && (fp = cx->fp) != NULL)
3739 start = fp->maybeScopeChain();
3741 if (start) {
3742 /* Find the topmost object in the scope chain. */
3743 do {
3744 obj = start;
3745 start = obj->getParent();
3746 } while (start);
3747 } else {
3748 obj = cx->globalObject;
3749 if (!obj) {
3750 vp->setUndefined();
3751 return JS_TRUE;
3755 OBJ_TO_INNER_OBJECT(cx, obj);
3756 if (!obj)
3757 return JS_FALSE;
3759 if (protoKey != JSProto_Null) {
3760 JS_ASSERT(JSProto_Null < protoKey);
3761 JS_ASSERT(protoKey < JSProto_LIMIT);
3762 if (!js_GetClassObject(cx, obj, protoKey, &cobj))
3763 return JS_FALSE;
3764 if (cobj) {
3765 vp->setObject(*cobj);
3766 return JS_TRUE;
3768 id = ATOM_TO_JSID(cx->runtime->atomState.classAtoms[protoKey]);
3769 } else {
3770 JSAtom *atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0);
3771 if (!atom)
3772 return false;
3773 id = ATOM_TO_JSID(atom);
3776 JS_ASSERT(obj->isNative());
3777 if (js_LookupPropertyWithFlags(cx, obj, id, JSRESOLVE_CLASSNAME,
3778 &pobj, &prop) < 0) {
3779 return JS_FALSE;
3781 Value v = UndefinedValue();
3782 if (prop && pobj->isNative()) {
3783 sprop = (JSScopeProperty *) prop;
3784 if (SPROP_HAS_VALID_SLOT(sprop, pobj->scope())) {
3785 v = pobj->lockedGetSlot(sprop->slot);
3786 if (v.isPrimitive())
3787 v.setUndefined();
3789 JS_UNLOCK_OBJ(cx, pobj);
3791 *vp = v;
3792 return JS_TRUE;
3795 JSObject *
3796 js_ConstructObject(JSContext *cx, Class *clasp, JSObject *proto, JSObject *parent,
3797 uintN argc, Value *argv)
3799 AutoArrayRooter argtvr(cx, argc, argv);
3801 JSProtoKey protoKey = GetClassProtoKey(clasp);
3803 /* Protect constructor in case a crazy getter for .prototype uproots it. */
3804 AutoValueRooter tvr(cx);
3805 if (!js_FindClassObject(cx, parent, protoKey, tvr.addr(), clasp))
3806 return NULL;
3808 const Value &cval = tvr.value();
3809 if (tvr.value().isPrimitive()) {
3810 js_ReportIsNotFunction(cx, tvr.addr(), JSV2F_CONSTRUCT | JSV2F_SEARCH_STACK);
3811 return NULL;
3815 * If proto is NULL, set it to Constructor.prototype, just like JSOP_NEW
3816 * does, likewise for the new object's parent.
3818 JSObject *ctor = &cval.toObject();
3819 if (!parent)
3820 parent = ctor->getParent();
3821 if (!proto) {
3822 Value rval;
3823 if (!ctor->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom),
3824 &rval)) {
3825 return NULL;
3827 if (rval.isObjectOrNull())
3828 proto = rval.toObjectOrNull();
3831 JSObject *obj = NewObject<WithProto::Class>(cx, clasp, proto, parent);
3832 if (!obj)
3833 return NULL;
3835 Value rval;
3836 if (!InternalConstruct(cx, obj, cval, argc, argv, &rval))
3837 return NULL;
3839 if (rval.isPrimitive())
3840 return obj;
3843 * If the instance's class differs from what was requested, throw a type
3844 * error. If the given class has both the JSCLASS_HAS_PRIVATE and the
3845 * JSCLASS_CONSTRUCT_PROTOTYPE flags, and the instance does not have its
3846 * private data set at this point, then the constructor was replaced and
3847 * we should throw a type error.
3849 obj = &rval.toObject();
3850 if (obj->getClass() != clasp ||
3851 (!(~clasp->flags & (JSCLASS_HAS_PRIVATE |
3852 JSCLASS_CONSTRUCT_PROTOTYPE)) &&
3853 !obj->getPrivate())) {
3854 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3855 JSMSG_WRONG_CONSTRUCTOR, clasp->name);
3856 return NULL;
3858 return obj;
3862 * FIXME bug 535629: If one adds props, deletes earlier props, adds more, the
3863 * last added won't recycle the deleted props' slots.
3865 JSBool
3866 js_AllocSlot(JSContext *cx, JSObject *obj, uint32 *slotp)
3868 JSScope *scope = obj->scope();
3869 JS_ASSERT(scope->object == obj);
3871 if (scope->freeslot >= obj->numSlots() &&
3872 !obj->growSlots(cx, scope->freeslot + 1)) {
3873 return JS_FALSE;
3876 /* js_ReallocSlots or js_FreeSlot should set the free slots to void. */
3877 JS_ASSERT(obj->getSlot(scope->freeslot).isUndefined());
3878 *slotp = scope->freeslot++;
3879 return JS_TRUE;
3882 void
3883 js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot)
3885 JSScope *scope = obj->scope();
3886 JS_ASSERT(scope->object == obj);
3887 obj->lockedSetSlot(slot, UndefinedValue());
3888 if (scope->freeslot == slot + 1)
3889 scope->freeslot = slot;
3893 /* JSBOXEDWORD_INT_MAX as a string */
3894 #define JSBOXEDWORD_INT_MAX_STRING "1073741823"
3897 * Convert string indexes that convert to int jsvals as ints to save memory.
3898 * Care must be taken to use this macro every time a property name is used, or
3899 * else double-sets, incorrect property cache misses, or other mistakes could
3900 * occur.
3902 jsid
3903 js_CheckForStringIndex(jsid id)
3905 if (!JSID_IS_ATOM(id))
3906 return id;
3908 JSAtom *atom = JSID_TO_ATOM(id);
3909 JSString *str = ATOM_TO_STRING(atom);
3910 const jschar *s = str->flatChars();
3911 jschar ch = *s;
3913 JSBool negative = (ch == '-');
3914 if (negative)
3915 ch = *++s;
3917 if (!JS7_ISDEC(ch))
3918 return id;
3920 size_t n = str->flatLength() - negative;
3921 if (n > sizeof(JSBOXEDWORD_INT_MAX_STRING) - 1)
3922 return id;
3924 const jschar *cp = s;
3925 const jschar *end = s + n;
3927 jsuint index = JS7_UNDEC(*cp++);
3928 jsuint oldIndex = 0;
3929 jsuint c = 0;
3931 if (index != 0) {
3932 while (JS7_ISDEC(*cp)) {
3933 oldIndex = index;
3934 c = JS7_UNDEC(*cp);
3935 index = 10 * index + c;
3936 cp++;
3941 * Non-integer indexes can't be represented as integers. Also, distinguish
3942 * index "-0" from "0", because JSBOXEDWORD_INT cannot.
3944 if (cp != end || (negative && index == 0))
3945 return id;
3947 if (oldIndex < JSID_INT_MAX / 10 ||
3948 (oldIndex == JSID_INT_MAX / 10 && c <= (JSID_INT_MAX % 10))) {
3949 if (negative)
3950 index = 0 - index;
3951 id = INT_TO_JSID((jsint)index);
3954 return id;
3957 static JSBool
3958 PurgeProtoChain(JSContext *cx, JSObject *obj, jsid id)
3960 JSScope *scope;
3961 JSScopeProperty *sprop;
3963 while (obj) {
3964 if (!obj->isNative()) {
3965 obj = obj->getProto();
3966 continue;
3968 JS_LOCK_OBJ(cx, obj);
3969 scope = obj->scope();
3970 sprop = scope->lookup(id);
3971 if (sprop) {
3972 PCMETER(JS_PROPERTY_CACHE(cx).pcpurges++);
3973 scope->shadowingShapeChange(cx, sprop);
3974 JS_UNLOCK_SCOPE(cx, scope);
3976 if (!obj->getParent()) {
3978 * All scope chains end in a global object, so this will change
3979 * the global shape. jstracer.cpp assumes that the global shape
3980 * never changes on trace, so we must deep-bail here.
3982 LeaveTrace(cx);
3984 return JS_TRUE;
3986 obj = obj->getProto();
3987 JS_UNLOCK_SCOPE(cx, scope);
3989 return JS_FALSE;
3992 void
3993 js_PurgeScopeChainHelper(JSContext *cx, JSObject *obj, jsid id)
3995 JS_ASSERT(obj->isDelegate());
3996 PurgeProtoChain(cx, obj->getProto(), id);
3999 * We must purge the scope chain only for Call objects as they are the only
4000 * kind of cacheable non-global object that can gain properties after outer
4001 * properties with the same names have been cached or traced. Call objects
4002 * may gain such properties via eval introducing new vars; see bug 490364.
4004 if (obj->getClass() == &js_CallClass) {
4005 while ((obj = obj->getParent()) != NULL) {
4006 if (PurgeProtoChain(cx, obj, id))
4007 break;
4012 JSScopeProperty *
4013 js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id,
4014 PropertyOp getter, PropertyOp setter, uint32 slot,
4015 uintN attrs, uintN flags, intN shortid)
4017 JSScope *scope;
4018 JSScopeProperty *sprop;
4020 JS_ASSERT(!(flags & JSScopeProperty::METHOD));
4023 * Purge the property cache of now-shadowed id in obj's scope chain. Do
4024 * this optimistically (assuming no failure below) before locking obj, so
4025 * we can lock the shadowed scope.
4027 js_PurgeScopeChain(cx, obj, id);
4029 JS_LOCK_OBJ(cx, obj);
4030 scope = js_GetMutableScope(cx, obj);
4031 if (!scope) {
4032 sprop = NULL;
4033 } else {
4034 /* Convert string indices to integers if appropriate. */
4035 id = js_CheckForStringIndex(id);
4036 sprop = scope->putProperty(cx, id, getter, setter, slot, attrs, flags, shortid);
4038 JS_UNLOCK_OBJ(cx, obj);
4039 return sprop;
4042 JSScopeProperty *
4043 js_ChangeNativePropertyAttrs(JSContext *cx, JSObject *obj,
4044 JSScopeProperty *sprop, uintN attrs, uintN mask,
4045 PropertyOp getter, PropertyOp setter)
4047 JSScope *scope;
4049 JS_LOCK_OBJ(cx, obj);
4050 scope = js_GetMutableScope(cx, obj);
4051 if (!scope) {
4052 sprop = NULL;
4053 } else {
4054 sprop = scope->changeProperty(cx, sprop, attrs, mask, getter, setter);
4056 JS_UNLOCK_OBJ(cx, obj);
4057 return sprop;
4060 JSBool
4061 js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, const Value *value,
4062 PropertyOp getter, PropertyOp setter, uintN attrs)
4064 return js_DefineNativeProperty(cx, obj, id, *value, getter, setter, attrs,
4065 0, 0, NULL);
4069 * Backward compatibility requires allowing addProperty hooks to mutate the
4070 * nominal initial value of a slot-full property, while GC safety wants that
4071 * value to be stored before the call-out through the hook. Optimize to do
4072 * both while saving cycles for classes that stub their addProperty hook.
4074 static inline bool
4075 AddPropertyHelper(JSContext *cx, Class *clasp, JSObject *obj, JSScope *scope,
4076 JSScopeProperty *sprop, Value *vp)
4078 if (clasp->addProperty != PropertyStub) {
4079 Value nominal = *vp;
4081 if (!callJSPropertyOp(cx, clasp->addProperty, obj, SPROP_USERID(sprop), vp))
4082 return false;
4083 if (*vp != nominal) {
4084 if (SPROP_HAS_VALID_SLOT(sprop, scope))
4085 obj->lockedSetSlot(sprop->slot, *vp);
4088 return true;
4091 JSBool
4092 js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, const Value &value,
4093 PropertyOp getter, PropertyOp setter, uintN attrs,
4094 uintN flags, intN shortid, JSProperty **propp,
4095 uintN defineHow /* = 0 */)
4097 Class *clasp;
4098 JSScope *scope;
4099 JSScopeProperty *sprop;
4100 JSBool added;
4101 Value valueCopy;
4103 JS_ASSERT((defineHow & ~(JSDNP_CACHE_RESULT | JSDNP_DONT_PURGE | JSDNP_SET_METHOD)) == 0);
4104 LeaveTraceIfGlobalObject(cx, obj);
4106 /* Convert string indices to integers if appropriate. */
4107 id = js_CheckForStringIndex(id);
4110 * If defining a getter or setter, we must check for its counterpart and
4111 * update the attributes and property ops. A getter or setter is really
4112 * only half of a property.
4114 sprop = NULL;
4115 if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
4116 JSObject *pobj;
4117 JSProperty *prop;
4120 * If JS_THREADSAFE and id is found, js_LookupProperty returns with
4121 * sprop non-null and pobj locked. If pobj == obj, the property is
4122 * already in obj and obj has its own (mutable) scope. So if we are
4123 * defining a getter whose setter was already defined, or vice versa,
4124 * finish the job via js_ChangeScopePropertyAttributes, and refresh
4125 * the property cache line for (obj, id) to map sprop.
4127 if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
4128 return JS_FALSE;
4129 sprop = (JSScopeProperty *) prop;
4130 if (sprop && pobj == obj && sprop->isAccessorDescriptor()) {
4131 sprop = obj->scope()->changeProperty(cx, sprop, attrs,
4132 JSPROP_GETTER | JSPROP_SETTER,
4133 (attrs & JSPROP_GETTER)
4134 ? getter
4135 : sprop->getter(),
4136 (attrs & JSPROP_SETTER)
4137 ? setter
4138 : sprop->setter());
4140 /* NB: obj == pobj, so we can share unlock code at the bottom. */
4141 if (!sprop)
4142 goto error;
4143 } else if (prop) {
4144 pobj->dropProperty(cx, prop);
4145 prop = NULL;
4146 sprop = NULL;
4151 * Purge the property cache of any properties named by id that are about
4152 * to be shadowed in obj's scope chain unless it is known a priori that it
4153 * is not possible. We do this before locking obj to avoid nesting locks.
4155 if (!(defineHow & JSDNP_DONT_PURGE))
4156 js_PurgeScopeChain(cx, obj, id);
4159 * Check whether a readonly property or setter is being defined on a known
4160 * prototype object. See the comment in jscntxt.h before protoHazardShape's
4161 * member declaration.
4163 if (obj->isDelegate() && (attrs & (JSPROP_READONLY | JSPROP_SETTER)))
4164 cx->runtime->protoHazardShape = js_GenerateShape(cx, false);
4166 /* Lock if object locking is required by this implementation. */
4167 JS_LOCK_OBJ(cx, obj);
4169 /* Use the object's class getter and setter by default. */
4170 clasp = obj->getClass();
4171 if (!(defineHow & JSDNP_SET_METHOD)) {
4172 if (!getter && !(attrs & JSPROP_GETTER))
4173 getter = clasp->getProperty;
4174 if (!setter && !(attrs & JSPROP_SETTER))
4175 setter = clasp->setProperty;
4178 /* Get obj's own scope if it has one, or create a new one for obj. */
4179 scope = js_GetMutableScope(cx, obj);
4180 if (!scope)
4181 goto error;
4183 added = false;
4184 if (!sprop) {
4185 /* Add a new property, or replace an existing one of the same id. */
4186 if (defineHow & JSDNP_SET_METHOD) {
4187 JS_ASSERT(clasp == &js_ObjectClass);
4188 JS_ASSERT(IsFunctionObject(value));
4189 JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
4190 JS_ASSERT(!getter && !setter);
4192 JSObject *funobj = &value.toObject();
4193 if (FUN_OBJECT(GET_FUNCTION_PRIVATE(cx, funobj)) == funobj) {
4194 flags |= JSScopeProperty::METHOD;
4195 getter = CastAsPropertyOp(funobj);
4199 added = !scope->hasProperty(id);
4200 uint32 oldShape = scope->shape;
4201 sprop = scope->putProperty(cx, id, getter, setter, SPROP_INVALID_SLOT,
4202 attrs, flags, shortid);
4203 if (!sprop)
4204 goto error;
4207 * If sprop is a method, the above call to putProperty suffices to
4208 * update the shape if necessary. But if scope->branded(), the shape
4209 * may not have changed and we may be overwriting a function-valued
4210 * property. See bug 560998.
4212 if (scope->shape == oldShape && scope->branded() && sprop->slot != SPROP_INVALID_SLOT)
4213 scope->methodWriteBarrier(cx, sprop->slot, value);
4216 /* Store value before calling addProperty, in case the latter GC's. */
4217 if (SPROP_HAS_VALID_SLOT(sprop, scope))
4218 obj->lockedSetSlot(sprop->slot, value);
4220 /* XXXbe called with lock held */
4221 valueCopy = value;
4222 if (!AddPropertyHelper(cx, clasp, obj, scope, sprop, &valueCopy)) {
4223 scope->removeProperty(cx, id);
4224 goto error;
4227 if (defineHow & JSDNP_CACHE_RESULT) {
4228 #ifdef JS_TRACER
4229 JS_ASSERT_NOT_ON_TRACE(cx);
4230 PropertyCacheEntry *entry =
4231 #endif
4232 JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, 0, obj, sprop, added);
4233 TRACE_2(SetPropHit, entry, sprop);
4235 if (propp)
4236 *propp = (JSProperty *) sprop;
4237 else
4238 JS_UNLOCK_OBJ(cx, obj);
4239 return JS_TRUE;
4241 error: // TRACE_2 jumps here on error, as well.
4242 JS_UNLOCK_OBJ(cx, obj);
4243 return JS_FALSE;
4246 JS_FRIEND_API(JSBool)
4247 js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
4248 JSProperty **propp)
4250 return js_LookupPropertyWithFlags(cx, obj, id, cx->resolveFlags,
4251 objp, propp) >= 0;
4254 #define SCOPE_DEPTH_ACCUM(bs,val) \
4255 JS_SCOPE_DEPTH_METERING(JS_BASIC_STATS_ACCUM(bs, val))
4258 * Call obj's resolve hook. obj is a native object and the caller holds its
4259 * scope lock.
4261 * cx, start, id, and flags are the parameters initially passed to the ongoing
4262 * lookup; objp and propp are its out parameters. obj is an object along
4263 * start's prototype chain.
4265 * There are four possible outcomes:
4267 * - On failure, report an error or exception, unlock obj, and return false.
4269 * - If we are alrady resolving a property of *curobjp, set *recursedp = true,
4270 * unlock obj, and return true.
4272 * - If the resolve hook finds or defines the sought property, set *objp and
4273 * *propp appropriately, set *recursedp = false, and return true with *objp's
4274 * lock held.
4276 * - Otherwise no property was resolved. Set *propp = NULL and *recursedp = false
4277 * and return true.
4279 static JSBool
4280 CallResolveOp(JSContext *cx, JSObject *start, JSObject *obj, jsid id, uintN flags,
4281 JSObject **objp, JSProperty **propp, bool *recursedp)
4283 Class *clasp = obj->getClass();
4284 JSResolveOp resolve = clasp->resolve;
4285 JSScope *scope = obj->scope();
4288 * Avoid recursion on (obj, id) already being resolved on cx.
4290 * Once we have successfully added an entry for (obj, key) to
4291 * cx->resolvingTable, control must go through cleanup: before
4292 * returning. But note that JS_DHASH_ADD may find an existing
4293 * entry, in which case we bail to suppress runaway recursion.
4295 JSResolvingKey key = {obj, id};
4296 JSResolvingEntry *entry;
4297 if (!js_StartResolving(cx, &key, JSRESFLAG_LOOKUP, &entry)) {
4298 JS_UNLOCK_OBJ(cx, obj);
4299 return false;
4301 if (!entry) {
4302 /* Already resolving id in obj -- suppress recursion. */
4303 JS_UNLOCK_OBJ(cx, obj);
4304 *recursedp = true;
4305 return true;
4307 uint32 generation = cx->resolvingTable->generation;
4308 *recursedp = false;
4310 *propp = NULL;
4312 JSBool ok;
4313 JSScopeProperty *sprop = NULL;
4314 if (clasp->flags & JSCLASS_NEW_RESOLVE) {
4315 JSNewResolveOp newresolve = (JSNewResolveOp)resolve;
4316 if (flags == JSRESOLVE_INFER)
4317 flags = js_InferFlags(cx, flags);
4318 JSObject *obj2 = (clasp->flags & JSCLASS_NEW_RESOLVE_GETS_START) ? start : NULL;
4319 JS_UNLOCK_OBJ(cx, obj);
4322 /* Protect id and all atoms from a GC nested in resolve. */
4323 AutoKeepAtoms keep(cx->runtime);
4324 ok = newresolve(cx, obj, id, flags, &obj2);
4326 if (!ok)
4327 goto cleanup;
4329 JS_LOCK_OBJ(cx, obj);
4330 if (obj2) {
4331 /* Resolved: juggle locks and lookup id again. */
4332 if (obj2 != obj) {
4333 JS_UNLOCK_OBJ(cx, obj);
4334 if (obj2->isNative())
4335 JS_LOCK_OBJ(cx, obj2);
4337 if (!obj2->isNative()) {
4338 /* Whoops, newresolve handed back a foreign obj2. */
4339 JS_ASSERT(obj2 != obj);
4340 ok = obj2->lookupProperty(cx, id, objp, propp);
4341 if (!ok || *propp)
4342 goto cleanup;
4343 JS_LOCK_OBJ(cx, obj2);
4344 } else {
4346 * Require that obj2 have its own scope now, as we
4347 * do for old-style resolve. If it doesn't, then
4348 * id was not truly resolved, and we'll find it in
4349 * the proto chain, or miss it if obj2's proto is
4350 * not on obj's proto chain. That last case is a
4351 * "too bad!" case.
4353 scope = obj2->scope();
4354 if (!scope->isSharedEmpty())
4355 sprop = scope->lookup(id);
4357 if (sprop) {
4358 JS_ASSERT(scope == obj2->scope());
4359 JS_ASSERT(!scope->isSharedEmpty());
4360 obj = obj2;
4361 } else if (obj2 != obj) {
4362 if (obj2->isNative())
4363 JS_UNLOCK_OBJ(cx, obj2);
4364 JS_LOCK_OBJ(cx, obj);
4367 } else {
4369 * Old resolve always requires id re-lookup if obj owns
4370 * its scope after resolve returns.
4372 JS_UNLOCK_OBJ(cx, obj);
4373 ok = resolve(cx, obj, id);
4374 if (!ok)
4375 goto cleanup;
4376 JS_LOCK_OBJ(cx, obj);
4377 JS_ASSERT(obj->isNative());
4378 scope = obj->scope();
4379 if (!scope->isSharedEmpty())
4380 sprop = scope->lookup(id);
4383 cleanup:
4384 if (ok && sprop) {
4385 JS_ASSERT(obj->scope() == scope);
4386 *objp = obj;
4387 *propp = (JSProperty *) sprop;
4389 js_StopResolving(cx, &key, JSRESFLAG_LOOKUP, entry, generation);
4390 return ok;
4394 js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags,
4395 JSObject **objp, JSProperty **propp)
4397 /* Convert string indices to integers if appropriate. */
4398 id = js_CheckForStringIndex(id);
4400 /* Search scopes starting with obj and following the prototype link. */
4401 JSObject *start = obj;
4402 int protoIndex;
4403 for (protoIndex = 0; ; protoIndex++) {
4404 JS_LOCK_OBJ(cx, obj);
4405 JSScopeProperty *sprop = obj->scope()->lookup(id);
4406 if (sprop) {
4407 SCOPE_DEPTH_ACCUM(&cx->runtime->protoLookupDepthStats, protoIndex);
4408 *objp = obj;
4409 *propp = (JSProperty *) sprop;
4410 return protoIndex;
4413 /* Try obj's class resolve hook if id was not found in obj's scope. */
4414 if (!sprop && obj->getClass()->resolve != JS_ResolveStub) {
4415 bool recursed;
4416 if (!CallResolveOp(cx, start, obj, id, flags, objp, propp, &recursed))
4417 return -1;
4418 if (recursed)
4419 break;
4420 if (*propp) {
4421 /* Recalculate protoIndex in case it was resolved on some other object. */
4422 protoIndex = 0;
4423 for (JSObject *proto = start; proto && proto != *objp; proto = proto->getProto())
4424 protoIndex++;
4425 SCOPE_DEPTH_ACCUM(&cx->runtime->protoLookupDepthStats, protoIndex);
4426 return protoIndex;
4430 JSObject *proto = obj->getProto();
4431 JS_UNLOCK_OBJ(cx, obj);
4432 if (!proto)
4433 break;
4434 if (!proto->isNative()) {
4435 if (!proto->lookupProperty(cx, id, objp, propp))
4436 return -1;
4437 return protoIndex + 1;
4440 obj = proto;
4443 *objp = NULL;
4444 *propp = NULL;
4445 return protoIndex;
4448 PropertyCacheEntry *
4449 js_FindPropertyHelper(JSContext *cx, jsid id, JSBool cacheResult,
4450 JSObject **objp, JSObject **pobjp, JSProperty **propp)
4452 JSObject *scopeChain, *obj, *parent, *pobj;
4453 PropertyCacheEntry *entry;
4454 int scopeIndex, protoIndex;
4455 JSProperty *prop;
4457 JS_ASSERT_IF(cacheResult, !JS_ON_TRACE(cx));
4458 scopeChain = js_GetTopStackFrame(cx)->getScopeChain();
4460 /* Scan entries on the scope chain that we can cache across. */
4461 entry = JS_NO_PROP_CACHE_FILL;
4462 obj = scopeChain;
4463 parent = obj->getParent();
4464 for (scopeIndex = 0;
4465 parent
4466 ? js_IsCacheableNonGlobalScope(obj)
4467 : !obj->getOps()->lookupProperty;
4468 ++scopeIndex) {
4469 protoIndex =
4470 js_LookupPropertyWithFlags(cx, obj, id, cx->resolveFlags,
4471 &pobj, &prop);
4472 if (protoIndex < 0)
4473 return NULL;
4475 if (prop) {
4476 #ifdef DEBUG
4477 if (parent) {
4478 Class *clasp = obj->getClass();
4479 JS_ASSERT(pobj->isNative());
4480 JS_ASSERT(pobj->getClass() == clasp);
4481 if (clasp == &js_BlockClass) {
4483 * A block instance on the scope chain is immutable and
4484 * the compile-time prototype provides all its properties.
4486 JS_ASSERT(pobj == obj->getProto());
4487 JS_ASSERT(protoIndex == 1);
4488 } else {
4489 /* Call and DeclEnvClass objects have no prototypes. */
4490 JS_ASSERT(!obj->getProto());
4491 JS_ASSERT(protoIndex == 0);
4493 } else {
4494 JS_ASSERT(obj->isNative());
4496 #endif
4498 * We must check if pobj is native as a global object can have
4499 * non-native prototype.
4501 if (cacheResult && pobj->isNative()) {
4502 entry = JS_PROPERTY_CACHE(cx).fill(cx, scopeChain, scopeIndex,
4503 protoIndex, pobj,
4504 (JSScopeProperty *) prop);
4506 SCOPE_DEPTH_ACCUM(&cx->runtime->scopeSearchDepthStats, scopeIndex);
4507 goto out;
4510 if (!parent) {
4511 pobj = NULL;
4512 goto out;
4514 obj = parent;
4515 parent = obj->getParent();
4518 for (;;) {
4519 if (!obj->lookupProperty(cx, id, &pobj, &prop))
4520 return NULL;
4521 if (prop) {
4522 PCMETER(JS_PROPERTY_CACHE(cx).nofills++);
4523 goto out;
4527 * We conservatively assume that a resolve hook could mutate the scope
4528 * chain during JSObject::lookupProperty. So we read parent here again.
4530 parent = obj->getParent();
4531 if (!parent) {
4532 pobj = NULL;
4533 break;
4535 obj = parent;
4538 out:
4539 JS_ASSERT(!!pobj == !!prop);
4540 *objp = obj;
4541 *pobjp = pobj;
4542 *propp = prop;
4543 return entry;
4546 JS_FRIEND_API(JSBool)
4547 js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp,
4548 JSProperty **propp)
4550 return !!js_FindPropertyHelper(cx, id, false, objp, pobjp, propp);
4553 JSObject *
4554 js_FindIdentifierBase(JSContext *cx, JSObject *scopeChain, jsid id)
4557 * This function should not be called for a global object or from the
4558 * trace and should have a valid cache entry for native scopeChain.
4560 JS_ASSERT(scopeChain->getParent());
4561 JS_ASSERT(!JS_ON_TRACE(cx));
4563 JSObject *obj = scopeChain;
4566 * Loop over cacheable objects on the scope chain until we find a
4567 * property. We also stop when we reach the global object skipping any
4568 * farther checks or lookups. For details see the JSOP_BINDNAME case of
4569 * js_Interpret.
4571 * The test order here matters because js_IsCacheableNonGlobalScope
4572 * must not be passed a global object (i.e. one with null parent).
4574 for (int scopeIndex = 0;
4575 !obj->getParent() || js_IsCacheableNonGlobalScope(obj);
4576 scopeIndex++) {
4577 JSObject *pobj;
4578 JSProperty *prop;
4579 int protoIndex = js_LookupPropertyWithFlags(cx, obj, id,
4580 cx->resolveFlags,
4581 &pobj, &prop);
4582 if (protoIndex < 0)
4583 return NULL;
4584 if (prop) {
4585 if (!pobj->isNative()) {
4586 JS_ASSERT(!obj->getParent());
4587 return obj;
4589 JS_ASSERT_IF(obj->getParent(), pobj->getClass() == obj->getClass());
4590 #ifdef DEBUG
4591 PropertyCacheEntry *entry =
4592 #endif
4593 JS_PROPERTY_CACHE(cx).fill(cx, scopeChain, scopeIndex, protoIndex, pobj,
4594 (JSScopeProperty *) prop);
4595 JS_ASSERT(entry);
4596 JS_UNLOCK_OBJ(cx, pobj);
4597 return obj;
4600 JSObject *parent = obj->getParent();
4601 if (!parent)
4602 return obj;
4603 obj = parent;
4606 /* Loop until we find a property or reach the global object. */
4607 do {
4608 JSObject *pobj;
4609 JSProperty *prop;
4610 if (!obj->lookupProperty(cx, id, &pobj, &prop))
4611 return NULL;
4612 if (prop) {
4613 pobj->dropProperty(cx, prop);
4614 break;
4618 * We conservatively assume that a resolve hook could mutate the scope
4619 * chain during JSObject::lookupProperty. So we must check if parent is
4620 * not null here even if it wasn't before the lookup.
4622 JSObject *parent = obj->getParent();
4623 if (!parent)
4624 break;
4625 obj = parent;
4626 } while (obj->getParent());
4627 return obj;
4630 JSBool
4631 js_NativeGet(JSContext *cx, JSObject *obj, JSObject *pobj,
4632 JSScopeProperty *sprop, uintN getHow, Value *vp)
4634 LeaveTraceIfGlobalObject(cx, pobj);
4636 JSScope *scope;
4637 uint32 slot;
4638 int32 sample;
4640 JS_ASSERT(pobj->isNative());
4641 JS_ASSERT(JS_IS_OBJ_LOCKED(cx, pobj));
4642 scope = pobj->scope();
4644 slot = sprop->slot;
4645 if (slot != SPROP_INVALID_SLOT)
4646 *vp = pobj->lockedGetSlot(slot);
4647 else
4648 vp->setUndefined();
4649 if (sprop->hasDefaultGetter())
4650 return true;
4652 if (JS_UNLIKELY(sprop->isMethod()) && (getHow & JSGET_NO_METHOD_BARRIER)) {
4653 JS_ASSERT(&sprop->methodObject() == &vp->toObject());
4654 return true;
4657 sample = cx->runtime->propertyRemovals;
4658 JS_UNLOCK_SCOPE(cx, scope);
4660 AutoScopePropertyRooter tvr(cx, sprop);
4661 AutoObjectRooter tvr2(cx, pobj);
4662 if (!sprop->get(cx, obj, pobj, vp))
4663 return false;
4665 JS_LOCK_SCOPE(cx, scope);
4667 if (SLOT_IN_SCOPE(slot, scope) &&
4668 (JS_LIKELY(cx->runtime->propertyRemovals == sample) ||
4669 scope->hasProperty(sprop))) {
4670 if (!scope->methodWriteBarrier(cx, sprop, *vp)) {
4671 JS_UNLOCK_SCOPE(cx, scope);
4672 return false;
4674 pobj->lockedSetSlot(slot, *vp);
4677 return true;
4680 JSBool
4681 js_NativeSet(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, bool added,
4682 Value *vp)
4684 LeaveTraceIfGlobalObject(cx, obj);
4686 JSScope *scope;
4687 uint32 slot;
4688 int32 sample;
4690 JS_ASSERT(obj->isNative());
4691 JS_ASSERT(JS_IS_OBJ_LOCKED(cx, obj));
4692 scope = obj->scope();
4694 slot = sprop->slot;
4695 if (slot != SPROP_INVALID_SLOT) {
4696 OBJ_CHECK_SLOT(obj, slot);
4698 /* If sprop has a stub setter, keep scope locked and just store *vp. */
4699 if (sprop->hasDefaultSetter()) {
4700 if (!added && !scope->methodWriteBarrier(cx, sprop, *vp)) {
4701 JS_UNLOCK_SCOPE(cx, scope);
4702 return false;
4704 obj->lockedSetSlot(slot, *vp);
4705 return true;
4707 } else {
4709 * Allow API consumers to create shared properties with stub setters.
4710 * Such properties effectively function as data descriptors which are
4711 * not writable, so attempting to set such a property should do nothing
4712 * or throw if we're in strict mode.
4714 if (!sprop->hasGetterValue() && sprop->hasDefaultSetter())
4715 return js_ReportGetterOnlyAssignment(cx);
4718 sample = cx->runtime->propertyRemovals;
4719 JS_UNLOCK_SCOPE(cx, scope);
4721 AutoScopePropertyRooter tvr(cx, sprop);
4722 if (!sprop->set(cx, obj, vp))
4723 return false;
4726 JS_LOCK_SCOPE(cx, scope);
4727 if (SLOT_IN_SCOPE(slot, scope) &&
4728 (JS_LIKELY(cx->runtime->propertyRemovals == sample) ||
4729 scope->hasProperty(sprop))) {
4730 if (!added && !scope->methodWriteBarrier(cx, sprop, *vp)) {
4731 JS_UNLOCK_SCOPE(cx, scope);
4732 return false;
4734 obj->lockedSetSlot(slot, *vp);
4737 return true;
4740 JSBool
4741 js_GetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN getHow,
4742 Value *vp)
4744 JSObject *aobj, *obj2;
4745 int protoIndex;
4746 JSProperty *prop;
4747 JSScopeProperty *sprop;
4749 JS_ASSERT_IF(getHow & JSGET_CACHE_RESULT, !JS_ON_TRACE(cx));
4751 /* Convert string indices to integers if appropriate. */
4752 id = js_CheckForStringIndex(id);
4754 aobj = js_GetProtoIfDenseArray(obj);
4755 protoIndex = js_LookupPropertyWithFlags(cx, aobj, id, cx->resolveFlags,
4756 &obj2, &prop);
4757 if (protoIndex < 0)
4758 return JS_FALSE;
4759 if (!prop) {
4760 vp->setUndefined();
4762 if (!callJSPropertyOp(cx, obj->getClass()->getProperty, obj, id, vp))
4763 return JS_FALSE;
4765 PCMETER(getHow & JSGET_CACHE_RESULT && JS_PROPERTY_CACHE(cx).nofills++);
4768 * Give a strict warning if foo.bar is evaluated by a script for an
4769 * object foo with no property named 'bar'.
4771 jsbytecode *pc;
4772 if (vp->isUndefined() && ((pc = js_GetCurrentBytecodePC(cx)) != NULL)) {
4773 JSOp op;
4774 uintN flags;
4776 op = (JSOp) *pc;
4777 if (op == JSOP_TRAP) {
4778 JS_ASSERT_NOT_ON_TRACE(cx);
4779 op = JS_GetTrapOpcode(cx, cx->fp->script, pc);
4781 if (op == JSOP_GETXPROP) {
4782 flags = JSREPORT_ERROR;
4783 } else {
4784 if (!JS_HAS_STRICT_OPTION(cx) ||
4785 (op != JSOP_GETPROP && op != JSOP_GETELEM) ||
4786 js_CurrentPCIsInImacro(cx)) {
4787 return JS_TRUE;
4791 * XXX do not warn about missing __iterator__ as the function
4792 * may be called from JS_GetMethodById. See bug 355145.
4794 if (JSID_IS_ATOM(id, cx->runtime->atomState.iteratorAtom))
4795 return JS_TRUE;
4797 /* Do not warn about tests like (obj[prop] == undefined). */
4798 if (cx->resolveFlags == JSRESOLVE_INFER) {
4799 LeaveTrace(cx);
4800 pc += js_CodeSpec[op].length;
4801 if (Detecting(cx, pc))
4802 return JS_TRUE;
4803 } else if (cx->resolveFlags & JSRESOLVE_DETECTING) {
4804 return JS_TRUE;
4807 flags = JSREPORT_WARNING | JSREPORT_STRICT;
4810 /* Ok, bad undefined property reference: whine about it. */
4811 if (!js_ReportValueErrorFlags(cx, flags, JSMSG_UNDEFINED_PROP,
4812 JSDVG_IGNORE_STACK, IdToValue(id),
4813 NULL, NULL, NULL)) {
4814 return JS_FALSE;
4817 return JS_TRUE;
4820 if (!obj2->isNative())
4821 return obj2->getProperty(cx, id, vp);
4823 sprop = (JSScopeProperty *) prop;
4825 if (getHow & JSGET_CACHE_RESULT) {
4826 JS_ASSERT_NOT_ON_TRACE(cx);
4827 JS_PROPERTY_CACHE(cx).fill(cx, aobj, 0, protoIndex, obj2, sprop);
4830 if (!js_NativeGet(cx, obj, obj2, sprop, getHow, vp))
4831 return JS_FALSE;
4833 JS_UNLOCK_OBJ(cx, obj2);
4834 return JS_TRUE;
4837 JSBool
4838 js_GetProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
4840 return js_GetPropertyHelper(cx, obj, id, JSGET_METHOD_BARRIER, vp);
4843 JSBool
4844 js_GetMethod(JSContext *cx, JSObject *obj, jsid id, uintN getHow, Value *vp)
4846 JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED);
4848 PropertyIdOp op = obj->getOps()->getProperty;
4849 if (!op) {
4850 #if JS_HAS_XML_SUPPORT
4851 JS_ASSERT(!obj->isXML());
4852 #endif
4853 return js_GetPropertyHelper(cx, obj, id, getHow, vp);
4855 JS_ASSERT_IF(getHow & JSGET_CACHE_RESULT, obj->isDenseArray());
4856 #if JS_HAS_XML_SUPPORT
4857 if (obj->isXML())
4858 return js_GetXMLMethod(cx, obj, id, vp);
4859 #endif
4860 return op(cx, obj, id, vp);
4863 JS_FRIEND_API(bool)
4864 js_CheckUndeclaredVarAssignment(JSContext *cx, JSString *propname)
4866 JSStackFrame *const fp = js_GetTopStackFrame(cx);
4867 if (!fp)
4868 return true;
4870 /* If neither cx nor the code is strict, then no check is needed. */
4871 if (!(fp->script && fp->script->strictModeCode) &&
4872 !JS_HAS_STRICT_OPTION(cx)) {
4873 return true;
4876 const char *bytes = js_GetStringBytes(cx, propname);
4877 return bytes &&
4878 JS_ReportErrorFlagsAndNumber(cx,
4879 (JSREPORT_WARNING | JSREPORT_STRICT
4880 | JSREPORT_STRICT_MODE_ERROR),
4881 js_GetErrorMessage, NULL,
4882 JSMSG_UNDECLARED_VAR, bytes);
4885 namespace js {
4887 JSBool
4888 ReportReadOnly(JSContext* cx, jsid id, uintN flags)
4890 return js_ReportValueErrorFlags(cx, flags, JSMSG_READ_ONLY,
4891 JSDVG_IGNORE_STACK, IdToValue(id), NULL,
4892 NULL, NULL);
4898 * Note: all non-error exits in this function must notify the tracer using
4899 * SetPropHit when called from the interpreter, which is detected by testing
4900 * (defineHow & JSDNP_CACHE_RESULT).
4902 JSBool
4903 js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow,
4904 Value *vp)
4906 int protoIndex;
4907 JSObject *pobj;
4908 JSProperty *prop;
4909 JSScopeProperty *sprop;
4910 JSScope *scope;
4911 uintN attrs, flags;
4912 intN shortid;
4913 Class *clasp;
4914 PropertyOp getter, setter;
4915 bool added;
4917 JS_ASSERT((defineHow &
4918 ~(JSDNP_CACHE_RESULT | JSDNP_SET_METHOD | JSDNP_UNQUALIFIED)) == 0);
4919 if (defineHow & JSDNP_CACHE_RESULT)
4920 JS_ASSERT_NOT_ON_TRACE(cx);
4922 /* Convert string indices to integers if appropriate. */
4923 id = js_CheckForStringIndex(id);
4926 * We peek at obj->scope() without locking obj. Any race means a failure
4927 * to seal before sharing, which is inherently ambiguous.
4929 if (obj->scope()->sealed() && obj->scope()->object == obj)
4930 return ReportReadOnly(cx, id, JSREPORT_ERROR);
4932 protoIndex = js_LookupPropertyWithFlags(cx, obj, id, cx->resolveFlags,
4933 &pobj, &prop);
4934 if (protoIndex < 0)
4935 return JS_FALSE;
4936 if (prop) {
4937 if (!pobj->isNative())
4938 prop = NULL;
4939 } else {
4940 /* We should never add properties to lexical blocks. */
4941 JS_ASSERT(obj->getClass() != &js_BlockClass);
4943 if (!obj->getParent() &&
4944 (defineHow & JSDNP_UNQUALIFIED) &&
4945 !js_CheckUndeclaredVarAssignment(cx, JSID_TO_STRING(id))) {
4946 return JS_FALSE;
4949 sprop = (JSScopeProperty *) prop;
4952 * Now either sprop is null, meaning id was not found in obj or one of its
4953 * prototypes; or sprop is non-null, meaning id was found in pobj's scope.
4954 * If JS_THREADSAFE and sprop is non-null, then scope is locked, and sprop
4955 * is held: we must JSObject::dropProperty or JS_UNLOCK_SCOPE before we
4956 * return (the two are equivalent for native objects, but we use
4957 * JS_UNLOCK_SCOPE because it is cheaper).
4959 attrs = JSPROP_ENUMERATE;
4960 flags = 0;
4961 shortid = 0;
4962 clasp = obj->getClass();
4963 getter = clasp->getProperty;
4964 setter = clasp->setProperty;
4966 if (sprop) {
4968 * Set scope for use below. It was locked by js_LookupProperty, and
4969 * we know pobj owns it (i.e., scope->object == pobj). Therefore we
4970 * optimize JS_UNLOCK_OBJ(cx, pobj) into JS_UNLOCK_SCOPE(cx, scope).
4972 scope = pobj->scope();
4974 /* ES5 8.12.4 [[Put]] step 2. */
4975 if (sprop->isAccessorDescriptor()) {
4976 if (sprop->hasDefaultSetter()) {
4977 JS_UNLOCK_SCOPE(cx, scope);
4978 if (defineHow & JSDNP_CACHE_RESULT)
4979 TRACE_2(SetPropHit, JS_NO_PROP_CACHE_FILL, sprop);
4980 return js_ReportGetterOnlyAssignment(cx);
4982 } else {
4983 JS_ASSERT(sprop->isDataDescriptor());
4985 if (!sprop->writable()) {
4986 JS_UNLOCK_SCOPE(cx, scope);
4988 PCMETER((defineHow & JSDNP_CACHE_RESULT) && JS_PROPERTY_CACHE(cx).rofills++);
4989 if (defineHow & JSDNP_CACHE_RESULT) {
4990 JS_ASSERT_NOT_ON_TRACE(cx);
4991 TRACE_2(SetPropHit, JS_NO_PROP_CACHE_FILL, sprop);
4994 /* Warn in strict mode, otherwise do nothing. */
4995 if (JS_HAS_STRICT_OPTION(cx))
4996 return ReportReadOnly(cx, id, JSREPORT_STRICT | JSREPORT_WARNING);
4997 return JS_TRUE;
4999 #ifdef JS_TRACER
5000 error: // TRACE_2 jumps here in case of error.
5001 return JS_FALSE;
5002 #endif
5005 if (scope->sealed() && !sprop->hasSlot()) {
5006 JS_UNLOCK_SCOPE(cx, scope);
5007 return ReportReadOnly(cx, id, JSREPORT_ERROR);
5010 attrs = sprop->attributes();
5011 if (pobj != obj) {
5013 * We found id in a prototype object: prepare to share or shadow.
5015 * NB: Thanks to the immutable, garbage-collected property tree
5016 * maintained by jsscope.c in cx->runtime, we needn't worry about
5017 * sprop going away behind our back after we've unlocked scope.
5019 JS_UNLOCK_SCOPE(cx, scope);
5021 /* Don't clone a prototype property that doesn't have a slot. */
5022 if (!sprop->hasSlot()) {
5023 if (defineHow & JSDNP_CACHE_RESULT) {
5024 #ifdef JS_TRACER
5025 JS_ASSERT_NOT_ON_TRACE(cx);
5026 PropertyCacheEntry *entry =
5027 #endif
5028 JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, protoIndex, pobj, sprop);
5029 TRACE_2(SetPropHit, entry, sprop);
5032 if (sprop->hasDefaultSetter() && !sprop->hasGetterValue())
5033 return JS_TRUE;
5035 return sprop->set(cx, obj, vp);
5038 /* Restore attrs to the ECMA default for new properties. */
5039 attrs = JSPROP_ENUMERATE;
5042 * Preserve the shortid, getter, and setter when shadowing any
5043 * property that has a shortid. An old API convention requires
5044 * that the property's getter and setter functions receive the
5045 * shortid, not id, when they are called on the shadow we are
5046 * about to create in obj's scope.
5048 if (sprop->hasShortID()) {
5049 flags = JSScopeProperty::HAS_SHORTID;
5050 shortid = sprop->shortid;
5051 getter = sprop->getter();
5052 setter = sprop->setter();
5056 * Forget we found the proto-property now that we've copied any
5057 * needed member values.
5059 sprop = NULL;
5061 #ifdef __GNUC__ /* suppress bogus gcc warnings */
5062 } else {
5063 scope = NULL;
5064 #endif
5067 added = false;
5068 if (!sprop) {
5070 * Purge the property cache of now-shadowed id in obj's scope chain.
5071 * Do this early, before locking obj to avoid nesting locks.
5073 js_PurgeScopeChain(cx, obj, id);
5075 /* Find or make a property descriptor with the right heritage. */
5076 JS_LOCK_OBJ(cx, obj);
5077 scope = js_GetMutableScope(cx, obj);
5078 if (!scope) {
5079 JS_UNLOCK_OBJ(cx, obj);
5080 return JS_FALSE;
5084 * Check for Object class here to avoid defining a method on a class
5085 * with magic resolve, addProperty, getProperty, etc. hooks.
5087 if ((defineHow & JSDNP_SET_METHOD) && obj->canHaveMethodBarrier()) {
5088 JS_ASSERT(IsFunctionObject(*vp));
5089 JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
5091 JSObject *funobj = &vp->toObject();
5092 JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
5093 if (fun == funobj) {
5094 flags |= JSScopeProperty::METHOD;
5095 getter = CastAsPropertyOp(funobj);
5099 sprop = scope->putProperty(cx, id, getter, setter, SPROP_INVALID_SLOT,
5100 attrs, flags, shortid);
5101 if (!sprop) {
5102 JS_UNLOCK_SCOPE(cx, scope);
5103 return JS_FALSE;
5107 * Initialize the new property value (passed to setter) to undefined.
5108 * Note that we store before calling addProperty, to match the order
5109 * in js_DefineNativeProperty.
5111 if (SPROP_HAS_VALID_SLOT(sprop, scope))
5112 obj->lockedSetSlot(sprop->slot, UndefinedValue());
5114 /* XXXbe called with obj locked */
5115 if (!AddPropertyHelper(cx, clasp, obj, scope, sprop, vp)) {
5116 scope->removeProperty(cx, id);
5117 JS_UNLOCK_SCOPE(cx, scope);
5118 return JS_FALSE;
5120 added = true;
5123 if (defineHow & JSDNP_CACHE_RESULT) {
5124 #ifdef JS_TRACER
5125 JS_ASSERT_NOT_ON_TRACE(cx);
5126 PropertyCacheEntry *entry =
5127 #endif
5128 JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, 0, obj, sprop, added);
5129 TRACE_2(SetPropHit, entry, sprop);
5132 if (!js_NativeSet(cx, obj, sprop, added, vp))
5133 return NULL;
5135 JS_UNLOCK_SCOPE(cx, scope);
5136 return JS_TRUE;
5139 JSBool
5140 js_SetProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
5142 return js_SetPropertyHelper(cx, obj, id, 0, vp);
5145 JSBool
5146 js_GetAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp)
5148 JSProperty *prop;
5149 if (!js_LookupProperty(cx, obj, id, &obj, &prop))
5150 return false;
5151 if (!prop) {
5152 *attrsp = 0;
5153 return true;
5155 if (!obj->isNative())
5156 return obj->getAttributes(cx, id, attrsp);
5158 JSScopeProperty *sprop = (JSScopeProperty *)prop;
5159 *attrsp = sprop->attributes();
5160 JS_UNLOCK_OBJ(cx, obj);
5161 return true;
5164 JSBool
5165 js_SetNativeAttributes(JSContext *cx, JSObject *obj, JSScopeProperty *sprop,
5166 uintN attrs)
5168 JS_ASSERT(obj->isNative());
5169 sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop, attrs, 0,
5170 sprop->getter(), sprop->setter());
5171 JS_UNLOCK_OBJ(cx, obj);
5172 return (sprop != NULL);
5175 JSBool
5176 js_SetAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp)
5178 JSProperty *prop;
5179 if (!js_LookupProperty(cx, obj, id, &obj, &prop))
5180 return false;
5181 if (!prop)
5182 return true;
5183 return obj->isNative()
5184 ? js_SetNativeAttributes(cx, obj, (JSScopeProperty *) prop, *attrsp)
5185 : obj->setAttributes(cx, id, attrsp);
5188 JSBool
5189 js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, Value *rval)
5191 JSObject *proto;
5192 JSProperty *prop;
5193 JSScopeProperty *sprop;
5194 JSScope *scope;
5195 JSBool ok;
5197 rval->setBoolean(true);
5199 /* Convert string indices to integers if appropriate. */
5200 id = js_CheckForStringIndex(id);
5202 if (!js_LookupProperty(cx, obj, id, &proto, &prop))
5203 return JS_FALSE;
5204 if (!prop || proto != obj) {
5206 * If the property was found in a native prototype, check whether it's
5207 * shared and permanent. Such a property stands for direct properties
5208 * in all delegating objects, matching ECMA semantics without bloating
5209 * each delegating object.
5211 if (prop) {
5212 if (proto->isNative()) {
5213 sprop = (JSScopeProperty *)prop;
5214 if (sprop->isSharedPermanent())
5215 rval->setBoolean(false);
5216 JS_UNLOCK_OBJ(cx, proto);
5218 if (rval->isBoolean() && rval->toBoolean() == false)
5219 return JS_TRUE;
5223 * If no property, or the property comes unshared or impermanent from
5224 * a prototype, call the class's delProperty hook, passing rval as the
5225 * result parameter.
5227 return callJSPropertyOp(cx, obj->getClass()->delProperty, obj, id, rval);
5230 sprop = (JSScopeProperty *)prop;
5231 if (!sprop->configurable()) {
5232 JS_UNLOCK_OBJ(cx, obj);
5233 rval->setBoolean(false);
5234 return JS_TRUE;
5237 /* XXXbe called with obj locked */
5238 if (!callJSPropertyOp(cx, obj->getClass()->delProperty, obj, SPROP_USERID(sprop), rval)) {
5239 JS_UNLOCK_OBJ(cx, obj);
5240 return JS_FALSE;
5243 scope = obj->scope();
5244 if (SPROP_HAS_VALID_SLOT(sprop, scope)) {
5245 const Value &v = obj->lockedGetSlot(sprop->slot);
5246 GC_POKE(cx, v);
5249 * Delete is rare enough that we can take the hit of checking for an
5250 * active cloned method function object that must be homed to a callee
5251 * slot on the active stack frame before this delete completes, in case
5252 * someone saved the clone and checks it against foo.caller for a foo
5253 * called from the active method.
5255 * We do not check suspended frames. They can't be reached via caller,
5256 * so the only way they could have the method's joined function object
5257 * as callee is through an API abusage. We break any such edge case.
5259 if (scope->hasMethodBarrier()) {
5260 JSObject *funobj;
5262 if (IsFunctionObject(v, &funobj)) {
5263 JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
5265 if (fun != funobj) {
5266 for (JSStackFrame *fp = cx->fp; fp; fp = fp->down) {
5267 if (fp->callee() == fun &&
5268 fp->thisv.isObject() &&
5269 &fp->thisv.toObject() == obj) {
5270 fp->setCalleeObject(*funobj);
5278 ok = scope->removeProperty(cx, id);
5279 JS_UNLOCK_OBJ(cx, obj);
5281 return ok && js_SuppressDeletedProperty(cx, obj, id);
5284 namespace js {
5286 JSBool
5287 DefaultValue(JSContext *cx, JSObject *obj, JSType hint, Value *vp)
5289 JS_ASSERT(hint != JSTYPE_OBJECT && hint != JSTYPE_FUNCTION);
5291 Value v = ObjectValue(*obj);
5292 if (hint == JSTYPE_STRING) {
5294 * Optimize for String objects with standard toString methods. Support
5295 * new String(...) instances whether mutated to have their own scope or
5296 * not, as well as direct String.prototype references.
5298 if (obj->getClass() == &js_StringClass) {
5299 jsid toStringId = ATOM_TO_JSID(cx->runtime->atomState.toStringAtom);
5301 JS_LOCK_OBJ(cx, obj);
5302 JSScope *scope = obj->scope();
5303 JSScopeProperty *sprop = scope->lookup(toStringId);
5304 JSObject *pobj = obj;
5306 if (!sprop) {
5307 pobj = obj->getProto();
5309 if (pobj && pobj->getClass() == &js_StringClass) {
5310 JS_UNLOCK_SCOPE(cx, scope);
5311 JS_LOCK_OBJ(cx, pobj);
5312 scope = pobj->scope();
5313 sprop = scope->lookup(toStringId);
5317 if (sprop && sprop->hasDefaultGetter() && SPROP_HAS_VALID_SLOT(sprop, scope)) {
5318 const Value &fval = pobj->lockedGetSlot(sprop->slot);
5320 JSObject *funobj;
5321 if (IsFunctionObject(fval, &funobj)) {
5322 JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
5324 if (FUN_FAST_NATIVE(fun) == js_str_toString) {
5325 JS_UNLOCK_SCOPE(cx, scope);
5326 *vp = obj->getPrimitiveThis();
5327 return JS_TRUE;
5331 JS_UNLOCK_SCOPE(cx, scope);
5335 * Propagate the exception if js_TryMethod finds an appropriate
5336 * method, and calling that method returned failure.
5338 if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom,
5339 0, NULL, &v)) {
5340 return JS_FALSE;
5343 if (!v.isPrimitive()) {
5344 if (!obj->getClass()->convert(cx, obj, hint, &v))
5345 return JS_FALSE;
5347 } else {
5348 if (!obj->getClass()->convert(cx, obj, hint, &v))
5349 return JS_FALSE;
5350 if (v.isObject()) {
5351 JS_ASSERT(hint != TypeOfValue(cx, v));
5352 if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0, NULL, &v))
5353 return JS_FALSE;
5356 if (v.isObject()) {
5357 /* Avoid recursive death when decompiling in js_ReportValueError. */
5358 JSString *str;
5359 if (hint == JSTYPE_STRING) {
5360 str = JS_InternString(cx, obj->getClass()->name);
5361 if (!str)
5362 return JS_FALSE;
5363 } else {
5364 str = NULL;
5366 vp->setObject(*obj);
5367 js_ReportValueError2(cx, JSMSG_CANT_CONVERT_TO,
5368 JSDVG_SEARCH_STACK, *vp, str,
5369 (hint == JSTYPE_VOID)
5370 ? "primitive type"
5371 : JS_TYPE_STR(hint));
5372 return JS_FALSE;
5374 *vp = v;
5375 return JS_TRUE;
5378 } /* namespace js */
5380 JS_FRIEND_API(JSBool)
5381 js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, Value *statep, jsid *idp)
5383 /* If the class has a custom JSCLASS_NEW_ENUMERATE hook, call it. */
5384 Class *clasp = obj->getClass();
5385 JSEnumerateOp enumerate = clasp->enumerate;
5386 if (clasp->flags & JSCLASS_NEW_ENUMERATE) {
5387 JS_ASSERT(enumerate != JS_EnumerateStub);
5388 return ((NewEnumerateOp) enumerate)(cx, obj, enum_op, statep, idp);
5391 if (!enumerate(cx, obj))
5392 return false;
5394 /* Tell InitNativeIterator to treat us like a native object. */
5395 JS_ASSERT(enum_op == JSENUMERATE_INIT || enum_op == JSENUMERATE_INIT_ALL);
5396 statep->setMagic(JS_NATIVE_ENUMERATE);
5397 return true;
5400 namespace js {
5402 JSBool
5403 CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
5404 Value *vp, uintN *attrsp)
5406 JSBool writing;
5407 JSObject *pobj;
5408 JSProperty *prop;
5409 Class *clasp;
5410 JSScopeProperty *sprop;
5411 JSSecurityCallbacks *callbacks;
5412 CheckAccessOp check;
5414 while (JS_UNLIKELY(obj->getClass() == &js_WithClass))
5415 obj = obj->getProto();
5417 writing = (mode & JSACC_WRITE) != 0;
5418 switch (mode & JSACC_TYPEMASK) {
5419 case JSACC_PROTO:
5420 pobj = obj;
5421 if (!writing)
5422 vp->setObjectOrNull(obj->getProto());
5423 *attrsp = JSPROP_PERMANENT;
5424 break;
5426 case JSACC_PARENT:
5427 JS_ASSERT(!writing);
5428 pobj = obj;
5429 vp->setObject(*obj->getParent());
5430 *attrsp = JSPROP_READONLY | JSPROP_PERMANENT;
5431 break;
5433 default:
5434 if (!obj->lookupProperty(cx, id, &pobj, &prop))
5435 return JS_FALSE;
5436 if (!prop) {
5437 if (!writing)
5438 vp->setUndefined();
5439 *attrsp = 0;
5440 pobj = obj;
5441 break;
5444 if (!pobj->isNative()) {
5445 if (!writing) {
5446 vp->setUndefined();
5447 *attrsp = 0;
5449 break;
5452 sprop = (JSScopeProperty *)prop;
5453 *attrsp = sprop->attributes();
5454 if (!writing) {
5455 if (SPROP_HAS_VALID_SLOT(sprop, pobj->scope()))
5456 *vp = pobj->lockedGetSlot(sprop->slot);
5457 else
5458 vp->setUndefined();
5460 JS_UNLOCK_OBJ(cx, pobj);
5464 * If obj's class has a stub (null) checkAccess hook, use the per-runtime
5465 * checkObjectAccess callback, if configured.
5467 * We don't want to require all classes to supply a checkAccess hook; we
5468 * need that hook only for certain classes used when precompiling scripts
5469 * and functions ("brutal sharing"). But for general safety of built-in
5470 * magic properties like __proto__, we route all access checks, even for
5471 * classes that stub out checkAccess, through the global checkObjectAccess
5472 * hook. This covers precompilation-based sharing and (possibly
5473 * unintended) runtime sharing across trust boundaries.
5475 clasp = pobj->getClass();
5476 check = clasp->checkAccess;
5477 if (!check) {
5478 callbacks = JS_GetSecurityCallbacks(cx);
5479 check = callbacks ? Valueify(callbacks->checkObjectAccess) : NULL;
5481 return !check || check(cx, pobj, id, mode, vp);
5486 JSType
5487 js_TypeOf(JSContext *cx, JSObject *obj)
5490 * Unfortunately we have wrappers that are native objects and thus don't
5491 * overwrite js_TypeOf (i.e. XPCCrossOriginWrapper), so we have to
5492 * unwrap here.
5494 obj = obj->wrappedObject(cx);
5497 * ECMA 262, 11.4.3 says that any native object that implements
5498 * [[Call]] should be of type "function". However, RegExp is of
5499 * type "object", not "function", for Web compatibility.
5501 if (obj->isCallable()) {
5502 return (obj->getClass() != &js_RegExpClass)
5503 ? JSTYPE_FUNCTION
5504 : JSTYPE_OBJECT;
5507 return JSTYPE_OBJECT;
5510 #ifdef JS_THREADSAFE
5511 void
5512 js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop)
5514 JS_UNLOCK_OBJ(cx, obj);
5516 #endif
5518 bool
5519 js_IsDelegate(JSContext *cx, JSObject *obj, const Value &v)
5521 if (v.isPrimitive())
5522 return false;
5523 JSObject *obj2 = v.toObject().wrappedObject(cx);
5524 while ((obj2 = obj2->getProto()) != NULL) {
5525 if (obj2 == obj)
5526 return true;
5528 return false;
5531 bool
5532 js::FindClassPrototype(JSContext *cx, JSObject *scope, JSProtoKey protoKey, JSObject **protop,
5533 Class *clasp)
5535 Value v;
5536 if (!js_FindClassObject(cx, scope, protoKey, &v, clasp))
5537 return false;
5539 if (IsFunctionObject(v)) {
5540 JSObject *ctor = &v.toObject();
5541 if (!ctor->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), &v))
5542 return false;
5545 *protop = v.isObject() ? &v.toObject() : NULL;
5546 return true;
5550 * The first part of this function has been hand-expanded and optimized into
5551 * NewBuiltinClassInstance in jsobjinlines.h.
5553 JSBool
5554 js_GetClassPrototype(JSContext *cx, JSObject *scope, JSProtoKey protoKey,
5555 JSObject **protop, Class *clasp)
5557 VOUCH_DOES_NOT_REQUIRE_STACK();
5558 JS_ASSERT(JSProto_Null <= protoKey);
5559 JS_ASSERT(protoKey < JSProto_LIMIT);
5561 if (protoKey != JSProto_Null) {
5562 if (!scope) {
5563 if (cx->fp)
5564 scope = cx->fp->maybeScopeChain();
5565 if (!scope) {
5566 scope = cx->globalObject;
5567 if (!scope) {
5568 *protop = NULL;
5569 return true;
5573 scope = scope->getGlobal();
5574 if (scope->getClass()->flags & JSCLASS_IS_GLOBAL) {
5575 const Value &v = scope->getReservedSlot(JSProto_LIMIT + protoKey);
5576 if (v.isObject()) {
5577 *protop = &v.toObject();
5578 return true;
5583 return FindClassPrototype(cx, scope, protoKey, protop, clasp);
5587 * For shared precompilation of function objects, we support cloning on entry
5588 * to an execution context in which the function declaration or expression
5589 * should be processed as if it were not precompiled, where the precompiled
5590 * function's scope chain does not match the execution context's. The cloned
5591 * function object carries its execution-context scope in its parent slot; it
5592 * links to the precompiled function (the "clone-parent") via its proto slot.
5594 * Note that this prototype-based delegation leaves an unchecked access path
5595 * from the clone to the clone-parent's 'constructor' property. If the clone
5596 * lives in a less privileged or shared scope than the clone-parent, this is
5597 * a security hole, a sharing hazard, or both. Therefore we check all such
5598 * accesses with the following getter/setter pair, which we use when defining
5599 * 'constructor' in f.prototype for all function objects f.
5601 static JSBool
5602 CheckCtorGetAccess(JSContext *cx, JSObject *obj, jsid id, Value *vp)
5604 JSAtom *atom = cx->runtime->atomState.constructorAtom;
5605 JS_ASSERT(id == ATOM_TO_JSID(atom));
5606 uintN attrs;
5607 return CheckAccess(cx, obj, ATOM_TO_JSID(atom), JSACC_READ, vp, &attrs);
5610 static JSBool
5611 CheckCtorSetAccess(JSContext *cx, JSObject *obj, jsid id, Value *vp)
5613 JSAtom *atom = cx->runtime->atomState.constructorAtom;
5614 JS_ASSERT(id == ATOM_TO_JSID(atom));
5615 uintN attrs;
5616 return CheckAccess(cx, obj, ATOM_TO_JSID(atom), JSACC_WRITE, vp, &attrs);
5619 JSBool
5620 js_SetClassPrototype(JSContext *cx, JSObject *ctor, JSObject *proto, uintN attrs)
5623 * Use the given attributes for the prototype property of the constructor,
5624 * as user-defined constructors have a DontDelete prototype (which may be
5625 * reset), while native or "system" constructors have DontEnum | ReadOnly |
5626 * DontDelete.
5628 if (!ctor->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom),
5629 ObjectOrNullValue(proto), PropertyStub, PropertyStub, attrs)) {
5630 return JS_FALSE;
5634 * ECMA says that Object.prototype.constructor, or f.prototype.constructor
5635 * for a user-defined function f, is DontEnum.
5637 return proto->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.constructorAtom),
5638 ObjectOrNullValue(ctor), CheckCtorGetAccess, CheckCtorSetAccess, 0);
5641 JSBool
5642 js_PrimitiveToObject(JSContext *cx, Value *vp)
5644 Value v = *vp;
5645 JS_ASSERT(v.isPrimitive());
5647 Class *clasp;
5648 if (v.isNumber())
5649 clasp = &js_NumberClass;
5650 else if (v.isString())
5651 clasp = &js_StringClass;
5652 else
5653 clasp = &js_BooleanClass;
5655 JSObject *obj = NewBuiltinClassInstance(cx, clasp);
5656 if (!obj)
5657 return JS_FALSE;
5659 obj->setPrimitiveThis(v);
5660 vp->setObject(*obj);
5661 return JS_TRUE;
5664 JSBool
5665 js_ValueToObjectOrNull(JSContext *cx, const Value &v, JSObject **objp)
5667 JSObject *obj;
5669 if (v.isObjectOrNull()) {
5670 obj = v.toObjectOrNull();
5671 } else if (v.isUndefined()) {
5672 obj = NULL;
5673 } else {
5674 Value tmp = v;
5675 if (!js_PrimitiveToObject(cx, &tmp))
5676 return JS_FALSE;
5677 obj = &tmp.toObject();
5679 *objp = obj;
5680 return JS_TRUE;
5683 JSObject *
5684 js_ValueToNonNullObject(JSContext *cx, const Value &v)
5686 JSObject *obj;
5688 if (!js_ValueToObjectOrNull(cx, v, &obj))
5689 return NULL;
5690 if (!obj)
5691 js_ReportIsNullOrUndefined(cx, JSDVG_SEARCH_STACK, v, NULL);
5692 return obj;
5695 JSBool
5696 js_TryValueOf(JSContext *cx, JSObject *obj, JSType type, Value *rval)
5698 Value argv[1];
5700 argv[0].setString(ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[type]));
5701 return js_TryMethod(cx, obj, cx->runtime->atomState.valueOfAtom,
5702 1, argv, rval);
5705 JSBool
5706 js_TryMethod(JSContext *cx, JSObject *obj, JSAtom *atom,
5707 uintN argc, Value *argv, Value *rval)
5709 JS_CHECK_RECURSION(cx, return JS_FALSE);
5712 * Report failure only if an appropriate method was found, and calling it
5713 * returned failure. We propagate failure in this case to make exceptions
5714 * behave properly.
5716 JSErrorReporter older = JS_SetErrorReporter(cx, NULL);
5717 jsid id = ATOM_TO_JSID(atom);
5718 Value fval;
5719 JSBool ok = js_GetMethod(cx, obj, id, JSGET_NO_METHOD_BARRIER, &fval);
5720 JS_SetErrorReporter(cx, older);
5721 if (!ok)
5722 return false;
5724 if (fval.isPrimitive())
5725 return JS_TRUE;
5726 return InternalCall(cx, obj, fval, argc, argv, rval);
5729 #if JS_HAS_XDR
5731 JSBool
5732 js_XDRObject(JSXDRState *xdr, JSObject **objp)
5734 JSContext *cx;
5735 JSAtom *atom;
5736 Class *clasp;
5737 uint32 classId, classDef;
5738 JSProtoKey protoKey;
5739 JSObject *proto;
5741 cx = xdr->cx;
5742 atom = NULL;
5743 if (xdr->mode == JSXDR_ENCODE) {
5744 clasp = (*objp)->getClass();
5745 classId = JS_XDRFindClassIdByName(xdr, clasp->name);
5746 classDef = !classId;
5747 if (classDef) {
5748 if (!JS_XDRRegisterClass(xdr, Jsvalify(clasp), &classId))
5749 return JS_FALSE;
5750 protoKey = JSCLASS_CACHED_PROTO_KEY(clasp);
5751 if (protoKey != JSProto_Null) {
5752 classDef |= (protoKey << 1);
5753 } else {
5754 atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0);
5755 if (!atom)
5756 return JS_FALSE;
5759 } else {
5760 clasp = NULL; /* quell GCC overwarning */
5761 classDef = 0;
5765 * XDR a flag word, which could be 0 for a class use, in which case no
5766 * name follows, only the id in xdr's class registry; 1 for a class def,
5767 * in which case the flag word is followed by the class name transferred
5768 * from or to atom; or a value greater than 1, an odd number that when
5769 * divided by two yields the JSProtoKey for class. In the last case, as
5770 * in the 0 classDef case, no name is transferred via atom.
5772 if (!JS_XDRUint32(xdr, &classDef))
5773 return JS_FALSE;
5774 if (classDef == 1 && !js_XDRAtom(xdr, &atom))
5775 return JS_FALSE;
5777 if (!JS_XDRUint32(xdr, &classId))
5778 return JS_FALSE;
5780 if (xdr->mode == JSXDR_DECODE) {
5781 if (classDef) {
5782 /* NB: we know that JSProto_Null is 0 here, for backward compat. */
5783 protoKey = (JSProtoKey) (classDef >> 1);
5784 if (!js_GetClassPrototype(cx, NULL, protoKey, &proto, clasp))
5785 return JS_FALSE;
5786 clasp = proto->getClass();
5787 if (!JS_XDRRegisterClass(xdr, Jsvalify(clasp), &classId))
5788 return JS_FALSE;
5789 } else {
5790 clasp = Valueify(JS_XDRFindClassById(xdr, classId));
5791 if (!clasp) {
5792 char numBuf[12];
5793 JS_snprintf(numBuf, sizeof numBuf, "%ld", (long)classId);
5794 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
5795 JSMSG_CANT_FIND_CLASS, numBuf);
5796 return JS_FALSE;
5801 if (!clasp->xdrObject) {
5802 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
5803 JSMSG_CANT_XDR_CLASS, clasp->name);
5804 return JS_FALSE;
5806 return clasp->xdrObject(xdr, objp);
5809 #endif /* JS_HAS_XDR */
5811 #ifdef JS_DUMP_SCOPE_METERS
5813 #include <stdio.h>
5815 JSBasicStats js_entry_count_bs = JS_INIT_STATIC_BASIC_STATS;
5817 static void
5818 MeterEntryCount(uintN count)
5820 JS_BASIC_STATS_ACCUM(&js_entry_count_bs, count);
5823 void
5824 js_DumpScopeMeters(JSRuntime *rt)
5826 static FILE *logfp;
5827 if (!logfp)
5828 logfp = fopen("/tmp/scope.stats", "a");
5831 double mean, sigma;
5833 mean = JS_MeanAndStdDevBS(&js_entry_count_bs, &sigma);
5835 fprintf(logfp, "scopes %u entries %g mean %g sigma %g max %u",
5836 js_entry_count_bs.num, js_entry_count_bs.sum, mean, sigma,
5837 js_entry_count_bs.max);
5840 JS_DumpHistogram(&js_entry_count_bs, logfp);
5841 JS_BASIC_STATS_INIT(&js_entry_count_bs);
5842 fflush(logfp);
5844 #endif
5846 #ifdef DEBUG
5847 void
5848 js_PrintObjectSlotName(JSTracer *trc, char *buf, size_t bufsize)
5850 JS_ASSERT(trc->debugPrinter == js_PrintObjectSlotName);
5852 JSObject *obj = (JSObject *)trc->debugPrintArg;
5853 uint32 slot = (uint32)trc->debugPrintIndex;
5854 JS_ASSERT(slot >= JSSLOT_START(obj->getClass()));
5856 JSScopeProperty *sprop;
5857 if (obj->isNative()) {
5858 JSScope *scope = obj->scope();
5859 sprop = scope->lastProperty();
5860 while (sprop && sprop->slot != slot)
5861 sprop = sprop->parent;
5862 } else {
5863 sprop = NULL;
5866 if (!sprop) {
5867 const char *slotname = NULL;
5868 Class *clasp = obj->getClass();
5869 if (clasp->flags & JSCLASS_IS_GLOBAL) {
5870 uint32 key = slot - JSSLOT_START(clasp);
5871 #define JS_PROTO(name,code,init) \
5872 if ((code) == key) { slotname = js_##name##_str; goto found; }
5873 #include "jsproto.tbl"
5874 #undef JS_PROTO
5876 found:
5877 if (slotname)
5878 JS_snprintf(buf, bufsize, "CLASS_OBJECT(%s)", slotname);
5879 else
5880 JS_snprintf(buf, bufsize, "**UNKNOWN SLOT %ld**", (long)slot);
5881 } else {
5882 jsid id = sprop->id;
5883 if (JSID_IS_INT(id)) {
5884 JS_snprintf(buf, bufsize, "%ld", (long)JSID_TO_INT(id));
5885 } else if (JSID_IS_ATOM(id)) {
5886 js_PutEscapedString(buf, bufsize, JSID_TO_STRING(id), 0);
5887 } else {
5888 JS_snprintf(buf, bufsize, "**FINALIZED ATOM KEY**");
5892 #endif
5894 void
5895 js_TraceObject(JSTracer *trc, JSObject *obj)
5897 JS_ASSERT(obj->isNative());
5899 JSContext *cx = trc->context;
5900 JSScope *scope = obj->scope();
5901 if (!scope->isSharedEmpty() && IS_GC_MARKING_TRACER(trc)) {
5903 * Check whether we should shrink the object's slots. Skip this check
5904 * if the scope is shared, since for Block objects and flat closures
5905 * that share their scope, scope->freeslot can be an underestimate.
5907 size_t slots = scope->freeslot;
5908 if (obj->numSlots() != slots)
5909 obj->shrinkSlots(cx, slots);
5912 #ifdef JS_DUMP_SCOPE_METERS
5913 MeterEntryCount(scope->entryCount);
5914 #endif
5916 scope->trace(trc);
5918 if (!JS_CLIST_IS_EMPTY(&cx->runtime->watchPointList))
5919 js_TraceWatchPoints(trc, obj);
5921 /* No one runs while the GC is running, so we can use LOCKED_... here. */
5922 Class *clasp = obj->getClass();
5923 if (clasp->mark) {
5924 if (clasp->flags & JSCLASS_MARK_IS_TRACE)
5925 ((JSTraceOp) clasp->mark)(trc, obj);
5926 else if (IS_GC_MARKING_TRACER(trc))
5927 (void) clasp->mark(cx, obj, trc);
5929 if (clasp->flags & JSCLASS_IS_GLOBAL) {
5930 JSCompartment *compartment = obj->getCompartment(cx);
5931 compartment->marked = true;
5935 * An unmutated object that shares a prototype object's scope. We can't
5936 * tell how many slots are in use in obj by looking at its scope, so we
5937 * use obj->numSlots().
5939 * NB: In case clasp->mark mutates something, leave this code here --
5940 * don't move it up and unify it with the |if (!traceScope)| section
5941 * above.
5943 uint32 nslots = obj->numSlots();
5944 if (!scope->isSharedEmpty() && scope->freeslot < nslots)
5945 nslots = scope->freeslot;
5946 JS_ASSERT(nslots >= JSSLOT_START(clasp));
5948 for (uint32 i = JSSLOT_START(clasp); i != nslots; ++i) {
5949 const Value &v = obj->getSlot(i);
5950 JS_SET_TRACING_DETAILS(trc, js_PrintObjectSlotName, obj, i);
5951 MarkValueRaw(trc, v);
5955 void
5956 js_ClearNative(JSContext *cx, JSObject *obj)
5959 * Clear our scope and the property cache of all obj's properties only if
5960 * obj owns the scope (i.e., not if obj is sharing another object's scope).
5961 * NB: we do not clear any reserved slots lying below JSSLOT_FREE(clasp).
5963 JS_LOCK_OBJ(cx, obj);
5964 JSScope *scope = obj->scope();
5965 if (!scope->isSharedEmpty()) {
5966 /* Now that we're done using scope->lastProp/table, clear scope. */
5967 scope->clear(cx);
5969 /* Clear slot values and reset freeslot so we're consistent. */
5970 uint32 freeslot = JSSLOT_FREE(obj->getClass());
5971 uint32 n = obj->numSlots();
5972 for (uint32 i = freeslot; i < n; ++i)
5973 obj->setSlot(i, UndefinedValue());
5974 scope->freeslot = freeslot;
5976 JS_UNLOCK_OBJ(cx, obj);
5979 bool
5980 js_GetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, Value *vp)
5982 if (!obj->isNative()) {
5983 vp->setUndefined();
5984 return true;
5987 uint32 slot = JSSLOT_START(obj->getClass()) + index;
5988 JS_LOCK_OBJ(cx, obj);
5989 if (slot < obj->numSlots())
5990 *vp = obj->getSlot(slot);
5991 else
5992 vp->setUndefined();
5993 JS_UNLOCK_OBJ(cx, obj);
5994 return true;
5997 bool
5998 js_SetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, const Value &v)
6000 if (!obj->isNative())
6001 return true;
6003 Class *clasp = obj->getClass();
6004 uint32 slot = JSSLOT_START(clasp) + index;
6006 JS_LOCK_OBJ(cx, obj);
6007 if (slot >= obj->numSlots()) {
6009 * At this point, obj may or may not own scope, and we may or may not
6010 * need to allocate slots. If scope is shared, scope->freeslot may not
6011 * be accurate for obj (see comment below).
6013 uint32 nslots = JSSLOT_FREE(clasp);
6014 JS_ASSERT(slot < nslots);
6015 if (!obj->allocSlots(cx, nslots)) {
6016 JS_UNLOCK_OBJ(cx, obj);
6017 return false;
6022 * Whether or not we grew nslots, we may need to advance freeslot.
6024 * If scope is shared, do not modify scope->freeslot. It is OK for freeslot
6025 * to be an underestimate in objects with shared scopes, as they will get
6026 * their own scopes before mutating, and elsewhere (e.g. js_TraceObject) we
6027 * use obj->numSlots() rather than rely on freeslot.
6029 JSScope *scope = obj->scope();
6030 if (!scope->isSharedEmpty() && slot >= scope->freeslot)
6031 scope->freeslot = slot + 1;
6033 obj->setSlot(slot, v);
6034 GC_POKE(cx, JS_NULL);
6035 JS_UNLOCK_SCOPE(cx, scope);
6036 return true;
6039 JSObject *
6040 JSObject::wrappedObject(JSContext *cx) const
6042 if (JSObjectOp op = getClass()->ext.wrappedObject) {
6043 if (JSObject *obj = op(cx, const_cast<JSObject *>(this)))
6044 return obj;
6046 return const_cast<JSObject *>(this);
6049 JSObject *
6050 JSObject::getGlobal()
6052 JSObject *obj = this;
6053 while (JSObject *parent = obj->getParent())
6054 obj = parent;
6055 return obj;
6058 JSBool
6059 js_ReportGetterOnlyAssignment(JSContext *cx)
6061 return JS_ReportErrorFlagsAndNumber(cx,
6062 JSREPORT_WARNING | JSREPORT_STRICT |
6063 JSREPORT_STRICT_MODE_ERROR,
6064 js_GetErrorMessage, NULL,
6065 JSMSG_GETTER_ONLY);
6068 JSCompartment *
6069 JSObject::getCompartment(JSContext *cx)
6071 JSObject *obj = getGlobal();
6073 Class *clasp = obj->getClass();
6074 if (!(clasp->flags & JSCLASS_IS_GLOBAL)) {
6075 // The magic AnyName object is runtime-wide.
6076 if (clasp == &js_AnyNameClass)
6077 return cx->runtime->defaultCompartment;
6079 // The magic function namespace object is runtime-wide.
6080 if (clasp == &js_NamespaceClass &&
6081 obj->getNameURI() == ATOM_TO_JSVAL(cx->runtime->atomState.lazy.functionNamespaceURIAtom)) {
6082 return cx->runtime->defaultCompartment;
6085 // Compile-time Function, Block, and RegExp objects are not parented.
6086 if (clasp == &js_FunctionClass || clasp == &js_BlockClass || clasp == &js_RegExpClass) {
6087 // This is a bogus answer, but it'll do for now.
6088 return cx->runtime->defaultCompartment;
6090 JS_NOT_REACHED("non-global object at end of scope chain");
6092 const Value &v = obj->getReservedSlot(JSRESERVED_GLOBAL_COMPARTMENT);
6093 return (JSCompartment *)v.toPrivate();
6096 JS_FRIEND_API(JSBool)
6097 js_GetterOnlyPropertyStub(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
6099 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_GETTER_ONLY);
6100 return JS_FALSE;
6103 #ifdef DEBUG
6106 * Routines to print out values during debugging. These are FRIEND_API to help
6107 * the debugger find them and to support temporarily hacking js_Dump* calls
6108 * into other code.
6111 void
6112 dumpChars(const jschar *s, size_t n)
6114 size_t i;
6116 if (n == (size_t) -1) {
6117 while (s[++n]) ;
6120 fputc('"', stderr);
6121 for (i = 0; i < n; i++) {
6122 if (s[i] == '\n')
6123 fprintf(stderr, "\\n");
6124 else if (s[i] == '\t')
6125 fprintf(stderr, "\\t");
6126 else if (s[i] >= 32 && s[i] < 127)
6127 fputc(s[i], stderr);
6128 else if (s[i] <= 255)
6129 fprintf(stderr, "\\x%02x", (unsigned int) s[i]);
6130 else
6131 fprintf(stderr, "\\u%04x", (unsigned int) s[i]);
6133 fputc('"', stderr);
6136 JS_FRIEND_API(void)
6137 js_DumpChars(const jschar *s, size_t n)
6139 fprintf(stderr, "jschar * (%p) = ", (void *) s);
6140 dumpChars(s, n);
6141 fputc('\n', stderr);
6144 void
6145 dumpString(JSString *str)
6147 dumpChars(str->chars(), str->length());
6150 JS_FRIEND_API(void)
6151 js_DumpString(JSString *str)
6153 fprintf(stderr, "JSString* (%p) = jschar * (%p) = ",
6154 (void *) str, (void *) str->chars());
6155 dumpString(str);
6156 fputc('\n', stderr);
6159 JS_FRIEND_API(void)
6160 js_DumpAtom(JSAtom *atom)
6162 fprintf(stderr, "JSAtom* (%p) = ", (void *) atom);
6163 js_DumpString(ATOM_TO_STRING(atom));
6166 void
6167 dumpValue(const Value &v)
6169 if (v.isNull())
6170 fprintf(stderr, "null");
6171 else if (v.isUndefined())
6172 fprintf(stderr, "undefined");
6173 else if (v.isInt32())
6174 fprintf(stderr, "%d", v.toInt32());
6175 else if (v.isDouble())
6176 fprintf(stderr, "%g", v.toDouble());
6177 else if (v.isString())
6178 dumpString(v.toString());
6179 else if (v.isObject() && v.toObject().isFunction()) {
6180 JSObject *funobj = &v.toObject();
6181 JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
6182 fprintf(stderr, "<%s %s at %p (JSFunction at %p)>",
6183 fun->atom ? "function" : "unnamed",
6184 fun->atom ? JS_GetStringBytes(ATOM_TO_STRING(fun->atom)) : "function",
6185 (void *) funobj,
6186 (void *) fun);
6187 } else if (v.isObject()) {
6188 JSObject *obj = &v.toObject();
6189 Class *clasp = obj->getClass();
6190 fprintf(stderr, "<%s%s at %p>",
6191 clasp->name,
6192 (clasp == &js_ObjectClass) ? "" : " object",
6193 (void *) obj);
6194 } else if (v.isBoolean()) {
6195 if (v.toBoolean())
6196 fprintf(stderr, "true");
6197 else
6198 fprintf(stderr, "false");
6199 } else if (v.isMagic()) {
6200 fprintf(stderr, "<invalid");
6201 #ifdef DEBUG
6202 switch (v.whyMagic()) {
6203 case JS_ARRAY_HOLE: fprintf(stderr, " array hole"); break;
6204 case JS_ARGS_HOLE: fprintf(stderr, " args hole"); break;
6205 case JS_NATIVE_ENUMERATE: fprintf(stderr, " native enumeration"); break;
6206 case JS_NO_ITER_VALUE: fprintf(stderr, " no iter value"); break;
6207 case JS_GENERATOR_CLOSING: fprintf(stderr, " generator closing"); break;
6208 default: fprintf(stderr, " ?!"); break;
6210 #endif
6211 fprintf(stderr, ">");
6212 } else {
6213 fprintf(stderr, "unexpected value");
6217 JS_FRIEND_API(void)
6218 js_DumpValue(const Value &val)
6220 dumpValue(val);
6221 fputc('\n', stderr);
6224 JS_FRIEND_API(void)
6225 js_DumpId(jsid id)
6227 fprintf(stderr, "jsid %p = ", (void *) JSID_BITS(id));
6228 dumpValue(IdToValue(id));
6229 fputc('\n', stderr);
6232 static void
6233 dumpScopeProp(JSScopeProperty *sprop)
6235 jsid id = sprop->id;
6236 uint8 attrs = sprop->attributes();
6238 fprintf(stderr, " ");
6239 if (attrs & JSPROP_ENUMERATE) fprintf(stderr, "enumerate ");
6240 if (attrs & JSPROP_READONLY) fprintf(stderr, "readonly ");
6241 if (attrs & JSPROP_PERMANENT) fprintf(stderr, "permanent ");
6242 if (attrs & JSPROP_GETTER) fprintf(stderr, "getter ");
6243 if (attrs & JSPROP_SETTER) fprintf(stderr, "setter ");
6244 if (attrs & JSPROP_SHARED) fprintf(stderr, "shared ");
6245 if (sprop->isAlias()) fprintf(stderr, "alias ");
6246 if (JSID_IS_ATOM(id))
6247 dumpString(JSID_TO_STRING(id));
6248 else if (JSID_IS_INT(id))
6249 fprintf(stderr, "%d", (int) JSID_TO_INT(id));
6250 else
6251 fprintf(stderr, "unknown jsid %p", (void *) JSID_BITS(id));
6252 fprintf(stderr, ": slot %d", sprop->slot);
6253 fprintf(stderr, "\n");
6256 JS_FRIEND_API(void)
6257 js_DumpObject(JSObject *obj)
6259 uint32 i, slots;
6260 Class *clasp;
6261 jsuint reservedEnd;
6263 fprintf(stderr, "object %p\n", (void *) obj);
6264 clasp = obj->getClass();
6265 fprintf(stderr, "class %p %s\n", (void *)clasp, clasp->name);
6267 if (obj->isDenseArray()) {
6268 slots = JS_MIN(obj->getArrayLength(), obj->getDenseArrayCapacity());
6269 fprintf(stderr, "elements\n");
6270 for (i = 0; i < slots; i++) {
6271 fprintf(stderr, " %3d: ", i);
6272 dumpValue(obj->getDenseArrayElement(i));
6273 fprintf(stderr, "\n");
6274 fflush(stderr);
6276 return;
6279 if (obj->isNative()) {
6280 JSScope *scope = obj->scope();
6281 if (scope->sealed())
6282 fprintf(stderr, "sealed\n");
6284 fprintf(stderr, "properties:\n");
6285 for (JSScopeProperty *sprop = scope->lastProperty(); sprop;
6286 sprop = sprop->parent) {
6287 dumpScopeProp(sprop);
6289 } else {
6290 if (!obj->isNative())
6291 fprintf(stderr, "not native\n");
6294 fprintf(stderr, "proto ");
6295 dumpValue(ObjectOrNullValue(obj->getProto()));
6296 fputc('\n', stderr);
6298 fprintf(stderr, "parent ");
6299 dumpValue(ObjectOrNullValue(obj->getParent()));
6300 fputc('\n', stderr);
6302 i = JSSLOT_PRIVATE;
6303 if (clasp->flags & JSCLASS_HAS_PRIVATE) {
6304 i = JSSLOT_PRIVATE + 1;
6305 fprintf(stderr, "private %p\n", obj->getPrivate());
6308 fprintf(stderr, "slots:\n");
6309 reservedEnd = i + JSCLASS_RESERVED_SLOTS(clasp);
6310 slots = (obj->isNative() && !obj->scope()->isSharedEmpty())
6311 ? obj->scope()->freeslot
6312 : obj->numSlots();
6313 for (; i < slots; i++) {
6314 fprintf(stderr, " %3d ", i);
6315 if (i < reservedEnd)
6316 fprintf(stderr, "(reserved) ");
6317 fprintf(stderr, "= ");
6318 dumpValue(obj->getSlot(i));
6319 fputc('\n', stderr);
6321 fputc('\n', stderr);
6324 static void
6325 MaybeDumpObject(const char *name, JSObject *obj)
6327 if (obj) {
6328 fprintf(stderr, " %s: ", name);
6329 dumpValue(ObjectValue(*obj));
6330 fputc('\n', stderr);
6334 static void
6335 MaybeDumpValue(const char *name, const Value &v)
6337 if (!v.isNull()) {
6338 fprintf(stderr, " %s: ", name);
6339 dumpValue(v);
6340 fputc('\n', stderr);
6344 JS_FRIEND_API(void)
6345 js_DumpStackFrame(JSContext *cx, JSStackFrame *start)
6347 /* This should only called during live debugging. */
6348 VOUCH_DOES_NOT_REQUIRE_STACK();
6350 if (!start)
6351 start = cx->fp;
6352 FrameRegsIter i(cx);
6353 while (!i.done() && i.fp() != start)
6354 ++i;
6356 if (i.done()) {
6357 fprintf(stderr, "fp = %p not found in cx = %p\n", (void *)start, (void *)cx);
6358 return;
6361 for (; !i.done(); ++i) {
6362 JSStackFrame *const fp = i.fp();
6364 fprintf(stderr, "JSStackFrame at %p\n", (void *) fp);
6365 if (fp->argv) {
6366 fprintf(stderr, "callee: ");
6367 dumpValue(fp->argv[-2]);
6368 } else {
6369 fprintf(stderr, "global frame, no callee");
6371 fputc('\n', stderr);
6373 if (fp->script)
6374 fprintf(stderr, "file %s line %u\n", fp->script->filename, (unsigned) fp->script->lineno);
6376 if (jsbytecode *pc = i.pc()) {
6377 if (!fp->script) {
6378 fprintf(stderr, "*** pc && !script, skipping frame\n\n");
6379 continue;
6381 if (fp->hasIMacroPC()) {
6382 fprintf(stderr, " pc in imacro at %p\n called from ", pc);
6383 pc = fp->getIMacroPC();
6384 } else {
6385 fprintf(stderr, " ");
6387 fprintf(stderr, "pc = %p\n", pc);
6388 fprintf(stderr, " current op: %s\n", js_CodeName[*pc]);
6390 Value *sp = i.sp();
6391 fprintf(stderr, " slots: %p\n", (void *) fp->slots());
6392 fprintf(stderr, " sp: %p = slots + %u\n", (void *) sp, (unsigned) (sp - fp->slots()));
6393 if (sp - fp->slots() < 10000) { // sanity
6394 for (Value *p = fp->slots(); p < sp; p++) {
6395 fprintf(stderr, " %p: ", (void *) p);
6396 dumpValue(*p);
6397 fputc('\n', stderr);
6400 fprintf(stderr, " argv: %p (argc: %u)\n", (void *) fp->argv, (unsigned) fp->argc);
6401 MaybeDumpObject("callobj", fp->maybeCallObj());
6402 MaybeDumpObject("argsobj", fp->maybeArgsObj());
6403 MaybeDumpValue("this", fp->thisv);
6404 fprintf(stderr, " rval: ");
6405 dumpValue(fp->rval);
6406 fputc('\n', stderr);
6408 fprintf(stderr, " flags:");
6409 if (fp->flags == 0)
6410 fprintf(stderr, " none");
6411 if (fp->flags & JSFRAME_CONSTRUCTING)
6412 fprintf(stderr, " constructing");
6413 if (fp->flags & JSFRAME_COMPUTED_THIS)
6414 fprintf(stderr, " computed_this");
6415 if (fp->flags & JSFRAME_ASSIGNING)
6416 fprintf(stderr, " assigning");
6417 if (fp->flags & JSFRAME_DEBUGGER)
6418 fprintf(stderr, " debugger");
6419 if (fp->flags & JSFRAME_EVAL)
6420 fprintf(stderr, " eval");
6421 if (fp->flags & JSFRAME_YIELDING)
6422 fprintf(stderr, " yielding");
6423 if (fp->flags & JSFRAME_GENERATOR)
6424 fprintf(stderr, " generator");
6425 if (fp->flags & JSFRAME_OVERRIDE_ARGS)
6426 fprintf(stderr, " overridden_args");
6427 fputc('\n', stderr);
6429 if (fp->hasScopeChain())
6430 fprintf(stderr, " scopeChain: (JSObject *) %p\n", (void *) fp->getScopeChain());
6431 if (fp->hasBlockChain())
6432 fprintf(stderr, " blockChain: (JSObject *) %p\n", (void *) fp->getBlockChain());
6434 fputc('\n', stderr);
6438 #endif