Tracer build fixes. (b=588021, r=dvander)
[mozilla-central.git] / js / src / jsobj.cpp
blobd88edad0b2c2edfdbcae1732d2f22a1858d023f5
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"
49 #include "jsbit.h"
50 #include "jsutil.h"
51 #include "jshash.h"
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"
79 #include "json.h"
81 #include "jsinterpinlines.h"
82 #include "jsscopeinlines.h"
83 #include "jsscriptinlines.h"
84 #include "jsobjinlines.h"
86 #if JS_HAS_GENERATORS
87 #include "jsiter.h"
88 #endif
90 #if JS_HAS_XML_SUPPORT
91 #include "jsxml.h"
92 #endif
94 #if JS_HAS_XDR
95 #include "jsxdrapi.h"
96 #endif
98 #include "jsprobes.h"
99 #include "jsatominlines.h"
100 #include "jsobjinlines.h"
101 #include "jsscriptinlines.h"
103 #include "jsautooplen.h"
105 using namespace js;
106 using namespace js::gc;
108 JS_FRIEND_DATA(const JSObjectMap) JSObjectMap::sharedNonNative(JSObjectMap::SHAPELESS);
110 Class js_ObjectClass = {
111 js_Object_str,
112 JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
113 PropertyStub, /* addProperty */
114 PropertyStub, /* delProperty */
115 PropertyStub, /* getProperty */
116 PropertyStub, /* setProperty */
117 EnumerateStub,
118 ResolveStub,
119 ConvertStub
122 JS_FRIEND_API(JSObject *)
123 js_ObjectToOuterObject(JSContext *cx, JSObject *obj)
125 OBJ_TO_OUTER_OBJECT(cx, obj);
126 return obj;
129 #if JS_HAS_OBJ_PROTO_PROP
131 static JSBool
132 obj_getProto(JSContext *cx, JSObject *obj, jsid id, Value *vp);
134 static JSBool
135 obj_setProto(JSContext *cx, JSObject *obj, jsid id, Value *vp);
137 static JSPropertySpec object_props[] = {
138 {js_proto_str, 0, JSPROP_PERMANENT|JSPROP_SHARED, Jsvalify(obj_getProto), Jsvalify(obj_setProto)},
139 {0,0,0,0,0}
142 static JSBool
143 obj_getProto(JSContext *cx, JSObject *obj, jsid id, Value *vp)
145 /* Let CheckAccess get the slot's value, based on the access mode. */
146 uintN attrs;
147 id = ATOM_TO_JSID(cx->runtime->atomState.protoAtom);
148 return CheckAccess(cx, obj, id, JSACC_PROTO, vp, &attrs);
151 static JSBool
152 obj_setProto(JSContext *cx, JSObject *obj, jsid id, Value *vp)
154 /* ECMAScript 5 8.6.2 forbids changing [[Prototype]] if not [[Extensible]]. */
155 if (!obj->isExtensible()) {
156 obj->reportNotExtensible(cx);
157 return false;
160 if (!vp->isObjectOrNull())
161 return JS_TRUE;
163 JSObject *pobj = vp->toObjectOrNull();
164 if (pobj) {
166 * Innerize pobj here to avoid sticking unwanted properties on the
167 * outer object. This ensures that any with statements only grant
168 * access to the inner object.
170 OBJ_TO_INNER_OBJECT(cx, pobj);
171 if (!pobj)
172 return JS_FALSE;
175 uintN attrs;
176 id = ATOM_TO_JSID(cx->runtime->atomState.protoAtom);
177 if (!CheckAccess(cx, obj, id, JSAccessMode(JSACC_PROTO|JSACC_WRITE), vp, &attrs))
178 return JS_FALSE;
180 return SetProto(cx, obj, pobj, JS_TRUE);
183 #else /* !JS_HAS_OBJ_PROTO_PROP */
185 #define object_props NULL
187 #endif /* !JS_HAS_OBJ_PROTO_PROP */
189 static JSHashNumber
190 js_hash_object(const void *key)
192 return JSHashNumber(uintptr_t(key) >> JS_GCTHING_ALIGN);
195 static JSHashEntry *
196 MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap)
198 JSSharpObjectMap *map;
199 JSHashTable *table;
200 JSHashNumber hash;
201 JSHashEntry **hep, *he;
202 jsatomid sharpid;
203 JSIdArray *ida;
204 JSBool ok;
205 jsint i, length;
206 jsid id;
207 JSObject *obj2;
208 JSProperty *prop;
210 JS_CHECK_RECURSION(cx, return NULL);
212 map = &cx->sharpObjectMap;
213 JS_ASSERT(map->depth >= 1);
214 table = map->table;
215 hash = js_hash_object(obj);
216 hep = JS_HashTableRawLookup(table, hash, obj);
217 he = *hep;
218 if (!he) {
219 sharpid = 0;
220 he = JS_HashTableRawAdd(table, hep, hash, obj, (void *) sharpid);
221 if (!he) {
222 JS_ReportOutOfMemory(cx);
223 return NULL;
226 ida = JS_Enumerate(cx, obj);
227 if (!ida)
228 return NULL;
230 ok = JS_TRUE;
231 for (i = 0, length = ida->length; i < length; i++) {
232 id = ida->vector[i];
233 ok = obj->lookupProperty(cx, id, &obj2, &prop);
234 if (!ok)
235 break;
236 if (!prop)
237 continue;
238 bool hasGetter, hasSetter;
239 AutoValueRooter v(cx);
240 AutoValueRooter setter(cx);
241 if (obj2->isNative()) {
242 const Shape *shape = (Shape *) prop;
243 hasGetter = shape->hasGetterValue();
244 hasSetter = shape->hasSetterValue();
245 if (hasGetter)
246 v.set(shape->getterValue());
247 if (hasSetter)
248 setter.set(shape->setterValue());
249 } else {
250 hasGetter = hasSetter = false;
252 if (hasSetter) {
253 /* Mark the getter, then set val to setter. */
254 if (hasGetter && v.value().isObject()) {
255 ok = !!MarkSharpObjects(cx, &v.value().toObject(), NULL);
256 if (!ok)
257 break;
259 v.set(setter.value());
260 } else if (!hasGetter) {
261 ok = obj->getProperty(cx, id, v.addr());
262 if (!ok)
263 break;
265 if (v.value().isObject() &&
266 !MarkSharpObjects(cx, &v.value().toObject(), NULL)) {
267 ok = JS_FALSE;
268 break;
271 if (!ok || !idap)
272 JS_DestroyIdArray(cx, ida);
273 if (!ok)
274 return NULL;
275 } else {
276 sharpid = uintptr_t(he->value);
277 if (sharpid == 0) {
278 sharpid = ++map->sharpgen << SHARP_ID_SHIFT;
279 he->value = (void *) sharpid;
281 ida = NULL;
283 if (idap)
284 *idap = ida;
285 return he;
288 JSHashEntry *
289 js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap,
290 jschar **sp)
292 JSSharpObjectMap *map;
293 JSHashTable *table;
294 JSIdArray *ida;
295 JSHashNumber hash;
296 JSHashEntry *he, **hep;
297 jsatomid sharpid;
298 char buf[20];
299 size_t len;
301 if (!JS_CHECK_OPERATION_LIMIT(cx))
302 return NULL;
304 /* Set to null in case we return an early error. */
305 *sp = NULL;
306 map = &cx->sharpObjectMap;
307 table = map->table;
308 if (!table) {
309 table = JS_NewHashTable(8, js_hash_object, JS_CompareValues,
310 JS_CompareValues, NULL, NULL);
311 if (!table) {
312 JS_ReportOutOfMemory(cx);
313 return NULL;
315 map->table = table;
316 JS_KEEP_ATOMS(cx->runtime);
319 /* From this point the control must flow either through out: or bad:. */
320 ida = NULL;
321 if (map->depth == 0) {
323 * Although MarkSharpObjects tries to avoid invoking getters,
324 * it ends up doing so anyway under some circumstances; for
325 * example, if a wrapped object has getters, the wrapper will
326 * prevent MarkSharpObjects from recognizing them as such.
327 * This could lead to js_LeaveSharpObject being called while
328 * MarkSharpObjects is still working.
330 * Increment map->depth while we call MarkSharpObjects, to
331 * ensure that such a call doesn't free the hash table we're
332 * still using.
334 ++map->depth;
335 he = MarkSharpObjects(cx, obj, &ida);
336 --map->depth;
337 if (!he)
338 goto bad;
339 JS_ASSERT((uintptr_t(he->value) & SHARP_BIT) == 0);
340 if (!idap) {
341 JS_DestroyIdArray(cx, ida);
342 ida = NULL;
344 } else {
345 hash = js_hash_object(obj);
346 hep = JS_HashTableRawLookup(table, hash, obj);
347 he = *hep;
350 * It's possible that the value of a property has changed from the
351 * first time the object's properties are traversed (when the property
352 * ids are entered into the hash table) to the second (when they are
353 * converted to strings), i.e., the JSObject::getProperty() call is not
354 * idempotent.
356 if (!he) {
357 he = JS_HashTableRawAdd(table, hep, hash, obj, NULL);
358 if (!he) {
359 JS_ReportOutOfMemory(cx);
360 goto bad;
362 sharpid = 0;
363 goto out;
367 sharpid = uintptr_t(he->value);
368 if (sharpid != 0) {
369 len = JS_snprintf(buf, sizeof buf, "#%u%c",
370 sharpid >> SHARP_ID_SHIFT,
371 (sharpid & SHARP_BIT) ? '#' : '=');
372 *sp = js_InflateString(cx, buf, &len);
373 if (!*sp) {
374 if (ida)
375 JS_DestroyIdArray(cx, ida);
376 goto bad;
380 out:
381 JS_ASSERT(he);
382 if ((sharpid & SHARP_BIT) == 0) {
383 if (idap && !ida) {
384 ida = JS_Enumerate(cx, obj);
385 if (!ida) {
386 if (*sp) {
387 cx->free(*sp);
388 *sp = NULL;
390 goto bad;
393 map->depth++;
396 if (idap)
397 *idap = ida;
398 return he;
400 bad:
401 /* Clean up the sharpObjectMap table on outermost error. */
402 if (map->depth == 0) {
403 JS_UNKEEP_ATOMS(cx->runtime);
404 map->sharpgen = 0;
405 JS_HashTableDestroy(map->table);
406 map->table = NULL;
408 return NULL;
411 void
412 js_LeaveSharpObject(JSContext *cx, JSIdArray **idap)
414 JSSharpObjectMap *map;
415 JSIdArray *ida;
417 map = &cx->sharpObjectMap;
418 JS_ASSERT(map->depth > 0);
419 if (--map->depth == 0) {
420 JS_UNKEEP_ATOMS(cx->runtime);
421 map->sharpgen = 0;
422 JS_HashTableDestroy(map->table);
423 map->table = NULL;
425 if (idap) {
426 ida = *idap;
427 if (ida) {
428 JS_DestroyIdArray(cx, ida);
429 *idap = NULL;
434 static intN
435 gc_sharp_table_entry_marker(JSHashEntry *he, intN i, void *arg)
437 MarkObject((JSTracer *)arg, *(JSObject *)he->key, "sharp table entry");
438 return JS_DHASH_NEXT;
441 void
442 js_TraceSharpMap(JSTracer *trc, JSSharpObjectMap *map)
444 JS_ASSERT(map->depth > 0);
445 JS_ASSERT(map->table);
448 * During recursive calls to MarkSharpObjects a non-native object or
449 * object with a custom getProperty method can potentially return an
450 * unrooted value or even cut from the object graph an argument of one of
451 * MarkSharpObjects recursive invocations. So we must protect map->table
452 * entries against GC.
454 * We can not simply use JSTempValueRooter to mark the obj argument of
455 * MarkSharpObjects during recursion as we have to protect *all* entries
456 * in JSSharpObjectMap including those that contains otherwise unreachable
457 * objects just allocated through custom getProperty. Otherwise newer
458 * allocations can re-use the address of an object stored in the hashtable
459 * confusing js_EnterSharpObject. So to address the problem we simply
460 * mark all objects from map->table.
462 * An alternative "proper" solution is to use JSTempValueRooter in
463 * MarkSharpObjects with code to remove during finalization entries
464 * with otherwise unreachable objects. But this is way too complex
465 * to justify spending efforts.
467 JS_HashTableEnumerateEntries(map->table, gc_sharp_table_entry_marker, trc);
470 #if JS_HAS_TOSOURCE
471 static JSBool
472 obj_toSource(JSContext *cx, uintN argc, Value *vp)
474 JSBool ok, outermost;
475 JSObject *obj;
476 JSHashEntry *he;
477 JSIdArray *ida;
478 jschar *chars, *ochars, *vsharp;
479 const jschar *idstrchars, *vchars;
480 size_t nchars, idstrlength, gsoplength, vlength, vsharplength, curlen;
481 const char *comma;
482 JSObject *obj2;
483 JSProperty *prop;
484 Value *val;
485 JSString *gsop[2];
486 JSString *idstr, *valstr, *str;
488 JS_CHECK_RECURSION(cx, return JS_FALSE);
490 Value localroot[4];
491 PodArrayZero(localroot);
492 AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(localroot), localroot);
494 /* If outermost, we need parentheses to be an expression, not a block. */
495 outermost = (cx->sharpObjectMap.depth == 0);
496 obj = ComputeThisFromVp(cx, vp);
497 if (!obj || !(he = js_EnterSharpObject(cx, obj, &ida, &chars))) {
498 ok = JS_FALSE;
499 goto out;
501 if (IS_SHARP(he)) {
503 * We didn't enter -- obj is already "sharp", meaning we've visited it
504 * already in our depth first search, and therefore chars contains a
505 * string of the form "#n#".
507 JS_ASSERT(!ida);
508 #if JS_HAS_SHARP_VARS
509 nchars = js_strlen(chars);
510 #else
511 chars[0] = '{';
512 chars[1] = '}';
513 chars[2] = 0;
514 nchars = 2;
515 #endif
516 goto make_string;
518 JS_ASSERT(ida);
519 ok = JS_TRUE;
521 if (!chars) {
522 /* If outermost, allocate 4 + 1 for "({})" and the terminator. */
523 chars = (jschar *) cx->runtime->malloc(((outermost ? 4 : 2) + 1) * sizeof(jschar));
524 nchars = 0;
525 if (!chars)
526 goto error;
527 if (outermost)
528 chars[nchars++] = '(';
529 } else {
530 /* js_EnterSharpObject returned a string of the form "#n=" in chars. */
531 MAKE_SHARP(he);
532 nchars = js_strlen(chars);
533 chars = (jschar *)
534 js_realloc((ochars = chars), (nchars + 2 + 1) * sizeof(jschar));
535 if (!chars) {
536 js_free(ochars);
537 goto error;
539 if (outermost) {
541 * No need for parentheses around the whole shebang, because #n=
542 * unambiguously begins an object initializer, and never a block
543 * statement.
545 outermost = JS_FALSE;
549 chars[nchars++] = '{';
551 comma = NULL;
554 * We have four local roots for cooked and raw value GC safety. Hoist the
555 * "localroot + 2" out of the loop using the val local, which refers to
556 * the raw (unconverted, "uncooked") values.
558 val = localroot + 2;
560 for (jsint i = 0, length = ida->length; i < length; i++) {
561 /* Get strings for id and value and GC-root them via vp. */
562 jsid id = ida->vector[i];
564 ok = obj->lookupProperty(cx, id, &obj2, &prop);
565 if (!ok)
566 goto error;
569 * Convert id to a value and then to a string. Decide early whether we
570 * prefer get/set or old getter/setter syntax.
572 idstr = js_ValueToString(cx, IdToValue(id));
573 if (!idstr) {
574 ok = JS_FALSE;
575 goto error;
577 vp->setString(idstr); /* local root */
579 jsint valcnt = 0;
580 if (prop) {
581 bool doGet = true;
582 if (obj2->isNative()) {
583 const Shape *shape = (Shape *) prop;
584 unsigned attrs = shape->attributes();
585 if (attrs & JSPROP_GETTER) {
586 doGet = false;
587 val[valcnt] = shape->getterValue();
588 gsop[valcnt] = ATOM_TO_STRING(cx->runtime->atomState.getAtom);
589 valcnt++;
591 if (attrs & JSPROP_SETTER) {
592 doGet = false;
593 val[valcnt] = shape->setterValue();
594 gsop[valcnt] = ATOM_TO_STRING(cx->runtime->atomState.setAtom);
595 valcnt++;
598 if (doGet) {
599 valcnt = 1;
600 gsop[0] = NULL;
601 ok = obj->getProperty(cx, id, &val[0]);
602 if (!ok)
603 goto error;
608 * If id is a string that's not an identifier, or if it's a negative
609 * integer, then it must be quoted.
611 bool idIsLexicalIdentifier = !!js_IsIdentifier(idstr);
612 if (JSID_IS_ATOM(id)
613 ? !idIsLexicalIdentifier
614 : (!JSID_IS_INT(id) || JSID_TO_INT(id) < 0)) {
615 idstr = js_QuoteString(cx, idstr, jschar('\''));
616 if (!idstr) {
617 ok = JS_FALSE;
618 goto error;
620 vp->setString(idstr); /* local root */
622 idstr->getCharsAndLength(idstrchars, idstrlength);
624 for (jsint j = 0; j < valcnt; j++) {
626 * Censor an accessor descriptor getter or setter part if it's
627 * undefined.
629 if (gsop[j] && val[j].isUndefined())
630 continue;
632 /* Convert val[j] to its canonical source form. */
633 valstr = js_ValueToSource(cx, val[j]);
634 if (!valstr) {
635 ok = JS_FALSE;
636 goto error;
638 localroot[j].setString(valstr); /* local root */
639 valstr->getCharsAndLength(vchars, vlength);
642 * If val[j] is a non-sharp object, and we're not serializing an
643 * accessor (ECMA syntax can't accommodate sharpened accessors),
644 * consider sharpening it.
646 vsharp = NULL;
647 vsharplength = 0;
648 #if JS_HAS_SHARP_VARS
649 if (!gsop[j] && val[j].isObject() && vchars[0] != '#') {
650 he = js_EnterSharpObject(cx, &val[j].toObject(), NULL, &vsharp);
651 if (!he) {
652 ok = JS_FALSE;
653 goto error;
655 if (IS_SHARP(he)) {
656 vchars = vsharp;
657 vlength = js_strlen(vchars);
658 } else {
659 if (vsharp) {
660 vsharplength = js_strlen(vsharp);
661 MAKE_SHARP(he);
663 js_LeaveSharpObject(cx, NULL);
666 #endif
669 * Remove '(function ' from the beginning of valstr and ')' from the
670 * end so that we can put "get" in front of the function definition.
672 if (gsop[j] && IsFunctionObject(val[j])) {
673 const jschar *start = vchars;
674 const jschar *end = vchars + vlength;
676 uint8 parenChomp = 0;
677 if (vchars[0] == '(') {
678 vchars++;
679 parenChomp = 1;
682 /* Try to jump "function" keyword. */
683 if (vchars)
684 vchars = js_strchr_limit(vchars, ' ', end);
687 * Jump over the function's name: it can't be encoded as part
688 * of an ECMA getter or setter.
690 if (vchars)
691 vchars = js_strchr_limit(vchars, '(', end);
693 if (vchars) {
694 if (*vchars == ' ')
695 vchars++;
696 vlength = end - vchars - parenChomp;
697 } else {
698 gsop[j] = NULL;
699 vchars = start;
703 #define SAFE_ADD(n) \
704 JS_BEGIN_MACRO \
705 size_t n_ = (n); \
706 curlen += n_; \
707 if (curlen < n_) \
708 goto overflow; \
709 JS_END_MACRO
711 curlen = nchars;
712 if (comma)
713 SAFE_ADD(2);
714 SAFE_ADD(idstrlength + 1);
715 if (gsop[j])
716 SAFE_ADD(gsop[j]->length() + 1);
717 SAFE_ADD(vsharplength);
718 SAFE_ADD(vlength);
719 /* Account for the trailing null. */
720 SAFE_ADD((outermost ? 2 : 1) + 1);
721 #undef SAFE_ADD
723 if (curlen > size_t(-1) / sizeof(jschar))
724 goto overflow;
726 /* Allocate 1 + 1 at end for closing brace and terminating 0. */
727 chars = (jschar *) js_realloc((ochars = chars), curlen * sizeof(jschar));
728 if (!chars) {
729 /* Save code space on error: let JS_free ignore null vsharp. */
730 cx->free(vsharp);
731 js_free(ochars);
732 goto error;
735 if (comma) {
736 chars[nchars++] = comma[0];
737 chars[nchars++] = comma[1];
739 comma = ", ";
741 if (gsop[j]) {
742 gsoplength = gsop[j]->length();
743 js_strncpy(&chars[nchars], gsop[j]->chars(),
744 gsoplength);
745 nchars += gsoplength;
746 chars[nchars++] = ' ';
748 js_strncpy(&chars[nchars], idstrchars, idstrlength);
749 nchars += idstrlength;
750 /* Extraneous space after id here will be extracted later */
751 chars[nchars++] = gsop[j] ? ' ' : ':';
753 if (vsharplength) {
754 js_strncpy(&chars[nchars], vsharp, vsharplength);
755 nchars += vsharplength;
757 js_strncpy(&chars[nchars], vchars, vlength);
758 nchars += vlength;
760 if (vsharp)
761 cx->free(vsharp);
765 chars[nchars++] = '}';
766 if (outermost)
767 chars[nchars++] = ')';
768 chars[nchars] = 0;
770 error:
771 js_LeaveSharpObject(cx, &ida);
773 if (!ok) {
774 if (chars)
775 js_free(chars);
776 goto out;
779 if (!chars) {
780 JS_ReportOutOfMemory(cx);
781 ok = JS_FALSE;
782 goto out;
784 make_string:
785 str = js_NewString(cx, chars, nchars);
786 if (!str) {
787 js_free(chars);
788 ok = JS_FALSE;
789 goto out;
791 vp->setString(str);
792 ok = JS_TRUE;
793 out:
794 return ok;
796 overflow:
797 cx->free(vsharp);
798 js_free(chars);
799 chars = NULL;
800 goto error;
802 #endif /* JS_HAS_TOSOURCE */
804 namespace js {
806 JSString *
807 obj_toStringHelper(JSContext *cx, JSObject *obj)
809 if (obj->isProxy())
810 return JSProxy::obj_toString(cx, obj);
812 const char *clazz = obj->getClass()->name;
813 size_t nchars = 9 + strlen(clazz); /* 9 for "[object ]" */
814 jschar *chars = (jschar *) cx->malloc((nchars + 1) * sizeof(jschar));
815 if (!chars)
816 return NULL;
818 const char *prefix = "[object ";
819 nchars = 0;
820 while ((chars[nchars] = (jschar)*prefix) != 0)
821 nchars++, prefix++;
822 while ((chars[nchars] = (jschar)*clazz) != 0)
823 nchars++, clazz++;
824 chars[nchars++] = ']';
825 chars[nchars] = 0;
827 JSString *str = js_NewString(cx, chars, nchars);
828 if (!str)
829 cx->free(chars);
830 return str;
835 /* ES5 15.2.4.2. Note steps 1 and 2 are errata. */
836 static JSBool
837 obj_toString(JSContext *cx, uintN argc, Value *vp)
839 Value &thisv = vp[1];
841 /* ES5 15.2.4.2 step 1. */
842 if (thisv.isUndefined()) {
843 vp->setString(ATOM_TO_STRING(cx->runtime->atomState.objectUndefinedAtom));
844 return true;
847 /* ES5 15.2.4.2 step 2. */
848 if (thisv.isNull()) {
849 vp->setString(ATOM_TO_STRING(cx->runtime->atomState.objectNullAtom));
850 return true;
853 /* ES5 15.2.4.2 step 3. */
854 if (!thisv.isObject() && !js_PrimitiveToObject(cx, &thisv))
855 return false;
857 /* ES5 15.2.4.2 steps 4-5. */
858 JSString *str = js::obj_toStringHelper(cx, &thisv.toObject());
859 if (!str)
860 return false;
861 vp->setString(str);
862 return true;
865 static JSBool
866 obj_toLocaleString(JSContext *cx, uintN argc, Value *vp)
868 if (!ComputeThisFromVp(cx, vp))
869 return JS_FALSE;
871 JSString *str = js_ValueToString(cx, vp[1]);
872 if (!str)
873 return JS_FALSE;
875 vp->setString(str);
876 return JS_TRUE;
879 static JSBool
880 obj_valueOf(JSContext *cx, uintN argc, Value *vp)
882 if (!ComputeThisFromVp(cx, vp))
883 return JS_FALSE;
884 *vp = vp[1];
885 return JS_TRUE;
889 * Check if CSP allows new Function() or eval() to run in the current
890 * principals.
892 JSBool
893 js_CheckContentSecurityPolicy(JSContext *cx)
895 JSSecurityCallbacks *callbacks = JS_GetSecurityCallbacks(cx);
897 // if there are callbacks, make sure that the CSP callback is installed and
898 // that it permits eval().
899 if (callbacks && callbacks->contentSecurityPolicyAllows)
900 return callbacks->contentSecurityPolicyAllows(cx);
902 return JS_TRUE;
906 * Check whether principals subsumes scopeobj's principals, and return true
907 * if so (or if scopeobj has no principals, for backward compatibility with
908 * the JS API, which does not require principals), and false otherwise.
910 JSBool
911 js_CheckPrincipalsAccess(JSContext *cx, JSObject *scopeobj,
912 JSPrincipals *principals, JSAtom *caller)
914 JSSecurityCallbacks *callbacks;
915 JSPrincipals *scopePrincipals;
917 callbacks = JS_GetSecurityCallbacks(cx);
918 if (callbacks && callbacks->findObjectPrincipals) {
919 scopePrincipals = callbacks->findObjectPrincipals(cx, scopeobj);
920 if (!principals || !scopePrincipals ||
921 !principals->subsume(principals, scopePrincipals)) {
922 JSAutoByteString callerstr;
923 if (!js_AtomToPrintableString(cx, caller, &callerstr))
924 return JS_FALSE;
925 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
926 JSMSG_BAD_INDIRECT_CALL, callerstr.ptr());
927 return JS_FALSE;
930 return JS_TRUE;
933 static bool
934 CheckScopeChainValidity(JSContext *cx, JSObject *scopeobj)
936 JSObject *inner = scopeobj;
937 OBJ_TO_INNER_OBJECT(cx, inner);
938 if (!inner)
939 return false;
940 JS_ASSERT(inner == scopeobj);
942 /* XXX This is an awful gross hack. */
943 while (scopeobj) {
944 JSObjectOp op = scopeobj->getClass()->ext.innerObject;
945 if (op && op(cx, scopeobj) != scopeobj) {
946 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_INDIRECT_CALL,
947 js_eval_str);
948 return false;
950 scopeobj = scopeobj->getParent();
953 return true;
956 const char *
957 js_ComputeFilename(JSContext *cx, JSStackFrame *caller,
958 JSPrincipals *principals, uintN *linenop)
960 uint32 flags;
961 #ifdef DEBUG
962 JSSecurityCallbacks *callbacks = JS_GetSecurityCallbacks(cx);
963 #endif
965 JS_ASSERT(principals || !(callbacks && callbacks->findObjectPrincipals));
966 flags = JS_GetScriptFilenameFlags(caller->script());
967 if ((flags & JSFILENAME_PROTECTED) &&
968 principals &&
969 strcmp(principals->codebase, "[System Principal]")) {
970 *linenop = 0;
971 return principals->codebase;
974 jsbytecode *pc = caller->pc(cx);
975 if (pc && js_GetOpcode(cx, caller->script(), pc) == JSOP_EVAL) {
976 JS_ASSERT(js_GetOpcode(cx, caller->script(), pc + JSOP_EVAL_LENGTH) == JSOP_LINENO);
977 *linenop = GET_UINT16(pc + JSOP_EVAL_LENGTH);
978 } else {
979 *linenop = js_FramePCToLineNumber(cx, caller);
981 return caller->script()->filename;
984 #ifndef EVAL_CACHE_CHAIN_LIMIT
985 # define EVAL_CACHE_CHAIN_LIMIT 4
986 #endif
988 static inline JSScript **
989 EvalCacheHash(JSContext *cx, JSString *str)
991 const jschar *s;
992 size_t n;
993 uint32 h;
995 str->getCharsAndLength(s, n);
996 if (n > 100)
997 n = 100;
998 for (h = 0; n; s++, n--)
999 h = JS_ROTATE_LEFT32(h, 4) ^ *s;
1001 h *= JS_GOLDEN_RATIO;
1002 h >>= 32 - JS_EVAL_CACHE_SHIFT;
1003 return &JS_SCRIPTS_TO_GC(cx)[h];
1006 static JS_ALWAYS_INLINE JSScript *
1007 EvalCacheLookup(JSContext *cx, JSString *str, JSStackFrame *caller, uintN staticLevel,
1008 JSPrincipals *principals, JSObject *scopeobj, JSScript **bucket)
1011 * Cache local eval scripts indexed by source qualified by scope.
1013 * An eval cache entry should never be considered a hit unless its
1014 * strictness matches that of the new eval code. The existing code takes
1015 * care of this, because hits are qualified by the function from which
1016 * eval was called, whose strictness doesn't change. Scripts produced by
1017 * calls to eval from global code are not cached.
1019 uintN count = 0;
1020 JSScript **scriptp = bucket;
1022 EVAL_CACHE_METER(probe);
1023 JSVersion version = cx->findVersion();
1024 JSScript *script;
1025 while ((script = *scriptp) != NULL) {
1026 if (script->savedCallerFun &&
1027 script->staticLevel == staticLevel &&
1028 script->version == version &&
1029 (script->principals == principals ||
1030 (principals->subsume(principals, script->principals) &&
1031 script->principals->subsume(script->principals, principals)))) {
1033 * Get the prior (cache-filling) eval's saved caller function.
1034 * See Compiler::compileScript in jsparse.cpp.
1036 JSFunction *fun = script->getFunction(0);
1038 if (fun == caller->fun()) {
1040 * Get the source string passed for safekeeping in the
1041 * atom map by the prior eval to Compiler::compileScript.
1043 JSString *src = ATOM_TO_STRING(script->atomMap.vector[0]);
1045 if (src == str || js_EqualStrings(src, str)) {
1047 * Source matches, qualify by comparing scopeobj to the
1048 * COMPILE_N_GO-memoized parent of the first literal
1049 * function or regexp object if any. If none, then this
1050 * script has no compiled-in dependencies on the prior
1051 * eval's scopeobj.
1053 JSObjectArray *objarray = script->objects();
1054 int i = 1;
1056 if (objarray->length == 1) {
1057 if (script->regexpsOffset != 0) {
1058 objarray = script->regexps();
1059 i = 0;
1060 } else {
1061 EVAL_CACHE_METER(noscope);
1062 i = -1;
1065 if (i < 0 ||
1066 objarray->vector[i]->getParent() == scopeobj) {
1067 JS_ASSERT(staticLevel == script->staticLevel);
1068 EVAL_CACHE_METER(hit);
1069 *scriptp = script->u.nextToGC;
1070 script->u.nextToGC = NULL;
1071 return script;
1077 if (++count == EVAL_CACHE_CHAIN_LIMIT)
1078 return NULL;
1079 EVAL_CACHE_METER(step);
1080 scriptp = &script->u.nextToGC;
1082 return NULL;
1085 /* ES5 15.1.2.1. */
1086 static JSBool
1087 eval(JSContext *cx, uintN argc, Value *vp)
1090 * NB: This method handles only indirect eval: direct eval is handled by
1091 * JSOP_EVAL.
1094 JSStackFrame *caller = js_GetScriptedCaller(cx, NULL);
1096 /* FIXME Bug 602994: This really should be perfectly cromulent. */
1097 if (!caller) {
1098 /* Eval code needs to inherit principals from the caller. */
1099 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1100 JSMSG_BAD_INDIRECT_CALL, js_eval_str);
1101 return false;
1104 return EvalKernel(cx, argc, vp, INDIRECT_EVAL, caller, vp[0].toObject().getGlobal());
1107 namespace js {
1109 bool
1110 EvalKernel(JSContext *cx, uintN argc, Value *vp, EvalType evalType, JSStackFrame *caller,
1111 JSObject *scopeobj)
1114 * FIXME Bug 602994: Calls with no scripted caller should be permitted and
1115 * should be implemented as indirect calls.
1117 JS_ASSERT(caller);
1118 JS_ASSERT(scopeobj);
1121 * We once supported a second argument to eval to use as the scope chain
1122 * when evaluating the code string. Warn when such uses are seen so that
1123 * authors will know that support for eval(s, o) has been removed.
1125 JSScript *callerScript = caller->script();
1126 if (argc > 1 && !callerScript->warnedAboutTwoArgumentEval) {
1127 static const char TWO_ARGUMENT_WARNING[] =
1128 "Support for eval(code, scopeObject) has been removed. "
1129 "Use |with (scopeObject) eval(code);| instead.";
1130 if (!JS_ReportWarning(cx, TWO_ARGUMENT_WARNING))
1131 return false;
1132 callerScript->warnedAboutTwoArgumentEval = true;
1136 * CSP check: Is eval() allowed at all?
1137 * Report errors via CSP is done in the script security mgr.
1139 if (!js_CheckContentSecurityPolicy(cx)) {
1140 JS_ReportError(cx, "call to eval() blocked by CSP");
1141 return false;
1144 /* ES5 15.1.2.1 step 1. */
1145 if (argc < 1) {
1146 vp->setUndefined();
1147 return true;
1149 if (!vp[2].isString()) {
1150 *vp = vp[2];
1151 return true;
1153 JSString *str = vp[2].toString();
1155 /* ES5 15.1.2.1 steps 2-8. */
1156 JSObject *callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, Jsvalify(vp)));
1157 JS_ASSERT(IsBuiltinEvalFunction(callee->getFunctionPrivate()));
1158 JSPrincipals *principals = js_EvalFramePrincipals(cx, callee, caller);
1161 * Per ES5, indirect eval runs in the global scope. (eval is specified this
1162 * way so that the compiler can make assumptions about what bindings may or
1163 * may not exist in the current frame if it doesn't see 'eval'.)
1165 uintN staticLevel;
1166 if (evalType == DIRECT_EVAL) {
1167 staticLevel = caller->script()->staticLevel + 1;
1169 #ifdef DEBUG
1170 jsbytecode *callerPC = caller->pc(cx);
1171 JS_ASSERT_IF(caller->isFunctionFrame(), caller->hasCallObj());
1172 JS_ASSERT(callerPC && js_GetOpcode(cx, caller->script(), callerPC) == JSOP_EVAL);
1173 #endif
1174 } else {
1175 /* Pretend that we're top level. */
1176 staticLevel = 0;
1178 JS_ASSERT(scopeobj == scopeobj->getGlobal());
1179 JS_ASSERT(scopeobj->isGlobal());
1182 /* Ensure we compile this eval with the right object in the scope chain. */
1183 if (!CheckScopeChainValidity(cx, scopeobj))
1184 return false;
1186 const jschar *chars;
1187 size_t length;
1188 str->getCharsAndLength(chars, length);
1191 * If the eval string starts with '(' and ends with ')', it may be JSON.
1192 * Try the JSON parser first because it's much faster. If the eval string
1193 * isn't JSON, JSON parsing will probably fail quickly, so little time
1194 * will be lost.
1196 if (length > 2 && chars[0] == '(' && chars[length - 1] == ')') {
1197 JSONParser *jp = js_BeginJSONParse(cx, vp, /* suppressErrors = */true);
1198 if (jp != NULL) {
1199 /* Run JSON-parser on string inside ( and ). */
1200 bool ok = js_ConsumeJSONText(cx, jp, chars + 1, length - 2);
1201 ok &= js_FinishJSONParse(cx, jp, NullValue());
1202 if (ok)
1203 return true;
1208 * Direct calls to eval are supposed to see the caller's |this|. If we
1209 * haven't wrapped that yet, do so now, before we make a copy of it for
1210 * the eval code to use.
1212 if (evalType == DIRECT_EVAL && !caller->computeThis(cx))
1213 return false;
1215 JSScript *script = NULL;
1216 JSScript **bucket = EvalCacheHash(cx, str);
1217 if (evalType == DIRECT_EVAL && caller->isFunctionFrame())
1218 script = EvalCacheLookup(cx, str, caller, staticLevel, principals, scopeobj, bucket);
1221 * We can't have a callerFrame (down in js::Execute's terms) if we're in
1222 * global code (or if we're an indirect eval).
1224 JSStackFrame *callerFrame = (staticLevel != 0) ? caller : NULL;
1225 if (!script) {
1226 uintN lineno;
1227 const char *filename = js_ComputeFilename(cx, caller, principals, &lineno);
1229 uint32 tcflags = TCF_COMPILE_N_GO | TCF_NEED_MUTABLE_SCRIPT | TCF_COMPILE_FOR_EVAL;
1230 script = Compiler::compileScript(cx, scopeobj, callerFrame,
1231 principals, tcflags,
1232 chars, length,
1233 NULL, filename, lineno, str, staticLevel);
1234 if (!script)
1235 return false;
1238 assertSameCompartment(cx, scopeobj, script);
1241 * Belt-and-braces: check that the lesser of eval's principals and the
1242 * caller's principals has access to scopeobj.
1244 JSBool ok = js_CheckPrincipalsAccess(cx, scopeobj, principals,
1245 cx->runtime->atomState.evalAtom) &&
1246 Execute(cx, scopeobj, script, callerFrame, JSFRAME_EVAL, vp);
1248 script->u.nextToGC = *bucket;
1249 *bucket = script;
1250 #ifdef CHECK_SCRIPT_OWNER
1251 script->owner = NULL;
1252 #endif
1254 return ok;
1257 bool
1258 IsBuiltinEvalFunction(JSFunction *fun)
1260 return fun->maybeNative() == eval;
1265 #if JS_HAS_OBJ_WATCHPOINT
1267 static JSBool
1268 obj_watch_handler(JSContext *cx, JSObject *obj, jsid id, jsval old,
1269 jsval *nvp, void *closure)
1271 JSObject *callable;
1272 JSSecurityCallbacks *callbacks;
1273 JSStackFrame *caller;
1274 JSPrincipals *subject, *watcher;
1275 JSResolvingKey key;
1276 JSResolvingEntry *entry;
1277 uint32 generation;
1278 Value argv[3];
1279 JSBool ok;
1281 callable = (JSObject *) closure;
1283 callbacks = JS_GetSecurityCallbacks(cx);
1284 if (callbacks && callbacks->findObjectPrincipals) {
1285 /* Skip over any obj_watch_* frames between us and the real subject. */
1286 caller = js_GetScriptedCaller(cx, NULL);
1287 if (caller) {
1289 * Only call the watch handler if the watcher is allowed to watch
1290 * the currently executing script.
1292 watcher = callbacks->findObjectPrincipals(cx, callable);
1293 subject = js_StackFramePrincipals(cx, caller);
1295 if (watcher && subject && !watcher->subsume(watcher, subject)) {
1296 /* Silently don't call the watch handler. */
1297 return JS_TRUE;
1302 /* Avoid recursion on (obj, id) already being watched on cx. */
1303 key.obj = obj;
1304 key.id = id;
1305 if (!js_StartResolving(cx, &key, JSRESFLAG_WATCH, &entry))
1306 return JS_FALSE;
1307 if (!entry)
1308 return JS_TRUE;
1309 generation = cx->resolvingTable->generation;
1311 argv[0] = IdToValue(id);
1312 argv[1] = Valueify(old);
1313 argv[2] = Valueify(*nvp);
1314 ok = ExternalInvoke(cx, obj, ObjectOrNullValue(callable), 3, argv, Valueify(nvp));
1315 js_StopResolving(cx, &key, JSRESFLAG_WATCH, entry, generation);
1316 return ok;
1319 static JSBool
1320 obj_watch(JSContext *cx, uintN argc, Value *vp)
1322 if (argc <= 1) {
1323 js_ReportMissingArg(cx, *vp, 1);
1324 return JS_FALSE;
1327 JSObject *callable = js_ValueToCallableObject(cx, &vp[3], 0);
1328 if (!callable)
1329 return JS_FALSE;
1331 /* Compute the unique int/atom symbol id needed by js_LookupProperty. */
1332 jsid propid;
1333 if (!ValueToId(cx, vp[2], &propid))
1334 return JS_FALSE;
1336 JSObject *obj = ComputeThisFromVp(cx, vp);
1337 Value tmp;
1338 uintN attrs;
1339 if (!obj || !CheckAccess(cx, obj, propid, JSACC_WATCH, &tmp, &attrs))
1340 return JS_FALSE;
1342 vp->setUndefined();
1344 if (attrs & JSPROP_READONLY)
1345 return JS_TRUE;
1346 if (obj->isDenseArray() && !obj->makeDenseArraySlow(cx))
1347 return JS_FALSE;
1348 return JS_SetWatchPoint(cx, obj, propid, obj_watch_handler, callable);
1351 static JSBool
1352 obj_unwatch(JSContext *cx, uintN argc, Value *vp)
1354 JSObject *obj = ComputeThisFromVp(cx, vp);
1355 if (!obj)
1356 return JS_FALSE;
1357 vp->setUndefined();
1358 jsid id;
1359 if (argc != 0) {
1360 if (!ValueToId(cx, vp[2], &id))
1361 return JS_FALSE;
1362 } else {
1363 id = JSID_VOID;
1365 return JS_ClearWatchPoint(cx, obj, id, NULL, NULL);
1368 #endif /* JS_HAS_OBJ_WATCHPOINT */
1371 * Prototype and property query methods, to complement the 'in' and
1372 * 'instanceof' operators.
1375 /* Proposed ECMA 15.2.4.5. */
1376 static JSBool
1377 obj_hasOwnProperty(JSContext *cx, uintN argc, Value *vp)
1379 JSObject *obj = ComputeThisFromVp(cx, vp);
1380 return obj &&
1381 js_HasOwnPropertyHelper(cx, obj->getOps()->lookupProperty, argc, vp);
1384 JSBool
1385 js_HasOwnPropertyHelper(JSContext *cx, LookupPropOp lookup, uintN argc,
1386 Value *vp)
1388 jsid id;
1389 if (!ValueToId(cx, argc != 0 ? vp[2] : UndefinedValue(), &id))
1390 return JS_FALSE;
1392 JSObject *obj = ComputeThisFromVp(cx, vp);
1393 JSObject *obj2;
1394 JSProperty *prop;
1395 if (!obj)
1396 return false;
1397 if (obj->isProxy()) {
1398 bool has;
1399 if (!JSProxy::hasOwn(cx, obj, id, &has))
1400 return false;
1401 vp->setBoolean(has);
1402 return true;
1404 if (!js_HasOwnProperty(cx, lookup, obj, id, &obj2, &prop))
1405 return JS_FALSE;
1406 vp->setBoolean(!!prop);
1407 return JS_TRUE;
1410 JSBool
1411 js_HasOwnProperty(JSContext *cx, LookupPropOp lookup, JSObject *obj, jsid id,
1412 JSObject **objp, JSProperty **propp)
1414 JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_DETECTING);
1415 if (!(lookup ? lookup : js_LookupProperty)(cx, obj, id, objp, propp))
1416 return false;
1417 if (!*propp)
1418 return true;
1420 if (*objp == obj)
1421 return true;
1423 Class *clasp = (*objp)->getClass();
1424 JSObject *outer = NULL;
1425 if (JSObjectOp op = (*objp)->getClass()->ext.outerObject) {
1426 outer = op(cx, *objp);
1427 if (!outer)
1428 return false;
1431 if (outer != *objp) {
1432 if ((*objp)->isNative() && obj->getClass() == clasp) {
1434 * The combination of JSPROP_SHARED and JSPROP_PERMANENT in a
1435 * delegated property makes that property appear to be direct in
1436 * all delegating instances of the same native class. This hack
1437 * avoids bloating every function instance with its own 'length'
1438 * (AKA 'arity') property. But it must not extend across class
1439 * boundaries, to avoid making hasOwnProperty lie (bug 320854).
1441 * It's not really a hack, of course: a permanent property can't
1442 * be deleted, and JSPROP_SHARED means "don't allocate a slot in
1443 * any instance, prototype or delegating". Without a slot, and
1444 * without the ability to remove and recreate (with differences)
1445 * the property, there is no way to tell whether it is directly
1446 * owned, or indirectly delegated.
1448 Shape *shape = reinterpret_cast<Shape *>(*propp);
1449 if (shape->isSharedPermanent())
1450 return true;
1453 *propp = NULL;
1455 return true;
1458 /* Proposed ECMA 15.2.4.6. */
1459 static JSBool
1460 obj_isPrototypeOf(JSContext *cx, uintN argc, Value *vp)
1462 JSObject *obj = ComputeThisFromVp(cx, vp);
1463 if (!obj)
1464 return JS_FALSE;
1465 const Value &v = argc != 0 ? vp[2] : UndefinedValue();
1466 vp->setBoolean(js_IsDelegate(cx, obj, v));
1467 return JS_TRUE;
1470 /* Proposed ECMA 15.2.4.7. */
1471 static JSBool
1472 obj_propertyIsEnumerable(JSContext *cx, uintN argc, Value *vp)
1474 jsid id;
1475 if (!ValueToId(cx, argc != 0 ? vp[2] : UndefinedValue(), &id))
1476 return JS_FALSE;
1478 JSObject *obj = ComputeThisFromVp(cx, vp);
1479 return obj && js_PropertyIsEnumerable(cx, obj, id, vp);
1482 JSBool
1483 js_PropertyIsEnumerable(JSContext *cx, JSObject *obj, jsid id, Value *vp)
1485 JSObject *pobj;
1486 JSProperty *prop;
1487 if (!obj->lookupProperty(cx, id, &pobj, &prop))
1488 return JS_FALSE;
1490 if (!prop) {
1491 vp->setBoolean(false);
1492 return JS_TRUE;
1496 * XXX ECMA spec error compatible: return false unless hasOwnProperty.
1497 * The ECMA spec really should be fixed so propertyIsEnumerable and the
1498 * for..in loop agree on whether prototype properties are enumerable,
1499 * obviously by fixing this method (not by breaking the for..in loop!).
1501 * We check here for shared permanent prototype properties, which should
1502 * be treated as if they are local to obj. They are an implementation
1503 * technique used to satisfy ECMA requirements; users should not be able
1504 * to distinguish a shared permanent proto-property from a local one.
1506 bool shared;
1507 uintN attrs;
1508 if (pobj->isNative()) {
1509 Shape *shape = (Shape *) prop;
1510 shared = shape->isSharedPermanent();
1511 attrs = shape->attributes();
1512 } else {
1513 shared = false;
1514 if (!pobj->getAttributes(cx, id, &attrs))
1515 return false;
1517 if (pobj != obj && !shared) {
1518 vp->setBoolean(false);
1519 return true;
1521 vp->setBoolean((attrs & JSPROP_ENUMERATE) != 0);
1522 return true;
1525 #if OLD_GETTER_SETTER_METHODS
1527 const char js_defineGetter_str[] = "__defineGetter__";
1528 const char js_defineSetter_str[] = "__defineSetter__";
1529 const char js_lookupGetter_str[] = "__lookupGetter__";
1530 const char js_lookupSetter_str[] = "__lookupSetter__";
1532 JS_FRIEND_API(JSBool)
1533 js_obj_defineGetter(JSContext *cx, uintN argc, Value *vp)
1535 if (argc <= 1 || !js_IsCallable(vp[3])) {
1536 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1537 JSMSG_BAD_GETTER_OR_SETTER,
1538 js_getter_str);
1539 return JS_FALSE;
1541 PropertyOp getter = CastAsPropertyOp(&vp[3].toObject());
1543 jsid id;
1544 if (!ValueToId(cx, vp[2], &id))
1545 return JS_FALSE;
1546 JSObject *obj = ComputeThisFromVp(cx, vp);
1547 if (!obj || !CheckRedeclaration(cx, obj, id, JSPROP_GETTER, NULL, NULL))
1548 return JS_FALSE;
1550 * Getters and setters are just like watchpoints from an access
1551 * control point of view.
1553 Value junk;
1554 uintN attrs;
1555 if (!CheckAccess(cx, obj, id, JSACC_WATCH, &junk, &attrs))
1556 return JS_FALSE;
1557 vp->setUndefined();
1558 return obj->defineProperty(cx, id, UndefinedValue(), getter, PropertyStub,
1559 JSPROP_ENUMERATE | JSPROP_GETTER | JSPROP_SHARED);
1562 JS_FRIEND_API(JSBool)
1563 js_obj_defineSetter(JSContext *cx, uintN argc, Value *vp)
1565 if (argc <= 1 || !js_IsCallable(vp[3])) {
1566 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1567 JSMSG_BAD_GETTER_OR_SETTER,
1568 js_setter_str);
1569 return JS_FALSE;
1571 PropertyOp setter = CastAsPropertyOp(&vp[3].toObject());
1573 jsid id;
1574 if (!ValueToId(cx, vp[2], &id))
1575 return JS_FALSE;
1576 JSObject *obj = ComputeThisFromVp(cx, vp);
1577 if (!obj || !CheckRedeclaration(cx, obj, id, JSPROP_SETTER, NULL, NULL))
1578 return JS_FALSE;
1580 * Getters and setters are just like watchpoints from an access
1581 * control point of view.
1583 Value junk;
1584 uintN attrs;
1585 if (!CheckAccess(cx, obj, id, JSACC_WATCH, &junk, &attrs))
1586 return JS_FALSE;
1587 vp->setUndefined();
1588 return obj->defineProperty(cx, id, UndefinedValue(), PropertyStub, setter,
1589 JSPROP_ENUMERATE | JSPROP_SETTER | JSPROP_SHARED);
1592 static JSBool
1593 obj_lookupGetter(JSContext *cx, uintN argc, Value *vp)
1595 jsid id;
1596 if (!ValueToId(cx, argc != 0 ? vp[2] : UndefinedValue(), &id))
1597 return JS_FALSE;
1598 JSObject *obj = ComputeThisFromVp(cx, vp);
1599 JSObject *pobj;
1600 JSProperty *prop;
1601 if (!obj || !obj->lookupProperty(cx, id, &pobj, &prop))
1602 return JS_FALSE;
1603 vp->setUndefined();
1604 if (prop) {
1605 if (pobj->isNative()) {
1606 Shape *shape = (Shape *) prop;
1607 if (shape->hasGetterValue())
1608 *vp = shape->getterValue();
1611 return JS_TRUE;
1614 static JSBool
1615 obj_lookupSetter(JSContext *cx, uintN argc, Value *vp)
1617 jsid id;
1618 if (!ValueToId(cx, argc != 0 ? vp[2] : UndefinedValue(), &id))
1619 return JS_FALSE;
1620 JSObject *obj = ComputeThisFromVp(cx, vp);
1621 JSObject *pobj;
1622 JSProperty *prop;
1623 if (!obj || !obj->lookupProperty(cx, id, &pobj, &prop))
1624 return JS_FALSE;
1625 vp->setUndefined();
1626 if (prop) {
1627 if (pobj->isNative()) {
1628 Shape *shape = (Shape *) prop;
1629 if (shape->hasSetterValue())
1630 *vp = shape->setterValue();
1633 return JS_TRUE;
1635 #endif /* OLD_GETTER_SETTER_METHODS */
1637 JSBool
1638 obj_getPrototypeOf(JSContext *cx, uintN argc, Value *vp)
1640 if (argc == 0) {
1641 js_ReportMissingArg(cx, *vp, 0);
1642 return JS_FALSE;
1645 if (vp[2].isPrimitive()) {
1646 char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, vp[2], NULL);
1647 if (!bytes)
1648 return JS_FALSE;
1649 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1650 JSMSG_UNEXPECTED_TYPE, bytes, "not an object");
1651 JS_free(cx, bytes);
1652 return JS_FALSE;
1655 JSObject *obj = &vp[2].toObject();
1656 uintN attrs;
1657 return CheckAccess(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.protoAtom),
1658 JSACC_PROTO, vp, &attrs);
1661 extern JSBool
1662 js_NewPropertyDescriptorObject(JSContext *cx, jsid id, uintN attrs,
1663 const Value &getter, const Value &setter,
1664 const Value &value, Value *vp)
1666 /* We have our own property, so start creating the descriptor. */
1667 JSObject *desc = NewBuiltinClassInstance(cx, &js_ObjectClass);
1668 if (!desc)
1669 return false;
1670 vp->setObject(*desc); /* Root and return. */
1672 const JSAtomState &atomState = cx->runtime->atomState;
1673 if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
1674 if (!desc->defineProperty(cx, ATOM_TO_JSID(atomState.getAtom), getter,
1675 PropertyStub, PropertyStub, JSPROP_ENUMERATE) ||
1676 !desc->defineProperty(cx, ATOM_TO_JSID(atomState.setAtom), setter,
1677 PropertyStub, PropertyStub, JSPROP_ENUMERATE)) {
1678 return false;
1680 } else {
1681 if (!desc->defineProperty(cx, ATOM_TO_JSID(atomState.valueAtom), value,
1682 PropertyStub, PropertyStub, JSPROP_ENUMERATE) ||
1683 !desc->defineProperty(cx, ATOM_TO_JSID(atomState.writableAtom),
1684 BooleanValue((attrs & JSPROP_READONLY) == 0),
1685 PropertyStub, PropertyStub, JSPROP_ENUMERATE)) {
1686 return false;
1690 return desc->defineProperty(cx, ATOM_TO_JSID(atomState.enumerableAtom),
1691 BooleanValue((attrs & JSPROP_ENUMERATE) != 0),
1692 PropertyStub, PropertyStub, JSPROP_ENUMERATE) &&
1693 desc->defineProperty(cx, ATOM_TO_JSID(atomState.configurableAtom),
1694 BooleanValue((attrs & JSPROP_PERMANENT) == 0),
1695 PropertyStub, PropertyStub, JSPROP_ENUMERATE);
1698 JSBool
1699 js_GetOwnPropertyDescriptor(JSContext *cx, JSObject *obj, jsid id, Value *vp)
1701 if (obj->isProxy())
1702 return JSProxy::getOwnPropertyDescriptor(cx, obj, id, false, vp);
1704 JSObject *pobj;
1705 JSProperty *prop;
1706 if (!js_HasOwnProperty(cx, obj->getOps()->lookupProperty, obj, id, &pobj, &prop))
1707 return false;
1708 if (!prop) {
1709 vp->setUndefined();
1710 return true;
1713 Value roots[] = { UndefinedValue(), UndefinedValue(), UndefinedValue() };
1714 AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(roots), roots);
1715 unsigned attrs;
1716 bool doGet = true;
1717 if (pobj->isNative()) {
1718 Shape *shape = (Shape *) prop;
1719 attrs = shape->attributes();
1720 if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
1721 doGet = false;
1722 if (attrs & JSPROP_GETTER)
1723 roots[0] = shape->getterValue();
1724 if (attrs & JSPROP_SETTER)
1725 roots[1] = shape->setterValue();
1727 } else {
1728 if (!pobj->getAttributes(cx, id, &attrs))
1729 return false;
1732 if (doGet && !obj->getProperty(cx, id, &roots[2]))
1733 return false;
1735 return js_NewPropertyDescriptorObject(cx, id,
1736 attrs,
1737 roots[0], /* getter */
1738 roots[1], /* setter */
1739 roots[2], /* value */
1740 vp);
1743 static bool
1744 GetFirstArgumentAsObject(JSContext *cx, uintN argc, Value *vp, const char *method, JSObject **objp)
1746 if (argc == 0) {
1747 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
1748 method, "0", "s");
1749 return false;
1752 const Value &v = vp[2];
1753 if (!v.isObject()) {
1754 char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NULL);
1755 if (!bytes)
1756 return false;
1757 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_UNEXPECTED_TYPE,
1758 bytes, "not an object");
1759 JS_free(cx, bytes);
1760 return false;
1763 *objp = &v.toObject();
1764 return true;
1767 static JSBool
1768 obj_getOwnPropertyDescriptor(JSContext *cx, uintN argc, Value *vp)
1770 JSObject *obj;
1771 if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.getOwnPropertyDescriptor", &obj))
1772 return JS_FALSE;
1773 AutoIdRooter nameidr(cx);
1774 if (!ValueToId(cx, argc >= 2 ? vp[3] : UndefinedValue(), nameidr.addr()))
1775 return JS_FALSE;
1776 return js_GetOwnPropertyDescriptor(cx, obj, nameidr.id(), vp);
1779 static JSBool
1780 obj_keys(JSContext *cx, uintN argc, Value *vp)
1782 JSObject *obj;
1783 if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.keys", &obj))
1784 return false;
1786 AutoIdVector props(cx);
1787 if (!GetPropertyNames(cx, obj, JSITER_OWNONLY, &props))
1788 return false;
1790 AutoValueVector vals(cx);
1791 if (!vals.reserve(props.length()))
1792 return false;
1793 for (size_t i = 0, len = props.length(); i < len; i++) {
1794 jsid id = props[i];
1795 if (JSID_IS_STRING(id)) {
1796 JS_ALWAYS_TRUE(vals.append(StringValue(JSID_TO_STRING(id))));
1797 } else if (JSID_IS_INT(id)) {
1798 JSString *str = js_IntToString(cx, JSID_TO_INT(id));
1799 if (!str)
1800 return false;
1801 JS_ALWAYS_TRUE(vals.append(StringValue(str)));
1802 } else {
1803 JS_ASSERT(JSID_IS_OBJECT(id));
1807 JS_ASSERT(props.length() <= UINT32_MAX);
1808 JSObject *aobj = js_NewArrayObject(cx, jsuint(vals.length()), vals.begin());
1809 if (!aobj)
1810 return false;
1811 vp->setObject(*aobj);
1813 return true;
1816 static bool
1817 HasProperty(JSContext* cx, JSObject* obj, jsid id, Value* vp, bool *foundp)
1819 if (!obj->hasProperty(cx, id, foundp, JSRESOLVE_QUALIFIED | JSRESOLVE_DETECTING))
1820 return false;
1821 if (!*foundp) {
1822 vp->setUndefined();
1823 return true;
1827 * We must go through the method read barrier in case id is 'get' or 'set'.
1828 * There is no obvious way to defer cloning a joined function object whose
1829 * identity will be used by DefinePropertyOnObject, e.g., or reflected via
1830 * js_GetOwnPropertyDescriptor, as the getter or setter callable object.
1832 return !!obj->getProperty(cx, id, vp);
1835 PropDesc::PropDesc()
1836 : pd(UndefinedValue()),
1837 id(INT_TO_JSID(0)),
1838 value(UndefinedValue()),
1839 get(UndefinedValue()),
1840 set(UndefinedValue()),
1841 attrs(0),
1842 hasGet(false),
1843 hasSet(false),
1844 hasValue(false),
1845 hasWritable(false),
1846 hasEnumerable(false),
1847 hasConfigurable(false)
1851 bool
1852 PropDesc::initialize(JSContext* cx, jsid id, const Value &origval)
1854 Value v = origval;
1855 this->id = id;
1857 /* 8.10.5 step 1 */
1858 if (v.isPrimitive()) {
1859 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_NONNULL_OBJECT);
1860 return false;
1862 JSObject* desc = &v.toObject();
1864 /* Make a copy of the descriptor. We might need it later. */
1865 pd = v;
1867 /* Start with the proper defaults. */
1868 attrs = JSPROP_PERMANENT | JSPROP_READONLY;
1870 bool found;
1872 /* 8.10.5 step 3 */
1873 if (!HasProperty(cx, desc, ATOM_TO_JSID(cx->runtime->atomState.enumerableAtom), &v, &found))
1874 return false;
1875 if (found) {
1876 hasEnumerable = JS_TRUE;
1877 if (js_ValueToBoolean(v))
1878 attrs |= JSPROP_ENUMERATE;
1881 /* 8.10.5 step 4 */
1882 if (!HasProperty(cx, desc, ATOM_TO_JSID(cx->runtime->atomState.configurableAtom), &v, &found))
1883 return false;
1884 if (found) {
1885 hasConfigurable = JS_TRUE;
1886 if (js_ValueToBoolean(v))
1887 attrs &= ~JSPROP_PERMANENT;
1890 /* 8.10.5 step 5 */
1891 if (!HasProperty(cx, desc, ATOM_TO_JSID(cx->runtime->atomState.valueAtom), &v, &found))
1892 return false;
1893 if (found) {
1894 hasValue = true;
1895 value = v;
1898 /* 8.10.6 step 6 */
1899 if (!HasProperty(cx, desc, ATOM_TO_JSID(cx->runtime->atomState.writableAtom), &v, &found))
1900 return false;
1901 if (found) {
1902 hasWritable = JS_TRUE;
1903 if (js_ValueToBoolean(v))
1904 attrs &= ~JSPROP_READONLY;
1907 /* 8.10.7 step 7 */
1908 if (!HasProperty(cx, desc, ATOM_TO_JSID(cx->runtime->atomState.getAtom), &v, &found))
1909 return false;
1910 if (found) {
1911 if ((v.isPrimitive() || !js_IsCallable(v)) && !v.isUndefined()) {
1912 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_GET_SET_FIELD,
1913 js_getter_str);
1914 return false;
1916 hasGet = true;
1917 get = v;
1918 attrs |= JSPROP_GETTER | JSPROP_SHARED;
1921 /* 8.10.7 step 8 */
1922 if (!HasProperty(cx, desc, ATOM_TO_JSID(cx->runtime->atomState.setAtom), &v, &found))
1923 return false;
1924 if (found) {
1925 if ((v.isPrimitive() || !js_IsCallable(v)) && !v.isUndefined()) {
1926 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_GET_SET_FIELD,
1927 js_setter_str);
1928 return false;
1930 hasSet = true;
1931 set = v;
1932 attrs |= JSPROP_SETTER | JSPROP_SHARED;
1935 /* 8.10.7 step 9 */
1936 if ((hasGet || hasSet) && (hasValue || hasWritable)) {
1937 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INVALID_DESCRIPTOR);
1938 return false;
1941 return true;
1944 static JSBool
1945 Reject(JSContext *cx, uintN errorNumber, bool throwError, jsid id, bool *rval)
1947 if (throwError) {
1948 jsid idstr;
1949 if (!js_ValueToStringId(cx, IdToValue(id), &idstr))
1950 return JS_FALSE;
1951 JSAutoByteString bytes(cx, JSID_TO_STRING(idstr));
1952 if (!bytes)
1953 return JS_FALSE;
1954 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, errorNumber, bytes.ptr());
1955 return JS_FALSE;
1958 *rval = false;
1959 return JS_TRUE;
1962 static JSBool
1963 Reject(JSContext *cx, JSObject *obj, uintN errorNumber, bool throwError, bool *rval)
1965 if (throwError) {
1966 if (js_ErrorFormatString[errorNumber].argCount == 1) {
1967 js_ReportValueErrorFlags(cx, JSREPORT_ERROR, errorNumber,
1968 JSDVG_IGNORE_STACK, ObjectValue(*obj),
1969 NULL, NULL, NULL);
1970 } else {
1971 JS_ASSERT(js_ErrorFormatString[errorNumber].argCount == 0);
1972 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, errorNumber);
1974 return JS_FALSE;
1977 *rval = false;
1978 return JS_TRUE;
1981 static JSBool
1982 DefinePropertyOnObject(JSContext *cx, JSObject *obj, const PropDesc &desc,
1983 bool throwError, bool *rval)
1985 /* 8.12.9 step 1. */
1986 JSProperty *current;
1987 JSObject *obj2;
1988 JS_ASSERT(!obj->getOps()->lookupProperty);
1989 if (!js_HasOwnProperty(cx, NULL, obj, desc.id, &obj2, &current))
1990 return JS_FALSE;
1992 JS_ASSERT(!obj->getOps()->defineProperty);
1994 /* 8.12.9 steps 2-4. */
1995 if (!current) {
1996 if (!obj->isExtensible())
1997 return Reject(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE, throwError, rval);
1999 *rval = true;
2001 if (desc.isGenericDescriptor() || desc.isDataDescriptor()) {
2002 JS_ASSERT(!obj->getOps()->defineProperty);
2003 return js_DefineProperty(cx, obj, desc.id, &desc.value,
2004 PropertyStub, PropertyStub, desc.attrs);
2007 JS_ASSERT(desc.isAccessorDescriptor());
2010 * Getters and setters are just like watchpoints from an access
2011 * control point of view.
2013 Value dummy;
2014 uintN dummyAttrs;
2015 if (!CheckAccess(cx, obj, desc.id, JSACC_WATCH, &dummy, &dummyAttrs))
2016 return JS_FALSE;
2018 Value tmp = UndefinedValue();
2019 return js_DefineProperty(cx, obj, desc.id, &tmp,
2020 desc.getter(), desc.setter(), desc.attrs);
2023 /* 8.12.9 steps 5-6 (note 5 is merely a special case of 6). */
2024 Value v = UndefinedValue();
2027 * In the special case of shared permanent properties, the "own" property
2028 * can be found on a different object. In that case the returned property
2029 * might not be native, except: the shared permanent property optimization
2030 * is not applied if the objects have different classes (bug 320854), as
2031 * must be enforced by js_HasOwnProperty for the Shape cast below to be
2032 * safe.
2034 JS_ASSERT(obj->getClass() == obj2->getClass());
2036 const Shape *shape = reinterpret_cast<Shape *>(current);
2037 do {
2038 if (desc.isAccessorDescriptor()) {
2039 if (!shape->isAccessorDescriptor())
2040 break;
2042 if (desc.hasGet &&
2043 !SameValue(desc.getterValue(), shape->getterOrUndefined(), cx)) {
2044 break;
2047 if (desc.hasSet &&
2048 !SameValue(desc.setterValue(), shape->setterOrUndefined(), cx)) {
2049 break;
2051 } else {
2053 * Determine the current value of the property once, if the current
2054 * value might actually need to be used or preserved later. NB: we
2055 * guard on whether the current property is a data descriptor to
2056 * avoid calling a getter; we won't need the value if it's not a
2057 * data descriptor.
2059 if (shape->isDataDescriptor()) {
2061 * Non-standard: if the property is non-configurable and is
2062 * represented by a native getter or setter, don't permit
2063 * redefinition. We expose properties with native getter/setter
2064 * as though they were data properties, for the most part, but
2065 * in this particular case we must worry about integrity
2066 * concerns for JSAPI users who expected that
2067 * permanent+getter/setter means precisely controlled behavior.
2068 * If we permitted such redefinitions, such a property could be
2069 * "fixed" to some specific previous value, no longer varying
2070 * according to the intent of the native getter/setter for the
2071 * property.
2073 * Other engines expose properties of this nature using ECMA
2074 * getter/setter pairs, but we can't because we use them even
2075 * for properties which ECMA specifies as being true data
2076 * descriptors ([].length, Function.length, /regex/.lastIndex,
2077 * &c.). Longer-term perhaps we should convert such properties
2078 * to use data descriptors (at which point representing a
2079 * descriptor with native getter/setter as an accessor
2080 * descriptor would be fine) and take a small memory hit, but
2081 * for now we'll simply forbid their redefinition.
2083 if (!shape->configurable() &&
2084 (!shape->hasDefaultGetter() || !shape->hasDefaultSetter())) {
2085 return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, desc.id, rval);
2088 if (!js_NativeGet(cx, obj, obj2, shape, JSGET_NO_METHOD_BARRIER, &v)) {
2089 /* current was dropped when the failure occurred. */
2090 return JS_FALSE;
2094 if (desc.isDataDescriptor()) {
2095 if (!shape->isDataDescriptor())
2096 break;
2098 if (desc.hasValue && !SameValue(desc.value, v, cx))
2099 break;
2100 if (desc.hasWritable && desc.writable() != shape->writable())
2101 break;
2102 } else {
2103 /* The only fields in desc will be handled below. */
2104 JS_ASSERT(desc.isGenericDescriptor());
2108 if (desc.hasConfigurable && desc.configurable() != shape->configurable())
2109 break;
2110 if (desc.hasEnumerable && desc.enumerable() != shape->enumerable())
2111 break;
2113 /* The conditions imposed by step 5 or step 6 apply. */
2114 *rval = true;
2115 return JS_TRUE;
2116 } while (0);
2118 /* 8.12.9 step 7. */
2119 if (!shape->configurable()) {
2121 * Since [[Configurable]] defaults to false, we don't need to check
2122 * whether it was specified. We can't do likewise for [[Enumerable]]
2123 * because its putative value is used in a comparison -- a comparison
2124 * whose result must always be false per spec if the [[Enumerable]]
2125 * field is not present. Perfectly pellucid logic, eh?
2127 JS_ASSERT_IF(!desc.hasConfigurable, !desc.configurable());
2128 if (desc.configurable() ||
2129 (desc.hasEnumerable && desc.enumerable() != shape->enumerable())) {
2130 return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, desc.id, rval);
2134 bool callDelProperty = false;
2136 if (desc.isGenericDescriptor()) {
2137 /* 8.12.9 step 8, no validation required */
2138 } else if (desc.isDataDescriptor() != shape->isDataDescriptor()) {
2139 /* 8.12.9 step 9. */
2140 if (!shape->configurable())
2141 return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, desc.id, rval);
2142 } else if (desc.isDataDescriptor()) {
2143 /* 8.12.9 step 10. */
2144 JS_ASSERT(shape->isDataDescriptor());
2145 if (!shape->configurable() && !shape->writable()) {
2146 if ((desc.hasWritable && desc.writable()) ||
2147 (desc.hasValue && !SameValue(desc.value, v, cx))) {
2148 return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, desc.id, rval);
2152 callDelProperty = !shape->hasDefaultGetter() || !shape->hasDefaultSetter();
2153 } else {
2154 /* 8.12.9 step 11. */
2155 JS_ASSERT(desc.isAccessorDescriptor() && shape->isAccessorDescriptor());
2156 if (!shape->configurable()) {
2157 if ((desc.hasSet &&
2158 !SameValue(desc.setterValue(), shape->setterOrUndefined(), cx)) ||
2159 (desc.hasGet &&
2160 !SameValue(desc.getterValue(), shape->getterOrUndefined(), cx))) {
2161 return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, desc.id, rval);
2166 /* 8.12.9 step 12. */
2167 uintN attrs;
2168 PropertyOp getter, setter;
2169 if (desc.isGenericDescriptor()) {
2170 uintN changed = 0;
2171 if (desc.hasConfigurable)
2172 changed |= JSPROP_PERMANENT;
2173 if (desc.hasEnumerable)
2174 changed |= JSPROP_ENUMERATE;
2176 attrs = (shape->attributes() & ~changed) | (desc.attrs & changed);
2177 if (shape->isMethod()) {
2178 JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
2179 getter = setter = PropertyStub;
2180 } else {
2181 getter = shape->getter();
2182 setter = shape->setter();
2184 } else if (desc.isDataDescriptor()) {
2185 uintN unchanged = 0;
2186 if (!desc.hasConfigurable)
2187 unchanged |= JSPROP_PERMANENT;
2188 if (!desc.hasEnumerable)
2189 unchanged |= JSPROP_ENUMERATE;
2190 if (!desc.hasWritable)
2191 unchanged |= JSPROP_READONLY;
2193 if (desc.hasValue)
2194 v = desc.value;
2195 attrs = (desc.attrs & ~unchanged) | (shape->attributes() & unchanged);
2196 getter = setter = PropertyStub;
2197 } else {
2198 JS_ASSERT(desc.isAccessorDescriptor());
2201 * Getters and setters are just like watchpoints from an access
2202 * control point of view.
2204 Value dummy;
2205 if (!CheckAccess(cx, obj2, desc.id, JSACC_WATCH, &dummy, &attrs))
2206 return JS_FALSE;
2208 JS_ASSERT_IF(shape->isMethod(), !(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
2210 /* 8.12.9 step 12. */
2211 uintN changed = 0;
2212 if (desc.hasConfigurable)
2213 changed |= JSPROP_PERMANENT;
2214 if (desc.hasEnumerable)
2215 changed |= JSPROP_ENUMERATE;
2216 if (desc.hasGet)
2217 changed |= JSPROP_GETTER | JSPROP_SHARED;
2218 if (desc.hasSet)
2219 changed |= JSPROP_SETTER | JSPROP_SHARED;
2221 attrs = (desc.attrs & changed) | (shape->attributes() & ~changed);
2222 if (desc.hasGet) {
2223 getter = desc.getter();
2224 } else {
2225 getter = (shape->isMethod() || (shape->hasDefaultGetter() && !shape->hasGetterValue()))
2226 ? PropertyStub
2227 : shape->getter();
2229 if (desc.hasSet) {
2230 setter = desc.setter();
2231 } else {
2232 setter = (shape->hasDefaultSetter() && !shape->hasSetterValue())
2233 ? PropertyStub
2234 : shape->setter();
2238 *rval = true;
2241 * Since "data" properties implemented using native C functions may rely on
2242 * side effects during setting, we must make them aware that they have been
2243 * "assigned"; deleting the property before redefining it does the trick.
2244 * See bug 539766, where we ran into problems when we redefined
2245 * arguments.length without making the property aware that its value had
2246 * been changed (which would have happened if we had deleted it before
2247 * redefining it or we had invoked its setter to change its value).
2249 if (callDelProperty) {
2250 Value dummy;
2251 if (!CallJSPropertyOp(cx, obj2->getClass()->delProperty, obj2, desc.id, &dummy))
2252 return false;
2255 return js_DefineProperty(cx, obj, desc.id, &v, getter, setter, attrs);
2258 static JSBool
2259 DefinePropertyOnArray(JSContext *cx, JSObject *obj, const PropDesc &desc,
2260 bool throwError, bool *rval)
2263 * We probably should optimize dense array property definitions where
2264 * the descriptor describes a traditional array property (enumerable,
2265 * configurable, writable, numeric index or length without altering its
2266 * attributes). Such definitions are probably unlikely, so we don't bother
2267 * for now.
2269 if (obj->isDenseArray() && !obj->makeDenseArraySlow(cx))
2270 return JS_FALSE;
2272 jsuint oldLen = obj->getArrayLength();
2274 if (JSID_IS_ATOM(desc.id, cx->runtime->atomState.lengthAtom)) {
2276 * Our optimization of storage of the length property of arrays makes
2277 * it very difficult to properly implement defining the property. For
2278 * now simply throw an exception (NB: not merely Reject) on any attempt
2279 * to define the "length" property, rather than attempting to implement
2280 * some difficult-for-authors-to-grasp subset of that functionality.
2282 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_DEFINE_ARRAY_LENGTH);
2283 return JS_FALSE;
2286 uint32 index;
2287 if (js_IdIsIndex(desc.id, &index)) {
2289 // Disabled until we support defining "length":
2290 if (index >= oldLen && lengthPropertyNotWritable())
2291 return ThrowTypeError(cx, JSMSG_CANT_APPEND_TO_ARRAY);
2293 if (!DefinePropertyOnObject(cx, obj, desc, false, rval))
2294 return JS_FALSE;
2295 if (!*rval)
2296 return Reject(cx, obj, JSMSG_CANT_DEFINE_ARRAY_INDEX, throwError, rval);
2298 if (index >= oldLen) {
2299 JS_ASSERT(index != UINT32_MAX);
2300 obj->setArrayLength(index + 1);
2303 *rval = true;
2304 return JS_TRUE;
2307 return DefinePropertyOnObject(cx, obj, desc, throwError, rval);
2310 static JSBool
2311 DefineProperty(JSContext *cx, JSObject *obj, const PropDesc &desc, bool throwError,
2312 bool *rval)
2314 if (obj->isArray())
2315 return DefinePropertyOnArray(cx, obj, desc, throwError, rval);
2317 if (obj->getOps()->lookupProperty) {
2318 if (obj->isProxy())
2319 return JSProxy::defineProperty(cx, obj, desc.id, desc.pd);
2320 return Reject(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE, throwError, rval);
2323 return DefinePropertyOnObject(cx, obj, desc, throwError, rval);
2326 JSBool
2327 js_DefineOwnProperty(JSContext *cx, JSObject *obj, jsid id,
2328 const Value &descriptor, JSBool *bp)
2330 AutoPropDescArrayRooter descs(cx);
2331 PropDesc *desc = descs.append();
2332 if (!desc || !desc->initialize(cx, id, descriptor))
2333 return false;
2335 bool rval;
2336 if (!DefineProperty(cx, obj, *desc, true, &rval))
2337 return false;
2338 *bp = !!rval;
2339 return true;
2342 /* ES5 15.2.3.6: Object.defineProperty(O, P, Attributes) */
2343 static JSBool
2344 obj_defineProperty(JSContext* cx, uintN argc, Value* vp)
2346 /* 15.2.3.6 steps 1 and 5. */
2347 JSObject *obj;
2348 if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.defineProperty", &obj))
2349 return JS_FALSE;
2350 vp->setObject(*obj);
2352 /* 15.2.3.6 step 2. */
2353 AutoIdRooter nameidr(cx);
2354 if (!ValueToId(cx, argc >= 2 ? vp[3] : UndefinedValue(), nameidr.addr()))
2355 return JS_FALSE;
2357 /* 15.2.3.6 step 3. */
2358 const Value &descval = argc >= 3 ? vp[4] : UndefinedValue();
2360 /* 15.2.3.6 step 4 */
2361 JSBool junk;
2362 return js_DefineOwnProperty(cx, obj, nameidr.id(), descval, &junk);
2365 static bool
2366 DefineProperties(JSContext *cx, JSObject *obj, JSObject *props)
2368 AutoIdArray ida(cx, JS_Enumerate(cx, props));
2369 if (!ida)
2370 return false;
2372 AutoPropDescArrayRooter descs(cx);
2373 size_t len = ida.length();
2374 for (size_t i = 0; i < len; i++) {
2375 jsid id = ida[i];
2376 PropDesc* desc = descs.append();
2377 AutoValueRooter tvr(cx);
2378 if (!desc ||
2379 !JS_GetPropertyById(cx, props, id, tvr.jsval_addr()) ||
2380 !desc->initialize(cx, id, tvr.value())) {
2381 return false;
2385 bool dummy;
2386 for (size_t i = 0; i < len; i++) {
2387 if (!DefineProperty(cx, obj, descs[i], true, &dummy))
2388 return false;
2391 return true;
2394 extern JSBool
2395 js_PopulateObject(JSContext *cx, JSObject *newborn, JSObject *props)
2397 return DefineProperties(cx, newborn, props);
2400 /* ES5 15.2.3.7: Object.defineProperties(O, Properties) */
2401 static JSBool
2402 obj_defineProperties(JSContext* cx, uintN argc, Value* vp)
2404 /* 15.2.3.6 steps 1 and 5. */
2405 JSObject *obj;
2406 if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.defineProperties", &obj))
2407 return false;
2408 vp->setObject(*obj);
2410 if (argc < 2) {
2411 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
2412 "Object.defineProperties", "0", "s");
2413 return false;
2416 JSObject* props = js_ValueToNonNullObject(cx, vp[3]);
2417 if (!props)
2418 return false;
2419 vp[3].setObject(*props);
2421 return DefineProperties(cx, obj, props);
2424 /* ES5 15.2.3.5: Object.create(O [, Properties]) */
2425 static JSBool
2426 obj_create(JSContext *cx, uintN argc, Value *vp)
2428 if (argc == 0) {
2429 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
2430 "Object.create", "0", "s");
2431 return JS_FALSE;
2434 const Value &v = vp[2];
2435 if (!v.isObjectOrNull()) {
2436 char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NULL);
2437 if (!bytes)
2438 return JS_FALSE;
2439 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_UNEXPECTED_TYPE,
2440 bytes, "not an object or null");
2441 JS_free(cx, bytes);
2442 return JS_FALSE;
2446 * Use the callee's global as the parent of the new object to avoid dynamic
2447 * scoping (i.e., using the caller's global).
2449 JSObject *obj = NewNonFunction<WithProto::Given>(cx, &js_ObjectClass, v.toObjectOrNull(),
2450 vp->toObject().getGlobal());
2451 if (!obj)
2452 return JS_FALSE;
2453 vp->setObject(*obj); /* Root and prepare for eventual return. */
2455 /* 15.2.3.5 step 4. */
2456 if (argc > 1 && !vp[3].isUndefined()) {
2457 if (vp[3].isPrimitive()) {
2458 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_NONNULL_OBJECT);
2459 return JS_FALSE;
2462 JSObject *props = &vp[3].toObject();
2463 AutoIdArray ida(cx, JS_Enumerate(cx, props));
2464 if (!ida)
2465 return JS_FALSE;
2467 AutoPropDescArrayRooter descs(cx);
2468 size_t len = ida.length();
2469 for (size_t i = 0; i < len; i++) {
2470 jsid id = ida[i];
2471 PropDesc *desc = descs.append();
2472 if (!desc || !JS_GetPropertyById(cx, props, id, Jsvalify(&vp[1])) ||
2473 !desc->initialize(cx, id, vp[1])) {
2474 return JS_FALSE;
2478 bool dummy;
2479 for (size_t i = 0; i < len; i++) {
2480 if (!DefineProperty(cx, obj, descs[i], true, &dummy))
2481 return JS_FALSE;
2485 /* 5. Return obj. */
2486 return JS_TRUE;
2489 static JSBool
2490 obj_getOwnPropertyNames(JSContext *cx, uintN argc, Value *vp)
2492 JSObject *obj;
2493 if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.getOwnPropertyNames", &obj))
2494 return false;
2496 AutoIdVector keys(cx);
2497 if (!GetPropertyNames(cx, obj, JSITER_OWNONLY | JSITER_HIDDEN, &keys))
2498 return false;
2500 AutoValueVector vals(cx);
2501 if (!vals.resize(keys.length()))
2502 return false;
2504 for (size_t i = 0, len = keys.length(); i < len; i++) {
2505 jsid id = keys[i];
2506 if (JSID_IS_INT(id)) {
2507 JSString *str = js_ValueToString(cx, Int32Value(JSID_TO_INT(id)));
2508 if (!str)
2509 return false;
2510 vals[i].setString(str);
2511 } else if (JSID_IS_ATOM(id)) {
2512 vals[i].setString(JSID_TO_STRING(id));
2513 } else {
2514 vals[i].setObject(*JSID_TO_OBJECT(id));
2518 JSObject *aobj = js_NewArrayObject(cx, vals.length(), vals.begin());
2519 if (!aobj)
2520 return false;
2522 vp->setObject(*aobj);
2523 return true;
2526 static JSBool
2527 obj_isExtensible(JSContext *cx, uintN argc, Value *vp)
2529 JSObject *obj;
2530 if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.isExtensible", &obj))
2531 return false;
2533 vp->setBoolean(obj->isExtensible());
2534 return true;
2537 static JSBool
2538 obj_preventExtensions(JSContext *cx, uintN argc, Value *vp)
2540 JSObject *obj;
2541 if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.preventExtensions", &obj))
2542 return false;
2544 vp->setObject(*obj);
2545 if (!obj->isExtensible())
2546 return true;
2548 AutoIdVector props(cx);
2549 return obj->preventExtensions(cx, &props);
2552 bool
2553 JSObject::sealOrFreeze(JSContext *cx, ImmutabilityType it)
2555 assertSameCompartment(cx, this);
2556 JS_ASSERT(it == SEAL || it == FREEZE);
2558 AutoIdVector props(cx);
2559 if (isExtensible()) {
2560 if (!preventExtensions(cx, &props))
2561 return false;
2562 } else {
2563 if (!GetPropertyNames(cx, this, JSITER_HIDDEN | JSITER_OWNONLY, &props))
2564 return false;
2567 /* preventExtensions must slowify dense arrays, so we can assign to holes without checks. */
2568 JS_ASSERT(!isDenseArray());
2570 for (size_t i = 0, len = props.length(); i < len; i++) {
2571 jsid id = props[i];
2573 uintN attrs;
2574 if (!getAttributes(cx, id, &attrs))
2575 return false;
2577 /* Make all attributes permanent; if freezing, make data attributes read-only. */
2578 uintN new_attrs;
2579 if (it == FREEZE && !(attrs & (JSPROP_GETTER | JSPROP_SETTER)))
2580 new_attrs = JSPROP_PERMANENT | JSPROP_READONLY;
2581 else
2582 new_attrs = JSPROP_PERMANENT;
2584 /* If we already have the attributes we need, skip the setAttributes call. */
2585 if ((attrs | new_attrs) == attrs)
2586 continue;
2588 attrs |= new_attrs;
2589 if (!setAttributes(cx, id, &attrs))
2590 return false;
2593 return true;
2596 static JSBool
2597 obj_freeze(JSContext *cx, uintN argc, Value *vp)
2599 JSObject *obj;
2600 if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.freeze", &obj))
2601 return false;
2603 vp->setObject(*obj);
2605 return obj->freeze(cx);
2608 static JSBool
2609 obj_isFrozen(JSContext *cx, uintN argc, Value *vp)
2611 JSObject *obj;
2612 if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.preventExtensions", &obj))
2613 return false;
2615 vp->setBoolean(false);
2617 if (obj->isExtensible())
2618 return true; /* The JavaScript value returned is false. */
2620 AutoIdVector props(cx);
2621 if (!GetPropertyNames(cx, obj, JSITER_HIDDEN | JSITER_OWNONLY, &props))
2622 return false;
2624 for (size_t i = 0, len = props.length(); i < len; i++) {
2625 jsid id = props[i];
2627 uintN attrs = 0;
2628 if (!obj->getAttributes(cx, id, &attrs))
2629 return false;
2631 /* The property must be non-configurable and either read-only or an accessor. */
2632 if (!(attrs & JSPROP_PERMANENT) ||
2633 !(attrs & (JSPROP_READONLY | JSPROP_GETTER | JSPROP_SETTER)))
2634 return true; /* The JavaScript value returned is false. */
2637 /* It really was sealed, so return true. */
2638 vp->setBoolean(true);
2639 return true;
2642 static JSBool
2643 obj_seal(JSContext *cx, uintN argc, Value *vp)
2645 JSObject *obj;
2646 if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.seal", &obj))
2647 return false;
2649 vp->setObject(*obj);
2651 return obj->seal(cx);
2654 static JSBool
2655 obj_isSealed(JSContext *cx, uintN argc, Value *vp)
2657 JSObject *obj;
2658 if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.isSealed", &obj))
2659 return false;
2661 /* Assume not sealed until proven otherwise. */
2662 vp->setBoolean(false);
2664 if (obj->isExtensible())
2665 return true; /* The JavaScript value returned is false. */
2667 AutoIdVector props(cx);
2668 if (!GetPropertyNames(cx, obj, JSITER_HIDDEN | JSITER_OWNONLY, &props))
2669 return false;
2671 for (size_t i = 0, len = props.length(); i < len; i++) {
2672 jsid id = props[i];
2674 uintN attrs;
2675 if (!obj->getAttributes(cx, id, &attrs))
2676 return false;
2678 if (!(attrs & JSPROP_PERMANENT))
2679 return true; /* The JavaScript value returned is false. */
2682 /* It really was sealed, so return true. */
2683 vp->setBoolean(true);
2684 return true;
2687 #if JS_HAS_OBJ_WATCHPOINT
2688 const char js_watch_str[] = "watch";
2689 const char js_unwatch_str[] = "unwatch";
2690 #endif
2691 const char js_hasOwnProperty_str[] = "hasOwnProperty";
2692 const char js_isPrototypeOf_str[] = "isPrototypeOf";
2693 const char js_propertyIsEnumerable_str[] = "propertyIsEnumerable";
2695 static JSFunctionSpec object_methods[] = {
2696 #if JS_HAS_TOSOURCE
2697 JS_FN(js_toSource_str, obj_toSource, 0,0),
2698 #endif
2699 JS_FN(js_toString_str, obj_toString, 0,JSFUN_PRIMITIVE_THIS),
2700 JS_FN(js_toLocaleString_str, obj_toLocaleString, 0,0),
2701 JS_FN(js_valueOf_str, obj_valueOf, 0,0),
2702 #if JS_HAS_OBJ_WATCHPOINT
2703 JS_FN(js_watch_str, obj_watch, 2,0),
2704 JS_FN(js_unwatch_str, obj_unwatch, 1,0),
2705 #endif
2706 JS_FN(js_hasOwnProperty_str, obj_hasOwnProperty, 1,0),
2707 JS_FN(js_isPrototypeOf_str, obj_isPrototypeOf, 1,0),
2708 JS_FN(js_propertyIsEnumerable_str, obj_propertyIsEnumerable, 1,0),
2709 #if OLD_GETTER_SETTER_METHODS
2710 JS_FN(js_defineGetter_str, js_obj_defineGetter, 2,0),
2711 JS_FN(js_defineSetter_str, js_obj_defineSetter, 2,0),
2712 JS_FN(js_lookupGetter_str, obj_lookupGetter, 1,0),
2713 JS_FN(js_lookupSetter_str, obj_lookupSetter, 1,0),
2714 #endif
2715 JS_FS_END
2718 static JSFunctionSpec object_static_methods[] = {
2719 JS_FN("getPrototypeOf", obj_getPrototypeOf, 1,0),
2720 JS_FN("getOwnPropertyDescriptor", obj_getOwnPropertyDescriptor,2,0),
2721 JS_FN("keys", obj_keys, 1,0),
2722 JS_FN("defineProperty", obj_defineProperty, 3,0),
2723 JS_FN("defineProperties", obj_defineProperties, 2,0),
2724 JS_FN("create", obj_create, 2,0),
2725 JS_FN("getOwnPropertyNames", obj_getOwnPropertyNames, 1,0),
2726 JS_FN("isExtensible", obj_isExtensible, 1,0),
2727 JS_FN("preventExtensions", obj_preventExtensions, 1,0),
2728 JS_FN("freeze", obj_freeze, 1,0),
2729 JS_FN("isFrozen", obj_isFrozen, 1,0),
2730 JS_FN("seal", obj_seal, 1,0),
2731 JS_FN("isSealed", obj_isSealed, 1,0),
2732 JS_FS_END
2735 JSBool
2736 js_Object(JSContext *cx, uintN argc, Value *vp)
2738 JSObject *obj;
2739 if (argc == 0) {
2740 /* Trigger logic below to construct a blank object. */
2741 obj = NULL;
2742 } else {
2743 /* If argv[0] is null or undefined, obj comes back null. */
2744 if (!js_ValueToObjectOrNull(cx, vp[2], &obj))
2745 return JS_FALSE;
2747 if (!obj) {
2748 /* Make an object whether this was called with 'new' or not. */
2749 JS_ASSERT(!argc || vp[2].isNull() || vp[2].isUndefined());
2750 gc::FinalizeKind kind = NewObjectGCKind(cx, &js_ObjectClass);
2751 obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind);
2752 if (!obj)
2753 return JS_FALSE;
2755 vp->setObject(*obj);
2756 return JS_TRUE;
2759 JSObject*
2760 js_CreateThis(JSContext *cx, JSObject *callee)
2762 Class *clasp = callee->getClass();
2764 Class *newclasp = &js_ObjectClass;
2765 if (clasp == &js_FunctionClass) {
2766 JSFunction *fun = callee->getFunctionPrivate();
2767 if (fun->isNative() && fun->u.n.clasp)
2768 newclasp = fun->u.n.clasp;
2771 Value protov;
2772 if (!callee->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), &protov))
2773 return NULL;
2775 JSObject *proto = protov.isObjectOrNull() ? protov.toObjectOrNull() : NULL;
2776 JSObject *parent = callee->getParent();
2777 gc::FinalizeKind kind = NewObjectGCKind(cx, newclasp);
2778 JSObject *obj = NewObject<WithProto::Class>(cx, newclasp, proto, parent, kind);
2779 if (obj)
2780 obj->syncSpecialEquality();
2781 return obj;
2784 JSObject *
2785 js_CreateThisForFunctionWithProto(JSContext *cx, JSObject *callee, JSObject *proto)
2787 gc::FinalizeKind kind = NewObjectGCKind(cx, &js_ObjectClass);
2788 return NewNonFunction<WithProto::Class>(cx, &js_ObjectClass, proto, callee->getParent(), kind);
2791 JSObject *
2792 js_CreateThisForFunction(JSContext *cx, JSObject *callee)
2794 Value protov;
2795 if (!callee->getProperty(cx,
2796 ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom),
2797 &protov)) {
2798 return NULL;
2800 JSObject *proto = protov.isObject() ? &protov.toObject() : NULL;
2801 return js_CreateThisForFunctionWithProto(cx, callee, proto);
2804 #ifdef JS_TRACER
2806 static JS_ALWAYS_INLINE JSObject*
2807 NewObjectWithClassProto(JSContext *cx, Class *clasp, JSObject *proto,
2808 /*gc::FinalizeKind*/ unsigned _kind)
2810 JS_ASSERT(clasp->isNative());
2811 gc::FinalizeKind kind = gc::FinalizeKind(_kind);
2813 JSObject* obj = js_NewGCObject(cx, kind);
2814 if (!obj)
2815 return NULL;
2817 if (!obj->initSharingEmptyShape(cx, clasp, proto, proto->getParent(), NULL, kind))
2818 return NULL;
2819 return obj;
2822 JSObject* FASTCALL
2823 js_Object_tn(JSContext* cx, JSObject* proto)
2825 JS_ASSERT(!(js_ObjectClass.flags & JSCLASS_HAS_PRIVATE));
2826 return NewObjectWithClassProto(cx, &js_ObjectClass, proto, FINALIZE_OBJECT8);
2829 JS_DEFINE_TRCINFO_1(js_Object,
2830 (2, (extern, CONSTRUCTOR_RETRY, js_Object_tn, CONTEXT, CALLEE_PROTOTYPE, 0,
2831 nanojit::ACCSET_STORE_ANY)))
2833 JSObject* FASTCALL
2834 js_InitializerObject(JSContext* cx, JSObject *proto, JSObject *baseobj)
2836 if (!baseobj) {
2837 gc::FinalizeKind kind = GuessObjectGCKind(0, false);
2838 return NewObjectWithClassProto(cx, &js_ObjectClass, proto, kind);
2841 return CopyInitializerObject(cx, baseobj);
2844 JS_DEFINE_CALLINFO_3(extern, OBJECT, js_InitializerObject, CONTEXT, OBJECT, OBJECT,
2845 0, nanojit::ACCSET_STORE_ANY)
2847 JSObject* FASTCALL
2848 js_String_tn(JSContext* cx, JSObject* proto, JSString* str)
2850 JS_ASSERT(JS_ON_TRACE(cx));
2851 JSObject *obj = NewObjectWithClassProto(cx, &js_StringClass, proto, FINALIZE_OBJECT2);
2852 if (!obj)
2853 return NULL;
2854 obj->setPrimitiveThis(StringValue(str));
2855 return obj;
2857 JS_DEFINE_CALLINFO_3(extern, OBJECT, js_String_tn, CONTEXT, CALLEE_PROTOTYPE, STRING, 0,
2858 nanojit::ACCSET_STORE_ANY)
2860 JSObject* FASTCALL
2861 js_CreateThisFromTrace(JSContext *cx, Class *clasp, JSObject *ctor)
2863 JS_ASSERT(JS_ON_TRACE(cx));
2864 JS_ASSERT(ctor->isFunction());
2866 if (!ctor->ensureClassReservedSlots(cx))
2867 return NULL;
2869 jsid classPrototypeId = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom);
2870 const Shape *shape = ctor->nativeLookup(classPrototypeId);
2871 Value pval = shape ? ctor->getSlot(shape->slot) : MagicValue(JS_GENERIC_MAGIC);
2873 JSObject *parent = ctor->getParent();
2874 JSObject *proto;
2875 if (pval.isObject()) {
2876 /* An object in ctor.prototype, let's use it as the new instance's proto. */
2877 proto = &pval.toObject();
2878 } else {
2879 /* A hole or a primitive: either way, we need to get Object.prototype. */
2880 if (!js_GetClassPrototype(cx, parent, JSProto_Object, &proto))
2881 return NULL;
2883 if (pval.isMagic(JS_GENERIC_MAGIC)) {
2885 * No ctor.prototype was set, so we inline-expand and optimize
2886 * fun_resolve's prototype creation code.
2888 proto = NewNativeClassInstance(cx, clasp, proto, parent);
2889 if (!proto)
2890 return NULL;
2891 if (!js_SetClassPrototype(cx, ctor, proto, JSPROP_ENUMERATE | JSPROP_PERMANENT))
2892 return NULL;
2893 } else {
2895 * A primitive value in .prototype means to use Object.prototype
2896 * for proto. See ES5 13.2.2 step 7.
2902 * FIXME: 561785 at least. Quasi-natives including XML objects prevent us
2903 * from easily or unconditionally calling NewNativeClassInstance here.
2905 gc::FinalizeKind kind = NewObjectGCKind(cx, clasp);
2906 return NewNonFunction<WithProto::Given>(cx, clasp, proto, parent, kind);
2909 JS_DEFINE_CALLINFO_3(extern, CONSTRUCTOR_RETRY, js_CreateThisFromTrace, CONTEXT, CLASS, OBJECT, 0,
2910 nanojit::ACCSET_STORE_ANY)
2912 #else /* !JS_TRACER */
2914 # define js_Object_trcinfo NULL
2916 #endif /* !JS_TRACER */
2919 * Given pc pointing after a property accessing bytecode, return true if the
2920 * access is "object-detecting" in the sense used by web scripts, e.g., when
2921 * checking whether document.all is defined.
2923 JS_REQUIRES_STACK JSBool
2924 Detecting(JSContext *cx, jsbytecode *pc)
2926 JSScript *script;
2927 jsbytecode *endpc;
2928 JSOp op;
2929 JSAtom *atom;
2931 script = cx->fp()->script();
2932 endpc = script->code + script->length;
2933 for (;; pc += js_CodeSpec[op].length) {
2934 JS_ASSERT_IF(!cx->fp()->hasImacropc(), script->code <= pc && pc < endpc);
2936 /* General case: a branch or equality op follows the access. */
2937 op = js_GetOpcode(cx, script, pc);
2938 if (js_CodeSpec[op].format & JOF_DETECTING)
2939 return JS_TRUE;
2941 switch (op) {
2942 case JSOP_NULL:
2944 * Special case #1: handle (document.all == null). Don't sweat
2945 * about JS1.2's revision of the equality operators here.
2947 if (++pc < endpc) {
2948 op = js_GetOpcode(cx, script, pc);
2949 return *pc == JSOP_EQ || *pc == JSOP_NE;
2951 return JS_FALSE;
2953 case JSOP_GETGNAME:
2954 case JSOP_NAME:
2956 * Special case #2: handle (document.all == undefined). Don't
2957 * worry about someone redefining undefined, which was added by
2958 * Edition 3, so is read/write for backward compatibility.
2960 GET_ATOM_FROM_BYTECODE(script, pc, 0, atom);
2961 if (atom == cx->runtime->atomState.typeAtoms[JSTYPE_VOID] &&
2962 (pc += js_CodeSpec[op].length) < endpc) {
2963 op = js_GetOpcode(cx, script, pc);
2964 return op == JSOP_EQ || op == JSOP_NE ||
2965 op == JSOP_STRICTEQ || op == JSOP_STRICTNE;
2967 return JS_FALSE;
2969 default:
2971 * At this point, anything but an extended atom index prefix means
2972 * we're not detecting.
2974 if (!(js_CodeSpec[op].format & JOF_INDEXBASE))
2975 return JS_FALSE;
2976 break;
2982 * Infer lookup flags from the currently executing bytecode. This does
2983 * not attempt to infer JSRESOLVE_WITH, because the current bytecode
2984 * does not indicate whether we are in a with statement. Return defaultFlags
2985 * if a currently executing bytecode cannot be determined.
2987 uintN
2988 js_InferFlags(JSContext *cx, uintN defaultFlags)
2990 #ifdef JS_TRACER
2991 if (JS_ON_TRACE(cx))
2992 return cx->bailExit->lookupFlags;
2993 #endif
2995 JS_ASSERT_NOT_ON_TRACE(cx);
2997 jsbytecode *pc;
2998 const JSCodeSpec *cs;
2999 uint32 format;
3000 uintN flags = 0;
3002 JSStackFrame *const fp = js_GetTopStackFrame(cx);
3003 if (!fp || !(pc = cx->regs->pc))
3004 return defaultFlags;
3005 cs = &js_CodeSpec[js_GetOpcode(cx, fp->script(), pc)];
3006 format = cs->format;
3007 if (JOF_MODE(format) != JOF_NAME)
3008 flags |= JSRESOLVE_QUALIFIED;
3009 if ((format & (JOF_SET | JOF_FOR)) || fp->isAssigning()) {
3010 flags |= JSRESOLVE_ASSIGNING;
3011 } else if (cs->length >= 0) {
3012 pc += cs->length;
3013 JSScript *script = cx->fp()->script();
3014 if (pc < script->code + script->length && Detecting(cx, pc))
3015 flags |= JSRESOLVE_DETECTING;
3017 if (format & JOF_DECLARING)
3018 flags |= JSRESOLVE_DECLARING;
3019 return flags;
3023 * ObjectOps and Class for with-statement stack objects.
3025 static JSBool
3026 with_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
3027 JSProperty **propp)
3029 /* Fixes bug 463997 */
3030 uintN flags = cx->resolveFlags;
3031 if (flags == JSRESOLVE_INFER)
3032 flags = js_InferFlags(cx, flags);
3033 flags |= JSRESOLVE_WITH;
3034 JSAutoResolveFlags rf(cx, flags);
3035 return obj->getProto()->lookupProperty(cx, id, objp, propp);
3038 static JSBool
3039 with_GetProperty(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp)
3041 return obj->getProto()->getProperty(cx, id, vp);
3044 static JSBool
3045 with_SetProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict)
3047 return obj->getProto()->setProperty(cx, id, vp, strict);
3050 static JSBool
3051 with_GetAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp)
3053 return obj->getProto()->getAttributes(cx, id, attrsp);
3056 static JSBool
3057 with_SetAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp)
3059 return obj->getProto()->setAttributes(cx, id, attrsp);
3062 static JSBool
3063 with_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool strict)
3065 return obj->getProto()->deleteProperty(cx, id, rval, strict);
3068 static JSBool
3069 with_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
3070 Value *statep, jsid *idp)
3072 return obj->getProto()->enumerate(cx, enum_op, statep, idp);
3075 static JSType
3076 with_TypeOf(JSContext *cx, JSObject *obj)
3078 return JSTYPE_OBJECT;
3081 static JSObject *
3082 with_ThisObject(JSContext *cx, JSObject *obj)
3084 return obj->getWithThis();
3087 Class js_WithClass = {
3088 "With",
3089 JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS,
3090 PropertyStub, /* addProperty */
3091 PropertyStub, /* delProperty */
3092 PropertyStub, /* getProperty */
3093 PropertyStub, /* setProperty */
3094 EnumerateStub,
3095 ResolveStub,
3096 ConvertStub,
3097 NULL, /* finalize */
3098 NULL, /* reserved */
3099 NULL, /* checkAccess */
3100 NULL, /* call */
3101 NULL, /* construct */
3102 NULL, /* xdrObject */
3103 NULL, /* hasInstance */
3104 NULL, /* mark */
3105 JS_NULL_CLASS_EXT,
3107 with_LookupProperty,
3108 NULL, /* defineProperty */
3109 with_GetProperty,
3110 with_SetProperty,
3111 with_GetAttributes,
3112 with_SetAttributes,
3113 with_DeleteProperty,
3114 with_Enumerate,
3115 with_TypeOf,
3116 NULL, /* trace */
3117 NULL, /* fix */
3118 with_ThisObject,
3119 NULL, /* clear */
3123 JS_REQUIRES_STACK JSObject *
3124 js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth)
3126 JSObject *obj;
3128 obj = js_NewGCObject(cx, FINALIZE_OBJECT2);
3129 if (!obj)
3130 return NULL;
3132 JSStackFrame *priv = js_FloatingFrameIfGenerator(cx, cx->fp());
3134 obj->init(cx, &js_WithClass, proto, parent, priv, false);
3135 obj->setMap(cx->runtime->emptyWithShape);
3136 OBJ_SET_BLOCK_DEPTH(cx, obj, depth);
3138 AutoObjectRooter tvr(cx, obj);
3139 JSObject *thisp = proto->thisObject(cx);
3140 if (!thisp)
3141 return NULL;
3143 assertSameCompartment(cx, obj, thisp);
3145 obj->setWithThis(thisp);
3146 return obj;
3149 JSObject *
3150 js_NewBlockObject(JSContext *cx)
3153 * Null obj's proto slot so that Object.prototype.* does not pollute block
3154 * scopes and to give the block object its own scope.
3156 JSObject *blockObj = js_NewGCObject(cx, FINALIZE_OBJECT2);
3157 if (!blockObj)
3158 return NULL;
3160 blockObj->init(cx, &js_BlockClass, NULL, NULL, NULL, false);
3161 blockObj->setMap(cx->runtime->emptyBlockShape);
3162 return blockObj;
3165 JSObject *
3166 js_CloneBlockObject(JSContext *cx, JSObject *proto, JSStackFrame *fp)
3168 JS_ASSERT(proto->isStaticBlock());
3170 size_t count = OBJ_BLOCK_COUNT(cx, proto);
3171 gc::FinalizeKind kind = gc::GetGCObjectKind(count + 1);
3173 JSObject *clone = js_NewGCObject(cx, kind);
3174 if (!clone)
3175 return NULL;
3177 JSStackFrame *priv = js_FloatingFrameIfGenerator(cx, fp);
3179 /* The caller sets parent on its own. */
3180 clone->init(cx, &js_BlockClass, proto, NULL, priv, false);
3182 clone->setMap(proto->map);
3183 if (!clone->ensureInstanceReservedSlots(cx, count + 1))
3184 return NULL;
3186 clone->setSlot(JSSLOT_BLOCK_DEPTH, proto->getSlot(JSSLOT_BLOCK_DEPTH));
3188 JS_ASSERT(clone->isClonedBlock());
3189 return clone;
3192 JS_REQUIRES_STACK JSBool
3193 js_PutBlockObject(JSContext *cx, JSBool normalUnwind)
3195 JSStackFrame *const fp = cx->fp();
3196 JSObject *obj = &fp->scopeChain();
3197 JS_ASSERT(obj->isClonedBlock());
3198 JS_ASSERT(obj->getPrivate() == js_FloatingFrameIfGenerator(cx, cx->fp()));
3200 /* Block objects should have all reserved slots allocated early. */
3201 uintN count = OBJ_BLOCK_COUNT(cx, obj);
3202 JS_ASSERT(obj->numSlots() >= JSSLOT_BLOCK_DEPTH + 1 + count);
3204 /* The block and its locals must be on the current stack for GC safety. */
3205 uintN depth = OBJ_BLOCK_DEPTH(cx, obj);
3206 JS_ASSERT(depth <= size_t(cx->regs->sp - fp->base()));
3207 JS_ASSERT(count <= size_t(cx->regs->sp - fp->base() - depth));
3209 /* See comments in CheckDestructuring from jsparse.cpp. */
3210 JS_ASSERT(count >= 1);
3212 if (normalUnwind) {
3213 uintN slot = JSSLOT_BLOCK_FIRST_FREE_SLOT;
3214 depth += fp->numFixed();
3215 memcpy(obj->getSlots() + slot, fp->slots() + depth, count * sizeof(Value));
3218 /* We must clear the private slot even with errors. */
3219 obj->setPrivate(NULL);
3220 fp->setScopeChainNoCallObj(*obj->getParent());
3221 return normalUnwind;
3224 static JSBool
3225 block_getProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
3228 * Block objects are never exposed to script, and the engine handles them
3229 * with care. So unlike other getters, this one can assert (rather than
3230 * check) certain invariants about obj.
3232 JS_ASSERT(obj->isClonedBlock());
3233 uintN index = (uintN) JSID_TO_INT(id);
3234 JS_ASSERT(index < OBJ_BLOCK_COUNT(cx, obj));
3236 JSStackFrame *fp = (JSStackFrame *) obj->getPrivate();
3237 if (fp) {
3238 fp = js_LiveFrameIfGenerator(fp);
3239 index += fp->numFixed() + OBJ_BLOCK_DEPTH(cx, obj);
3240 JS_ASSERT(index < fp->numSlots());
3241 *vp = fp->slots()[index];
3242 return true;
3245 /* Values are in slots immediately following the class-reserved ones. */
3246 JS_ASSERT(obj->getSlot(JSSLOT_FREE(&js_BlockClass) + index) == *vp);
3247 return true;
3250 static JSBool
3251 block_setProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
3253 JS_ASSERT(obj->isClonedBlock());
3254 uintN index = (uintN) JSID_TO_INT(id);
3255 JS_ASSERT(index < OBJ_BLOCK_COUNT(cx, obj));
3257 JSStackFrame *fp = (JSStackFrame *) obj->getPrivate();
3258 if (fp) {
3259 fp = js_LiveFrameIfGenerator(fp);
3260 index += fp->numFixed() + OBJ_BLOCK_DEPTH(cx, obj);
3261 JS_ASSERT(index < fp->numSlots());
3262 fp->slots()[index] = *vp;
3263 return true;
3267 * The value in *vp will be written back to the slot in obj that was
3268 * allocated when this let binding was defined.
3270 return true;
3273 const Shape *
3274 JSObject::defineBlockVariable(JSContext *cx, jsid id, intN index)
3276 JS_ASSERT(isStaticBlock());
3278 /* Use JSPROP_ENUMERATE to aid the disassembler. */
3279 uint32 slot = JSSLOT_FREE(&js_BlockClass) + index;
3280 const Shape *shape = addProperty(cx, id,
3281 block_getProperty, block_setProperty,
3282 slot, JSPROP_ENUMERATE | JSPROP_PERMANENT,
3283 Shape::HAS_SHORTID, index);
3284 if (!shape)
3285 return NULL;
3286 if (slot >= numSlots() && !growSlots(cx, slot + 1))
3287 return NULL;
3288 return shape;
3291 static size_t
3292 GetObjectSize(JSObject *obj)
3294 return (obj->isFunction() && !obj->getPrivate())
3295 ? sizeof(JSFunction)
3296 : sizeof(JSObject) + sizeof(js::Value) * obj->numFixedSlots();
3300 * Use this method with extreme caution. It trades the guts of two objects and updates
3301 * scope ownership. This operation is not thread-safe, just as fast array to slow array
3302 * transitions are inherently not thread-safe. Don't perform a swap operation on objects
3303 * shared across threads or, or bad things will happen. You have been warned.
3305 bool
3306 JSObject::swap(JSContext *cx, JSObject *other)
3308 size_t size = GetObjectSize(this);
3310 if (size != GetObjectSize(other)) {
3312 * Objects with different numbers of fixed slots can be swapped only if they
3313 * are both shapeless non-natives, to preserve the invariant that objects with the
3314 * same shape have the same number of fixed slots. Use a dynamic array for both.
3316 JS_ASSERT(!isNative());
3317 JS_ASSERT(!other->isNative());
3318 size = sizeof(JSObject);
3319 if (!hasSlotsArray()) {
3320 if (!allocSlots(cx, numSlots()))
3321 return false;
3323 if (!other->hasSlotsArray()) {
3324 if (!other->allocSlots(cx, other->numSlots()))
3325 return false;
3329 bool thisInline = !hasSlotsArray();
3330 bool otherInline = !other->hasSlotsArray();
3332 JS_STATIC_ASSERT(FINALIZE_OBJECT_LAST == FINALIZE_OBJECT16);
3334 char tmp[tl::Max<sizeof(JSFunction), sizeof(JSObject_Slots16)>::result];
3335 JS_ASSERT(size <= sizeof(tmp));
3337 /* Trade the guts of the objects. */
3338 memcpy(tmp, this, size);
3339 memcpy(this, other, size);
3340 memcpy(other, tmp, size);
3342 /* Fixup pointers for inline slots on the objects. */
3343 if (thisInline)
3344 other->slots = other->fixedSlots();
3345 if (otherInline)
3346 this->slots = this->fixedSlots();
3348 return true;
3351 #if JS_HAS_XDR
3353 #define NO_PARENT_INDEX ((uint32)-1)
3355 uint32
3356 FindObjectIndex(JSObjectArray *array, JSObject *obj)
3358 size_t i;
3360 if (array) {
3361 i = array->length;
3362 do {
3364 if (array->vector[--i] == obj)
3365 return i;
3366 } while (i != 0);
3369 return NO_PARENT_INDEX;
3372 JSBool
3373 js_XDRBlockObject(JSXDRState *xdr, JSObject **objp)
3375 JSContext *cx;
3376 uint32 parentId;
3377 JSObject *obj, *parent;
3378 uintN depth, count;
3379 uint32 depthAndCount;
3380 const Shape *shape;
3382 cx = xdr->cx;
3383 #ifdef __GNUC__
3384 obj = NULL; /* quell GCC overwarning */
3385 #endif
3387 if (xdr->mode == JSXDR_ENCODE) {
3388 obj = *objp;
3389 parent = obj->getParent();
3390 parentId = (xdr->script->objectsOffset == 0)
3391 ? NO_PARENT_INDEX
3392 : FindObjectIndex(xdr->script->objects(), parent);
3393 depth = (uint16)OBJ_BLOCK_DEPTH(cx, obj);
3394 count = (uint16)OBJ_BLOCK_COUNT(cx, obj);
3395 depthAndCount = (uint32)(depth << 16) | count;
3397 #ifdef __GNUC__ /* suppress bogus gcc warnings */
3398 else count = 0;
3399 #endif
3401 /* First, XDR the parent atomid. */
3402 if (!JS_XDRUint32(xdr, &parentId))
3403 return JS_FALSE;
3405 if (xdr->mode == JSXDR_DECODE) {
3406 obj = js_NewBlockObject(cx);
3407 if (!obj)
3408 return JS_FALSE;
3409 *objp = obj;
3412 * If there's a parent id, then get the parent out of our script's
3413 * object array. We know that we XDR block object in outer-to-inner
3414 * order, which means that getting the parent now will work.
3416 if (parentId == NO_PARENT_INDEX)
3417 parent = NULL;
3418 else
3419 parent = xdr->script->getObject(parentId);
3420 obj->setParent(parent);
3423 AutoObjectRooter tvr(cx, obj);
3425 if (!JS_XDRUint32(xdr, &depthAndCount))
3426 return false;
3428 if (xdr->mode == JSXDR_DECODE) {
3429 depth = (uint16)(depthAndCount >> 16);
3430 count = (uint16)depthAndCount;
3431 obj->setSlot(JSSLOT_BLOCK_DEPTH, Value(Int32Value(depth)));
3434 * XDR the block object's properties. We know that there are 'count'
3435 * properties to XDR, stored as id/shortid pairs.
3437 for (uintN i = 0; i < count; i++) {
3438 JSAtom *atom;
3439 uint16 shortid;
3441 /* XDR the real id, then the shortid. */
3442 if (!js_XDRAtom(xdr, &atom) || !JS_XDRUint16(xdr, &shortid))
3443 return false;
3445 if (!obj->defineBlockVariable(cx, ATOM_TO_JSID(atom), shortid))
3446 return false;
3448 } else {
3449 Vector<const Shape *, 8> shapes(cx);
3450 shapes.growByUninitialized(count);
3452 for (Shape::Range r(obj->lastProperty()); !r.empty(); r.popFront()) {
3453 shape = &r.front();
3454 shapes[shape->shortid] = shape;
3458 * XDR the block object's properties. We know that there are 'count'
3459 * properties to XDR, stored as id/shortid pairs.
3461 for (uintN i = 0; i < count; i++) {
3462 shape = shapes[i];
3463 JS_ASSERT(shape->getter() == block_getProperty);
3465 jsid propid = shape->id;
3466 JS_ASSERT(JSID_IS_ATOM(propid));
3467 JSAtom *atom = JSID_TO_ATOM(propid);
3469 uint16 shortid = uint16(shape->shortid);
3470 JS_ASSERT(shortid == i);
3472 /* XDR the real id, then the shortid. */
3473 if (!js_XDRAtom(xdr, &atom) || !JS_XDRUint16(xdr, &shortid))
3474 return false;
3477 return true;
3480 #endif
3482 Class js_BlockClass = {
3483 "Block",
3484 JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_IS_ANONYMOUS,
3485 PropertyStub, /* addProperty */
3486 PropertyStub, /* delProperty */
3487 PropertyStub, /* getProperty */
3488 PropertyStub, /* setProperty */
3489 EnumerateStub,
3490 ResolveStub,
3491 ConvertStub
3494 JSObject *
3495 js_InitObjectClass(JSContext *cx, JSObject *obj)
3497 JSObject *proto = js_InitClass(cx, obj, NULL, &js_ObjectClass, js_Object, 1,
3498 object_props, object_methods, NULL, object_static_methods);
3499 if (!proto)
3500 return NULL;
3502 /* ECMA (15.1.2.1) says 'eval' is a property of the global object. */
3503 jsid id = ATOM_TO_JSID(cx->runtime->atomState.evalAtom);
3504 if (!js_DefineFunction(cx, obj, id, eval, 1, JSFUN_STUB_GSOPS))
3505 return NULL;
3507 return proto;
3510 static bool
3511 DefineStandardSlot(JSContext *cx, JSObject *obj, JSProtoKey key, JSAtom *atom,
3512 const Value &v, uint32 attrs, bool &named)
3514 jsid id = ATOM_TO_JSID(atom);
3516 if (key != JSProto_Null) {
3518 * Initializing an actual standard class on a global object. If the
3519 * property is not yet present, force it into a new one bound to a
3520 * reserved slot. Otherwise, go through the normal property path.
3522 JS_ASSERT(obj->getClass()->flags & JSCLASS_IS_GLOBAL);
3523 JS_ASSERT(obj->isNative());
3525 if (!obj->ensureClassReservedSlots(cx))
3526 return false;
3528 const Shape *shape = obj->nativeLookup(id);
3529 if (!shape) {
3530 uint32 slot = 2 * JSProto_LIMIT + key;
3531 if (!js_SetReservedSlot(cx, obj, slot, v))
3532 return false;
3533 if (!obj->addProperty(cx, id, PropertyStub, PropertyStub, slot, attrs, 0, 0))
3534 return false;
3536 named = true;
3537 return true;
3541 named = obj->defineProperty(cx, id, v, PropertyStub, PropertyStub, attrs);
3542 return named;
3545 JSObject *
3546 js_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto,
3547 Class *clasp, Native constructor, uintN nargs,
3548 JSPropertySpec *ps, JSFunctionSpec *fs,
3549 JSPropertySpec *static_ps, JSFunctionSpec *static_fs)
3551 JSAtom *atom;
3552 JSProtoKey key;
3553 JSFunction *fun;
3554 bool named = false;
3556 atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0);
3557 if (!atom)
3558 return NULL;
3561 * When initializing a standard class, if no parent_proto (grand-proto of
3562 * instances of the class, parent-proto of the class's prototype object)
3563 * is given, we must use Object.prototype if it is available. Otherwise,
3564 * we could look up the wrong binding for a class name in obj. Example:
3566 * String = Array;
3567 * print("hi there".join);
3569 * should print undefined, not Array.prototype.join. This is required by
3570 * ECMA-262, alas. It might have been better to make String readonly and
3571 * permanent in the global object, instead -- but that's too big a change
3572 * to swallow at this point.
3574 key = JSCLASS_CACHED_PROTO_KEY(clasp);
3575 if (key != JSProto_Null &&
3576 !parent_proto &&
3577 !js_GetClassPrototype(cx, obj, JSProto_Object, &parent_proto)) {
3578 return NULL;
3582 * Create a prototype object for this class.
3584 * FIXME: lazy standard (built-in) class initialization and even older
3585 * eager boostrapping code rely on all of these properties:
3587 * 1. NewObject attempting to compute a default prototype object when
3588 * passed null for proto; and
3590 * 2. NewObject tolerating no default prototype (null proto slot value)
3591 * due to this js_InitClass call coming from js_InitFunctionClass on an
3592 * otherwise-uninitialized global.
3594 * 3. NewObject allocating a JSFunction-sized GC-thing when clasp is
3595 * &js_FunctionClass, not a JSObject-sized (smaller) GC-thing.
3597 * The JS_NewObjectForGivenProto and JS_NewObject APIs also allow clasp to
3598 * be &js_FunctionClass (we could break compatibility easily). But fixing
3599 * (3) is not enough without addressing the bootstrapping dependency on (1)
3600 * and (2).
3602 JSObject *proto = NewObject<WithProto::Class>(cx, clasp, parent_proto, obj);
3603 if (!proto)
3604 return NULL;
3606 proto->syncSpecialEquality();
3608 /* After this point, control must exit via label bad or out. */
3609 AutoObjectRooter tvr(cx, proto);
3611 JSObject *ctor;
3612 if (!constructor) {
3614 * Lacking a constructor, name the prototype (e.g., Math) unless this
3615 * class (a) is anonymous, i.e. for internal use only; (b) the class
3616 * of obj (the global object) is has a reserved slot indexed by key;
3617 * and (c) key is not the null key.
3619 if (!(clasp->flags & JSCLASS_IS_ANONYMOUS) ||
3620 !(obj->getClass()->flags & JSCLASS_IS_GLOBAL) ||
3621 key == JSProto_Null)
3623 uint32 attrs = (clasp->flags & JSCLASS_IS_ANONYMOUS)
3624 ? JSPROP_READONLY | JSPROP_PERMANENT
3625 : 0;
3626 if (!DefineStandardSlot(cx, obj, key, atom, ObjectValue(*proto), attrs, named))
3627 goto bad;
3630 ctor = proto;
3631 } else {
3632 fun = js_NewFunction(cx, NULL, constructor, nargs, JSFUN_CONSTRUCTOR, obj, atom);
3633 if (!fun)
3634 goto bad;
3636 AutoValueRooter tvr2(cx, ObjectValue(*fun));
3637 if (!DefineStandardSlot(cx, obj, key, atom, tvr2.value(), 0, named))
3638 goto bad;
3641 * Remember the class this function is a constructor for so that
3642 * we know to create an object of this class when we call the
3643 * constructor.
3645 FUN_CLASP(fun) = clasp;
3648 * Optionally construct the prototype object, before the class has
3649 * been fully initialized. Allow the ctor to replace proto with a
3650 * different object, as is done for operator new -- and as at least
3651 * XML support requires.
3653 ctor = FUN_OBJECT(fun);
3654 if (clasp->flags & JSCLASS_CONSTRUCT_PROTOTYPE) {
3655 Value rval;
3656 if (!InvokeConstructorWithGivenThis(cx, proto, ObjectOrNullValue(ctor),
3657 0, NULL, &rval)) {
3658 goto bad;
3660 if (rval.isObject() && &rval.toObject() != proto)
3661 proto = &rval.toObject();
3664 /* Connect constructor and prototype by named properties. */
3665 if (!js_SetClassPrototype(cx, ctor, proto,
3666 JSPROP_READONLY | JSPROP_PERMANENT)) {
3667 goto bad;
3670 /* Bootstrap Function.prototype (see also JS_InitStandardClasses). */
3671 if (ctor->getClass() == clasp)
3672 ctor->setProto(proto);
3675 /* Add properties and methods to the prototype and the constructor. */
3676 if ((ps && !JS_DefineProperties(cx, proto, ps)) ||
3677 (fs && !JS_DefineFunctions(cx, proto, fs)) ||
3678 (static_ps && !JS_DefineProperties(cx, ctor, static_ps)) ||
3679 (static_fs && !JS_DefineFunctions(cx, ctor, static_fs))) {
3680 goto bad;
3684 * Pre-brand the prototype and constructor if they have built-in methods.
3685 * This avoids extra shape guard branch exits in the tracejitted code.
3687 if (fs && !proto->brand(cx))
3688 goto bad;
3689 if (ctor != proto && static_fs && !ctor->brand(cx))
3690 goto bad;
3693 * Make sure proto's emptyShape is available to be shared by objects of
3694 * this class. JSObject::emptyShape is a one-slot cache. If we omit this,
3695 * some other class could snap it up. (The risk is particularly great for
3696 * Object.prototype.)
3698 * All callers of JSObject::initSharingEmptyShape depend on this.
3700 * FIXME: bug 592296 -- js_InitArrayClass should pass &js_SlowArrayClass
3701 * and make the Array.prototype slow from the start.
3703 JS_ASSERT_IF(proto->clasp != clasp,
3704 clasp == &js_ArrayClass && proto->clasp == &js_SlowArrayClass);
3705 if (!proto->getEmptyShape(cx, proto->clasp, FINALIZE_OBJECT0))
3706 goto bad;
3708 if (clasp->flags & (JSCLASS_FREEZE_PROTO|JSCLASS_FREEZE_CTOR)) {
3709 JS_ASSERT_IF(ctor == proto, !(clasp->flags & JSCLASS_FREEZE_CTOR));
3710 if (proto && (clasp->flags & JSCLASS_FREEZE_PROTO) && !proto->freeze(cx))
3711 goto bad;
3712 if (ctor && (clasp->flags & JSCLASS_FREEZE_CTOR) && !ctor->freeze(cx))
3713 goto bad;
3716 /* If this is a standard class, cache its prototype. */
3717 if (key != JSProto_Null && !js_SetClassObject(cx, obj, key, ctor, proto))
3718 goto bad;
3720 return proto;
3722 bad:
3723 if (named) {
3724 Value rval;
3725 obj->deleteProperty(cx, ATOM_TO_JSID(atom), &rval, false);
3727 return NULL;
3730 bool
3731 JSObject::allocSlots(JSContext *cx, size_t newcap)
3733 uint32 oldcap = numSlots();
3735 JS_ASSERT(newcap >= oldcap && !hasSlotsArray());
3737 if (newcap > NSLOTS_LIMIT) {
3738 if (!JS_ON_TRACE(cx))
3739 js_ReportAllocationOverflow(cx);
3740 return false;
3743 Value *tmpslots = (Value*) cx->malloc(newcap * sizeof(Value));
3744 if (!tmpslots)
3745 return false; /* Leave slots at inline buffer. */
3746 slots = tmpslots;
3747 capacity = newcap;
3749 /* Copy over anything from the inline buffer. */
3750 memcpy(slots, fixedSlots(), oldcap * sizeof(Value));
3751 ClearValueRange(slots + oldcap, newcap - oldcap, isDenseArray());
3752 return true;
3755 bool
3756 JSObject::growSlots(JSContext *cx, size_t newcap)
3759 * When an object with CAPACITY_DOUBLING_MAX or fewer slots needs to
3760 * grow, double its capacity, to add N elements in amortized O(N) time.
3762 * Above this limit, grow by 12.5% each time. Speed is still amortized
3763 * O(N), with a higher constant factor, and we waste less space.
3765 static const size_t CAPACITY_DOUBLING_MAX = 1024 * 1024;
3766 static const size_t CAPACITY_CHUNK = CAPACITY_DOUBLING_MAX / sizeof(Value);
3768 uint32 oldcap = numSlots();
3769 JS_ASSERT(oldcap < newcap);
3771 uint32 nextsize = (oldcap <= CAPACITY_DOUBLING_MAX)
3772 ? oldcap * 2
3773 : oldcap + (oldcap >> 3);
3775 uint32 actualCapacity = JS_MAX(newcap, nextsize);
3776 if (actualCapacity >= CAPACITY_CHUNK)
3777 actualCapacity = JS_ROUNDUP(actualCapacity, CAPACITY_CHUNK);
3778 else if (actualCapacity < SLOT_CAPACITY_MIN)
3779 actualCapacity = SLOT_CAPACITY_MIN;
3781 /* Don't let nslots get close to wrapping around uint32. */
3782 if (actualCapacity >= NSLOTS_LIMIT) {
3783 JS_ReportOutOfMemory(cx);
3784 return false;
3787 /* If nothing was allocated yet, treat it as initial allocation. */
3788 if (!hasSlotsArray())
3789 return allocSlots(cx, actualCapacity);
3791 Value *tmpslots = (Value*) cx->realloc(slots, actualCapacity * sizeof(Value));
3792 if (!tmpslots)
3793 return false; /* Leave dslots as its old size. */
3794 slots = tmpslots;
3795 capacity = actualCapacity;
3797 /* Initialize the additional slots we added. */
3798 ClearValueRange(slots + oldcap, actualCapacity - oldcap, isDenseArray());
3799 return true;
3802 void
3803 JSObject::shrinkSlots(JSContext *cx, size_t newcap)
3805 uint32 oldcap = numSlots();
3806 JS_ASSERT(newcap <= oldcap);
3807 JS_ASSERT(newcap >= slotSpan());
3809 if (oldcap <= SLOT_CAPACITY_MIN || !hasSlotsArray()) {
3810 /* We won't shrink the slots any more. Clear excess holes. */
3811 ClearValueRange(slots + newcap, oldcap - newcap, isDenseArray());
3812 return;
3815 uint32 fill = newcap;
3816 if (newcap < SLOT_CAPACITY_MIN)
3817 newcap = SLOT_CAPACITY_MIN;
3818 if (newcap < numFixedSlots())
3819 newcap = numFixedSlots();
3821 Value *tmpslots = (Value*) cx->realloc(slots, newcap * sizeof(Value));
3822 if (!tmpslots)
3823 return; /* Leave slots at its old size. */
3824 slots = tmpslots;
3825 capacity = newcap;
3827 if (fill < newcap) {
3828 /* Clear any excess holes if we tried to shrink below SLOT_CAPACITY_MIN. */
3829 ClearValueRange(slots + fill, newcap - fill, isDenseArray());
3833 bool
3834 JSObject::ensureInstanceReservedSlots(JSContext *cx, size_t nreserved)
3836 JS_ASSERT_IF(isNative(),
3837 isBlock() || isCall() || (isFunction() && isBoundFunction()));
3839 uintN nslots = JSSLOT_FREE(clasp) + nreserved;
3840 return nslots <= numSlots() || allocSlots(cx, nslots);
3843 static JSObject *
3844 js_InitNullClass(JSContext *cx, JSObject *obj)
3846 JS_ASSERT(0);
3847 return NULL;
3850 #define JS_PROTO(name,code,init) extern JSObject *init(JSContext *, JSObject *);
3851 #include "jsproto.tbl"
3852 #undef JS_PROTO
3854 static JSObjectOp lazy_prototype_init[JSProto_LIMIT] = {
3855 #define JS_PROTO(name,code,init) init,
3856 #include "jsproto.tbl"
3857 #undef JS_PROTO
3860 namespace js {
3862 bool
3863 SetProto(JSContext *cx, JSObject *obj, JSObject *proto, bool checkForCycles)
3865 JS_ASSERT_IF(!checkForCycles, obj != proto);
3866 JS_ASSERT(obj->isExtensible());
3868 if (obj->isNative()) {
3869 if (!obj->ensureClassReservedSlots(cx))
3870 return false;
3874 * Regenerate property cache shape ids for all of the scopes along the
3875 * old prototype chain to invalidate their property cache entries, in
3876 * case any entries were filled by looking up through obj.
3878 JSObject *oldproto = obj;
3879 while (oldproto && oldproto->isNative()) {
3880 oldproto->protoShapeChange(cx);
3881 oldproto = oldproto->getProto();
3884 if (!proto || !checkForCycles) {
3885 obj->setProto(proto);
3886 } else if (!SetProtoCheckingForCycles(cx, obj, proto)) {
3887 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CYCLIC_VALUE, js_proto_str);
3888 return false;
3890 return true;
3895 JSBool
3896 js_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key,
3897 JSObject **objp)
3899 JSObject *tmp, *cobj;
3900 JSResolvingKey rkey;
3901 JSResolvingEntry *rentry;
3902 uint32 generation;
3903 JSObjectOp init;
3904 Value v;
3906 while ((tmp = obj->getParent()) != NULL)
3907 obj = tmp;
3908 if (!(obj->getClass()->flags & JSCLASS_IS_GLOBAL)) {
3909 *objp = NULL;
3910 return JS_TRUE;
3913 v = obj->getReservedSlot(key);
3914 if (v.isObject()) {
3915 *objp = &v.toObject();
3916 return JS_TRUE;
3919 rkey.obj = obj;
3920 rkey.id = ATOM_TO_JSID(cx->runtime->atomState.classAtoms[key]);
3921 if (!js_StartResolving(cx, &rkey, JSRESFLAG_LOOKUP, &rentry))
3922 return JS_FALSE;
3923 if (!rentry) {
3924 /* Already caching key in obj -- suppress recursion. */
3925 *objp = NULL;
3926 return JS_TRUE;
3928 generation = cx->resolvingTable->generation;
3930 JSBool ok = true;
3931 cobj = NULL;
3932 init = lazy_prototype_init[key];
3933 if (init) {
3934 if (!init(cx, obj)) {
3935 ok = JS_FALSE;
3936 } else {
3937 v = obj->getReservedSlot(key);
3938 if (v.isObject())
3939 cobj = &v.toObject();
3943 js_StopResolving(cx, &rkey, JSRESFLAG_LOOKUP, rentry, generation);
3944 *objp = cobj;
3945 return ok;
3948 JSBool
3949 js_SetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, JSObject *cobj, JSObject *proto)
3951 JS_ASSERT(!obj->getParent());
3952 if (!(obj->getClass()->flags & JSCLASS_IS_GLOBAL))
3953 return JS_TRUE;
3955 return js_SetReservedSlot(cx, obj, key, ObjectOrNullValue(cobj)) &&
3956 js_SetReservedSlot(cx, obj, JSProto_LIMIT + key, ObjectOrNullValue(proto));
3959 JSBool
3960 js_FindClassObject(JSContext *cx, JSObject *start, JSProtoKey protoKey,
3961 Value *vp, Class *clasp)
3963 JSStackFrame *fp;
3964 JSObject *obj, *cobj, *pobj;
3965 jsid id;
3966 JSProperty *prop;
3967 const Shape *shape;
3970 * Find the global object. Use cx->fp() directly to avoid falling off
3971 * trace; all JIT-elided stack frames have the same global object as
3972 * cx->fp().
3974 VOUCH_DOES_NOT_REQUIRE_STACK();
3975 if (!start && (fp = cx->maybefp()) != NULL)
3976 start = &fp->scopeChain();
3978 if (start) {
3979 /* Find the topmost object in the scope chain. */
3980 do {
3981 obj = start;
3982 start = obj->getParent();
3983 } while (start);
3984 } else {
3985 obj = cx->globalObject;
3986 if (!obj) {
3987 vp->setUndefined();
3988 return JS_TRUE;
3992 OBJ_TO_INNER_OBJECT(cx, obj);
3993 if (!obj)
3994 return JS_FALSE;
3996 if (protoKey != JSProto_Null) {
3997 JS_ASSERT(JSProto_Null < protoKey);
3998 JS_ASSERT(protoKey < JSProto_LIMIT);
3999 if (!js_GetClassObject(cx, obj, protoKey, &cobj))
4000 return JS_FALSE;
4001 if (cobj) {
4002 vp->setObject(*cobj);
4003 return JS_TRUE;
4005 id = ATOM_TO_JSID(cx->runtime->atomState.classAtoms[protoKey]);
4006 } else {
4007 JSAtom *atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0);
4008 if (!atom)
4009 return false;
4010 id = ATOM_TO_JSID(atom);
4013 JS_ASSERT(obj->isNative());
4014 if (js_LookupPropertyWithFlags(cx, obj, id, JSRESOLVE_CLASSNAME,
4015 &pobj, &prop) < 0) {
4016 return JS_FALSE;
4018 Value v = UndefinedValue();
4019 if (prop && pobj->isNative()) {
4020 shape = (Shape *) prop;
4021 if (pobj->containsSlot(shape->slot)) {
4022 v = pobj->nativeGetSlot(shape->slot);
4023 if (v.isPrimitive())
4024 v.setUndefined();
4027 *vp = v;
4028 return JS_TRUE;
4031 JSObject *
4032 js_ConstructObject(JSContext *cx, Class *clasp, JSObject *proto, JSObject *parent,
4033 uintN argc, Value *argv)
4035 AutoArrayRooter argtvr(cx, argc, argv);
4037 JSProtoKey protoKey = GetClassProtoKey(clasp);
4039 /* Protect constructor in case a crazy getter for .prototype uproots it. */
4040 AutoValueRooter tvr(cx);
4041 if (!js_FindClassObject(cx, parent, protoKey, tvr.addr(), clasp))
4042 return NULL;
4044 const Value &cval = tvr.value();
4045 if (tvr.value().isPrimitive()) {
4046 js_ReportIsNotFunction(cx, tvr.addr(), JSV2F_CONSTRUCT | JSV2F_SEARCH_STACK);
4047 return NULL;
4051 * If proto is NULL, set it to Constructor.prototype, just like JSOP_NEW
4052 * does, likewise for the new object's parent.
4054 JSObject *ctor = &cval.toObject();
4055 if (!parent)
4056 parent = ctor->getParent();
4057 if (!proto) {
4058 Value rval;
4059 if (!ctor->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom),
4060 &rval)) {
4061 return NULL;
4063 if (rval.isObjectOrNull())
4064 proto = rval.toObjectOrNull();
4067 JSObject *obj = NewObject<WithProto::Class>(cx, clasp, proto, parent);
4068 if (!obj)
4069 return NULL;
4071 obj->syncSpecialEquality();
4073 Value rval;
4074 if (!InvokeConstructorWithGivenThis(cx, obj, cval, argc, argv, &rval))
4075 return NULL;
4077 if (rval.isPrimitive())
4078 return obj;
4081 * If the instance's class differs from what was requested, throw a type
4082 * error. If the given class has both the JSCLASS_HAS_PRIVATE and the
4083 * JSCLASS_CONSTRUCT_PROTOTYPE flags, and the instance does not have its
4084 * private data set at this point, then the constructor was replaced and
4085 * we should throw a type error.
4087 obj = &rval.toObject();
4088 if (obj->getClass() != clasp ||
4089 (!(~clasp->flags & (JSCLASS_HAS_PRIVATE |
4090 JSCLASS_CONSTRUCT_PROTOTYPE)) &&
4091 !obj->getPrivate())) {
4092 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4093 JSMSG_WRONG_CONSTRUCTOR, clasp->name);
4094 return NULL;
4096 return obj;
4099 bool
4100 JSObject::allocSlot(JSContext *cx, uint32 *slotp)
4102 uint32 slot = slotSpan();
4103 JS_ASSERT(slot >= JSSLOT_FREE(clasp));
4106 * If this object is in dictionary mode and it has a property table, try to
4107 * pull a free slot from the property table's slot-number freelist.
4109 if (inDictionaryMode() && lastProp->table) {
4110 uint32 &last = lastProp->table->freelist;
4111 if (last != SHAPE_INVALID_SLOT) {
4112 #ifdef DEBUG
4113 JS_ASSERT(last < slot);
4114 uint32 next = getSlot(last).toPrivateUint32();
4115 JS_ASSERT_IF(next != SHAPE_INVALID_SLOT, next < slot);
4116 #endif
4118 *slotp = last;
4120 Value &vref = getSlotRef(last);
4121 last = vref.toPrivateUint32();
4122 vref.setUndefined();
4123 return true;
4127 if (slot >= numSlots() && !growSlots(cx, slot + 1))
4128 return false;
4130 /* JSObject::growSlots or JSObject::freeSlot should set the free slots to void. */
4131 JS_ASSERT(getSlot(slot).isUndefined());
4132 *slotp = slot;
4133 return true;
4136 void
4137 JSObject::freeSlot(JSContext *cx, uint32 slot)
4139 uint32 limit = slotSpan();
4140 JS_ASSERT(slot < limit);
4142 Value &vref = getSlotRef(slot);
4143 if (inDictionaryMode() && lastProp->table) {
4144 uint32 &last = lastProp->table->freelist;
4146 /* Can't afford to check the whole freelist, but let's check the head. */
4147 JS_ASSERT_IF(last != SHAPE_INVALID_SLOT, last < limit && last != slot);
4150 * Freeing a slot other than the last one mapped by this object's
4151 * shape (and not a reserved slot; see bug 595230): push the slot onto
4152 * the dictionary property table's freelist. We want to let the last
4153 * slot be freed by shrinking the dslots vector; see js_TraceObject.
4155 if (JSSLOT_FREE(clasp) <= slot && slot + 1 < limit) {
4156 JS_ASSERT_IF(last != SHAPE_INVALID_SLOT, last < slotSpan());
4157 vref.setPrivateUint32(last);
4158 last = slot;
4159 return;
4162 vref.setUndefined();
4165 /* JSBOXEDWORD_INT_MAX as a string */
4166 #define JSBOXEDWORD_INT_MAX_STRING "1073741823"
4169 * Convert string indexes that convert to int jsvals as ints to save memory.
4170 * Care must be taken to use this macro every time a property name is used, or
4171 * else double-sets, incorrect property cache misses, or other mistakes could
4172 * occur.
4174 jsid
4175 js_CheckForStringIndex(jsid id)
4177 if (!JSID_IS_ATOM(id))
4178 return id;
4180 JSAtom *atom = JSID_TO_ATOM(id);
4181 JSString *str = ATOM_TO_STRING(atom);
4182 const jschar *s = str->flatChars();
4183 jschar ch = *s;
4185 JSBool negative = (ch == '-');
4186 if (negative)
4187 ch = *++s;
4189 if (!JS7_ISDEC(ch))
4190 return id;
4192 size_t n = str->flatLength() - negative;
4193 if (n > sizeof(JSBOXEDWORD_INT_MAX_STRING) - 1)
4194 return id;
4196 const jschar *cp = s;
4197 const jschar *end = s + n;
4199 jsuint index = JS7_UNDEC(*cp++);
4200 jsuint oldIndex = 0;
4201 jsuint c = 0;
4203 if (index != 0) {
4204 while (JS7_ISDEC(*cp)) {
4205 oldIndex = index;
4206 c = JS7_UNDEC(*cp);
4207 index = 10 * index + c;
4208 cp++;
4213 * Non-integer indexes can't be represented as integers. Also, distinguish
4214 * index "-0" from "0", because JSBOXEDWORD_INT cannot.
4216 if (cp != end || (negative && index == 0))
4217 return id;
4219 if (negative) {
4220 if (oldIndex < -(JSID_INT_MIN / 10) ||
4221 (oldIndex == -(JSID_INT_MIN / 10) && c <= (-JSID_INT_MIN % 10)))
4223 id = INT_TO_JSID(-jsint(index));
4225 } else {
4226 if (oldIndex < JSID_INT_MAX / 10 ||
4227 (oldIndex == JSID_INT_MAX / 10 && c <= (JSID_INT_MAX % 10)))
4229 id = INT_TO_JSID(jsint(index));
4233 return id;
4236 static JSBool
4237 PurgeProtoChain(JSContext *cx, JSObject *obj, jsid id)
4239 const Shape *shape;
4241 while (obj) {
4242 if (!obj->isNative()) {
4243 obj = obj->getProto();
4244 continue;
4246 shape = obj->nativeLookup(id);
4247 if (shape) {
4248 PCMETER(JS_PROPERTY_CACHE(cx).pcpurges++);
4249 obj->shadowingShapeChange(cx, *shape);
4251 if (!obj->getParent()) {
4253 * All scope chains end in a global object, so this will change
4254 * the global shape. jstracer.cpp assumes that the global shape
4255 * never changes on trace, so we must deep-bail here.
4257 LeaveTrace(cx);
4259 return JS_TRUE;
4261 obj = obj->getProto();
4263 return JS_FALSE;
4266 void
4267 js_PurgeScopeChainHelper(JSContext *cx, JSObject *obj, jsid id)
4269 JS_ASSERT(obj->isDelegate());
4270 PurgeProtoChain(cx, obj->getProto(), id);
4273 * We must purge the scope chain only for Call objects as they are the only
4274 * kind of cacheable non-global object that can gain properties after outer
4275 * properties with the same names have been cached or traced. Call objects
4276 * may gain such properties via eval introducing new vars; see bug 490364.
4278 if (obj->isCall()) {
4279 while ((obj = obj->getParent()) != NULL) {
4280 if (PurgeProtoChain(cx, obj, id))
4281 break;
4286 const Shape *
4287 js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id,
4288 PropertyOp getter, PropertyOp setter, uint32 slot,
4289 uintN attrs, uintN flags, intN shortid)
4291 JS_ASSERT(!(flags & Shape::METHOD));
4294 * Purge the property cache of now-shadowed id in obj's scope chain. Do
4295 * this optimistically (assuming no failure below) before locking obj, so
4296 * we can lock the shadowed scope.
4298 js_PurgeScopeChain(cx, obj, id);
4300 if (!obj->ensureClassReservedSlots(cx))
4301 return NULL;
4303 /* Convert string indices to integers if appropriate. */
4304 id = js_CheckForStringIndex(id);
4305 return obj->putProperty(cx, id, getter, setter, slot, attrs, flags, shortid);
4308 const Shape *
4309 js_ChangeNativePropertyAttrs(JSContext *cx, JSObject *obj,
4310 const Shape *shape, uintN attrs, uintN mask,
4311 PropertyOp getter, PropertyOp setter)
4313 if (!obj->ensureClassReservedSlots(cx))
4314 return NULL;
4315 return obj->changeProperty(cx, shape, attrs, mask, getter, setter);
4318 JSBool
4319 js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, const Value *value,
4320 PropertyOp getter, PropertyOp setter, uintN attrs)
4322 return js_DefineNativeProperty(cx, obj, id, *value, getter, setter, attrs,
4323 0, 0, NULL);
4327 * Backward compatibility requires allowing addProperty hooks to mutate the
4328 * nominal initial value of a slotful property, while GC safety wants that
4329 * value to be stored before the call-out through the hook. Optimize to do
4330 * both while saving cycles for classes that stub their addProperty hook.
4332 static inline bool
4333 CallAddPropertyHook(JSContext *cx, Class *clasp, JSObject *obj, const Shape *shape, Value *vp)
4335 if (clasp->addProperty != PropertyStub) {
4336 Value nominal = *vp;
4338 if (!CallJSPropertyOp(cx, clasp->addProperty, obj, SHAPE_USERID(shape), vp))
4339 return false;
4340 if (*vp != nominal) {
4341 if (obj->containsSlot(shape->slot))
4342 obj->nativeSetSlot(shape->slot, *vp);
4345 return true;
4348 JSBool
4349 js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, const Value &value,
4350 PropertyOp getter, PropertyOp setter, uintN attrs,
4351 uintN flags, intN shortid, JSProperty **propp,
4352 uintN defineHow /* = 0 */)
4354 Class *clasp;
4355 const Shape *shape;
4356 JSBool added;
4357 Value valueCopy;
4359 JS_ASSERT((defineHow & ~(JSDNP_CACHE_RESULT | JSDNP_DONT_PURGE | JSDNP_SET_METHOD)) == 0);
4360 LeaveTraceIfGlobalObject(cx, obj);
4362 /* Convert string indices to integers if appropriate. */
4363 id = js_CheckForStringIndex(id);
4366 * If defining a getter or setter, we must check for its counterpart and
4367 * update the attributes and property ops. A getter or setter is really
4368 * only half of a property.
4370 shape = NULL;
4371 if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
4372 JSObject *pobj;
4373 JSProperty *prop;
4376 * If JS_THREADSAFE and id is found, js_LookupProperty returns with
4377 * shape non-null and pobj locked. If pobj == obj, the property is
4378 * already in obj and obj has its own (mutable) scope. So if we are
4379 * defining a getter whose setter was already defined, or vice versa,
4380 * finish the job via obj->changeProperty, and refresh the property
4381 * cache line for (obj, id) to map shape.
4383 if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
4384 return JS_FALSE;
4385 shape = (Shape *) prop;
4386 if (shape && pobj == obj && shape->isAccessorDescriptor()) {
4387 shape = obj->changeProperty(cx, shape, attrs,
4388 JSPROP_GETTER | JSPROP_SETTER,
4389 (attrs & JSPROP_GETTER)
4390 ? getter
4391 : shape->getter(),
4392 (attrs & JSPROP_SETTER)
4393 ? setter
4394 : shape->setter());
4396 if (!shape)
4397 return false;
4398 } else if (prop) {
4399 prop = NULL;
4400 shape = NULL;
4405 * Purge the property cache of any properties named by id that are about
4406 * to be shadowed in obj's scope chain unless it is known a priori that it
4407 * is not possible. We do this before locking obj to avoid nesting locks.
4409 if (!(defineHow & JSDNP_DONT_PURGE))
4410 js_PurgeScopeChain(cx, obj, id);
4413 * Check whether a readonly property or setter is being defined on a known
4414 * prototype object. See the comment in jscntxt.h before protoHazardShape's
4415 * member declaration.
4417 if (obj->isDelegate() && (attrs & (JSPROP_READONLY | JSPROP_SETTER)))
4418 cx->runtime->protoHazardShape = js_GenerateShape(cx, false);
4420 /* Use the object's class getter and setter by default. */
4421 clasp = obj->getClass();
4422 if (!(defineHow & JSDNP_SET_METHOD)) {
4423 if (!getter && !(attrs & JSPROP_GETTER))
4424 getter = clasp->getProperty;
4425 if (!setter && !(attrs & JSPROP_SETTER))
4426 setter = clasp->setProperty;
4429 /* Get obj's own scope if it has one, or create a new one for obj. */
4430 if (!obj->ensureClassReservedSlots(cx))
4431 return false;
4433 added = false;
4434 if (!shape) {
4435 /* Add a new property, or replace an existing one of the same id. */
4436 if (defineHow & JSDNP_SET_METHOD) {
4437 JS_ASSERT(clasp == &js_ObjectClass);
4438 JS_ASSERT(IsFunctionObject(value));
4439 JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
4440 JS_ASSERT(!getter && !setter);
4442 JSObject *funobj = &value.toObject();
4443 if (FUN_OBJECT(GET_FUNCTION_PRIVATE(cx, funobj)) == funobj) {
4444 flags |= Shape::METHOD;
4445 getter = CastAsPropertyOp(funobj);
4449 added = !obj->nativeContains(id);
4450 uint32 oldShape = obj->shape();
4451 shape = obj->putProperty(cx, id, getter, setter, SHAPE_INVALID_SLOT,
4452 attrs, flags, shortid);
4453 if (!shape)
4454 return false;
4457 * If shape is a method, the above call to putProperty suffices to
4458 * update the shape if necessary. But if scope->branded(), the shape
4459 * may not have changed and we may be overwriting a function-valued
4460 * property. See bug 560998.
4462 if (obj->shape() == oldShape && obj->branded() && shape->slot != SHAPE_INVALID_SLOT)
4463 obj->methodWriteBarrier(cx, shape->slot, value);
4466 /* Store value before calling addProperty, in case the latter GC's. */
4467 if (obj->containsSlot(shape->slot))
4468 obj->nativeSetSlot(shape->slot, value);
4470 /* XXXbe called with lock held */
4471 valueCopy = value;
4472 if (!CallAddPropertyHook(cx, clasp, obj, shape, &valueCopy)) {
4473 obj->removeProperty(cx, id);
4474 return false;
4477 if (defineHow & JSDNP_CACHE_RESULT) {
4478 #ifdef JS_TRACER
4479 JS_ASSERT_NOT_ON_TRACE(cx);
4480 PropertyCacheEntry *entry =
4481 #endif
4482 JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, 0, obj, shape, added);
4483 TRACE_2(SetPropHit, entry, shape);
4485 if (propp)
4486 *propp = (JSProperty *) shape;
4487 return true;
4489 #ifdef JS_TRACER
4490 error: // TRACE_2 jumps here on error.
4491 #endif
4492 return false;
4495 #define SCOPE_DEPTH_ACCUM(bs,val) \
4496 JS_SCOPE_DEPTH_METERING(JS_BASIC_STATS_ACCUM(bs, val))
4499 * Call obj's resolve hook. obj is a native object and the caller holds its
4500 * scope lock.
4502 * cx, start, id, and flags are the parameters initially passed to the ongoing
4503 * lookup; objp and propp are its out parameters. obj is an object along
4504 * start's prototype chain.
4506 * There are four possible outcomes:
4508 * - On failure, report an error or exception, unlock obj, and return false.
4510 * - If we are alrady resolving a property of *curobjp, set *recursedp = true,
4511 * unlock obj, and return true.
4513 * - If the resolve hook finds or defines the sought property, set *objp and
4514 * *propp appropriately, set *recursedp = false, and return true with *objp's
4515 * lock held.
4517 * - Otherwise no property was resolved. Set *propp = NULL and *recursedp = false
4518 * and return true.
4520 static JSBool
4521 CallResolveOp(JSContext *cx, JSObject *start, JSObject *obj, jsid id, uintN flags,
4522 JSObject **objp, JSProperty **propp, bool *recursedp)
4524 Class *clasp = obj->getClass();
4525 JSResolveOp resolve = clasp->resolve;
4528 * Avoid recursion on (obj, id) already being resolved on cx.
4530 * Once we have successfully added an entry for (obj, key) to
4531 * cx->resolvingTable, control must go through cleanup: before
4532 * returning. But note that JS_DHASH_ADD may find an existing
4533 * entry, in which case we bail to suppress runaway recursion.
4535 JSResolvingKey key = {obj, id};
4536 JSResolvingEntry *entry;
4537 if (!js_StartResolving(cx, &key, JSRESFLAG_LOOKUP, &entry))
4538 return false;
4539 if (!entry) {
4540 /* Already resolving id in obj -- suppress recursion. */
4541 *recursedp = true;
4542 return true;
4544 uint32 generation = cx->resolvingTable->generation;
4545 *recursedp = false;
4547 *propp = NULL;
4549 JSBool ok;
4550 const Shape *shape = NULL;
4551 if (clasp->flags & JSCLASS_NEW_RESOLVE) {
4552 JSNewResolveOp newresolve = (JSNewResolveOp)resolve;
4553 if (flags == JSRESOLVE_INFER)
4554 flags = js_InferFlags(cx, 0);
4555 JSObject *obj2 = (clasp->flags & JSCLASS_NEW_RESOLVE_GETS_START) ? start : NULL;
4558 /* Protect id and all atoms from a GC nested in resolve. */
4559 AutoKeepAtoms keep(cx->runtime);
4560 ok = newresolve(cx, obj, id, flags, &obj2);
4562 if (!ok)
4563 goto cleanup;
4565 if (obj2) {
4566 /* Resolved: lookup id again for backward compatibility. */
4567 if (!obj2->isNative()) {
4568 /* Whoops, newresolve handed back a foreign obj2. */
4569 JS_ASSERT(obj2 != obj);
4570 ok = obj2->lookupProperty(cx, id, objp, propp);
4571 if (!ok || *propp)
4572 goto cleanup;
4573 } else {
4575 * Require that obj2 not be empty now, as we do for old-style
4576 * resolve. If it doesn't, then id was not truly resolved, and
4577 * we'll find it in the proto chain, or miss it if obj2's proto
4578 * is not on obj's proto chain. That last case is a "too bad!"
4579 * case.
4581 if (!obj2->nativeEmpty())
4582 shape = obj2->nativeLookup(id);
4584 if (shape) {
4585 JS_ASSERT(!obj2->nativeEmpty());
4586 obj = obj2;
4589 } else {
4591 * Old resolve always requires id re-lookup if obj is not empty after
4592 * resolve returns.
4594 ok = resolve(cx, obj, id);
4595 if (!ok)
4596 goto cleanup;
4597 JS_ASSERT(obj->isNative());
4598 if (!obj->nativeEmpty())
4599 shape = obj->nativeLookup(id);
4602 cleanup:
4603 if (ok && shape) {
4604 *objp = obj;
4605 *propp = (JSProperty *) shape;
4607 js_StopResolving(cx, &key, JSRESFLAG_LOOKUP, entry, generation);
4608 return ok;
4611 static JS_ALWAYS_INLINE int
4612 js_LookupPropertyWithFlagsInline(JSContext *cx, JSObject *obj, jsid id, uintN flags,
4613 JSObject **objp, JSProperty **propp)
4615 /* We should not get string indices which aren't already integers here. */
4616 JS_ASSERT(id == js_CheckForStringIndex(id));
4618 /* Search scopes starting with obj and following the prototype link. */
4619 JSObject *start = obj;
4620 int protoIndex;
4621 for (protoIndex = 0; ; protoIndex++) {
4622 const Shape *shape = obj->nativeLookup(id);
4623 if (shape) {
4624 SCOPE_DEPTH_ACCUM(&cx->runtime->protoLookupDepthStats, protoIndex);
4625 *objp = obj;
4626 *propp = (JSProperty *) shape;
4627 return protoIndex;
4630 /* Try obj's class resolve hook if id was not found in obj's scope. */
4631 if (!shape && obj->getClass()->resolve != JS_ResolveStub) {
4632 bool recursed;
4633 if (!CallResolveOp(cx, start, obj, id, flags, objp, propp, &recursed))
4634 return -1;
4635 if (recursed)
4636 break;
4637 if (*propp) {
4638 /* Recalculate protoIndex in case it was resolved on some other object. */
4639 protoIndex = 0;
4640 for (JSObject *proto = start; proto && proto != *objp; proto = proto->getProto())
4641 protoIndex++;
4642 SCOPE_DEPTH_ACCUM(&cx->runtime->protoLookupDepthStats, protoIndex);
4643 return protoIndex;
4647 JSObject *proto = obj->getProto();
4648 if (!proto)
4649 break;
4650 if (!proto->isNative()) {
4651 if (!proto->lookupProperty(cx, id, objp, propp))
4652 return -1;
4653 return protoIndex + 1;
4656 obj = proto;
4659 *objp = NULL;
4660 *propp = NULL;
4661 return protoIndex;
4664 JS_FRIEND_API(JSBool)
4665 js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
4666 JSProperty **propp)
4668 /* Convert string indices to integers if appropriate. */
4669 id = js_CheckForStringIndex(id);
4671 return js_LookupPropertyWithFlagsInline(cx, obj, id, cx->resolveFlags, objp, propp) >= 0;
4675 js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags,
4676 JSObject **objp, JSProperty **propp)
4678 /* Convert string indices to integers if appropriate. */
4679 id = js_CheckForStringIndex(id);
4681 return js_LookupPropertyWithFlagsInline(cx, obj, id, flags, objp, propp);
4684 PropertyCacheEntry *
4685 js_FindPropertyHelper(JSContext *cx, jsid id, JSBool cacheResult,
4686 JSObject **objp, JSObject **pobjp, JSProperty **propp)
4688 JSObject *scopeChain, *obj, *parent, *pobj;
4689 PropertyCacheEntry *entry;
4690 int scopeIndex, protoIndex;
4691 JSProperty *prop;
4693 JS_ASSERT_IF(cacheResult, !JS_ON_TRACE(cx));
4694 scopeChain = &js_GetTopStackFrame(cx)->scopeChain();
4696 /* Scan entries on the scope chain that we can cache across. */
4697 entry = JS_NO_PROP_CACHE_FILL;
4698 obj = scopeChain;
4699 parent = obj->getParent();
4700 for (scopeIndex = 0;
4701 parent
4702 ? js_IsCacheableNonGlobalScope(obj)
4703 : !obj->getOps()->lookupProperty;
4704 ++scopeIndex) {
4705 protoIndex =
4706 js_LookupPropertyWithFlags(cx, obj, id, cx->resolveFlags,
4707 &pobj, &prop);
4708 if (protoIndex < 0)
4709 return NULL;
4711 if (prop) {
4712 #ifdef DEBUG
4713 if (parent) {
4714 Class *clasp = obj->getClass();
4715 JS_ASSERT(pobj->isNative());
4716 JS_ASSERT(pobj->getClass() == clasp);
4717 if (clasp == &js_BlockClass) {
4719 * A block instance on the scope chain is immutable and it
4720 * shares its shapes with its compile-time prototype.
4722 JS_ASSERT(pobj == obj);
4723 JS_ASSERT(pobj->isClonedBlock());
4724 JS_ASSERT(protoIndex == 0);
4725 } else {
4726 /* Call and DeclEnvClass objects have no prototypes. */
4727 JS_ASSERT(!obj->getProto());
4728 JS_ASSERT(protoIndex == 0);
4730 } else {
4731 JS_ASSERT(obj->isNative());
4733 #endif
4735 * We must check if pobj is native as a global object can have
4736 * non-native prototype.
4738 if (cacheResult && pobj->isNative()) {
4739 entry = JS_PROPERTY_CACHE(cx).fill(cx, scopeChain, scopeIndex,
4740 protoIndex, pobj,
4741 (Shape *) prop);
4743 SCOPE_DEPTH_ACCUM(&cx->runtime->scopeSearchDepthStats, scopeIndex);
4744 goto out;
4747 if (!parent) {
4748 pobj = NULL;
4749 goto out;
4751 obj = parent;
4752 parent = obj->getParent();
4755 for (;;) {
4756 if (!obj->lookupProperty(cx, id, &pobj, &prop))
4757 return NULL;
4758 if (prop) {
4759 PCMETER(JS_PROPERTY_CACHE(cx).nofills++);
4760 goto out;
4764 * We conservatively assume that a resolve hook could mutate the scope
4765 * chain during JSObject::lookupProperty. So we read parent here again.
4767 parent = obj->getParent();
4768 if (!parent) {
4769 pobj = NULL;
4770 break;
4772 obj = parent;
4775 out:
4776 JS_ASSERT(!!pobj == !!prop);
4777 *objp = obj;
4778 *pobjp = pobj;
4779 *propp = prop;
4780 return entry;
4784 * On return, if |*pobjp| is a native object, then |*propp| is a |Shape *|.
4785 * Otherwise, its type and meaning depends on the host object's implementation.
4787 JS_FRIEND_API(JSBool)
4788 js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp,
4789 JSProperty **propp)
4791 return !!js_FindPropertyHelper(cx, id, false, objp, pobjp, propp);
4794 JSObject *
4795 js_FindIdentifierBase(JSContext *cx, JSObject *scopeChain, jsid id)
4798 * This function should not be called for a global object or from the
4799 * trace and should have a valid cache entry for native scopeChain.
4801 JS_ASSERT(scopeChain->getParent());
4802 JS_ASSERT(!JS_ON_TRACE(cx));
4804 JSObject *obj = scopeChain;
4807 * Loop over cacheable objects on the scope chain until we find a
4808 * property. We also stop when we reach the global object skipping any
4809 * farther checks or lookups. For details see the JSOP_BINDNAME case of
4810 * js_Interpret.
4812 * The test order here matters because js_IsCacheableNonGlobalScope
4813 * must not be passed a global object (i.e. one with null parent).
4815 for (int scopeIndex = 0;
4816 !obj->getParent() || js_IsCacheableNonGlobalScope(obj);
4817 scopeIndex++) {
4818 JSObject *pobj;
4819 JSProperty *prop;
4820 int protoIndex = js_LookupPropertyWithFlags(cx, obj, id,
4821 cx->resolveFlags,
4822 &pobj, &prop);
4823 if (protoIndex < 0)
4824 return NULL;
4825 if (prop) {
4826 if (!pobj->isNative()) {
4827 JS_ASSERT(!obj->getParent());
4828 return obj;
4830 JS_ASSERT_IF(obj->getParent(), pobj->getClass() == obj->getClass());
4831 #ifdef DEBUG
4832 PropertyCacheEntry *entry =
4833 #endif
4834 JS_PROPERTY_CACHE(cx).fill(cx, scopeChain, scopeIndex, protoIndex, pobj,
4835 (Shape *) prop);
4836 JS_ASSERT(entry);
4837 return obj;
4840 JSObject *parent = obj->getParent();
4841 if (!parent)
4842 return obj;
4843 obj = parent;
4846 /* Loop until we find a property or reach the global object. */
4847 do {
4848 JSObject *pobj;
4849 JSProperty *prop;
4850 if (!obj->lookupProperty(cx, id, &pobj, &prop))
4851 return NULL;
4852 if (prop)
4853 break;
4856 * We conservatively assume that a resolve hook could mutate the scope
4857 * chain during JSObject::lookupProperty. So we must check if parent is
4858 * not null here even if it wasn't before the lookup.
4860 JSObject *parent = obj->getParent();
4861 if (!parent)
4862 break;
4863 obj = parent;
4864 } while (obj->getParent());
4865 return obj;
4868 static JS_ALWAYS_INLINE JSBool
4869 js_NativeGetInline(JSContext *cx, JSObject *receiver, JSObject *obj, JSObject *pobj,
4870 const Shape *shape, uintN getHow, Value *vp)
4872 LeaveTraceIfGlobalObject(cx, pobj);
4874 uint32 slot;
4875 int32 sample;
4877 JS_ASSERT(pobj->isNative());
4879 slot = shape->slot;
4880 if (slot != SHAPE_INVALID_SLOT) {
4881 *vp = pobj->nativeGetSlot(slot);
4882 JS_ASSERT(!vp->isMagic());
4883 } else {
4884 vp->setUndefined();
4886 if (shape->hasDefaultGetter())
4887 return true;
4889 if (JS_UNLIKELY(shape->isMethod()) && (getHow & JSGET_NO_METHOD_BARRIER)) {
4890 JS_ASSERT(&shape->methodObject() == &vp->toObject());
4891 return true;
4894 sample = cx->runtime->propertyRemovals;
4896 AutoShapeRooter tvr(cx, shape);
4897 AutoObjectRooter tvr2(cx, pobj);
4898 if (!shape->get(cx, receiver, obj, pobj, vp))
4899 return false;
4902 if (pobj->containsSlot(slot) &&
4903 (JS_LIKELY(cx->runtime->propertyRemovals == sample) ||
4904 pobj->nativeContains(*shape))) {
4905 if (!pobj->methodWriteBarrier(cx, *shape, *vp))
4906 return false;
4907 pobj->nativeSetSlot(slot, *vp);
4910 return true;
4913 JSBool
4914 js_NativeGet(JSContext *cx, JSObject *obj, JSObject *pobj, const Shape *shape, uintN getHow,
4915 Value *vp)
4917 return js_NativeGetInline(cx, obj, obj, pobj, shape, getHow, vp);
4920 JSBool
4921 js_NativeSet(JSContext *cx, JSObject *obj, const Shape *shape, bool added, Value *vp)
4923 LeaveTraceIfGlobalObject(cx, obj);
4925 uint32 slot;
4926 int32 sample;
4928 JS_ASSERT(obj->isNative());
4930 slot = shape->slot;
4931 if (slot != SHAPE_INVALID_SLOT) {
4932 JS_ASSERT(obj->containsSlot(slot));
4934 /* If shape has a stub setter, keep obj locked and just store *vp. */
4935 if (shape->hasDefaultSetter()) {
4936 if (!added && !obj->methodWriteBarrier(cx, *shape, *vp))
4937 return false;
4938 obj->nativeSetSlot(slot, *vp);
4939 return true;
4941 } else {
4943 * Allow API consumers to create shared properties with stub setters.
4944 * Such properties effectively function as data descriptors which are
4945 * not writable, so attempting to set such a property should do nothing
4946 * or throw if we're in strict mode.
4948 if (!shape->hasGetterValue() && shape->hasDefaultSetter())
4949 return js_ReportGetterOnlyAssignment(cx);
4952 sample = cx->runtime->propertyRemovals;
4954 AutoShapeRooter tvr(cx, shape);
4955 if (!shape->set(cx, obj, vp))
4956 return false;
4959 if (obj->containsSlot(slot) &&
4960 (JS_LIKELY(cx->runtime->propertyRemovals == sample) ||
4961 obj->nativeContains(*shape))) {
4962 if (!added && !obj->methodWriteBarrier(cx, *shape, *vp))
4963 return false;
4964 obj->setSlot(slot, *vp);
4967 return true;
4970 static JS_ALWAYS_INLINE bool
4971 js_GetPropertyHelperWithShapeInline(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id,
4972 uintN getHow, Value *vp,
4973 const Shape **shapeOut, JSObject **holderOut)
4975 JSObject *aobj, *obj2;
4976 int protoIndex;
4977 JSProperty *prop;
4978 const Shape *shape;
4980 JS_ASSERT_IF(getHow & JSGET_CACHE_RESULT, !JS_ON_TRACE(cx));
4982 *shapeOut = NULL;
4984 /* Convert string indices to integers if appropriate. */
4985 id = js_CheckForStringIndex(id);
4987 aobj = js_GetProtoIfDenseArray(obj);
4988 /* This call site is hot -- use the always-inlined variant of js_LookupPropertyWithFlags(). */
4989 protoIndex = js_LookupPropertyWithFlagsInline(cx, aobj, id, cx->resolveFlags,
4990 &obj2, &prop);
4991 if (protoIndex < 0)
4992 return JS_FALSE;
4994 *holderOut = obj2;
4996 if (!prop) {
4997 vp->setUndefined();
4999 if (!CallJSPropertyOp(cx, obj->getClass()->getProperty, obj, id, vp))
5000 return JS_FALSE;
5002 PCMETER(getHow & JSGET_CACHE_RESULT && JS_PROPERTY_CACHE(cx).nofills++);
5005 * Give a strict warning if foo.bar is evaluated by a script for an
5006 * object foo with no property named 'bar'.
5008 jsbytecode *pc;
5009 if (vp->isUndefined() && ((pc = js_GetCurrentBytecodePC(cx)) != NULL)) {
5010 JSOp op;
5011 uintN flags;
5013 op = (JSOp) *pc;
5014 if (op == JSOP_TRAP) {
5015 JS_ASSERT_NOT_ON_TRACE(cx);
5016 op = JS_GetTrapOpcode(cx, cx->fp()->script(), pc);
5018 if (op == JSOP_GETXPROP) {
5019 flags = JSREPORT_ERROR;
5020 } else {
5021 if (!JS_HAS_STRICT_OPTION(cx) ||
5022 (op != JSOP_GETPROP && op != JSOP_GETELEM) ||
5023 js_CurrentPCIsInImacro(cx)) {
5024 return JS_TRUE;
5028 * XXX do not warn about missing __iterator__ as the function
5029 * may be called from JS_GetMethodById. See bug 355145.
5031 if (JSID_IS_ATOM(id, cx->runtime->atomState.iteratorAtom))
5032 return JS_TRUE;
5034 /* Do not warn about tests like (obj[prop] == undefined). */
5035 if (cx->resolveFlags == JSRESOLVE_INFER) {
5036 LeaveTrace(cx);
5037 pc += js_CodeSpec[op].length;
5038 if (Detecting(cx, pc))
5039 return JS_TRUE;
5040 } else if (cx->resolveFlags & JSRESOLVE_DETECTING) {
5041 return JS_TRUE;
5044 flags = JSREPORT_WARNING | JSREPORT_STRICT;
5047 /* Ok, bad undefined property reference: whine about it. */
5048 if (!js_ReportValueErrorFlags(cx, flags, JSMSG_UNDEFINED_PROP,
5049 JSDVG_IGNORE_STACK, IdToValue(id),
5050 NULL, NULL, NULL)) {
5051 return JS_FALSE;
5054 return JS_TRUE;
5057 if (!obj2->isNative()) {
5058 return obj2->isProxy()
5059 ? JSProxy::get(cx, obj2, receiver, id, vp)
5060 : obj2->getProperty(cx, id, vp);
5063 shape = (Shape *) prop;
5064 *shapeOut = shape;
5066 if (getHow & JSGET_CACHE_RESULT) {
5067 JS_ASSERT_NOT_ON_TRACE(cx);
5068 JS_PROPERTY_CACHE(cx).fill(cx, aobj, 0, protoIndex, obj2, shape);
5071 /* This call site is hot -- use the always-inlined variant of js_NativeGet(). */
5072 if (!js_NativeGetInline(cx, receiver, obj, obj2, shape, getHow, vp))
5073 return JS_FALSE;
5075 return JS_TRUE;
5078 bool
5079 js_GetPropertyHelperWithShape(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id,
5080 uint32 getHow, Value *vp,
5081 const Shape **shapeOut, JSObject **holderOut)
5083 return js_GetPropertyHelperWithShapeInline(cx, obj, receiver, id, getHow, vp,
5084 shapeOut, holderOut);
5087 static JS_ALWAYS_INLINE JSBool
5088 js_GetPropertyHelperInline(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id,
5089 uint32 getHow, Value *vp)
5091 const Shape *shape;
5092 JSObject *holder;
5093 return js_GetPropertyHelperWithShapeInline(cx, obj, receiver, id, getHow, vp, &shape, &holder);
5096 JSBool
5097 js_GetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uint32 getHow, Value *vp)
5099 return js_GetPropertyHelperInline(cx, obj, obj, id, getHow, vp);
5102 JSBool
5103 js_GetProperty(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp)
5105 /* This call site is hot -- use the always-inlined variant of js_GetPropertyHelper(). */
5106 return js_GetPropertyHelperInline(cx, obj, receiver, id, JSGET_METHOD_BARRIER, vp);
5109 JSBool
5110 js::GetPropertyDefault(JSContext *cx, JSObject *obj, jsid id, const Value &def, Value *vp)
5112 JSProperty *prop;
5113 JSObject *obj2;
5114 if (js_LookupPropertyWithFlags(cx, obj, id, JSRESOLVE_QUALIFIED, &obj2, &prop) < 0)
5115 return false;
5117 if (!prop) {
5118 *vp = def;
5119 return true;
5122 return js_GetProperty(cx, obj2, id, vp);
5125 JSBool
5126 js_GetMethod(JSContext *cx, JSObject *obj, jsid id, uintN getHow, Value *vp)
5128 JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED);
5130 PropertyIdOp op = obj->getOps()->getProperty;
5131 if (!op) {
5132 #if JS_HAS_XML_SUPPORT
5133 JS_ASSERT(!obj->isXML());
5134 #endif
5135 return js_GetPropertyHelper(cx, obj, id, getHow, vp);
5137 JS_ASSERT_IF(getHow & JSGET_CACHE_RESULT, obj->isDenseArray());
5138 #if JS_HAS_XML_SUPPORT
5139 if (obj->isXML())
5140 return js_GetXMLMethod(cx, obj, id, vp);
5141 #endif
5142 return op(cx, obj, obj, id, vp);
5145 JS_FRIEND_API(bool)
5146 js_CheckUndeclaredVarAssignment(JSContext *cx, JSString *propname)
5148 JSStackFrame *const fp = js_GetTopStackFrame(cx);
5149 if (!fp)
5150 return true;
5152 /* If neither cx nor the code is strict, then no check is needed. */
5153 if (!(fp->isScriptFrame() && fp->script()->strictModeCode) &&
5154 !JS_HAS_STRICT_OPTION(cx)) {
5155 return true;
5158 JSAutoByteString bytes(cx, propname);
5159 return !!bytes &&
5160 JS_ReportErrorFlagsAndNumber(cx,
5161 (JSREPORT_WARNING | JSREPORT_STRICT
5162 | JSREPORT_STRICT_MODE_ERROR),
5163 js_GetErrorMessage, NULL,
5164 JSMSG_UNDECLARED_VAR, bytes.ptr());
5167 bool
5168 JSObject::reportReadOnly(JSContext* cx, jsid id, uintN report)
5170 return js_ReportValueErrorFlags(cx, report, JSMSG_READ_ONLY,
5171 JSDVG_IGNORE_STACK, IdToValue(id), NULL,
5172 NULL, NULL);
5175 bool
5176 JSObject::reportNotConfigurable(JSContext* cx, jsid id, uintN report)
5178 return js_ReportValueErrorFlags(cx, report, JSMSG_CANT_DELETE,
5179 JSDVG_IGNORE_STACK, IdToValue(id), NULL,
5180 NULL, NULL);
5183 bool
5184 JSObject::reportNotExtensible(JSContext *cx, uintN report)
5186 return js_ReportValueErrorFlags(cx, report, JSMSG_OBJECT_NOT_EXTENSIBLE,
5187 JSDVG_IGNORE_STACK, ObjectValue(*this),
5188 NULL, NULL, NULL);
5192 * Note: all non-error exits in this function must notify the tracer using
5193 * SetPropHit when called from the interpreter, which is detected by testing
5194 * (defineHow & JSDNP_CACHE_RESULT).
5196 JSBool
5197 js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow,
5198 Value *vp, JSBool strict)
5200 int protoIndex;
5201 JSObject *pobj;
5202 JSProperty *prop;
5203 const Shape *shape;
5204 uintN attrs, flags;
5205 intN shortid;
5206 Class *clasp;
5207 PropertyOp getter, setter;
5208 bool added;
5210 JS_ASSERT((defineHow &
5211 ~(JSDNP_CACHE_RESULT | JSDNP_SET_METHOD | JSDNP_UNQUALIFIED)) == 0);
5212 if (defineHow & JSDNP_CACHE_RESULT)
5213 JS_ASSERT_NOT_ON_TRACE(cx);
5215 /* Convert string indices to integers if appropriate. */
5216 id = js_CheckForStringIndex(id);
5218 protoIndex = js_LookupPropertyWithFlags(cx, obj, id, cx->resolveFlags,
5219 &pobj, &prop);
5220 if (protoIndex < 0)
5221 return JS_FALSE;
5222 if (prop) {
5223 if (!pobj->isNative()) {
5224 if (pobj->isProxy()) {
5225 AutoPropertyDescriptorRooter pd(cx);
5226 if (!pobj->getProxyHandler()->getPropertyDescriptor(cx, pobj, id, true, &pd))
5227 return false;
5229 if (pd.attrs & JSPROP_SHARED)
5230 return CallSetter(cx, obj, id, pd.setter, pd.attrs, pd.shortid, vp);
5232 if (pd.attrs & JSPROP_READONLY) {
5233 if (strict)
5234 return obj->reportReadOnly(cx, id);
5235 if (JS_HAS_STRICT_OPTION(cx))
5236 return obj->reportReadOnly(cx, id, JSREPORT_STRICT | JSREPORT_WARNING);
5237 return true;
5241 prop = NULL;
5243 } else {
5244 /* We should never add properties to lexical blocks. */
5245 JS_ASSERT(!obj->isBlock());
5247 if (!obj->getParent() &&
5248 (defineHow & JSDNP_UNQUALIFIED) &&
5249 !js_CheckUndeclaredVarAssignment(cx, JSID_TO_STRING(id))) {
5250 return JS_FALSE;
5253 shape = (Shape *) prop;
5256 * Now either shape is null, meaning id was not found in obj or one of its
5257 * prototypes; or shape is non-null, meaning id was found directly in pobj.
5259 attrs = JSPROP_ENUMERATE;
5260 flags = 0;
5261 shortid = 0;
5262 clasp = obj->getClass();
5263 getter = clasp->getProperty;
5264 setter = clasp->setProperty;
5266 if (shape) {
5267 /* ES5 8.12.4 [[Put]] step 2. */
5268 if (shape->isAccessorDescriptor()) {
5269 if (shape->hasDefaultSetter()) {
5270 if (defineHow & JSDNP_CACHE_RESULT)
5271 TRACE_2(SetPropHit, JS_NO_PROP_CACHE_FILL, shape);
5272 return js_ReportGetterOnlyAssignment(cx);
5274 } else {
5275 JS_ASSERT(shape->isDataDescriptor());
5277 if (!shape->writable()) {
5278 PCMETER((defineHow & JSDNP_CACHE_RESULT) && JS_PROPERTY_CACHE(cx).rofills++);
5279 if (defineHow & JSDNP_CACHE_RESULT) {
5280 JS_ASSERT_NOT_ON_TRACE(cx);
5281 TRACE_2(SetPropHit, JS_NO_PROP_CACHE_FILL, shape);
5284 /* Error in strict mode code, warn with strict option, otherwise do nothing. */
5285 if (strict)
5286 return obj->reportReadOnly(cx, id);
5287 if (JS_HAS_STRICT_OPTION(cx))
5288 return obj->reportReadOnly(cx, id, JSREPORT_STRICT | JSREPORT_WARNING);
5289 return JS_TRUE;
5291 #ifdef JS_TRACER
5292 error: // TRACE_2 jumps here in case of error.
5293 return JS_FALSE;
5294 #endif
5298 attrs = shape->attributes();
5299 if (pobj != obj) {
5301 * We found id in a prototype object: prepare to share or shadow.
5303 * Don't clone a prototype property that doesn't have a slot.
5305 if (!shape->hasSlot()) {
5306 if (defineHow & JSDNP_CACHE_RESULT) {
5307 #ifdef JS_TRACER
5308 JS_ASSERT_NOT_ON_TRACE(cx);
5309 PropertyCacheEntry *entry =
5310 #endif
5311 JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, protoIndex, pobj, shape);
5312 TRACE_2(SetPropHit, entry, shape);
5315 if (shape->hasDefaultSetter() && !shape->hasGetterValue())
5316 return JS_TRUE;
5318 return shape->set(cx, obj, vp);
5321 /* Restore attrs to the ECMA default for new properties. */
5322 attrs = JSPROP_ENUMERATE;
5325 * Preserve the shortid, getter, and setter when shadowing any
5326 * property that has a shortid. An old API convention requires
5327 * that the property's getter and setter functions receive the
5328 * shortid, not id, when they are called on the shadow we are
5329 * about to create in obj.
5331 if (shape->hasShortID()) {
5332 flags = Shape::HAS_SHORTID;
5333 shortid = shape->shortid;
5334 getter = shape->getter();
5335 setter = shape->setter();
5339 * Forget we found the proto-property now that we've copied any
5340 * needed member values.
5342 shape = NULL;
5345 if (shape) {
5346 if (shape->isMethod()) {
5347 JS_ASSERT(pobj->hasMethodBarrier());
5348 } else if ((defineHow & JSDNP_SET_METHOD) && obj->canHaveMethodBarrier()) {
5349 JS_ASSERT(IsFunctionObject(*vp));
5350 JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
5352 JSObject *funobj = &vp->toObject();
5353 JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
5354 if (fun == funobj) {
5355 funobj = CloneFunctionObject(cx, fun, fun->parent);
5356 if (!funobj)
5357 return JS_FALSE;
5358 vp->setObject(*funobj);
5364 added = false;
5365 if (!shape) {
5366 if (!obj->isExtensible()) {
5367 if (defineHow & JSDNP_CACHE_RESULT) {
5368 JS_ASSERT_NOT_ON_TRACE(cx);
5369 TRACE_2(SetPropHit, JS_NO_PROP_CACHE_FILL, shape);
5372 /* Error in strict mode code, warn with strict option, otherwise do nothing. */
5373 if (strict)
5374 return obj->reportNotExtensible(cx);
5375 if (JS_HAS_STRICT_OPTION(cx))
5376 return obj->reportNotExtensible(cx, JSREPORT_STRICT | JSREPORT_WARNING);
5377 return JS_TRUE;
5381 * Purge the property cache of now-shadowed id in obj's scope chain.
5382 * Do this early, before locking obj to avoid nesting locks.
5384 js_PurgeScopeChain(cx, obj, id);
5386 /* Find or make a property descriptor with the right heritage. */
5387 if (!obj->ensureClassReservedSlots(cx))
5388 return JS_FALSE;
5391 * Check for Object class here to avoid defining a method on a class
5392 * with magic resolve, addProperty, getProperty, etc. hooks.
5394 if ((defineHow & JSDNP_SET_METHOD) && obj->canHaveMethodBarrier()) {
5395 JS_ASSERT(IsFunctionObject(*vp));
5396 JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
5398 JSObject *funobj = &vp->toObject();
5399 JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
5400 if (fun == funobj) {
5401 flags |= Shape::METHOD;
5402 getter = CastAsPropertyOp(funobj);
5406 shape = obj->putProperty(cx, id, getter, setter, SHAPE_INVALID_SLOT,
5407 attrs, flags, shortid);
5408 if (!shape)
5409 return JS_FALSE;
5412 * Initialize the new property value (passed to setter) to undefined.
5413 * Note that we store before calling addProperty, to match the order
5414 * in js_DefineNativeProperty.
5416 if (obj->containsSlot(shape->slot))
5417 obj->nativeSetSlot(shape->slot, UndefinedValue());
5419 /* XXXbe called with obj locked */
5420 if (!CallAddPropertyHook(cx, clasp, obj, shape, vp)) {
5421 obj->removeProperty(cx, id);
5422 return JS_FALSE;
5424 added = true;
5427 if (defineHow & JSDNP_CACHE_RESULT) {
5428 #ifdef JS_TRACER
5429 JS_ASSERT_NOT_ON_TRACE(cx);
5430 PropertyCacheEntry *entry =
5431 #endif
5432 JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, 0, obj, shape, added);
5433 TRACE_2(SetPropHit, entry, shape);
5436 return js_NativeSet(cx, obj, shape, added, vp);
5439 JSBool
5440 js_SetProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict)
5442 return js_SetPropertyHelper(cx, obj, id, 0, vp, strict);
5445 JSBool
5446 js_GetAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp)
5448 JSProperty *prop;
5449 if (!js_LookupProperty(cx, obj, id, &obj, &prop))
5450 return false;
5451 if (!prop) {
5452 *attrsp = 0;
5453 return true;
5455 if (!obj->isNative())
5456 return obj->getAttributes(cx, id, attrsp);
5458 const Shape *shape = (Shape *)prop;
5459 *attrsp = shape->attributes();
5460 return true;
5463 JSBool
5464 js_SetNativeAttributes(JSContext *cx, JSObject *obj, Shape *shape, uintN attrs)
5466 JS_ASSERT(obj->isNative());
5467 return !!js_ChangeNativePropertyAttrs(cx, obj, shape, attrs, 0,
5468 shape->getter(), shape->setter());
5471 JSBool
5472 js_SetAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp)
5474 JSProperty *prop;
5475 if (!js_LookupProperty(cx, obj, id, &obj, &prop))
5476 return false;
5477 if (!prop)
5478 return true;
5479 return obj->isNative()
5480 ? js_SetNativeAttributes(cx, obj, (Shape *) prop, *attrsp)
5481 : obj->setAttributes(cx, id, attrsp);
5484 JSBool
5485 js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool strict)
5487 JSObject *proto;
5488 JSProperty *prop;
5489 const Shape *shape;
5491 rval->setBoolean(true);
5493 /* Convert string indices to integers if appropriate. */
5494 id = js_CheckForStringIndex(id);
5496 if (!js_LookupProperty(cx, obj, id, &proto, &prop))
5497 return false;
5498 if (!prop || proto != obj) {
5500 * If the property was found in a native prototype, check whether it's
5501 * shared and permanent. Such a property stands for direct properties
5502 * in all delegating objects, matching ECMA semantics without bloating
5503 * each delegating object.
5505 if (prop && proto->isNative()) {
5506 shape = (Shape *)prop;
5507 if (shape->isSharedPermanent()) {
5508 if (strict)
5509 return obj->reportNotConfigurable(cx, id);
5510 rval->setBoolean(false);
5511 return true;
5516 * If no property, or the property comes unshared or impermanent from
5517 * a prototype, call the class's delProperty hook, passing rval as the
5518 * result parameter.
5520 return CallJSPropertyOp(cx, obj->getClass()->delProperty, obj, id, rval);
5523 shape = (Shape *)prop;
5524 if (!shape->configurable()) {
5525 if (strict)
5526 return obj->reportNotConfigurable(cx, id);
5527 rval->setBoolean(false);
5528 return true;
5531 if (!CallJSPropertyOp(cx, obj->getClass()->delProperty, obj, SHAPE_USERID(shape), rval))
5532 return false;
5534 if (obj->containsSlot(shape->slot)) {
5535 const Value &v = obj->nativeGetSlot(shape->slot);
5536 GC_POKE(cx, v);
5539 * Delete is rare enough that we can take the hit of checking for an
5540 * active cloned method function object that must be homed to a callee
5541 * slot on the active stack frame before this delete completes, in case
5542 * someone saved the clone and checks it against foo.caller for a foo
5543 * called from the active method.
5545 * We do not check suspended frames. They can't be reached via caller,
5546 * so the only way they could have the method's joined function object
5547 * as callee is through an API abusage. We break any such edge case.
5549 if (obj->hasMethodBarrier()) {
5550 JSObject *funobj;
5552 if (IsFunctionObject(v, &funobj)) {
5553 JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
5555 if (fun != funobj) {
5556 for (JSStackFrame *fp = cx->maybefp(); fp; fp = fp->prev()) {
5557 if (fp->isFunctionFrame() &&
5558 &fp->callee() == &fun->compiledFunObj() &&
5559 fp->thisValue().isObject() &&
5560 &fp->thisValue().toObject() == obj) {
5561 fp->calleeValue().setObject(*funobj);
5569 return obj->removeProperty(cx, id) && js_SuppressDeletedProperty(cx, obj, id);
5572 namespace js {
5574 JSObject *
5575 HasNativeMethod(JSObject *obj, jsid methodid, Native native)
5577 const Shape *shape = obj->nativeLookup(methodid);
5578 if (!shape || !shape->hasDefaultGetter() || !obj->containsSlot(shape->slot))
5579 return NULL;
5581 const Value &fval = obj->nativeGetSlot(shape->slot);
5582 JSObject *funobj;
5583 if (!IsFunctionObject(fval, &funobj) || funobj->getFunctionPrivate()->maybeNative() != native)
5584 return NULL;
5586 return funobj;
5590 * When we have an object of a builtin class, we don't quite know what its
5591 * valueOf/toString methods are, since these methods may have been overwritten
5592 * or shadowed. However, we can still do better than js_TryMethod by
5593 * hard-coding the necessary properties for us to find the native we expect.
5595 * TODO: a per-thread shape-based cache would be faster and simpler.
5597 static JS_ALWAYS_INLINE bool
5598 ClassMethodIsNative(JSContext *cx, JSObject *obj, Class *clasp, jsid methodid,
5599 Native native)
5601 JS_ASSERT(obj->getClass() == clasp);
5603 if (HasNativeMethod(obj, methodid, native))
5604 return true;
5606 JSObject *pobj = obj->getProto();
5607 return pobj && pobj->getClass() == clasp &&
5608 HasNativeMethod(pobj, methodid, native);
5611 bool
5612 DefaultValue(JSContext *cx, JSObject *obj, JSType hint, Value *vp)
5614 JS_ASSERT(hint != JSTYPE_OBJECT && hint != JSTYPE_FUNCTION);
5616 Value v = ObjectValue(*obj);
5617 if (hint == JSTYPE_STRING) {
5618 /* Optimize (new String(...)).toString(). */
5619 if (obj->getClass() == &js_StringClass &&
5620 ClassMethodIsNative(cx, obj,
5621 &js_StringClass,
5622 ATOM_TO_JSID(cx->runtime->atomState.toStringAtom),
5623 js_str_toString)) {
5624 *vp = obj->getPrimitiveThis();
5625 return true;
5628 if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0, NULL, &v))
5629 return false;
5630 if (!v.isPrimitive()) {
5631 if (!obj->getClass()->convert(cx, obj, hint, &v))
5632 return false;
5634 } else {
5635 /* Optimize (new String(...)).valueOf(). */
5636 Class *clasp = obj->getClass();
5637 if ((clasp == &js_StringClass &&
5638 ClassMethodIsNative(cx, obj, &js_StringClass,
5639 ATOM_TO_JSID(cx->runtime->atomState.valueOfAtom),
5640 js_str_toString)) ||
5641 (clasp == &js_NumberClass &&
5642 ClassMethodIsNative(cx, obj, &js_NumberClass,
5643 ATOM_TO_JSID(cx->runtime->atomState.valueOfAtom),
5644 js_num_valueOf))) {
5645 *vp = obj->getPrimitiveThis();
5646 return true;
5649 if (!obj->getClass()->convert(cx, obj, hint, &v))
5650 return false;
5651 if (v.isObject()) {
5652 JS_ASSERT(hint != TypeOfValue(cx, v));
5653 if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0, NULL, &v))
5654 return false;
5657 if (v.isObject()) {
5658 /* Avoid recursive death when decompiling in js_ReportValueError. */
5659 JSString *str;
5660 if (hint == JSTYPE_STRING) {
5661 str = JS_InternString(cx, obj->getClass()->name);
5662 if (!str)
5663 return false;
5664 } else {
5665 str = NULL;
5667 vp->setObject(*obj);
5668 js_ReportValueError2(cx, JSMSG_CANT_CONVERT_TO,
5669 JSDVG_SEARCH_STACK, *vp, str,
5670 (hint == JSTYPE_VOID)
5671 ? "primitive type"
5672 : JS_TYPE_STR(hint));
5673 return false;
5675 *vp = v;
5676 return true;
5679 } /* namespace js */
5681 JS_FRIEND_API(JSBool)
5682 js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, Value *statep, jsid *idp)
5684 /* If the class has a custom JSCLASS_NEW_ENUMERATE hook, call it. */
5685 Class *clasp = obj->getClass();
5686 JSEnumerateOp enumerate = clasp->enumerate;
5687 if (clasp->flags & JSCLASS_NEW_ENUMERATE) {
5688 JS_ASSERT(enumerate != JS_EnumerateStub);
5689 return ((NewEnumerateOp) enumerate)(cx, obj, enum_op, statep, idp);
5692 if (!enumerate(cx, obj))
5693 return false;
5695 /* Tell InitNativeIterator to treat us like a native object. */
5696 JS_ASSERT(enum_op == JSENUMERATE_INIT || enum_op == JSENUMERATE_INIT_ALL);
5697 statep->setMagic(JS_NATIVE_ENUMERATE);
5698 return true;
5701 namespace js {
5703 JSBool
5704 CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
5705 Value *vp, uintN *attrsp)
5707 JSBool writing;
5708 JSObject *pobj;
5709 JSProperty *prop;
5710 Class *clasp;
5711 const Shape *shape;
5712 JSSecurityCallbacks *callbacks;
5713 CheckAccessOp check;
5715 while (JS_UNLIKELY(obj->getClass() == &js_WithClass))
5716 obj = obj->getProto();
5718 writing = (mode & JSACC_WRITE) != 0;
5719 switch (mode & JSACC_TYPEMASK) {
5720 case JSACC_PROTO:
5721 pobj = obj;
5722 if (!writing)
5723 vp->setObjectOrNull(obj->getProto());
5724 *attrsp = JSPROP_PERMANENT;
5725 break;
5727 case JSACC_PARENT:
5728 JS_ASSERT(!writing);
5729 pobj = obj;
5730 vp->setObject(*obj->getParent());
5731 *attrsp = JSPROP_READONLY | JSPROP_PERMANENT;
5732 break;
5734 default:
5735 if (!obj->lookupProperty(cx, id, &pobj, &prop))
5736 return JS_FALSE;
5737 if (!prop) {
5738 if (!writing)
5739 vp->setUndefined();
5740 *attrsp = 0;
5741 pobj = obj;
5742 break;
5745 if (!pobj->isNative()) {
5746 if (!writing) {
5747 vp->setUndefined();
5748 *attrsp = 0;
5750 break;
5753 shape = (Shape *)prop;
5754 *attrsp = shape->attributes();
5755 if (!writing) {
5756 if (pobj->containsSlot(shape->slot))
5757 *vp = pobj->nativeGetSlot(shape->slot);
5758 else
5759 vp->setUndefined();
5764 * If obj's class has a stub (null) checkAccess hook, use the per-runtime
5765 * checkObjectAccess callback, if configured.
5767 * We don't want to require all classes to supply a checkAccess hook; we
5768 * need that hook only for certain classes used when precompiling scripts
5769 * and functions ("brutal sharing"). But for general safety of built-in
5770 * magic properties like __proto__, we route all access checks, even for
5771 * classes that stub out checkAccess, through the global checkObjectAccess
5772 * hook. This covers precompilation-based sharing and (possibly
5773 * unintended) runtime sharing across trust boundaries.
5775 clasp = pobj->getClass();
5776 check = clasp->checkAccess;
5777 if (!check) {
5778 callbacks = JS_GetSecurityCallbacks(cx);
5779 check = callbacks ? Valueify(callbacks->checkObjectAccess) : NULL;
5781 return !check || check(cx, pobj, id, mode, vp);
5786 JSType
5787 js_TypeOf(JSContext *cx, JSObject *obj)
5790 * ECMA 262, 11.4.3 says that any native object that implements
5791 * [[Call]] should be of type "function". However, RegExp is of
5792 * type "object", not "function", for Web compatibility.
5794 if (obj->isCallable()) {
5795 return (obj->getClass() != &js_RegExpClass)
5796 ? JSTYPE_FUNCTION
5797 : JSTYPE_OBJECT;
5800 return JSTYPE_OBJECT;
5803 bool
5804 js_IsDelegate(JSContext *cx, JSObject *obj, const Value &v)
5806 if (v.isPrimitive())
5807 return false;
5808 JSObject *obj2 = &v.toObject();
5809 while ((obj2 = obj2->getProto()) != NULL) {
5810 if (obj2 == obj)
5811 return true;
5813 return false;
5816 bool
5817 js::FindClassPrototype(JSContext *cx, JSObject *scopeobj, JSProtoKey protoKey,
5818 JSObject **protop, Class *clasp)
5820 Value v;
5821 if (!js_FindClassObject(cx, scopeobj, protoKey, &v, clasp))
5822 return false;
5824 if (IsFunctionObject(v)) {
5825 JSObject *ctor = &v.toObject();
5826 if (!ctor->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), &v))
5827 return false;
5830 *protop = v.isObject() ? &v.toObject() : NULL;
5831 return true;
5835 * The first part of this function has been hand-expanded and optimized into
5836 * NewBuiltinClassInstance in jsobjinlines.h.
5838 JSBool
5839 js_GetClassPrototype(JSContext *cx, JSObject *scopeobj, JSProtoKey protoKey,
5840 JSObject **protop, Class *clasp)
5842 VOUCH_DOES_NOT_REQUIRE_STACK();
5843 JS_ASSERT(JSProto_Null <= protoKey);
5844 JS_ASSERT(protoKey < JSProto_LIMIT);
5846 if (protoKey != JSProto_Null) {
5847 if (!scopeobj) {
5848 if (cx->hasfp())
5849 scopeobj = &cx->fp()->scopeChain();
5850 if (!scopeobj) {
5851 scopeobj = cx->globalObject;
5852 if (!scopeobj) {
5853 *protop = NULL;
5854 return true;
5858 scopeobj = scopeobj->getGlobal();
5859 if (scopeobj->getClass()->flags & JSCLASS_IS_GLOBAL) {
5860 const Value &v = scopeobj->getReservedSlot(JSProto_LIMIT + protoKey);
5861 if (v.isObject()) {
5862 *protop = &v.toObject();
5863 return true;
5868 return FindClassPrototype(cx, scopeobj, protoKey, protop, clasp);
5871 JSBool
5872 js_SetClassPrototype(JSContext *cx, JSObject *ctor, JSObject *proto, uintN attrs)
5875 * Use the given attributes for the prototype property of the constructor,
5876 * as user-defined constructors have a DontDelete prototype (which may be
5877 * reset), while native or "system" constructors have DontEnum | ReadOnly |
5878 * DontDelete.
5880 if (!ctor->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom),
5881 ObjectOrNullValue(proto), PropertyStub, PropertyStub, attrs)) {
5882 return JS_FALSE;
5886 * ECMA says that Object.prototype.constructor, or f.prototype.constructor
5887 * for a user-defined function f, is DontEnum.
5889 return proto->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.constructorAtom),
5890 ObjectOrNullValue(ctor), PropertyStub, PropertyStub, 0);
5893 JSBool
5894 js_PrimitiveToObject(JSContext *cx, Value *vp)
5896 Value v = *vp;
5897 JS_ASSERT(v.isPrimitive());
5899 Class *clasp;
5900 if (v.isNumber()) {
5901 clasp = &js_NumberClass;
5902 } else if (v.isString()) {
5903 clasp = &js_StringClass;
5904 } else {
5905 JS_ASSERT(v.isBoolean());
5906 clasp = &js_BooleanClass;
5909 JSObject *obj = NewBuiltinClassInstance(cx, clasp);
5910 if (!obj)
5911 return JS_FALSE;
5913 obj->setPrimitiveThis(v);
5914 vp->setObject(*obj);
5915 return JS_TRUE;
5918 JSBool
5919 js_ValueToObjectOrNull(JSContext *cx, const Value &v, JSObject **objp)
5921 JSObject *obj;
5923 if (v.isObjectOrNull()) {
5924 obj = v.toObjectOrNull();
5925 } else if (v.isUndefined()) {
5926 obj = NULL;
5927 } else {
5928 Value tmp = v;
5929 if (!js_PrimitiveToObject(cx, &tmp))
5930 return JS_FALSE;
5931 obj = &tmp.toObject();
5933 *objp = obj;
5934 return JS_TRUE;
5937 JSObject *
5938 js_ValueToNonNullObject(JSContext *cx, const Value &v)
5940 JSObject *obj;
5942 if (!js_ValueToObjectOrNull(cx, v, &obj))
5943 return NULL;
5944 if (!obj)
5945 js_ReportIsNullOrUndefined(cx, JSDVG_SEARCH_STACK, v, NULL);
5946 return obj;
5949 JSBool
5950 js_TryValueOf(JSContext *cx, JSObject *obj, JSType type, Value *rval)
5952 Value argv[1];
5954 argv[0].setString(ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[type]));
5955 return js_TryMethod(cx, obj, cx->runtime->atomState.valueOfAtom,
5956 1, argv, rval);
5959 JSBool
5960 js_TryMethod(JSContext *cx, JSObject *obj, JSAtom *atom,
5961 uintN argc, Value *argv, Value *rval)
5963 JS_CHECK_RECURSION(cx, return JS_FALSE);
5966 * Report failure only if an appropriate method was found, and calling it
5967 * returned failure. We propagate failure in this case to make exceptions
5968 * behave properly.
5970 JSErrorReporter older = JS_SetErrorReporter(cx, NULL);
5971 jsid id = ATOM_TO_JSID(atom);
5972 Value fval;
5973 JSBool ok = js_GetMethod(cx, obj, id, JSGET_NO_METHOD_BARRIER, &fval);
5974 JS_SetErrorReporter(cx, older);
5975 if (!ok)
5976 return false;
5978 if (fval.isPrimitive())
5979 return JS_TRUE;
5980 return ExternalInvoke(cx, obj, fval, argc, argv, rval);
5983 #if JS_HAS_XDR
5985 JSBool
5986 js_XDRObject(JSXDRState *xdr, JSObject **objp)
5988 JSContext *cx;
5989 JSAtom *atom;
5990 Class *clasp;
5991 uint32 classId, classDef;
5992 JSProtoKey protoKey;
5993 JSObject *proto;
5995 cx = xdr->cx;
5996 atom = NULL;
5997 if (xdr->mode == JSXDR_ENCODE) {
5998 clasp = (*objp)->getClass();
5999 classId = JS_XDRFindClassIdByName(xdr, clasp->name);
6000 classDef = !classId;
6001 if (classDef) {
6002 if (!JS_XDRRegisterClass(xdr, Jsvalify(clasp), &classId))
6003 return JS_FALSE;
6004 protoKey = JSCLASS_CACHED_PROTO_KEY(clasp);
6005 if (protoKey != JSProto_Null) {
6006 classDef |= (protoKey << 1);
6007 } else {
6008 atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0);
6009 if (!atom)
6010 return JS_FALSE;
6013 } else {
6014 clasp = NULL; /* quell GCC overwarning */
6015 classDef = 0;
6019 * XDR a flag word, which could be 0 for a class use, in which case no
6020 * name follows, only the id in xdr's class registry; 1 for a class def,
6021 * in which case the flag word is followed by the class name transferred
6022 * from or to atom; or a value greater than 1, an odd number that when
6023 * divided by two yields the JSProtoKey for class. In the last case, as
6024 * in the 0 classDef case, no name is transferred via atom.
6026 if (!JS_XDRUint32(xdr, &classDef))
6027 return JS_FALSE;
6028 if (classDef == 1 && !js_XDRAtom(xdr, &atom))
6029 return JS_FALSE;
6031 if (!JS_XDRUint32(xdr, &classId))
6032 return JS_FALSE;
6034 if (xdr->mode == JSXDR_DECODE) {
6035 if (classDef) {
6036 /* NB: we know that JSProto_Null is 0 here, for backward compat. */
6037 protoKey = (JSProtoKey) (classDef >> 1);
6038 if (!js_GetClassPrototype(cx, NULL, protoKey, &proto, clasp))
6039 return JS_FALSE;
6040 clasp = proto->getClass();
6041 if (!JS_XDRRegisterClass(xdr, Jsvalify(clasp), &classId))
6042 return JS_FALSE;
6043 } else {
6044 clasp = Valueify(JS_XDRFindClassById(xdr, classId));
6045 if (!clasp) {
6046 char numBuf[12];
6047 JS_snprintf(numBuf, sizeof numBuf, "%ld", (long)classId);
6048 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
6049 JSMSG_CANT_FIND_CLASS, numBuf);
6050 return JS_FALSE;
6055 if (!clasp->xdrObject) {
6056 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
6057 JSMSG_CANT_XDR_CLASS, clasp->name);
6058 return JS_FALSE;
6060 return clasp->xdrObject(xdr, objp);
6063 #endif /* JS_HAS_XDR */
6065 #ifdef JS_DUMP_SCOPE_METERS
6067 #include <stdio.h>
6069 JSBasicStats js_entry_count_bs = JS_INIT_STATIC_BASIC_STATS;
6071 static void
6072 MeterEntryCount(uintN count)
6074 JS_BASIC_STATS_ACCUM(&js_entry_count_bs, count);
6077 void
6078 js_DumpScopeMeters(JSRuntime *rt)
6080 static FILE *logfp;
6081 if (!logfp)
6082 logfp = fopen("/tmp/scope.stats", "a");
6085 double mean, sigma;
6087 mean = JS_MeanAndStdDevBS(&js_entry_count_bs, &sigma);
6089 fprintf(logfp, "scopes %u entries %g mean %g sigma %g max %u",
6090 js_entry_count_bs.num, js_entry_count_bs.sum, mean, sigma,
6091 js_entry_count_bs.max);
6094 JS_DumpHistogram(&js_entry_count_bs, logfp);
6095 JS_BASIC_STATS_INIT(&js_entry_count_bs);
6096 fflush(logfp);
6098 #endif
6100 #ifdef DEBUG
6101 void
6102 js_PrintObjectSlotName(JSTracer *trc, char *buf, size_t bufsize)
6104 JS_ASSERT(trc->debugPrinter == js_PrintObjectSlotName);
6106 JSObject *obj = (JSObject *)trc->debugPrintArg;
6107 uint32 slot = (uint32)trc->debugPrintIndex;
6109 const Shape *shape;
6110 if (obj->isNative()) {
6111 shape = obj->lastProperty();
6112 while (shape->previous() && shape->slot != slot)
6113 shape = shape->previous();
6114 if (shape->slot != slot)
6115 shape = NULL;
6116 } else {
6117 shape = NULL;
6120 if (!shape) {
6121 const char *slotname = NULL;
6122 Class *clasp = obj->getClass();
6123 if (clasp->flags & JSCLASS_IS_GLOBAL) {
6124 #define JS_PROTO(name,code,init) \
6125 if ((code) == slot) { slotname = js_##name##_str; goto found; }
6126 #include "jsproto.tbl"
6127 #undef JS_PROTO
6129 found:
6130 if (slotname)
6131 JS_snprintf(buf, bufsize, "CLASS_OBJECT(%s)", slotname);
6132 else
6133 JS_snprintf(buf, bufsize, "**UNKNOWN SLOT %ld**", (long)slot);
6134 } else {
6135 jsid id = shape->id;
6136 if (JSID_IS_INT(id)) {
6137 JS_snprintf(buf, bufsize, "%ld", (long)JSID_TO_INT(id));
6138 } else if (JSID_IS_ATOM(id)) {
6139 PutEscapedString(buf, bufsize, JSID_TO_STRING(id), 0);
6140 } else {
6141 JS_snprintf(buf, bufsize, "**FINALIZED ATOM KEY**");
6145 #endif
6147 void
6148 js_TraceObject(JSTracer *trc, JSObject *obj)
6150 JS_ASSERT(obj->isNative());
6152 JSContext *cx = trc->context;
6153 if (obj->hasSlotsArray() && !obj->nativeEmpty() && IS_GC_MARKING_TRACER(trc)) {
6155 * Trim overlong dslots allocations from the GC, to avoid thrashing in
6156 * case of delete-happy code that settles down at a given population.
6157 * The !obj->nativeEmpty() guard above is due to the bug described by
6158 * the FIXME comment below.
6160 size_t slots = obj->slotSpan();
6161 if (obj->numSlots() != slots)
6162 obj->shrinkSlots(cx, slots);
6165 #ifdef JS_DUMP_SCOPE_METERS
6166 MeterEntryCount(obj->propertyCount);
6167 #endif
6169 obj->trace(trc);
6171 if (!JS_CLIST_IS_EMPTY(&cx->runtime->watchPointList))
6172 js_TraceWatchPoints(trc, obj);
6174 /* No one runs while the GC is running, so we can use LOCKED_... here. */
6175 Class *clasp = obj->getClass();
6176 if (clasp->mark) {
6177 if (clasp->flags & JSCLASS_MARK_IS_TRACE)
6178 ((JSTraceOp) clasp->mark)(trc, obj);
6179 else if (IS_GC_MARKING_TRACER(trc))
6180 (void) clasp->mark(cx, obj, trc);
6182 if (clasp->flags & JSCLASS_IS_GLOBAL) {
6183 JSCompartment *compartment = obj->getCompartment();
6184 compartment->marked = true;
6188 * NB: In case clasp->mark mutates something (which would be a bug, but we
6189 * want to be defensive), leave this code here -- don't move it up and
6190 * unify it with the |if (!traceScope)| section above.
6192 * FIXME: We minimize nslots against obj->slotSpan because native objects
6193 * such as Date instances may have failed to advance slotSpan to cover all
6194 * reserved slots (this Date issue may be a bug in JSObject::growSlots, but
6195 * the general problem occurs in other built-in class implementations).
6197 uint32 nslots = obj->numSlots();
6198 if (!obj->nativeEmpty() && obj->slotSpan() < nslots)
6199 nslots = obj->slotSpan();
6201 for (uint32 i = 0; i != nslots; ++i) {
6202 const Value &v = obj->getSlot(i);
6203 JS_SET_TRACING_DETAILS(trc, js_PrintObjectSlotName, obj, i);
6204 MarkValueRaw(trc, v);
6208 void
6209 js_ClearNative(JSContext *cx, JSObject *obj)
6212 * Clear obj of all obj's properties. FIXME: we do not clear reserved slots
6213 * lying below JSSLOT_FREE(clasp). JS_ClearScope does that.
6215 if (!obj->nativeEmpty()) {
6216 /* Now that we're done using real properties, clear obj. */
6217 obj->clear(cx);
6219 /* Clear slot values since obj->clear reset our shape to empty. */
6220 uint32 freeslot = JSSLOT_FREE(obj->getClass());
6221 uint32 n = obj->numSlots();
6222 for (uint32 i = freeslot; i < n; ++i)
6223 obj->setSlot(i, UndefinedValue());
6227 bool
6228 js_GetReservedSlot(JSContext *cx, JSObject *obj, uint32 slot, Value *vp)
6230 if (!obj->isNative()) {
6231 vp->setUndefined();
6232 return true;
6235 if (slot < obj->numSlots())
6236 *vp = obj->getSlot(slot);
6237 else
6238 vp->setUndefined();
6239 return true;
6242 bool
6243 js_SetReservedSlot(JSContext *cx, JSObject *obj, uint32 slot, const Value &v)
6245 if (!obj->isNative())
6246 return true;
6248 Class *clasp = obj->getClass();
6250 if (slot >= obj->numSlots()) {
6251 uint32 nslots = JSSLOT_FREE(clasp);
6252 JS_ASSERT(slot < nslots);
6253 if (!obj->allocSlots(cx, nslots))
6254 return false;
6257 obj->setSlot(slot, v);
6258 GC_POKE(cx, JS_NULL);
6259 return true;
6262 JSObject *
6263 JSObject::getGlobal() const
6265 JSObject *obj = const_cast<JSObject *>(this);
6266 while (JSObject *parent = obj->getParent())
6267 obj = parent;
6268 return obj;
6271 JSBool
6272 js_ReportGetterOnlyAssignment(JSContext *cx)
6274 return JS_ReportErrorFlagsAndNumber(cx,
6275 JSREPORT_WARNING | JSREPORT_STRICT |
6276 JSREPORT_STRICT_MODE_ERROR,
6277 js_GetErrorMessage, NULL,
6278 JSMSG_GETTER_ONLY);
6281 JS_FRIEND_API(JSBool)
6282 js_GetterOnlyPropertyStub(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
6284 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_GETTER_ONLY);
6285 return JS_FALSE;
6288 #ifdef DEBUG
6291 * Routines to print out values during debugging. These are FRIEND_API to help
6292 * the debugger find them and to support temporarily hacking js_Dump* calls
6293 * into other code.
6296 void
6297 dumpChars(const jschar *s, size_t n)
6299 size_t i;
6301 if (n == (size_t) -1) {
6302 while (s[++n]) ;
6305 fputc('"', stderr);
6306 for (i = 0; i < n; i++) {
6307 if (s[i] == '\n')
6308 fprintf(stderr, "\\n");
6309 else if (s[i] == '\t')
6310 fprintf(stderr, "\\t");
6311 else if (s[i] >= 32 && s[i] < 127)
6312 fputc(s[i], stderr);
6313 else if (s[i] <= 255)
6314 fprintf(stderr, "\\x%02x", (unsigned int) s[i]);
6315 else
6316 fprintf(stderr, "\\u%04x", (unsigned int) s[i]);
6318 fputc('"', stderr);
6321 JS_FRIEND_API(void)
6322 js_DumpChars(const jschar *s, size_t n)
6324 fprintf(stderr, "jschar * (%p) = ", (void *) s);
6325 dumpChars(s, n);
6326 fputc('\n', stderr);
6329 void
6330 dumpString(JSString *str)
6332 dumpChars(str->chars(), str->length());
6335 JS_FRIEND_API(void)
6336 js_DumpString(JSString *str)
6338 fprintf(stderr, "JSString* (%p) = jschar * (%p) = ",
6339 (void *) str, (void *) str->chars());
6340 dumpString(str);
6341 fputc('\n', stderr);
6344 JS_FRIEND_API(void)
6345 js_DumpAtom(JSAtom *atom)
6347 fprintf(stderr, "JSAtom* (%p) = ", (void *) atom);
6348 js_DumpString(ATOM_TO_STRING(atom));
6351 void
6352 dumpValue(const Value &v)
6354 if (v.isNull())
6355 fprintf(stderr, "null");
6356 else if (v.isUndefined())
6357 fprintf(stderr, "undefined");
6358 else if (v.isInt32())
6359 fprintf(stderr, "%d", v.toInt32());
6360 else if (v.isDouble())
6361 fprintf(stderr, "%g", v.toDouble());
6362 else if (v.isString())
6363 dumpString(v.toString());
6364 else if (v.isObject() && v.toObject().isFunction()) {
6365 JSObject *funobj = &v.toObject();
6366 JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
6367 if (fun->atom) {
6368 fputs("<function ", stderr);
6369 FileEscapedString(stderr, ATOM_TO_STRING(fun->atom), 0);
6370 } else {
6371 fputs("<unnamed function", stderr);
6373 fprintf(stderr, " at %p (JSFunction at %p)>", (void *) funobj, (void *) fun);
6374 } else if (v.isObject()) {
6375 JSObject *obj = &v.toObject();
6376 Class *clasp = obj->getClass();
6377 fprintf(stderr, "<%s%s at %p>",
6378 clasp->name,
6379 (clasp == &js_ObjectClass) ? "" : " object",
6380 (void *) obj);
6381 } else if (v.isBoolean()) {
6382 if (v.toBoolean())
6383 fprintf(stderr, "true");
6384 else
6385 fprintf(stderr, "false");
6386 } else if (v.isMagic()) {
6387 fprintf(stderr, "<invalid");
6388 #ifdef DEBUG
6389 switch (v.whyMagic()) {
6390 case JS_ARRAY_HOLE: fprintf(stderr, " array hole"); break;
6391 case JS_ARGS_HOLE: fprintf(stderr, " args hole"); break;
6392 case JS_NATIVE_ENUMERATE: fprintf(stderr, " native enumeration"); break;
6393 case JS_NO_ITER_VALUE: fprintf(stderr, " no iter value"); break;
6394 case JS_GENERATOR_CLOSING: fprintf(stderr, " generator closing"); break;
6395 default: fprintf(stderr, " ?!"); break;
6397 #endif
6398 fprintf(stderr, ">");
6399 } else {
6400 fprintf(stderr, "unexpected value");
6404 JS_FRIEND_API(void)
6405 js_DumpValue(const Value &val)
6407 dumpValue(val);
6408 fputc('\n', stderr);
6411 JS_FRIEND_API(void)
6412 js_DumpId(jsid id)
6414 fprintf(stderr, "jsid %p = ", (void *) JSID_BITS(id));
6415 dumpValue(IdToValue(id));
6416 fputc('\n', stderr);
6419 static void
6420 DumpShape(const Shape &shape)
6422 jsid id = shape.id;
6423 uint8 attrs = shape.attributes();
6425 fprintf(stderr, " ");
6426 if (attrs & JSPROP_ENUMERATE) fprintf(stderr, "enumerate ");
6427 if (attrs & JSPROP_READONLY) fprintf(stderr, "readonly ");
6428 if (attrs & JSPROP_PERMANENT) fprintf(stderr, "permanent ");
6429 if (attrs & JSPROP_GETTER) fprintf(stderr, "getter ");
6430 if (attrs & JSPROP_SETTER) fprintf(stderr, "setter ");
6431 if (attrs & JSPROP_SHARED) fprintf(stderr, "shared ");
6432 if (shape.isAlias()) fprintf(stderr, "alias ");
6433 if (JSID_IS_ATOM(id))
6434 dumpString(JSID_TO_STRING(id));
6435 else if (JSID_IS_INT(id))
6436 fprintf(stderr, "%d", (int) JSID_TO_INT(id));
6437 else
6438 fprintf(stderr, "unknown jsid %p", (void *) JSID_BITS(id));
6439 fprintf(stderr, ": slot %d", shape.slot);
6440 fprintf(stderr, "\n");
6443 JS_FRIEND_API(void)
6444 js_DumpObject(JSObject *obj)
6446 fprintf(stderr, "object %p\n", (void *) obj);
6447 Class *clasp = obj->getClass();
6448 fprintf(stderr, "class %p %s\n", (void *)clasp, clasp->name);
6450 fprintf(stderr, "flags:");
6451 uint32 flags = obj->flags;
6452 if (flags & JSObject::DELEGATE) fprintf(stderr, " delegate");
6453 if (flags & JSObject::SYSTEM) fprintf(stderr, " system");
6454 if (flags & JSObject::NOT_EXTENSIBLE) fprintf(stderr, " not extensible");
6455 if (flags & JSObject::BRANDED) fprintf(stderr, " branded");
6456 if (flags & JSObject::GENERIC) fprintf(stderr, " generic");
6457 if (flags & JSObject::METHOD_BARRIER) fprintf(stderr, " method_barrier");
6458 if (flags & JSObject::INDEXED) fprintf(stderr, " indexed");
6459 if (flags & JSObject::OWN_SHAPE) fprintf(stderr, " own_shape");
6460 if (flags & JSObject::HAS_EQUALITY) fprintf(stderr, " has_equality");
6462 bool anyFlags = flags != 0;
6463 if (obj->isNative()) {
6464 if (obj->inDictionaryMode()) {
6465 fprintf(stderr, " inDictionaryMode");
6466 anyFlags = true;
6468 if (obj->hasPropertyTable()) {
6469 fprintf(stderr, " hasPropertyTable");
6470 anyFlags = true;
6473 if (!anyFlags)
6474 fprintf(stderr, " none");
6475 fprintf(stderr, "\n");
6477 if (obj->isDenseArray()) {
6478 unsigned slots = JS_MIN(obj->getArrayLength(), obj->getDenseArrayCapacity());
6479 fprintf(stderr, "elements\n");
6480 for (unsigned i = 0; i < slots; i++) {
6481 fprintf(stderr, " %3d: ", i);
6482 dumpValue(obj->getDenseArrayElement(i));
6483 fprintf(stderr, "\n");
6484 fflush(stderr);
6486 return;
6489 if (obj->isNative()) {
6490 fprintf(stderr, "properties:\n");
6491 for (Shape::Range r = obj->lastProperty()->all(); !r.empty(); r.popFront())
6492 DumpShape(r.front());
6493 } else {
6494 if (!obj->isNative())
6495 fprintf(stderr, "not native\n");
6498 fprintf(stderr, "proto ");
6499 dumpValue(ObjectOrNullValue(obj->getProto()));
6500 fputc('\n', stderr);
6502 fprintf(stderr, "parent ");
6503 dumpValue(ObjectOrNullValue(obj->getParent()));
6504 fputc('\n', stderr);
6506 if (clasp->flags & JSCLASS_HAS_PRIVATE)
6507 fprintf(stderr, "private %p\n", obj->getPrivate());
6509 fprintf(stderr, "slots:\n");
6510 unsigned reservedEnd = JSCLASS_RESERVED_SLOTS(clasp);
6511 unsigned slots = obj->slotSpan();
6512 for (unsigned i = 0; i < slots; i++) {
6513 fprintf(stderr, " %3d ", i);
6514 if (i < reservedEnd)
6515 fprintf(stderr, "(reserved) ");
6516 fprintf(stderr, "= ");
6517 dumpValue(obj->getSlot(i));
6518 fputc('\n', stderr);
6520 fputc('\n', stderr);
6523 static void
6524 MaybeDumpObject(const char *name, JSObject *obj)
6526 if (obj) {
6527 fprintf(stderr, " %s: ", name);
6528 dumpValue(ObjectValue(*obj));
6529 fputc('\n', stderr);
6533 static void
6534 MaybeDumpValue(const char *name, const Value &v)
6536 if (!v.isNull()) {
6537 fprintf(stderr, " %s: ", name);
6538 dumpValue(v);
6539 fputc('\n', stderr);
6543 JS_FRIEND_API(void)
6544 js_DumpStackFrame(JSContext *cx, JSStackFrame *start)
6546 /* This should only called during live debugging. */
6547 VOUCH_DOES_NOT_REQUIRE_STACK();
6549 if (!start)
6550 start = cx->maybefp();
6551 FrameRegsIter i(cx);
6552 while (!i.done() && i.fp() != start)
6553 ++i;
6555 if (i.done()) {
6556 fprintf(stderr, "fp = %p not found in cx = %p\n", (void *)start, (void *)cx);
6557 return;
6560 for (; !i.done(); ++i) {
6561 JSStackFrame *const fp = i.fp();
6563 fprintf(stderr, "JSStackFrame at %p\n", (void *) fp);
6564 if (fp->isFunctionFrame()) {
6565 fprintf(stderr, "callee fun: ");
6566 dumpValue(ObjectValue(fp->callee()));
6567 } else {
6568 fprintf(stderr, "global frame, no callee");
6570 fputc('\n', stderr);
6572 if (fp->isScriptFrame()) {
6573 fprintf(stderr, "file %s line %u\n",
6574 fp->script()->filename, (unsigned) fp->script()->lineno);
6577 if (jsbytecode *pc = i.pc()) {
6578 if (!fp->isScriptFrame()) {
6579 fprintf(stderr, "*** pc && !script, skipping frame\n\n");
6580 continue;
6582 if (fp->hasImacropc()) {
6583 fprintf(stderr, " pc in imacro at %p\n called from ", pc);
6584 pc = fp->imacropc();
6585 } else {
6586 fprintf(stderr, " ");
6588 fprintf(stderr, "pc = %p\n", pc);
6589 fprintf(stderr, " current op: %s\n", js_CodeName[*pc]);
6591 Value *sp = i.sp();
6592 fprintf(stderr, " slots: %p\n", (void *) fp->slots());
6593 fprintf(stderr, " sp: %p = slots + %u\n", (void *) sp, (unsigned) (sp - fp->slots()));
6594 if (sp - fp->slots() < 10000) { // sanity
6595 for (Value *p = fp->slots(); p < sp; p++) {
6596 fprintf(stderr, " %p: ", (void *) p);
6597 dumpValue(*p);
6598 fputc('\n', stderr);
6601 if (fp->isFunctionFrame() && !fp->isEvalFrame()) {
6602 fprintf(stderr, " actuals: %p (%u) ", (void *) fp->actualArgs(), (unsigned) fp->numActualArgs());
6603 fprintf(stderr, " formals: %p (%u)\n", (void *) fp->formalArgs(), (unsigned) fp->numFormalArgs());
6605 MaybeDumpObject("callobj", fp->maybeCallObj());
6606 MaybeDumpObject("argsobj", fp->maybeArgsObj());
6607 if (!fp->isDummyFrame()) {
6608 MaybeDumpValue("this", fp->thisValue());
6609 fprintf(stderr, " rval: ");
6610 dumpValue(fp->returnValue());
6611 } else {
6612 fprintf(stderr, "dummy frame");
6614 fputc('\n', stderr);
6616 fprintf(stderr, " flags:");
6617 if (fp->isConstructing())
6618 fprintf(stderr, " constructing");
6619 if (fp->hasOverriddenArgs())
6620 fprintf(stderr, " overridden_args");
6621 if (fp->isAssigning())
6622 fprintf(stderr, " assigning");
6623 if (fp->isDebuggerFrame())
6624 fprintf(stderr, " debugger");
6625 if (fp->isEvalFrame())
6626 fprintf(stderr, " eval");
6627 if (fp->isYielding())
6628 fprintf(stderr, " yielding");
6629 if (fp->isGeneratorFrame())
6630 fprintf(stderr, " generator");
6631 fputc('\n', stderr);
6633 fprintf(stderr, " scopeChain: (JSObject *) %p\n", (void *) &fp->scopeChain());
6635 fputc('\n', stderr);
6639 #ifdef DEBUG
6640 bool
6641 IsSaneThisObject(JSObject &obj)
6643 Class *clasp = obj.getClass();
6644 return clasp != &js_CallClass &&
6645 clasp != &js_BlockClass &&
6646 clasp != &js_DeclEnvClass &&
6647 clasp != &js_WithClass;
6649 #endif
6651 #endif /* DEBUG */