Backed out changeset b88172246b66 due to Win32 debug failures.
[mozilla-central.git] / js / src / jsobj.cpp
blob15eb3d519236936c57eec764a816c8fca8782eb4
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"
80 #include "jswrapper.h"
82 #include "jsinterpinlines.h"
83 #include "jsscopeinlines.h"
84 #include "jsscriptinlines.h"
85 #include "jsobjinlines.h"
87 #if JS_HAS_GENERATORS
88 #include "jsiter.h"
89 #endif
91 #if JS_HAS_XML_SUPPORT
92 #include "jsxml.h"
93 #endif
95 #if JS_HAS_XDR
96 #include "jsxdrapi.h"
97 #endif
99 #include "jsprobes.h"
100 #include "jsatominlines.h"
101 #include "jsobjinlines.h"
102 #include "jsscriptinlines.h"
104 #include "jsautooplen.h"
106 using namespace js;
107 using namespace js::gc;
109 JS_FRIEND_DATA(const JSObjectMap) JSObjectMap::sharedNonNative(JSObjectMap::SHAPELESS);
111 Class js_ObjectClass = {
112 js_Object_str,
113 JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
114 PropertyStub, /* addProperty */
115 PropertyStub, /* delProperty */
116 PropertyStub, /* getProperty */
117 PropertyStub, /* setProperty */
118 EnumerateStub,
119 ResolveStub,
120 ConvertStub
123 JS_FRIEND_API(JSObject *)
124 js_ObjectToOuterObject(JSContext *cx, JSObject *obj)
126 OBJ_TO_OUTER_OBJECT(cx, obj);
127 return obj;
130 #if JS_HAS_OBJ_PROTO_PROP
132 static JSBool
133 obj_getProto(JSContext *cx, JSObject *obj, jsid id, Value *vp);
135 static JSBool
136 obj_setProto(JSContext *cx, JSObject *obj, jsid id, Value *vp);
138 static JSPropertySpec object_props[] = {
139 {js_proto_str, 0, JSPROP_PERMANENT|JSPROP_SHARED, Jsvalify(obj_getProto), Jsvalify(obj_setProto)},
140 {0,0,0,0,0}
143 static JSBool
144 obj_getProto(JSContext *cx, JSObject *obj, jsid id, Value *vp)
146 /* Let CheckAccess get the slot's value, based on the access mode. */
147 uintN attrs;
148 id = ATOM_TO_JSID(cx->runtime->atomState.protoAtom);
149 return CheckAccess(cx, obj, id, JSACC_PROTO, vp, &attrs);
152 static JSBool
153 obj_setProto(JSContext *cx, JSObject *obj, jsid id, Value *vp)
155 /* ECMAScript 5 8.6.2 forbids changing [[Prototype]] if not [[Extensible]]. */
156 if (!obj->isExtensible()) {
157 obj->reportNotExtensible(cx);
158 return false;
161 if (!vp->isObjectOrNull())
162 return JS_TRUE;
164 JSObject *pobj = vp->toObjectOrNull();
165 if (pobj) {
167 * Innerize pobj here to avoid sticking unwanted properties on the
168 * outer object. This ensures that any with statements only grant
169 * access to the inner object.
171 OBJ_TO_INNER_OBJECT(cx, pobj);
172 if (!pobj)
173 return JS_FALSE;
176 uintN attrs;
177 id = ATOM_TO_JSID(cx->runtime->atomState.protoAtom);
178 if (!CheckAccess(cx, obj, id, JSAccessMode(JSACC_PROTO|JSACC_WRITE), vp, &attrs))
179 return JS_FALSE;
181 return SetProto(cx, obj, pobj, JS_TRUE);
184 #else /* !JS_HAS_OBJ_PROTO_PROP */
186 #define object_props NULL
188 #endif /* !JS_HAS_OBJ_PROTO_PROP */
190 static JSHashNumber
191 js_hash_object(const void *key)
193 return JSHashNumber(uintptr_t(key) >> JS_GCTHING_ALIGN);
196 static JSHashEntry *
197 MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap)
199 JSSharpObjectMap *map;
200 JSHashTable *table;
201 JSHashNumber hash;
202 JSHashEntry **hep, *he;
203 jsatomid sharpid;
204 JSIdArray *ida;
205 JSBool ok;
206 jsint i, length;
207 jsid id;
208 JSObject *obj2;
209 JSProperty *prop;
211 JS_CHECK_RECURSION(cx, return NULL);
213 map = &cx->sharpObjectMap;
214 JS_ASSERT(map->depth >= 1);
215 table = map->table;
216 hash = js_hash_object(obj);
217 hep = JS_HashTableRawLookup(table, hash, obj);
218 he = *hep;
219 if (!he) {
220 sharpid = 0;
221 he = JS_HashTableRawAdd(table, hep, hash, obj, (void *) sharpid);
222 if (!he) {
223 JS_ReportOutOfMemory(cx);
224 return NULL;
227 ida = JS_Enumerate(cx, obj);
228 if (!ida)
229 return NULL;
231 ok = JS_TRUE;
232 for (i = 0, length = ida->length; i < length; i++) {
233 id = ida->vector[i];
234 ok = obj->lookupProperty(cx, id, &obj2, &prop);
235 if (!ok)
236 break;
237 if (!prop)
238 continue;
239 bool hasGetter, hasSetter;
240 AutoValueRooter v(cx);
241 AutoValueRooter setter(cx);
242 if (obj2->isNative()) {
243 const Shape *shape = (Shape *) prop;
244 hasGetter = shape->hasGetterValue();
245 hasSetter = shape->hasSetterValue();
246 if (hasGetter)
247 v.set(shape->getterValue());
248 if (hasSetter)
249 setter.set(shape->setterValue());
250 } else {
251 hasGetter = hasSetter = false;
253 if (hasSetter) {
254 /* Mark the getter, then set val to setter. */
255 if (hasGetter && v.value().isObject()) {
256 ok = !!MarkSharpObjects(cx, &v.value().toObject(), NULL);
257 if (!ok)
258 break;
260 v.set(setter.value());
261 } else if (!hasGetter) {
262 ok = obj->getProperty(cx, id, v.addr());
263 if (!ok)
264 break;
266 if (v.value().isObject() &&
267 !MarkSharpObjects(cx, &v.value().toObject(), NULL)) {
268 ok = JS_FALSE;
269 break;
272 if (!ok || !idap)
273 JS_DestroyIdArray(cx, ida);
274 if (!ok)
275 return NULL;
276 } else {
277 sharpid = uintptr_t(he->value);
278 if (sharpid == 0) {
279 sharpid = ++map->sharpgen << SHARP_ID_SHIFT;
280 he->value = (void *) sharpid;
282 ida = NULL;
284 if (idap)
285 *idap = ida;
286 return he;
289 JSHashEntry *
290 js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap,
291 jschar **sp)
293 JSSharpObjectMap *map;
294 JSHashTable *table;
295 JSIdArray *ida;
296 JSHashNumber hash;
297 JSHashEntry *he, **hep;
298 jsatomid sharpid;
299 char buf[20];
300 size_t len;
302 if (!JS_CHECK_OPERATION_LIMIT(cx))
303 return NULL;
305 /* Set to null in case we return an early error. */
306 *sp = NULL;
307 map = &cx->sharpObjectMap;
308 table = map->table;
309 if (!table) {
310 table = JS_NewHashTable(8, js_hash_object, JS_CompareValues,
311 JS_CompareValues, NULL, NULL);
312 if (!table) {
313 JS_ReportOutOfMemory(cx);
314 return NULL;
316 map->table = table;
317 JS_KEEP_ATOMS(cx->runtime);
320 /* From this point the control must flow either through out: or bad:. */
321 ida = NULL;
322 if (map->depth == 0) {
324 * Although MarkSharpObjects tries to avoid invoking getters,
325 * it ends up doing so anyway under some circumstances; for
326 * example, if a wrapped object has getters, the wrapper will
327 * prevent MarkSharpObjects from recognizing them as such.
328 * This could lead to js_LeaveSharpObject being called while
329 * MarkSharpObjects is still working.
331 * Increment map->depth while we call MarkSharpObjects, to
332 * ensure that such a call doesn't free the hash table we're
333 * still using.
335 ++map->depth;
336 he = MarkSharpObjects(cx, obj, &ida);
337 --map->depth;
338 if (!he)
339 goto bad;
340 JS_ASSERT((uintptr_t(he->value) & SHARP_BIT) == 0);
341 if (!idap) {
342 JS_DestroyIdArray(cx, ida);
343 ida = NULL;
345 } else {
346 hash = js_hash_object(obj);
347 hep = JS_HashTableRawLookup(table, hash, obj);
348 he = *hep;
351 * It's possible that the value of a property has changed from the
352 * first time the object's properties are traversed (when the property
353 * ids are entered into the hash table) to the second (when they are
354 * converted to strings), i.e., the JSObject::getProperty() call is not
355 * idempotent.
357 if (!he) {
358 he = JS_HashTableRawAdd(table, hep, hash, obj, NULL);
359 if (!he) {
360 JS_ReportOutOfMemory(cx);
361 goto bad;
363 sharpid = 0;
364 goto out;
368 sharpid = uintptr_t(he->value);
369 if (sharpid != 0) {
370 len = JS_snprintf(buf, sizeof buf, "#%u%c",
371 sharpid >> SHARP_ID_SHIFT,
372 (sharpid & SHARP_BIT) ? '#' : '=');
373 *sp = js_InflateString(cx, buf, &len);
374 if (!*sp) {
375 if (ida)
376 JS_DestroyIdArray(cx, ida);
377 goto bad;
381 out:
382 JS_ASSERT(he);
383 if ((sharpid & SHARP_BIT) == 0) {
384 if (idap && !ida) {
385 ida = JS_Enumerate(cx, obj);
386 if (!ida) {
387 if (*sp) {
388 cx->free(*sp);
389 *sp = NULL;
391 goto bad;
394 map->depth++;
397 if (idap)
398 *idap = ida;
399 return he;
401 bad:
402 /* Clean up the sharpObjectMap table on outermost error. */
403 if (map->depth == 0) {
404 JS_UNKEEP_ATOMS(cx->runtime);
405 map->sharpgen = 0;
406 JS_HashTableDestroy(map->table);
407 map->table = NULL;
409 return NULL;
412 void
413 js_LeaveSharpObject(JSContext *cx, JSIdArray **idap)
415 JSSharpObjectMap *map;
416 JSIdArray *ida;
418 map = &cx->sharpObjectMap;
419 JS_ASSERT(map->depth > 0);
420 if (--map->depth == 0) {
421 JS_UNKEEP_ATOMS(cx->runtime);
422 map->sharpgen = 0;
423 JS_HashTableDestroy(map->table);
424 map->table = NULL;
426 if (idap) {
427 ida = *idap;
428 if (ida) {
429 JS_DestroyIdArray(cx, ida);
430 *idap = NULL;
435 static intN
436 gc_sharp_table_entry_marker(JSHashEntry *he, intN i, void *arg)
438 MarkObject((JSTracer *)arg, *(JSObject *)he->key, "sharp table entry");
439 return JS_DHASH_NEXT;
442 void
443 js_TraceSharpMap(JSTracer *trc, JSSharpObjectMap *map)
445 JS_ASSERT(map->depth > 0);
446 JS_ASSERT(map->table);
449 * During recursive calls to MarkSharpObjects a non-native object or
450 * object with a custom getProperty method can potentially return an
451 * unrooted value or even cut from the object graph an argument of one of
452 * MarkSharpObjects recursive invocations. So we must protect map->table
453 * entries against GC.
455 * We can not simply use JSTempValueRooter to mark the obj argument of
456 * MarkSharpObjects during recursion as we have to protect *all* entries
457 * in JSSharpObjectMap including those that contains otherwise unreachable
458 * objects just allocated through custom getProperty. Otherwise newer
459 * allocations can re-use the address of an object stored in the hashtable
460 * confusing js_EnterSharpObject. So to address the problem we simply
461 * mark all objects from map->table.
463 * An alternative "proper" solution is to use JSTempValueRooter in
464 * MarkSharpObjects with code to remove during finalization entries
465 * with otherwise unreachable objects. But this is way too complex
466 * to justify spending efforts.
468 JS_HashTableEnumerateEntries(map->table, gc_sharp_table_entry_marker, trc);
471 #if JS_HAS_TOSOURCE
472 static JSBool
473 obj_toSource(JSContext *cx, uintN argc, Value *vp)
475 JSBool ok, outermost;
476 JSObject *obj;
477 JSHashEntry *he;
478 JSIdArray *ida;
479 jschar *chars, *ochars, *vsharp;
480 const jschar *idstrchars, *vchars;
481 size_t nchars, idstrlength, gsoplength, vlength, vsharplength, curlen;
482 const char *comma;
483 JSObject *obj2;
484 JSProperty *prop;
485 Value *val;
486 JSString *gsop[2];
487 JSString *valstr, *str;
488 JSLinearString *idstr;
490 JS_CHECK_RECURSION(cx, return JS_FALSE);
492 Value localroot[4];
493 PodArrayZero(localroot);
494 AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(localroot), localroot);
496 /* If outermost, we need parentheses to be an expression, not a block. */
497 outermost = (cx->sharpObjectMap.depth == 0);
498 obj = ComputeThisFromVp(cx, vp);
499 if (!obj || !(he = js_EnterSharpObject(cx, obj, &ida, &chars))) {
500 ok = JS_FALSE;
501 goto out;
503 if (IS_SHARP(he)) {
505 * We didn't enter -- obj is already "sharp", meaning we've visited it
506 * already in our depth first search, and therefore chars contains a
507 * string of the form "#n#".
509 JS_ASSERT(!ida);
510 #if JS_HAS_SHARP_VARS
511 nchars = js_strlen(chars);
512 #else
513 chars[0] = '{';
514 chars[1] = '}';
515 chars[2] = 0;
516 nchars = 2;
517 #endif
518 goto make_string;
520 JS_ASSERT(ida);
521 ok = JS_TRUE;
523 if (!chars) {
524 /* If outermost, allocate 4 + 1 for "({})" and the terminator. */
525 chars = (jschar *) cx->runtime->malloc(((outermost ? 4 : 2) + 1) * sizeof(jschar));
526 nchars = 0;
527 if (!chars)
528 goto error;
529 if (outermost)
530 chars[nchars++] = '(';
531 } else {
532 /* js_EnterSharpObject returned a string of the form "#n=" in chars. */
533 MAKE_SHARP(he);
534 nchars = js_strlen(chars);
535 chars = (jschar *)
536 js_realloc((ochars = chars), (nchars + 2 + 1) * sizeof(jschar));
537 if (!chars) {
538 js_free(ochars);
539 goto error;
541 if (outermost) {
543 * No need for parentheses around the whole shebang, because #n=
544 * unambiguously begins an object initializer, and never a block
545 * statement.
547 outermost = JS_FALSE;
551 chars[nchars++] = '{';
553 comma = NULL;
556 * We have four local roots for cooked and raw value GC safety. Hoist the
557 * "localroot + 2" out of the loop using the val local, which refers to
558 * the raw (unconverted, "uncooked") values.
560 val = localroot + 2;
562 for (jsint i = 0, length = ida->length; i < length; i++) {
563 /* Get strings for id and value and GC-root them via vp. */
564 jsid id = ida->vector[i];
566 ok = obj->lookupProperty(cx, id, &obj2, &prop);
567 if (!ok)
568 goto error;
571 * Convert id to a value and then to a string. Decide early whether we
572 * prefer get/set or old getter/setter syntax.
574 JSString *s = js_ValueToString(cx, IdToValue(id));
575 if (!s || !(idstr = s->ensureLinear(cx))) {
576 ok = JS_FALSE;
577 goto error;
579 vp->setString(idstr); /* local root */
581 jsint valcnt = 0;
582 if (prop) {
583 bool doGet = true;
584 if (obj2->isNative()) {
585 const Shape *shape = (Shape *) prop;
586 unsigned attrs = shape->attributes();
587 if (attrs & JSPROP_GETTER) {
588 doGet = false;
589 val[valcnt] = shape->getterValue();
590 gsop[valcnt] = ATOM_TO_STRING(cx->runtime->atomState.getAtom);
591 valcnt++;
593 if (attrs & JSPROP_SETTER) {
594 doGet = false;
595 val[valcnt] = shape->setterValue();
596 gsop[valcnt] = ATOM_TO_STRING(cx->runtime->atomState.setAtom);
597 valcnt++;
600 if (doGet) {
601 valcnt = 1;
602 gsop[0] = NULL;
603 ok = obj->getProperty(cx, id, &val[0]);
604 if (!ok)
605 goto error;
610 * If id is a string that's not an identifier, or if it's a negative
611 * integer, then it must be quoted.
613 bool idIsLexicalIdentifier = js_IsIdentifier(idstr);
614 if (JSID_IS_ATOM(id)
615 ? !idIsLexicalIdentifier
616 : (!JSID_IS_INT(id) || JSID_TO_INT(id) < 0)) {
617 s = js_QuoteString(cx, idstr, jschar('\''));
618 if (!s || !(idstr = s->ensureLinear(cx))) {
619 ok = JS_FALSE;
620 goto error;
622 vp->setString(idstr); /* local root */
624 idstrlength = idstr->length();
625 idstrchars = idstr->getChars(cx);
626 if (!idstrchars) {
627 ok = JS_FALSE;
628 goto error;
631 for (jsint j = 0; j < valcnt; j++) {
633 * Censor an accessor descriptor getter or setter part if it's
634 * undefined.
636 if (gsop[j] && val[j].isUndefined())
637 continue;
639 /* Convert val[j] to its canonical source form. */
640 valstr = js_ValueToSource(cx, val[j]);
641 if (!valstr) {
642 ok = JS_FALSE;
643 goto error;
645 localroot[j].setString(valstr); /* local root */
646 vchars = valstr->getChars(cx);
647 if (!vchars) {
648 ok = JS_FALSE;
649 goto error;
651 vlength = valstr->length();
654 * If val[j] is a non-sharp object, and we're not serializing an
655 * accessor (ECMA syntax can't accommodate sharpened accessors),
656 * consider sharpening it.
658 vsharp = NULL;
659 vsharplength = 0;
660 #if JS_HAS_SHARP_VARS
661 if (!gsop[j] && val[j].isObject() && vchars[0] != '#') {
662 he = js_EnterSharpObject(cx, &val[j].toObject(), NULL, &vsharp);
663 if (!he) {
664 ok = JS_FALSE;
665 goto error;
667 if (IS_SHARP(he)) {
668 vchars = vsharp;
669 vlength = js_strlen(vchars);
670 } else {
671 if (vsharp) {
672 vsharplength = js_strlen(vsharp);
673 MAKE_SHARP(he);
675 js_LeaveSharpObject(cx, NULL);
678 #endif
681 * Remove '(function ' from the beginning of valstr and ')' from the
682 * end so that we can put "get" in front of the function definition.
684 if (gsop[j] && IsFunctionObject(val[j])) {
685 const jschar *start = vchars;
686 const jschar *end = vchars + vlength;
688 uint8 parenChomp = 0;
689 if (vchars[0] == '(') {
690 vchars++;
691 parenChomp = 1;
694 /* Try to jump "function" keyword. */
695 if (vchars)
696 vchars = js_strchr_limit(vchars, ' ', end);
699 * Jump over the function's name: it can't be encoded as part
700 * of an ECMA getter or setter.
702 if (vchars)
703 vchars = js_strchr_limit(vchars, '(', end);
705 if (vchars) {
706 if (*vchars == ' ')
707 vchars++;
708 vlength = end - vchars - parenChomp;
709 } else {
710 gsop[j] = NULL;
711 vchars = start;
715 #define SAFE_ADD(n) \
716 JS_BEGIN_MACRO \
717 size_t n_ = (n); \
718 curlen += n_; \
719 if (curlen < n_) \
720 goto overflow; \
721 JS_END_MACRO
723 curlen = nchars;
724 if (comma)
725 SAFE_ADD(2);
726 SAFE_ADD(idstrlength + 1);
727 if (gsop[j])
728 SAFE_ADD(gsop[j]->length() + 1);
729 SAFE_ADD(vsharplength);
730 SAFE_ADD(vlength);
731 /* Account for the trailing null. */
732 SAFE_ADD((outermost ? 2 : 1) + 1);
733 #undef SAFE_ADD
735 if (curlen > size_t(-1) / sizeof(jschar))
736 goto overflow;
738 /* Allocate 1 + 1 at end for closing brace and terminating 0. */
739 chars = (jschar *) js_realloc((ochars = chars), curlen * sizeof(jschar));
740 if (!chars) {
741 chars = ochars;
742 goto overflow;
745 if (comma) {
746 chars[nchars++] = comma[0];
747 chars[nchars++] = comma[1];
749 comma = ", ";
751 if (gsop[j]) {
752 gsoplength = gsop[j]->length();
753 const jschar *gsopchars = gsop[j]->getChars(cx);
754 if (!gsopchars)
755 goto overflow;
756 js_strncpy(&chars[nchars], gsopchars, gsoplength);
757 nchars += gsoplength;
758 chars[nchars++] = ' ';
760 js_strncpy(&chars[nchars], idstrchars, idstrlength);
761 nchars += idstrlength;
762 /* Extraneous space after id here will be extracted later */
763 chars[nchars++] = gsop[j] ? ' ' : ':';
765 if (vsharplength) {
766 js_strncpy(&chars[nchars], vsharp, vsharplength);
767 nchars += vsharplength;
769 js_strncpy(&chars[nchars], vchars, vlength);
770 nchars += vlength;
772 if (vsharp)
773 cx->free(vsharp);
777 chars[nchars++] = '}';
778 if (outermost)
779 chars[nchars++] = ')';
780 chars[nchars] = 0;
782 error:
783 js_LeaveSharpObject(cx, &ida);
785 if (!ok) {
786 if (chars)
787 js_free(chars);
788 goto out;
791 if (!chars) {
792 JS_ReportOutOfMemory(cx);
793 ok = JS_FALSE;
794 goto out;
796 make_string:
797 str = js_NewString(cx, chars, nchars);
798 if (!str) {
799 js_free(chars);
800 ok = JS_FALSE;
801 goto out;
803 vp->setString(str);
804 ok = JS_TRUE;
805 out:
806 return ok;
808 overflow:
809 cx->free(vsharp);
810 js_free(chars);
811 chars = NULL;
812 goto error;
814 #endif /* JS_HAS_TOSOURCE */
816 namespace js {
818 JSString *
819 obj_toStringHelper(JSContext *cx, JSObject *obj)
821 if (obj->isProxy())
822 return JSProxy::obj_toString(cx, obj);
824 const char *clazz = obj->getClass()->name;
825 size_t nchars = 9 + strlen(clazz); /* 9 for "[object ]" */
826 jschar *chars = (jschar *) cx->malloc((nchars + 1) * sizeof(jschar));
827 if (!chars)
828 return NULL;
830 const char *prefix = "[object ";
831 nchars = 0;
832 while ((chars[nchars] = (jschar)*prefix) != 0)
833 nchars++, prefix++;
834 while ((chars[nchars] = (jschar)*clazz) != 0)
835 nchars++, clazz++;
836 chars[nchars++] = ']';
837 chars[nchars] = 0;
839 JSString *str = js_NewString(cx, chars, nchars);
840 if (!str)
841 cx->free(chars);
842 return str;
847 /* ES5 15.2.4.2. Note steps 1 and 2 are errata. */
848 static JSBool
849 obj_toString(JSContext *cx, uintN argc, Value *vp)
851 Value &thisv = vp[1];
853 /* ES5 15.2.4.2 step 1. */
854 if (thisv.isUndefined()) {
855 vp->setString(ATOM_TO_STRING(cx->runtime->atomState.objectUndefinedAtom));
856 return true;
859 /* ES5 15.2.4.2 step 2. */
860 if (thisv.isNull()) {
861 vp->setString(ATOM_TO_STRING(cx->runtime->atomState.objectNullAtom));
862 return true;
865 /* ES5 15.2.4.2 step 3. */
866 if (!thisv.isObject() && !js_PrimitiveToObject(cx, &thisv))
867 return false;
869 /* ES5 15.2.4.2 steps 4-5. */
870 JSString *str = js::obj_toStringHelper(cx, &thisv.toObject());
871 if (!str)
872 return false;
873 vp->setString(str);
874 return true;
877 static JSBool
878 obj_toLocaleString(JSContext *cx, uintN argc, Value *vp)
880 if (!ComputeThisFromVp(cx, vp))
881 return JS_FALSE;
883 JSString *str = js_ValueToString(cx, vp[1]);
884 if (!str)
885 return JS_FALSE;
887 vp->setString(str);
888 return JS_TRUE;
891 static JSBool
892 obj_valueOf(JSContext *cx, uintN argc, Value *vp)
894 if (!ComputeThisFromVp(cx, vp))
895 return JS_FALSE;
896 *vp = vp[1];
897 return JS_TRUE;
901 * Check if CSP allows new Function() or eval() to run in the current
902 * principals.
904 JSBool
905 js_CheckContentSecurityPolicy(JSContext *cx, JSObject *scopeobj)
907 // CSP is static per document, so if our check said yes before, that
908 // answer is still valid.
909 JSObject *global = scopeobj->getGlobal();
910 Value v = global->getReservedSlot(JSRESERVED_GLOBAL_EVAL_ALLOWED);
911 if (v.isUndefined()) {
912 JSSecurityCallbacks *callbacks = JS_GetSecurityCallbacks(cx);
914 // if there are callbacks, make sure that the CSP callback is installed and
915 // that it permits eval().
916 v.setBoolean((!callbacks || !callbacks->contentSecurityPolicyAllows) ||
917 callbacks->contentSecurityPolicyAllows(cx));
919 // update the cache in the global object for the result of the security check
920 js_SetReservedSlot(cx, global, JSRESERVED_GLOBAL_EVAL_ALLOWED, v);
922 return !v.isFalse();
926 * Check whether principals subsumes scopeobj's principals, and return true
927 * if so (or if scopeobj has no principals, for backward compatibility with
928 * the JS API, which does not require principals), and false otherwise.
930 JSBool
931 js_CheckPrincipalsAccess(JSContext *cx, JSObject *scopeobj,
932 JSPrincipals *principals, JSAtom *caller)
934 JSSecurityCallbacks *callbacks;
935 JSPrincipals *scopePrincipals;
937 callbacks = JS_GetSecurityCallbacks(cx);
938 if (callbacks && callbacks->findObjectPrincipals) {
939 scopePrincipals = callbacks->findObjectPrincipals(cx, scopeobj);
940 if (!principals || !scopePrincipals ||
941 !principals->subsume(principals, scopePrincipals)) {
942 JSAutoByteString callerstr;
943 if (!js_AtomToPrintableString(cx, caller, &callerstr))
944 return JS_FALSE;
945 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
946 JSMSG_BAD_INDIRECT_CALL, callerstr.ptr());
947 return JS_FALSE;
950 return JS_TRUE;
953 static bool
954 CheckScopeChainValidity(JSContext *cx, JSObject *scopeobj)
956 JSObject *inner = scopeobj;
957 OBJ_TO_INNER_OBJECT(cx, inner);
958 if (!inner)
959 return false;
960 JS_ASSERT(inner == scopeobj);
962 /* XXX This is an awful gross hack. */
963 while (scopeobj) {
964 JSObjectOp op = scopeobj->getClass()->ext.innerObject;
965 if (op && op(cx, scopeobj) != scopeobj) {
966 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_INDIRECT_CALL,
967 js_eval_str);
968 return false;
970 scopeobj = scopeobj->getParent();
973 return true;
976 const char *
977 js_ComputeFilename(JSContext *cx, JSStackFrame *caller,
978 JSPrincipals *principals, uintN *linenop)
980 uint32 flags;
981 #ifdef DEBUG
982 JSSecurityCallbacks *callbacks = JS_GetSecurityCallbacks(cx);
983 #endif
985 JS_ASSERT(principals || !(callbacks && callbacks->findObjectPrincipals));
986 flags = JS_GetScriptFilenameFlags(caller->script());
987 if ((flags & JSFILENAME_PROTECTED) &&
988 principals &&
989 strcmp(principals->codebase, "[System Principal]")) {
990 *linenop = 0;
991 return principals->codebase;
994 jsbytecode *pc = caller->pc(cx);
995 if (pc && js_GetOpcode(cx, caller->script(), pc) == JSOP_EVAL) {
996 JS_ASSERT(js_GetOpcode(cx, caller->script(), pc + JSOP_EVAL_LENGTH) == JSOP_LINENO);
997 *linenop = GET_UINT16(pc + JSOP_EVAL_LENGTH);
998 } else {
999 *linenop = js_FramePCToLineNumber(cx, caller);
1001 return caller->script()->filename;
1004 #ifndef EVAL_CACHE_CHAIN_LIMIT
1005 # define EVAL_CACHE_CHAIN_LIMIT 4
1006 #endif
1008 static inline JSScript **
1009 EvalCacheHash(JSContext *cx, JSLinearString *str)
1011 const jschar *s = str->chars();
1012 size_t n = str->length();
1014 if (n > 100)
1015 n = 100;
1016 uint32 h;
1017 for (h = 0; n; s++, n--)
1018 h = JS_ROTATE_LEFT32(h, 4) ^ *s;
1020 h *= JS_GOLDEN_RATIO;
1021 h >>= 32 - JS_EVAL_CACHE_SHIFT;
1022 return &JS_SCRIPTS_TO_GC(cx)[h];
1025 static JS_ALWAYS_INLINE JSScript *
1026 EvalCacheLookup(JSContext *cx, JSLinearString *str, JSStackFrame *caller, uintN staticLevel,
1027 JSPrincipals *principals, JSObject *scopeobj, JSScript **bucket)
1030 * Cache local eval scripts indexed by source qualified by scope.
1032 * An eval cache entry should never be considered a hit unless its
1033 * strictness matches that of the new eval code. The existing code takes
1034 * care of this, because hits are qualified by the function from which
1035 * eval was called, whose strictness doesn't change. (We don't cache evals
1036 * in eval code, so the calling function corresponds to the calling script,
1037 * and its strictness never varies.) Scripts produced by calls to eval from
1038 * global code aren't cached.
1040 * FIXME bug 620141: Qualify hits by calling script rather than function.
1041 * Then we wouldn't need the unintuitive !isEvalFrame() hack in EvalKernel
1042 * to avoid caching nested evals in functions (thus potentially mismatching
1043 * on strict mode), and we could cache evals in global code if desired.
1045 uintN count = 0;
1046 JSScript **scriptp = bucket;
1048 EVAL_CACHE_METER(probe);
1049 JSVersion version = cx->findVersion();
1050 JSScript *script;
1051 while ((script = *scriptp) != NULL) {
1052 if (script->savedCallerFun &&
1053 script->staticLevel == staticLevel &&
1054 script->version == version &&
1055 !script->hasSingletons &&
1056 (script->principals == principals ||
1057 (principals->subsume(principals, script->principals) &&
1058 script->principals->subsume(script->principals, principals)))) {
1060 * Get the prior (cache-filling) eval's saved caller function.
1061 * See Compiler::compileScript in jsparse.cpp.
1063 JSFunction *fun = script->getFunction(0);
1065 if (fun == caller->fun()) {
1067 * Get the source string passed for safekeeping in the
1068 * atom map by the prior eval to Compiler::compileScript.
1070 JSAtom *src = script->atomMap.vector[0];
1072 if (src == str || EqualStrings(src, str)) {
1074 * Source matches, qualify by comparing scopeobj to the
1075 * COMPILE_N_GO-memoized parent of the first literal
1076 * function or regexp object if any. If none, then this
1077 * script has no compiled-in dependencies on the prior
1078 * eval's scopeobj.
1080 JSObjectArray *objarray = script->objects();
1081 int i = 1;
1083 if (objarray->length == 1) {
1084 if (JSScript::isValidOffset(script->regexpsOffset)) {
1085 objarray = script->regexps();
1086 i = 0;
1087 } else {
1088 EVAL_CACHE_METER(noscope);
1089 i = -1;
1092 if (i < 0 ||
1093 objarray->vector[i]->getParent() == scopeobj) {
1094 JS_ASSERT(staticLevel == script->staticLevel);
1095 EVAL_CACHE_METER(hit);
1096 *scriptp = script->u.nextToGC;
1097 script->u.nextToGC = NULL;
1098 return script;
1104 if (++count == EVAL_CACHE_CHAIN_LIMIT)
1105 return NULL;
1106 EVAL_CACHE_METER(step);
1107 scriptp = &script->u.nextToGC;
1109 return NULL;
1112 /* ES5 15.1.2.1. */
1113 static JSBool
1114 eval(JSContext *cx, uintN argc, Value *vp)
1117 * NB: This method handles only indirect eval: direct eval is handled by
1118 * JSOP_EVAL.
1121 JSStackFrame *caller = js_GetScriptedCaller(cx, NULL);
1123 /* FIXME Bug 602994: This really should be perfectly cromulent. */
1124 if (!caller) {
1125 /* Eval code needs to inherit principals from the caller. */
1126 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1127 JSMSG_BAD_INDIRECT_CALL, js_eval_str);
1128 return false;
1131 return EvalKernel(cx, argc, vp, INDIRECT_EVAL, caller, vp[0].toObject().getGlobal());
1134 namespace js {
1136 bool
1137 EvalKernel(JSContext *cx, uintN argc, Value *vp, EvalType evalType, JSStackFrame *caller,
1138 JSObject *scopeobj)
1141 * FIXME Bug 602994: Calls with no scripted caller should be permitted and
1142 * should be implemented as indirect calls.
1144 JS_ASSERT(caller);
1145 JS_ASSERT(scopeobj);
1148 * We once supported a second argument to eval to use as the scope chain
1149 * when evaluating the code string. Warn when such uses are seen so that
1150 * authors will know that support for eval(s, o) has been removed.
1152 JSScript *callerScript = caller->script();
1153 if (argc > 1 && !callerScript->warnedAboutTwoArgumentEval) {
1154 static const char TWO_ARGUMENT_WARNING[] =
1155 "Support for eval(code, scopeObject) has been removed. "
1156 "Use |with (scopeObject) eval(code);| instead.";
1157 if (!JS_ReportWarning(cx, TWO_ARGUMENT_WARNING))
1158 return false;
1159 callerScript->warnedAboutTwoArgumentEval = true;
1163 * CSP check: Is eval() allowed at all?
1164 * Report errors via CSP is done in the script security mgr.
1166 if (!js_CheckContentSecurityPolicy(cx, scopeobj)) {
1167 JS_ReportError(cx, "call to eval() blocked by CSP");
1168 return false;
1171 /* ES5 15.1.2.1 step 1. */
1172 if (argc < 1) {
1173 vp->setUndefined();
1174 return true;
1176 if (!vp[2].isString()) {
1177 *vp = vp[2];
1178 return true;
1180 JSString *str = vp[2].toString();
1182 /* ES5 15.1.2.1 steps 2-8. */
1183 JSObject *callee = JSVAL_TO_OBJECT(JS_CALLEE(cx, Jsvalify(vp)));
1184 JS_ASSERT(IsBuiltinEvalFunction(callee->getFunctionPrivate()));
1185 JSPrincipals *principals = js_EvalFramePrincipals(cx, callee, caller);
1188 * Per ES5, indirect eval runs in the global scope. (eval is specified this
1189 * way so that the compiler can make assumptions about what bindings may or
1190 * may not exist in the current frame if it doesn't see 'eval'.)
1192 uintN staticLevel;
1193 if (evalType == DIRECT_EVAL) {
1194 staticLevel = caller->script()->staticLevel + 1;
1196 #ifdef DEBUG
1197 jsbytecode *callerPC = caller->pc(cx);
1198 JS_ASSERT_IF(caller->isFunctionFrame(), caller->hasCallObj());
1199 JS_ASSERT(callerPC && js_GetOpcode(cx, caller->script(), callerPC) == JSOP_EVAL);
1200 #endif
1201 } else {
1202 /* Pretend that we're top level. */
1203 staticLevel = 0;
1205 JS_ASSERT(scopeobj == scopeobj->getGlobal());
1206 JS_ASSERT(scopeobj->isGlobal());
1209 /* Ensure we compile this eval with the right object in the scope chain. */
1210 if (!CheckScopeChainValidity(cx, scopeobj))
1211 return false;
1213 JSLinearString *linearStr = str->ensureLinear(cx);
1214 if (!linearStr)
1215 return false;
1216 const jschar *chars = linearStr->chars();
1217 size_t length = linearStr->length();
1220 * If the eval string starts with '(' and ends with ')', it may be JSON.
1221 * Try the JSON parser first because it's much faster. If the eval string
1222 * isn't JSON, JSON parsing will probably fail quickly, so little time
1223 * will be lost.
1225 if (length > 2 && chars[0] == '(' && chars[length - 1] == ')') {
1226 JSONParser *jp = js_BeginJSONParse(cx, vp, /* suppressErrors = */true);
1227 if (jp != NULL) {
1228 /* Run JSON-parser on string inside ( and ). */
1229 bool ok = js_ConsumeJSONText(cx, jp, chars + 1, length - 2);
1230 ok &= js_FinishJSONParse(cx, jp, NullValue());
1231 if (ok)
1232 return true;
1237 * Direct calls to eval are supposed to see the caller's |this|. If we
1238 * haven't wrapped that yet, do so now, before we make a copy of it for
1239 * the eval code to use.
1241 if (evalType == DIRECT_EVAL && !caller->computeThis(cx))
1242 return false;
1244 JSScript *script = NULL;
1245 JSScript **bucket = EvalCacheHash(cx, linearStr);
1246 if (evalType == DIRECT_EVAL && caller->isFunctionFrame() && !caller->isEvalFrame())
1247 script = EvalCacheLookup(cx, linearStr, caller, staticLevel, principals, scopeobj, bucket);
1250 * We can't have a callerFrame (down in js::Execute's terms) if we're in
1251 * global code (or if we're an indirect eval).
1253 JSStackFrame *callerFrame = (staticLevel != 0) ? caller : NULL;
1254 if (!script) {
1255 uintN lineno;
1256 const char *filename = js_ComputeFilename(cx, caller, principals, &lineno);
1258 uint32 tcflags = TCF_COMPILE_N_GO | TCF_NEED_MUTABLE_SCRIPT | TCF_COMPILE_FOR_EVAL;
1259 script = Compiler::compileScript(cx, scopeobj, callerFrame,
1260 principals, tcflags, chars, length,
1261 filename, lineno, linearStr, staticLevel);
1262 if (!script)
1263 return false;
1266 assertSameCompartment(cx, scopeobj, script);
1269 * Belt-and-braces: check that the lesser of eval's principals and the
1270 * caller's principals has access to scopeobj.
1272 JSBool ok = js_CheckPrincipalsAccess(cx, scopeobj, principals,
1273 cx->runtime->atomState.evalAtom) &&
1274 Execute(cx, scopeobj, script, callerFrame, JSFRAME_EVAL, vp);
1276 script->u.nextToGC = *bucket;
1277 *bucket = script;
1278 #ifdef CHECK_SCRIPT_OWNER
1279 script->owner = NULL;
1280 #endif
1282 return ok;
1285 bool
1286 IsBuiltinEvalFunction(JSFunction *fun)
1288 return fun->maybeNative() == eval;
1293 #if JS_HAS_OBJ_WATCHPOINT
1295 static JSBool
1296 obj_watch_handler(JSContext *cx, JSObject *obj, jsid id, jsval old,
1297 jsval *nvp, void *closure)
1299 JSObject *callable;
1300 JSSecurityCallbacks *callbacks;
1301 JSStackFrame *caller;
1302 JSPrincipals *subject, *watcher;
1303 JSResolvingKey key;
1304 JSResolvingEntry *entry;
1305 uint32 generation;
1306 Value argv[3];
1307 JSBool ok;
1309 callable = (JSObject *) closure;
1311 callbacks = JS_GetSecurityCallbacks(cx);
1312 if (callbacks && callbacks->findObjectPrincipals) {
1313 /* Skip over any obj_watch_* frames between us and the real subject. */
1314 caller = js_GetScriptedCaller(cx, NULL);
1315 if (caller) {
1317 * Only call the watch handler if the watcher is allowed to watch
1318 * the currently executing script.
1320 watcher = callbacks->findObjectPrincipals(cx, callable);
1321 subject = js_StackFramePrincipals(cx, caller);
1323 if (watcher && subject && !watcher->subsume(watcher, subject)) {
1324 /* Silently don't call the watch handler. */
1325 return JS_TRUE;
1330 /* Avoid recursion on (obj, id) already being watched on cx. */
1331 key.obj = obj;
1332 key.id = id;
1333 if (!js_StartResolving(cx, &key, JSRESFLAG_WATCH, &entry))
1334 return JS_FALSE;
1335 if (!entry)
1336 return JS_TRUE;
1337 generation = cx->resolvingTable->generation;
1339 argv[0] = IdToValue(id);
1340 argv[1] = Valueify(old);
1341 argv[2] = Valueify(*nvp);
1342 ok = ExternalInvoke(cx, obj, ObjectOrNullValue(callable), 3, argv, Valueify(nvp));
1343 js_StopResolving(cx, &key, JSRESFLAG_WATCH, entry, generation);
1344 return ok;
1347 static JSBool
1348 obj_watch(JSContext *cx, uintN argc, Value *vp)
1350 if (argc <= 1) {
1351 js_ReportMissingArg(cx, *vp, 1);
1352 return JS_FALSE;
1355 JSObject *callable = js_ValueToCallableObject(cx, &vp[3], 0);
1356 if (!callable)
1357 return JS_FALSE;
1359 /* Compute the unique int/atom symbol id needed by js_LookupProperty. */
1360 jsid propid;
1361 if (!ValueToId(cx, vp[2], &propid))
1362 return JS_FALSE;
1364 JSObject *obj = ComputeThisFromVp(cx, vp);
1365 Value tmp;
1366 uintN attrs;
1367 if (!obj || !CheckAccess(cx, obj, propid, JSACC_WATCH, &tmp, &attrs))
1368 return JS_FALSE;
1370 vp->setUndefined();
1372 if (attrs & JSPROP_READONLY)
1373 return JS_TRUE;
1374 if (obj->isDenseArray() && !obj->makeDenseArraySlow(cx))
1375 return JS_FALSE;
1376 return JS_SetWatchPoint(cx, obj, propid, obj_watch_handler, callable);
1379 static JSBool
1380 obj_unwatch(JSContext *cx, uintN argc, Value *vp)
1382 JSObject *obj = ComputeThisFromVp(cx, vp);
1383 if (!obj)
1384 return JS_FALSE;
1385 vp->setUndefined();
1386 jsid id;
1387 if (argc != 0) {
1388 if (!ValueToId(cx, vp[2], &id))
1389 return JS_FALSE;
1390 } else {
1391 id = JSID_VOID;
1393 return JS_ClearWatchPoint(cx, obj, id, NULL, NULL);
1396 #endif /* JS_HAS_OBJ_WATCHPOINT */
1399 * Prototype and property query methods, to complement the 'in' and
1400 * 'instanceof' operators.
1403 /* Proposed ECMA 15.2.4.5. */
1404 static JSBool
1405 obj_hasOwnProperty(JSContext *cx, uintN argc, Value *vp)
1407 JSObject *obj = ComputeThisFromVp(cx, vp);
1408 return obj &&
1409 js_HasOwnPropertyHelper(cx, obj->getOps()->lookupProperty, argc, vp);
1412 JSBool
1413 js_HasOwnPropertyHelper(JSContext *cx, LookupPropOp lookup, uintN argc,
1414 Value *vp)
1416 jsid id;
1417 if (!ValueToId(cx, argc != 0 ? vp[2] : UndefinedValue(), &id))
1418 return JS_FALSE;
1420 JSObject *obj = ComputeThisFromVp(cx, vp);
1421 JSObject *obj2;
1422 JSProperty *prop;
1423 if (!obj)
1424 return false;
1425 if (obj->isProxy()) {
1426 bool has;
1427 if (!JSProxy::hasOwn(cx, obj, id, &has))
1428 return false;
1429 vp->setBoolean(has);
1430 return true;
1432 if (!js_HasOwnProperty(cx, lookup, obj, id, &obj2, &prop))
1433 return JS_FALSE;
1434 vp->setBoolean(!!prop);
1435 return JS_TRUE;
1438 JSBool
1439 js_HasOwnProperty(JSContext *cx, LookupPropOp lookup, JSObject *obj, jsid id,
1440 JSObject **objp, JSProperty **propp)
1442 JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_DETECTING);
1443 if (!(lookup ? lookup : js_LookupProperty)(cx, obj, id, objp, propp))
1444 return false;
1445 if (!*propp)
1446 return true;
1448 if (*objp == obj)
1449 return true;
1451 Class *clasp = (*objp)->getClass();
1452 JSObject *outer = NULL;
1453 if (JSObjectOp op = (*objp)->getClass()->ext.outerObject) {
1454 outer = op(cx, *objp);
1455 if (!outer)
1456 return false;
1459 if (outer != *objp) {
1460 if ((*objp)->isNative() && obj->getClass() == clasp) {
1462 * The combination of JSPROP_SHARED and JSPROP_PERMANENT in a
1463 * delegated property makes that property appear to be direct in
1464 * all delegating instances of the same native class. This hack
1465 * avoids bloating every function instance with its own 'length'
1466 * (AKA 'arity') property. But it must not extend across class
1467 * boundaries, to avoid making hasOwnProperty lie (bug 320854).
1469 * It's not really a hack, of course: a permanent property can't
1470 * be deleted, and JSPROP_SHARED means "don't allocate a slot in
1471 * any instance, prototype or delegating". Without a slot, and
1472 * without the ability to remove and recreate (with differences)
1473 * the property, there is no way to tell whether it is directly
1474 * owned, or indirectly delegated.
1476 Shape *shape = reinterpret_cast<Shape *>(*propp);
1477 if (shape->isSharedPermanent())
1478 return true;
1481 *propp = NULL;
1483 return true;
1486 /* Proposed ECMA 15.2.4.6. */
1487 static JSBool
1488 obj_isPrototypeOf(JSContext *cx, uintN argc, Value *vp)
1490 JSObject *obj = ComputeThisFromVp(cx, vp);
1491 if (!obj)
1492 return JS_FALSE;
1493 const Value &v = argc != 0 ? vp[2] : UndefinedValue();
1494 vp->setBoolean(js_IsDelegate(cx, obj, v));
1495 return JS_TRUE;
1498 /* Proposed ECMA 15.2.4.7. */
1499 static JSBool
1500 obj_propertyIsEnumerable(JSContext *cx, uintN argc, Value *vp)
1502 jsid id;
1503 if (!ValueToId(cx, argc != 0 ? vp[2] : UndefinedValue(), &id))
1504 return JS_FALSE;
1506 JSObject *obj = ComputeThisFromVp(cx, vp);
1507 return obj && js_PropertyIsEnumerable(cx, obj, id, vp);
1510 JSBool
1511 js_PropertyIsEnumerable(JSContext *cx, JSObject *obj, jsid id, Value *vp)
1513 JSObject *pobj;
1514 JSProperty *prop;
1515 if (!obj->lookupProperty(cx, id, &pobj, &prop))
1516 return JS_FALSE;
1518 if (!prop) {
1519 vp->setBoolean(false);
1520 return JS_TRUE;
1524 * XXX ECMA spec error compatible: return false unless hasOwnProperty.
1525 * The ECMA spec really should be fixed so propertyIsEnumerable and the
1526 * for..in loop agree on whether prototype properties are enumerable,
1527 * obviously by fixing this method (not by breaking the for..in loop!).
1529 * We check here for shared permanent prototype properties, which should
1530 * be treated as if they are local to obj. They are an implementation
1531 * technique used to satisfy ECMA requirements; users should not be able
1532 * to distinguish a shared permanent proto-property from a local one.
1534 bool shared;
1535 uintN attrs;
1536 if (pobj->isNative()) {
1537 Shape *shape = (Shape *) prop;
1538 shared = shape->isSharedPermanent();
1539 attrs = shape->attributes();
1540 } else {
1541 shared = false;
1542 if (!pobj->getAttributes(cx, id, &attrs))
1543 return false;
1545 if (pobj != obj && !shared) {
1546 vp->setBoolean(false);
1547 return true;
1549 vp->setBoolean((attrs & JSPROP_ENUMERATE) != 0);
1550 return true;
1553 #if OLD_GETTER_SETTER_METHODS
1555 const char js_defineGetter_str[] = "__defineGetter__";
1556 const char js_defineSetter_str[] = "__defineSetter__";
1557 const char js_lookupGetter_str[] = "__lookupGetter__";
1558 const char js_lookupSetter_str[] = "__lookupSetter__";
1560 JS_FRIEND_API(JSBool)
1561 js_obj_defineGetter(JSContext *cx, uintN argc, Value *vp)
1563 if (argc <= 1 || !js_IsCallable(vp[3])) {
1564 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1565 JSMSG_BAD_GETTER_OR_SETTER,
1566 js_getter_str);
1567 return JS_FALSE;
1569 PropertyOp getter = CastAsPropertyOp(&vp[3].toObject());
1571 jsid id;
1572 if (!ValueToId(cx, vp[2], &id))
1573 return JS_FALSE;
1574 JSObject *obj = ComputeThisFromVp(cx, vp);
1575 if (!obj || !CheckRedeclaration(cx, obj, id, JSPROP_GETTER, NULL, NULL))
1576 return JS_FALSE;
1578 * Getters and setters are just like watchpoints from an access
1579 * control point of view.
1581 Value junk;
1582 uintN attrs;
1583 if (!CheckAccess(cx, obj, id, JSACC_WATCH, &junk, &attrs))
1584 return JS_FALSE;
1585 vp->setUndefined();
1586 return obj->defineProperty(cx, id, UndefinedValue(), getter, PropertyStub,
1587 JSPROP_ENUMERATE | JSPROP_GETTER | JSPROP_SHARED);
1590 JS_FRIEND_API(JSBool)
1591 js_obj_defineSetter(JSContext *cx, uintN argc, Value *vp)
1593 if (argc <= 1 || !js_IsCallable(vp[3])) {
1594 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1595 JSMSG_BAD_GETTER_OR_SETTER,
1596 js_setter_str);
1597 return JS_FALSE;
1599 PropertyOp setter = CastAsPropertyOp(&vp[3].toObject());
1601 jsid id;
1602 if (!ValueToId(cx, vp[2], &id))
1603 return JS_FALSE;
1604 JSObject *obj = ComputeThisFromVp(cx, vp);
1605 if (!obj || !CheckRedeclaration(cx, obj, id, JSPROP_SETTER, NULL, NULL))
1606 return JS_FALSE;
1608 * Getters and setters are just like watchpoints from an access
1609 * control point of view.
1611 Value junk;
1612 uintN attrs;
1613 if (!CheckAccess(cx, obj, id, JSACC_WATCH, &junk, &attrs))
1614 return JS_FALSE;
1615 vp->setUndefined();
1616 return obj->defineProperty(cx, id, UndefinedValue(), PropertyStub, setter,
1617 JSPROP_ENUMERATE | JSPROP_SETTER | JSPROP_SHARED);
1620 static JSBool
1621 obj_lookupGetter(JSContext *cx, uintN argc, Value *vp)
1623 jsid id;
1624 if (!ValueToId(cx, argc != 0 ? vp[2] : UndefinedValue(), &id))
1625 return JS_FALSE;
1626 JSObject *obj = ComputeThisFromVp(cx, vp);
1627 JSObject *pobj;
1628 JSProperty *prop;
1629 if (!obj || !obj->lookupProperty(cx, id, &pobj, &prop))
1630 return JS_FALSE;
1631 vp->setUndefined();
1632 if (prop) {
1633 if (pobj->isNative()) {
1634 Shape *shape = (Shape *) prop;
1635 if (shape->hasGetterValue())
1636 *vp = shape->getterValue();
1639 return JS_TRUE;
1642 static JSBool
1643 obj_lookupSetter(JSContext *cx, uintN argc, Value *vp)
1645 jsid id;
1646 if (!ValueToId(cx, argc != 0 ? vp[2] : UndefinedValue(), &id))
1647 return JS_FALSE;
1648 JSObject *obj = ComputeThisFromVp(cx, vp);
1649 JSObject *pobj;
1650 JSProperty *prop;
1651 if (!obj || !obj->lookupProperty(cx, id, &pobj, &prop))
1652 return JS_FALSE;
1653 vp->setUndefined();
1654 if (prop) {
1655 if (pobj->isNative()) {
1656 Shape *shape = (Shape *) prop;
1657 if (shape->hasSetterValue())
1658 *vp = shape->setterValue();
1661 return JS_TRUE;
1663 #endif /* OLD_GETTER_SETTER_METHODS */
1665 JSBool
1666 obj_getPrototypeOf(JSContext *cx, uintN argc, Value *vp)
1668 if (argc == 0) {
1669 js_ReportMissingArg(cx, *vp, 0);
1670 return JS_FALSE;
1673 if (vp[2].isPrimitive()) {
1674 char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, vp[2], NULL);
1675 if (!bytes)
1676 return JS_FALSE;
1677 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1678 JSMSG_UNEXPECTED_TYPE, bytes, "not an object");
1679 JS_free(cx, bytes);
1680 return JS_FALSE;
1683 JSObject *obj = &vp[2].toObject();
1684 uintN attrs;
1685 return CheckAccess(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.protoAtom),
1686 JSACC_PROTO, vp, &attrs);
1689 extern JSBool
1690 js_NewPropertyDescriptorObject(JSContext *cx, jsid id, uintN attrs,
1691 const Value &getter, const Value &setter,
1692 const Value &value, Value *vp)
1694 /* We have our own property, so start creating the descriptor. */
1695 JSObject *desc = NewBuiltinClassInstance(cx, &js_ObjectClass);
1696 if (!desc)
1697 return false;
1698 vp->setObject(*desc); /* Root and return. */
1700 const JSAtomState &atomState = cx->runtime->atomState;
1701 if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
1702 if (!desc->defineProperty(cx, ATOM_TO_JSID(atomState.getAtom), getter,
1703 PropertyStub, PropertyStub, JSPROP_ENUMERATE) ||
1704 !desc->defineProperty(cx, ATOM_TO_JSID(atomState.setAtom), setter,
1705 PropertyStub, PropertyStub, JSPROP_ENUMERATE)) {
1706 return false;
1708 } else {
1709 if (!desc->defineProperty(cx, ATOM_TO_JSID(atomState.valueAtom), value,
1710 PropertyStub, PropertyStub, JSPROP_ENUMERATE) ||
1711 !desc->defineProperty(cx, ATOM_TO_JSID(atomState.writableAtom),
1712 BooleanValue((attrs & JSPROP_READONLY) == 0),
1713 PropertyStub, PropertyStub, JSPROP_ENUMERATE)) {
1714 return false;
1718 return desc->defineProperty(cx, ATOM_TO_JSID(atomState.enumerableAtom),
1719 BooleanValue((attrs & JSPROP_ENUMERATE) != 0),
1720 PropertyStub, PropertyStub, JSPROP_ENUMERATE) &&
1721 desc->defineProperty(cx, ATOM_TO_JSID(atomState.configurableAtom),
1722 BooleanValue((attrs & JSPROP_PERMANENT) == 0),
1723 PropertyStub, PropertyStub, JSPROP_ENUMERATE);
1726 JSBool
1727 js_GetOwnPropertyDescriptor(JSContext *cx, JSObject *obj, jsid id, Value *vp)
1729 if (obj->isProxy())
1730 return JSProxy::getOwnPropertyDescriptor(cx, obj, id, false, vp);
1732 JSObject *pobj;
1733 JSProperty *prop;
1734 if (!js_HasOwnProperty(cx, obj->getOps()->lookupProperty, obj, id, &pobj, &prop))
1735 return false;
1736 if (!prop) {
1737 vp->setUndefined();
1738 return true;
1741 Value roots[] = { UndefinedValue(), UndefinedValue(), UndefinedValue() };
1742 AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(roots), roots);
1743 unsigned attrs;
1744 bool doGet = true;
1745 if (pobj->isNative()) {
1746 Shape *shape = (Shape *) prop;
1747 attrs = shape->attributes();
1748 if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
1749 doGet = false;
1750 if (attrs & JSPROP_GETTER)
1751 roots[0] = shape->getterValue();
1752 if (attrs & JSPROP_SETTER)
1753 roots[1] = shape->setterValue();
1755 } else {
1756 if (!pobj->getAttributes(cx, id, &attrs))
1757 return false;
1760 if (doGet && !obj->getProperty(cx, id, &roots[2]))
1761 return false;
1763 return js_NewPropertyDescriptorObject(cx, id,
1764 attrs,
1765 roots[0], /* getter */
1766 roots[1], /* setter */
1767 roots[2], /* value */
1768 vp);
1771 static bool
1772 GetFirstArgumentAsObject(JSContext *cx, uintN argc, Value *vp, const char *method, JSObject **objp)
1774 if (argc == 0) {
1775 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
1776 method, "0", "s");
1777 return false;
1780 const Value &v = vp[2];
1781 if (!v.isObject()) {
1782 char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NULL);
1783 if (!bytes)
1784 return false;
1785 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_UNEXPECTED_TYPE,
1786 bytes, "not an object");
1787 JS_free(cx, bytes);
1788 return false;
1791 *objp = &v.toObject();
1792 return true;
1795 static JSBool
1796 obj_getOwnPropertyDescriptor(JSContext *cx, uintN argc, Value *vp)
1798 JSObject *obj;
1799 if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.getOwnPropertyDescriptor", &obj))
1800 return JS_FALSE;
1801 AutoIdRooter nameidr(cx);
1802 if (!ValueToId(cx, argc >= 2 ? vp[3] : UndefinedValue(), nameidr.addr()))
1803 return JS_FALSE;
1804 return js_GetOwnPropertyDescriptor(cx, obj, nameidr.id(), vp);
1807 static JSBool
1808 obj_keys(JSContext *cx, uintN argc, Value *vp)
1810 JSObject *obj;
1811 if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.keys", &obj))
1812 return false;
1814 AutoIdVector props(cx);
1815 if (!GetPropertyNames(cx, obj, JSITER_OWNONLY, &props))
1816 return false;
1818 AutoValueVector vals(cx);
1819 if (!vals.reserve(props.length()))
1820 return false;
1821 for (size_t i = 0, len = props.length(); i < len; i++) {
1822 jsid id = props[i];
1823 if (JSID_IS_STRING(id)) {
1824 JS_ALWAYS_TRUE(vals.append(StringValue(JSID_TO_STRING(id))));
1825 } else if (JSID_IS_INT(id)) {
1826 JSString *str = js_IntToString(cx, JSID_TO_INT(id));
1827 if (!str)
1828 return false;
1829 JS_ALWAYS_TRUE(vals.append(StringValue(str)));
1830 } else {
1831 JS_ASSERT(JSID_IS_OBJECT(id));
1835 JS_ASSERT(props.length() <= UINT32_MAX);
1836 JSObject *aobj = NewDenseCopiedArray(cx, jsuint(vals.length()), vals.begin());
1837 if (!aobj)
1838 return false;
1839 vp->setObject(*aobj);
1841 return true;
1844 static bool
1845 HasProperty(JSContext* cx, JSObject* obj, jsid id, Value* vp, bool *foundp)
1847 if (!obj->hasProperty(cx, id, foundp, JSRESOLVE_QUALIFIED | JSRESOLVE_DETECTING))
1848 return false;
1849 if (!*foundp) {
1850 vp->setUndefined();
1851 return true;
1855 * We must go through the method read barrier in case id is 'get' or 'set'.
1856 * There is no obvious way to defer cloning a joined function object whose
1857 * identity will be used by DefinePropertyOnObject, e.g., or reflected via
1858 * js_GetOwnPropertyDescriptor, as the getter or setter callable object.
1860 return !!obj->getProperty(cx, id, vp);
1863 PropDesc::PropDesc()
1864 : pd(UndefinedValue()),
1865 id(INT_TO_JSID(0)),
1866 value(UndefinedValue()),
1867 get(UndefinedValue()),
1868 set(UndefinedValue()),
1869 attrs(0),
1870 hasGet(false),
1871 hasSet(false),
1872 hasValue(false),
1873 hasWritable(false),
1874 hasEnumerable(false),
1875 hasConfigurable(false)
1879 bool
1880 PropDesc::initialize(JSContext* cx, jsid id, const Value &origval)
1882 Value v = origval;
1883 this->id = id;
1885 /* 8.10.5 step 1 */
1886 if (v.isPrimitive()) {
1887 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_NONNULL_OBJECT);
1888 return false;
1890 JSObject* desc = &v.toObject();
1892 /* Make a copy of the descriptor. We might need it later. */
1893 pd = v;
1895 /* Start with the proper defaults. */
1896 attrs = JSPROP_PERMANENT | JSPROP_READONLY;
1898 bool found;
1900 /* 8.10.5 step 3 */
1901 #ifdef __GNUC__ /* quell GCC overwarning */
1902 found = false;
1903 #endif
1904 if (!HasProperty(cx, desc, ATOM_TO_JSID(cx->runtime->atomState.enumerableAtom), &v, &found))
1905 return false;
1906 if (found) {
1907 hasEnumerable = JS_TRUE;
1908 if (js_ValueToBoolean(v))
1909 attrs |= JSPROP_ENUMERATE;
1912 /* 8.10.5 step 4 */
1913 if (!HasProperty(cx, desc, ATOM_TO_JSID(cx->runtime->atomState.configurableAtom), &v, &found))
1914 return false;
1915 if (found) {
1916 hasConfigurable = JS_TRUE;
1917 if (js_ValueToBoolean(v))
1918 attrs &= ~JSPROP_PERMANENT;
1921 /* 8.10.5 step 5 */
1922 if (!HasProperty(cx, desc, ATOM_TO_JSID(cx->runtime->atomState.valueAtom), &v, &found))
1923 return false;
1924 if (found) {
1925 hasValue = true;
1926 value = v;
1929 /* 8.10.6 step 6 */
1930 if (!HasProperty(cx, desc, ATOM_TO_JSID(cx->runtime->atomState.writableAtom), &v, &found))
1931 return false;
1932 if (found) {
1933 hasWritable = JS_TRUE;
1934 if (js_ValueToBoolean(v))
1935 attrs &= ~JSPROP_READONLY;
1938 /* 8.10.7 step 7 */
1939 if (!HasProperty(cx, desc, ATOM_TO_JSID(cx->runtime->atomState.getAtom), &v, &found))
1940 return false;
1941 if (found) {
1942 if ((v.isPrimitive() || !js_IsCallable(v)) && !v.isUndefined()) {
1943 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_GET_SET_FIELD,
1944 js_getter_str);
1945 return false;
1947 hasGet = true;
1948 get = v;
1949 attrs |= JSPROP_GETTER | JSPROP_SHARED;
1952 /* 8.10.7 step 8 */
1953 if (!HasProperty(cx, desc, ATOM_TO_JSID(cx->runtime->atomState.setAtom), &v, &found))
1954 return false;
1955 if (found) {
1956 if ((v.isPrimitive() || !js_IsCallable(v)) && !v.isUndefined()) {
1957 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_GET_SET_FIELD,
1958 js_setter_str);
1959 return false;
1961 hasSet = true;
1962 set = v;
1963 attrs |= JSPROP_SETTER | JSPROP_SHARED;
1966 /* 8.10.7 step 9 */
1967 if ((hasGet || hasSet) && (hasValue || hasWritable)) {
1968 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INVALID_DESCRIPTOR);
1969 return false;
1972 return true;
1975 static JSBool
1976 Reject(JSContext *cx, uintN errorNumber, bool throwError, jsid id, bool *rval)
1978 if (throwError) {
1979 jsid idstr;
1980 if (!js_ValueToStringId(cx, IdToValue(id), &idstr))
1981 return JS_FALSE;
1982 JSAutoByteString bytes(cx, JSID_TO_STRING(idstr));
1983 if (!bytes)
1984 return JS_FALSE;
1985 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, errorNumber, bytes.ptr());
1986 return JS_FALSE;
1989 *rval = false;
1990 return JS_TRUE;
1993 static JSBool
1994 Reject(JSContext *cx, JSObject *obj, uintN errorNumber, bool throwError, bool *rval)
1996 if (throwError) {
1997 if (js_ErrorFormatString[errorNumber].argCount == 1) {
1998 js_ReportValueErrorFlags(cx, JSREPORT_ERROR, errorNumber,
1999 JSDVG_IGNORE_STACK, ObjectValue(*obj),
2000 NULL, NULL, NULL);
2001 } else {
2002 JS_ASSERT(js_ErrorFormatString[errorNumber].argCount == 0);
2003 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, errorNumber);
2005 return JS_FALSE;
2008 *rval = false;
2009 return JS_TRUE;
2012 static JSBool
2013 DefinePropertyOnObject(JSContext *cx, JSObject *obj, const PropDesc &desc,
2014 bool throwError, bool *rval)
2016 /* 8.12.9 step 1. */
2017 JSProperty *current;
2018 JSObject *obj2;
2019 JS_ASSERT(!obj->getOps()->lookupProperty);
2020 if (!js_HasOwnProperty(cx, NULL, obj, desc.id, &obj2, &current))
2021 return JS_FALSE;
2023 JS_ASSERT(!obj->getOps()->defineProperty);
2026 * If we find a shared permanent property in a different object obj2 from
2027 * obj, then if the property is shared permanent (an old hack to optimize
2028 * per-object properties into one prototype property), ignore that lookup
2029 * result (null current).
2031 * FIXME: bug 575997 (see also bug 607863).
2033 if (current && obj2 != obj && obj2->isNative()) {
2034 /* See same assertion with comment further below. */
2035 JS_ASSERT(obj2->getClass() == obj->getClass());
2037 Shape *shape = (Shape *) current;
2038 if (shape->isSharedPermanent())
2039 current = NULL;
2042 /* 8.12.9 steps 2-4. */
2043 if (!current) {
2044 if (!obj->isExtensible())
2045 return Reject(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE, throwError, rval);
2047 *rval = true;
2049 if (desc.isGenericDescriptor() || desc.isDataDescriptor()) {
2050 JS_ASSERT(!obj->getOps()->defineProperty);
2051 return js_DefineProperty(cx, obj, desc.id, &desc.value,
2052 PropertyStub, PropertyStub, desc.attrs);
2055 JS_ASSERT(desc.isAccessorDescriptor());
2058 * Getters and setters are just like watchpoints from an access
2059 * control point of view.
2061 Value dummy;
2062 uintN dummyAttrs;
2063 if (!CheckAccess(cx, obj, desc.id, JSACC_WATCH, &dummy, &dummyAttrs))
2064 return JS_FALSE;
2066 Value tmp = UndefinedValue();
2067 return js_DefineProperty(cx, obj, desc.id, &tmp,
2068 desc.getter(), desc.setter(), desc.attrs);
2071 /* 8.12.9 steps 5-6 (note 5 is merely a special case of 6). */
2072 Value v = UndefinedValue();
2075 * In the special case of shared permanent properties, the "own" property
2076 * can be found on a different object. In that case the returned property
2077 * might not be native, except: the shared permanent property optimization
2078 * is not applied if the objects have different classes (bug 320854), as
2079 * must be enforced by js_HasOwnProperty for the Shape cast below to be
2080 * safe.
2082 JS_ASSERT(obj->getClass() == obj2->getClass());
2084 const Shape *shape = reinterpret_cast<Shape *>(current);
2085 do {
2086 if (desc.isAccessorDescriptor()) {
2087 if (!shape->isAccessorDescriptor())
2088 break;
2090 if (desc.hasGet) {
2091 JSBool same;
2092 if (!SameValue(cx, desc.getterValue(), shape->getterOrUndefined(), &same))
2093 return JS_FALSE;
2094 if (!same)
2095 break;
2098 if (desc.hasSet) {
2099 JSBool same;
2100 if (!SameValue(cx, desc.setterValue(), shape->setterOrUndefined(), &same))
2101 return JS_FALSE;
2102 if (!same)
2103 break;
2105 } else {
2107 * Determine the current value of the property once, if the current
2108 * value might actually need to be used or preserved later. NB: we
2109 * guard on whether the current property is a data descriptor to
2110 * avoid calling a getter; we won't need the value if it's not a
2111 * data descriptor.
2113 if (shape->isDataDescriptor()) {
2115 * Non-standard: if the property is non-configurable and is
2116 * represented by a native getter or setter, don't permit
2117 * redefinition. We expose properties with native getter/setter
2118 * as though they were data properties, for the most part, but
2119 * in this particular case we must worry about integrity
2120 * concerns for JSAPI users who expected that
2121 * permanent+getter/setter means precisely controlled behavior.
2122 * If we permitted such redefinitions, such a property could be
2123 * "fixed" to some specific previous value, no longer varying
2124 * according to the intent of the native getter/setter for the
2125 * property.
2127 * Other engines expose properties of this nature using ECMA
2128 * getter/setter pairs, but we can't because we use them even
2129 * for properties which ECMA specifies as being true data
2130 * descriptors ([].length, Function.length, /regex/.lastIndex,
2131 * &c.). Longer-term perhaps we should convert such properties
2132 * to use data descriptors (at which point representing a
2133 * descriptor with native getter/setter as an accessor
2134 * descriptor would be fine) and take a small memory hit, but
2135 * for now we'll simply forbid their redefinition.
2137 if (!shape->configurable() &&
2138 (!shape->hasDefaultGetter() || !shape->hasDefaultSetter())) {
2139 return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, desc.id, rval);
2142 if (!js_NativeGet(cx, obj, obj2, shape, JSGET_NO_METHOD_BARRIER, &v)) {
2143 /* current was dropped when the failure occurred. */
2144 return JS_FALSE;
2148 if (desc.isDataDescriptor()) {
2149 if (!shape->isDataDescriptor())
2150 break;
2152 JSBool same;
2153 if (desc.hasValue) {
2154 if (!SameValue(cx, desc.value, v, &same))
2155 return JS_FALSE;
2156 if (!same)
2157 break;
2159 if (desc.hasWritable && desc.writable() != shape->writable())
2160 break;
2161 } else {
2162 /* The only fields in desc will be handled below. */
2163 JS_ASSERT(desc.isGenericDescriptor());
2167 if (desc.hasConfigurable && desc.configurable() != shape->configurable())
2168 break;
2169 if (desc.hasEnumerable && desc.enumerable() != shape->enumerable())
2170 break;
2172 /* The conditions imposed by step 5 or step 6 apply. */
2173 *rval = true;
2174 return JS_TRUE;
2175 } while (0);
2177 /* 8.12.9 step 7. */
2178 if (!shape->configurable()) {
2180 * Since [[Configurable]] defaults to false, we don't need to check
2181 * whether it was specified. We can't do likewise for [[Enumerable]]
2182 * because its putative value is used in a comparison -- a comparison
2183 * whose result must always be false per spec if the [[Enumerable]]
2184 * field is not present. Perfectly pellucid logic, eh?
2186 JS_ASSERT_IF(!desc.hasConfigurable, !desc.configurable());
2187 if (desc.configurable() ||
2188 (desc.hasEnumerable && desc.enumerable() != shape->enumerable())) {
2189 return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, desc.id, rval);
2193 bool callDelProperty = false;
2195 if (desc.isGenericDescriptor()) {
2196 /* 8.12.9 step 8, no validation required */
2197 } else if (desc.isDataDescriptor() != shape->isDataDescriptor()) {
2198 /* 8.12.9 step 9. */
2199 if (!shape->configurable())
2200 return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, desc.id, rval);
2201 } else if (desc.isDataDescriptor()) {
2202 /* 8.12.9 step 10. */
2203 JS_ASSERT(shape->isDataDescriptor());
2204 if (!shape->configurable() && !shape->writable()) {
2205 if (desc.hasWritable && desc.writable())
2206 return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, desc.id, rval);
2207 if (desc.hasValue) {
2208 JSBool same;
2209 if (!SameValue(cx, desc.value, v, &same))
2210 return JS_FALSE;
2211 if (!same)
2212 return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, desc.id, rval);
2216 callDelProperty = !shape->hasDefaultGetter() || !shape->hasDefaultSetter();
2217 } else {
2218 /* 8.12.9 step 11. */
2219 JS_ASSERT(desc.isAccessorDescriptor() && shape->isAccessorDescriptor());
2220 if (!shape->configurable()) {
2221 if (desc.hasSet) {
2222 JSBool same;
2223 if (!SameValue(cx, desc.setterValue(), shape->setterOrUndefined(), &same))
2224 return JS_FALSE;
2225 if (!same)
2226 return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, desc.id, rval);
2229 if (desc.hasGet) {
2230 JSBool same;
2231 if (!SameValue(cx, desc.getterValue(), shape->getterOrUndefined(), &same))
2232 return JS_FALSE;
2233 if (!same)
2234 return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, desc.id, rval);
2239 /* 8.12.9 step 12. */
2240 uintN attrs;
2241 PropertyOp getter, setter;
2242 if (desc.isGenericDescriptor()) {
2243 uintN changed = 0;
2244 if (desc.hasConfigurable)
2245 changed |= JSPROP_PERMANENT;
2246 if (desc.hasEnumerable)
2247 changed |= JSPROP_ENUMERATE;
2249 attrs = (shape->attributes() & ~changed) | (desc.attrs & changed);
2250 if (shape->isMethod()) {
2251 JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
2252 getter = setter = PropertyStub;
2253 } else {
2254 getter = shape->getter();
2255 setter = shape->setter();
2257 } else if (desc.isDataDescriptor()) {
2258 uintN unchanged = 0;
2259 if (!desc.hasConfigurable)
2260 unchanged |= JSPROP_PERMANENT;
2261 if (!desc.hasEnumerable)
2262 unchanged |= JSPROP_ENUMERATE;
2263 if (!desc.hasWritable)
2264 unchanged |= JSPROP_READONLY;
2266 if (desc.hasValue)
2267 v = desc.value;
2268 attrs = (desc.attrs & ~unchanged) | (shape->attributes() & unchanged);
2269 getter = setter = PropertyStub;
2270 } else {
2271 JS_ASSERT(desc.isAccessorDescriptor());
2274 * Getters and setters are just like watchpoints from an access
2275 * control point of view.
2277 Value dummy;
2278 if (!CheckAccess(cx, obj2, desc.id, JSACC_WATCH, &dummy, &attrs))
2279 return JS_FALSE;
2281 JS_ASSERT_IF(shape->isMethod(), !(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
2283 /* 8.12.9 step 12. */
2284 uintN changed = 0;
2285 if (desc.hasConfigurable)
2286 changed |= JSPROP_PERMANENT;
2287 if (desc.hasEnumerable)
2288 changed |= JSPROP_ENUMERATE;
2289 if (desc.hasGet)
2290 changed |= JSPROP_GETTER | JSPROP_SHARED;
2291 if (desc.hasSet)
2292 changed |= JSPROP_SETTER | JSPROP_SHARED;
2294 attrs = (desc.attrs & changed) | (shape->attributes() & ~changed);
2295 if (desc.hasGet) {
2296 getter = desc.getter();
2297 } else {
2298 getter = (shape->isMethod() || (shape->hasDefaultGetter() && !shape->hasGetterValue()))
2299 ? PropertyStub
2300 : shape->getter();
2302 if (desc.hasSet) {
2303 setter = desc.setter();
2304 } else {
2305 setter = (shape->hasDefaultSetter() && !shape->hasSetterValue())
2306 ? PropertyStub
2307 : shape->setter();
2311 *rval = true;
2314 * Since "data" properties implemented using native C functions may rely on
2315 * side effects during setting, we must make them aware that they have been
2316 * "assigned"; deleting the property before redefining it does the trick.
2317 * See bug 539766, where we ran into problems when we redefined
2318 * arguments.length without making the property aware that its value had
2319 * been changed (which would have happened if we had deleted it before
2320 * redefining it or we had invoked its setter to change its value).
2322 if (callDelProperty) {
2323 Value dummy;
2324 if (!CallJSPropertyOp(cx, obj2->getClass()->delProperty, obj2, desc.id, &dummy))
2325 return false;
2328 return js_DefineProperty(cx, obj, desc.id, &v, getter, setter, attrs);
2331 static JSBool
2332 DefinePropertyOnArray(JSContext *cx, JSObject *obj, const PropDesc &desc,
2333 bool throwError, bool *rval)
2336 * We probably should optimize dense array property definitions where
2337 * the descriptor describes a traditional array property (enumerable,
2338 * configurable, writable, numeric index or length without altering its
2339 * attributes). Such definitions are probably unlikely, so we don't bother
2340 * for now.
2342 if (obj->isDenseArray() && !obj->makeDenseArraySlow(cx))
2343 return JS_FALSE;
2345 jsuint oldLen = obj->getArrayLength();
2347 if (JSID_IS_ATOM(desc.id, cx->runtime->atomState.lengthAtom)) {
2349 * Our optimization of storage of the length property of arrays makes
2350 * it very difficult to properly implement defining the property. For
2351 * now simply throw an exception (NB: not merely Reject) on any attempt
2352 * to define the "length" property, rather than attempting to implement
2353 * some difficult-for-authors-to-grasp subset of that functionality.
2355 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_DEFINE_ARRAY_LENGTH);
2356 return JS_FALSE;
2359 uint32 index;
2360 if (js_IdIsIndex(desc.id, &index)) {
2362 // Disabled until we support defining "length":
2363 if (index >= oldLen && lengthPropertyNotWritable())
2364 return ThrowTypeError(cx, JSMSG_CANT_APPEND_TO_ARRAY);
2366 if (!DefinePropertyOnObject(cx, obj, desc, false, rval))
2367 return JS_FALSE;
2368 if (!*rval)
2369 return Reject(cx, obj, JSMSG_CANT_DEFINE_ARRAY_INDEX, throwError, rval);
2371 if (index >= oldLen) {
2372 JS_ASSERT(index != UINT32_MAX);
2373 obj->setArrayLength(index + 1);
2376 *rval = true;
2377 return JS_TRUE;
2380 return DefinePropertyOnObject(cx, obj, desc, throwError, rval);
2383 static JSBool
2384 DefineProperty(JSContext *cx, JSObject *obj, const PropDesc &desc, bool throwError,
2385 bool *rval)
2387 if (obj->isArray())
2388 return DefinePropertyOnArray(cx, obj, desc, throwError, rval);
2390 if (obj->getOps()->lookupProperty) {
2391 if (obj->isProxy())
2392 return JSProxy::defineProperty(cx, obj, desc.id, desc.pd);
2393 return Reject(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE, throwError, rval);
2396 return DefinePropertyOnObject(cx, obj, desc, throwError, rval);
2399 JSBool
2400 js_DefineOwnProperty(JSContext *cx, JSObject *obj, jsid id,
2401 const Value &descriptor, JSBool *bp)
2403 AutoPropDescArrayRooter descs(cx);
2404 PropDesc *desc = descs.append();
2405 if (!desc || !desc->initialize(cx, id, descriptor))
2406 return false;
2408 bool rval;
2409 if (!DefineProperty(cx, obj, *desc, true, &rval))
2410 return false;
2411 *bp = !!rval;
2412 return true;
2415 /* ES5 15.2.3.6: Object.defineProperty(O, P, Attributes) */
2416 static JSBool
2417 obj_defineProperty(JSContext* cx, uintN argc, Value* vp)
2419 /* 15.2.3.6 steps 1 and 5. */
2420 JSObject *obj;
2421 if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.defineProperty", &obj))
2422 return JS_FALSE;
2423 vp->setObject(*obj);
2425 /* 15.2.3.6 step 2. */
2426 AutoIdRooter nameidr(cx);
2427 if (!ValueToId(cx, argc >= 2 ? vp[3] : UndefinedValue(), nameidr.addr()))
2428 return JS_FALSE;
2430 /* 15.2.3.6 step 3. */
2431 const Value &descval = argc >= 3 ? vp[4] : UndefinedValue();
2433 /* 15.2.3.6 step 4 */
2434 JSBool junk;
2435 return js_DefineOwnProperty(cx, obj, nameidr.id(), descval, &junk);
2438 static bool
2439 DefineProperties(JSContext *cx, JSObject *obj, JSObject *props)
2441 AutoIdArray ida(cx, JS_Enumerate(cx, props));
2442 if (!ida)
2443 return false;
2445 AutoPropDescArrayRooter descs(cx);
2446 size_t len = ida.length();
2447 for (size_t i = 0; i < len; i++) {
2448 jsid id = ida[i];
2449 PropDesc* desc = descs.append();
2450 AutoValueRooter tvr(cx);
2451 if (!desc ||
2452 !JS_GetPropertyById(cx, props, id, tvr.jsval_addr()) ||
2453 !desc->initialize(cx, id, tvr.value())) {
2454 return false;
2458 bool dummy;
2459 for (size_t i = 0; i < len; i++) {
2460 if (!DefineProperty(cx, obj, descs[i], true, &dummy))
2461 return false;
2464 return true;
2467 extern JSBool
2468 js_PopulateObject(JSContext *cx, JSObject *newborn, JSObject *props)
2470 return DefineProperties(cx, newborn, props);
2473 /* ES5 15.2.3.7: Object.defineProperties(O, Properties) */
2474 static JSBool
2475 obj_defineProperties(JSContext* cx, uintN argc, Value* vp)
2477 /* 15.2.3.6 steps 1 and 5. */
2478 JSObject *obj;
2479 if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.defineProperties", &obj))
2480 return false;
2481 vp->setObject(*obj);
2483 if (argc < 2) {
2484 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
2485 "Object.defineProperties", "0", "s");
2486 return false;
2489 JSObject* props = js_ValueToNonNullObject(cx, vp[3]);
2490 if (!props)
2491 return false;
2492 vp[3].setObject(*props);
2494 return DefineProperties(cx, obj, props);
2497 /* ES5 15.2.3.5: Object.create(O [, Properties]) */
2498 static JSBool
2499 obj_create(JSContext *cx, uintN argc, Value *vp)
2501 if (argc == 0) {
2502 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
2503 "Object.create", "0", "s");
2504 return JS_FALSE;
2507 const Value &v = vp[2];
2508 if (!v.isObjectOrNull()) {
2509 char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NULL);
2510 if (!bytes)
2511 return JS_FALSE;
2512 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_UNEXPECTED_TYPE,
2513 bytes, "not an object or null");
2514 JS_free(cx, bytes);
2515 return JS_FALSE;
2519 * Use the callee's global as the parent of the new object to avoid dynamic
2520 * scoping (i.e., using the caller's global).
2522 JSObject *obj = NewNonFunction<WithProto::Given>(cx, &js_ObjectClass, v.toObjectOrNull(),
2523 vp->toObject().getGlobal());
2524 if (!obj)
2525 return JS_FALSE;
2526 vp->setObject(*obj); /* Root and prepare for eventual return. */
2528 /* 15.2.3.5 step 4. */
2529 if (argc > 1 && !vp[3].isUndefined()) {
2530 if (vp[3].isPrimitive()) {
2531 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_NONNULL_OBJECT);
2532 return JS_FALSE;
2535 JSObject *props = &vp[3].toObject();
2536 AutoIdArray ida(cx, JS_Enumerate(cx, props));
2537 if (!ida)
2538 return JS_FALSE;
2540 AutoPropDescArrayRooter descs(cx);
2541 size_t len = ida.length();
2542 for (size_t i = 0; i < len; i++) {
2543 jsid id = ida[i];
2544 PropDesc *desc = descs.append();
2545 if (!desc || !JS_GetPropertyById(cx, props, id, Jsvalify(&vp[1])) ||
2546 !desc->initialize(cx, id, vp[1])) {
2547 return JS_FALSE;
2551 bool dummy;
2552 for (size_t i = 0; i < len; i++) {
2553 if (!DefineProperty(cx, obj, descs[i], true, &dummy))
2554 return JS_FALSE;
2558 /* 5. Return obj. */
2559 return JS_TRUE;
2562 static JSBool
2563 obj_getOwnPropertyNames(JSContext *cx, uintN argc, Value *vp)
2565 JSObject *obj;
2566 if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.getOwnPropertyNames", &obj))
2567 return false;
2569 AutoIdVector keys(cx);
2570 if (!GetPropertyNames(cx, obj, JSITER_OWNONLY | JSITER_HIDDEN, &keys))
2571 return false;
2573 AutoValueVector vals(cx);
2574 if (!vals.resize(keys.length()))
2575 return false;
2577 for (size_t i = 0, len = keys.length(); i < len; i++) {
2578 jsid id = keys[i];
2579 if (JSID_IS_INT(id)) {
2580 JSString *str = js_ValueToString(cx, Int32Value(JSID_TO_INT(id)));
2581 if (!str)
2582 return false;
2583 vals[i].setString(str);
2584 } else if (JSID_IS_ATOM(id)) {
2585 vals[i].setString(JSID_TO_STRING(id));
2586 } else {
2587 vals[i].setObject(*JSID_TO_OBJECT(id));
2591 JSObject *aobj = NewDenseCopiedArray(cx, vals.length(), vals.begin());
2592 if (!aobj)
2593 return false;
2595 vp->setObject(*aobj);
2596 return true;
2599 static JSBool
2600 obj_isExtensible(JSContext *cx, uintN argc, Value *vp)
2602 JSObject *obj;
2603 if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.isExtensible", &obj))
2604 return false;
2606 vp->setBoolean(obj->isExtensible());
2607 return true;
2610 static JSBool
2611 obj_preventExtensions(JSContext *cx, uintN argc, Value *vp)
2613 JSObject *obj;
2614 if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.preventExtensions", &obj))
2615 return false;
2617 vp->setObject(*obj);
2618 if (!obj->isExtensible())
2619 return true;
2621 AutoIdVector props(cx);
2622 return obj->preventExtensions(cx, &props);
2625 bool
2626 JSObject::sealOrFreeze(JSContext *cx, ImmutabilityType it)
2628 assertSameCompartment(cx, this);
2629 JS_ASSERT(it == SEAL || it == FREEZE);
2631 AutoIdVector props(cx);
2632 if (isExtensible()) {
2633 if (!preventExtensions(cx, &props))
2634 return false;
2635 } else {
2636 if (!GetPropertyNames(cx, this, JSITER_HIDDEN | JSITER_OWNONLY, &props))
2637 return false;
2640 /* preventExtensions must slowify dense arrays, so we can assign to holes without checks. */
2641 JS_ASSERT(!isDenseArray());
2643 for (size_t i = 0, len = props.length(); i < len; i++) {
2644 jsid id = props[i];
2646 uintN attrs;
2647 if (!getAttributes(cx, id, &attrs))
2648 return false;
2650 /* Make all attributes permanent; if freezing, make data attributes read-only. */
2651 uintN new_attrs;
2652 if (it == FREEZE && !(attrs & (JSPROP_GETTER | JSPROP_SETTER)))
2653 new_attrs = JSPROP_PERMANENT | JSPROP_READONLY;
2654 else
2655 new_attrs = JSPROP_PERMANENT;
2657 /* If we already have the attributes we need, skip the setAttributes call. */
2658 if ((attrs | new_attrs) == attrs)
2659 continue;
2661 attrs |= new_attrs;
2662 if (!setAttributes(cx, id, &attrs))
2663 return false;
2666 return true;
2669 static JSBool
2670 obj_freeze(JSContext *cx, uintN argc, Value *vp)
2672 JSObject *obj;
2673 if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.freeze", &obj))
2674 return false;
2676 vp->setObject(*obj);
2678 return obj->freeze(cx);
2681 static JSBool
2682 obj_isFrozen(JSContext *cx, uintN argc, Value *vp)
2684 JSObject *obj;
2685 if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.preventExtensions", &obj))
2686 return false;
2688 vp->setBoolean(false);
2690 if (obj->isExtensible())
2691 return true; /* The JavaScript value returned is false. */
2693 AutoIdVector props(cx);
2694 if (!GetPropertyNames(cx, obj, JSITER_HIDDEN | JSITER_OWNONLY, &props))
2695 return false;
2697 for (size_t i = 0, len = props.length(); i < len; i++) {
2698 jsid id = props[i];
2700 uintN attrs = 0;
2701 if (!obj->getAttributes(cx, id, &attrs))
2702 return false;
2704 /* The property must be non-configurable and either read-only or an accessor. */
2705 if (!(attrs & JSPROP_PERMANENT) ||
2706 !(attrs & (JSPROP_READONLY | JSPROP_GETTER | JSPROP_SETTER)))
2707 return true; /* The JavaScript value returned is false. */
2710 /* It really was sealed, so return true. */
2711 vp->setBoolean(true);
2712 return true;
2715 static JSBool
2716 obj_seal(JSContext *cx, uintN argc, Value *vp)
2718 JSObject *obj;
2719 if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.seal", &obj))
2720 return false;
2722 vp->setObject(*obj);
2724 return obj->seal(cx);
2727 static JSBool
2728 obj_isSealed(JSContext *cx, uintN argc, Value *vp)
2730 JSObject *obj;
2731 if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.isSealed", &obj))
2732 return false;
2734 /* Assume not sealed until proven otherwise. */
2735 vp->setBoolean(false);
2737 if (obj->isExtensible())
2738 return true; /* The JavaScript value returned is false. */
2740 AutoIdVector props(cx);
2741 if (!GetPropertyNames(cx, obj, JSITER_HIDDEN | JSITER_OWNONLY, &props))
2742 return false;
2744 for (size_t i = 0, len = props.length(); i < len; i++) {
2745 jsid id = props[i];
2747 uintN attrs;
2748 if (!obj->getAttributes(cx, id, &attrs))
2749 return false;
2751 if (!(attrs & JSPROP_PERMANENT))
2752 return true; /* The JavaScript value returned is false. */
2755 /* It really was sealed, so return true. */
2756 vp->setBoolean(true);
2757 return true;
2760 #if JS_HAS_OBJ_WATCHPOINT
2761 const char js_watch_str[] = "watch";
2762 const char js_unwatch_str[] = "unwatch";
2763 #endif
2764 const char js_hasOwnProperty_str[] = "hasOwnProperty";
2765 const char js_isPrototypeOf_str[] = "isPrototypeOf";
2766 const char js_propertyIsEnumerable_str[] = "propertyIsEnumerable";
2768 static JSFunctionSpec object_methods[] = {
2769 #if JS_HAS_TOSOURCE
2770 JS_FN(js_toSource_str, obj_toSource, 0,0),
2771 #endif
2772 JS_FN(js_toString_str, obj_toString, 0,0),
2773 JS_FN(js_toLocaleString_str, obj_toLocaleString, 0,0),
2774 JS_FN(js_valueOf_str, obj_valueOf, 0,0),
2775 #if JS_HAS_OBJ_WATCHPOINT
2776 JS_FN(js_watch_str, obj_watch, 2,0),
2777 JS_FN(js_unwatch_str, obj_unwatch, 1,0),
2778 #endif
2779 JS_FN(js_hasOwnProperty_str, obj_hasOwnProperty, 1,0),
2780 JS_FN(js_isPrototypeOf_str, obj_isPrototypeOf, 1,0),
2781 JS_FN(js_propertyIsEnumerable_str, obj_propertyIsEnumerable, 1,0),
2782 #if OLD_GETTER_SETTER_METHODS
2783 JS_FN(js_defineGetter_str, js_obj_defineGetter, 2,0),
2784 JS_FN(js_defineSetter_str, js_obj_defineSetter, 2,0),
2785 JS_FN(js_lookupGetter_str, obj_lookupGetter, 1,0),
2786 JS_FN(js_lookupSetter_str, obj_lookupSetter, 1,0),
2787 #endif
2788 JS_FS_END
2791 static JSFunctionSpec object_static_methods[] = {
2792 JS_FN("getPrototypeOf", obj_getPrototypeOf, 1,0),
2793 JS_FN("getOwnPropertyDescriptor", obj_getOwnPropertyDescriptor,2,0),
2794 JS_FN("keys", obj_keys, 1,0),
2795 JS_FN("defineProperty", obj_defineProperty, 3,0),
2796 JS_FN("defineProperties", obj_defineProperties, 2,0),
2797 JS_FN("create", obj_create, 2,0),
2798 JS_FN("getOwnPropertyNames", obj_getOwnPropertyNames, 1,0),
2799 JS_FN("isExtensible", obj_isExtensible, 1,0),
2800 JS_FN("preventExtensions", obj_preventExtensions, 1,0),
2801 JS_FN("freeze", obj_freeze, 1,0),
2802 JS_FN("isFrozen", obj_isFrozen, 1,0),
2803 JS_FN("seal", obj_seal, 1,0),
2804 JS_FN("isSealed", obj_isSealed, 1,0),
2805 JS_FS_END
2808 JSBool
2809 js_Object(JSContext *cx, uintN argc, Value *vp)
2811 JSObject *obj;
2812 if (argc == 0) {
2813 /* Trigger logic below to construct a blank object. */
2814 obj = NULL;
2815 } else {
2816 /* If argv[0] is null or undefined, obj comes back null. */
2817 if (!js_ValueToObjectOrNull(cx, vp[2], &obj))
2818 return JS_FALSE;
2820 if (!obj) {
2821 /* Make an object whether this was called with 'new' or not. */
2822 JS_ASSERT(!argc || vp[2].isNull() || vp[2].isUndefined());
2823 gc::FinalizeKind kind = NewObjectGCKind(cx, &js_ObjectClass);
2824 obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind);
2825 if (!obj)
2826 return JS_FALSE;
2828 vp->setObject(*obj);
2829 return JS_TRUE;
2832 JSObject*
2833 js_CreateThis(JSContext *cx, JSObject *callee)
2835 Class *clasp = callee->getClass();
2837 Class *newclasp = &js_ObjectClass;
2838 if (clasp == &js_FunctionClass) {
2839 JSFunction *fun = callee->getFunctionPrivate();
2840 if (fun->isNative() && fun->u.n.clasp)
2841 newclasp = fun->u.n.clasp;
2844 Value protov;
2845 if (!callee->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), &protov))
2846 return NULL;
2848 JSObject *proto = protov.isObjectOrNull() ? protov.toObjectOrNull() : NULL;
2849 JSObject *parent = callee->getParent();
2850 gc::FinalizeKind kind = NewObjectGCKind(cx, newclasp);
2851 JSObject *obj = NewObject<WithProto::Class>(cx, newclasp, proto, parent, kind);
2852 if (obj)
2853 obj->syncSpecialEquality();
2854 return obj;
2857 JSObject *
2858 js_CreateThisForFunctionWithProto(JSContext *cx, JSObject *callee, JSObject *proto)
2860 gc::FinalizeKind kind = NewObjectGCKind(cx, &js_ObjectClass);
2861 return NewNonFunction<WithProto::Class>(cx, &js_ObjectClass, proto, callee->getParent(), kind);
2864 JSObject *
2865 js_CreateThisForFunction(JSContext *cx, JSObject *callee)
2867 Value protov;
2868 if (!callee->getProperty(cx,
2869 ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom),
2870 &protov)) {
2871 return NULL;
2873 JSObject *proto = protov.isObject() ? &protov.toObject() : NULL;
2874 return js_CreateThisForFunctionWithProto(cx, callee, proto);
2877 #ifdef JS_TRACER
2879 static JS_ALWAYS_INLINE JSObject*
2880 NewObjectWithClassProto(JSContext *cx, Class *clasp, JSObject *proto,
2881 /*gc::FinalizeKind*/ unsigned _kind)
2883 JS_ASSERT(clasp->isNative());
2884 gc::FinalizeKind kind = gc::FinalizeKind(_kind);
2886 JSObject* obj = js_NewGCObject(cx, kind);
2887 if (!obj)
2888 return NULL;
2890 if (!obj->initSharingEmptyShape(cx, clasp, proto, proto->getParent(), NULL, kind))
2891 return NULL;
2892 return obj;
2895 JSObject* FASTCALL
2896 js_Object_tn(JSContext* cx, JSObject* proto)
2898 JS_ASSERT(!(js_ObjectClass.flags & JSCLASS_HAS_PRIVATE));
2899 return NewObjectWithClassProto(cx, &js_ObjectClass, proto, FINALIZE_OBJECT8);
2902 JS_DEFINE_TRCINFO_1(js_Object,
2903 (2, (extern, CONSTRUCTOR_RETRY, js_Object_tn, CONTEXT, CALLEE_PROTOTYPE, 0,
2904 nanojit::ACCSET_STORE_ANY)))
2906 JSObject* FASTCALL
2907 js_InitializerObject(JSContext* cx, JSObject *proto, JSObject *baseobj)
2909 if (!baseobj) {
2910 gc::FinalizeKind kind = GuessObjectGCKind(0, false);
2911 return NewObjectWithClassProto(cx, &js_ObjectClass, proto, kind);
2914 return CopyInitializerObject(cx, baseobj);
2917 JS_DEFINE_CALLINFO_3(extern, OBJECT, js_InitializerObject, CONTEXT, OBJECT, OBJECT,
2918 0, nanojit::ACCSET_STORE_ANY)
2920 JSObject* FASTCALL
2921 js_String_tn(JSContext* cx, JSObject* proto, JSString* str)
2923 JS_ASSERT(JS_ON_TRACE(cx));
2924 JSObject *obj = NewObjectWithClassProto(cx, &js_StringClass, proto, FINALIZE_OBJECT2);
2925 if (!obj)
2926 return NULL;
2927 obj->setPrimitiveThis(StringValue(str));
2928 return obj;
2930 JS_DEFINE_CALLINFO_3(extern, OBJECT, js_String_tn, CONTEXT, CALLEE_PROTOTYPE, STRING, 0,
2931 nanojit::ACCSET_STORE_ANY)
2933 JSObject* FASTCALL
2934 js_CreateThisFromTrace(JSContext *cx, Class *clasp, JSObject *ctor)
2936 JS_ASSERT(JS_ON_TRACE(cx));
2937 JS_ASSERT(ctor->isFunction());
2939 if (!ctor->ensureClassReservedSlots(cx))
2940 return NULL;
2942 jsid classPrototypeId = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom);
2943 const Shape *shape = ctor->nativeLookup(classPrototypeId);
2944 Value pval = shape ? ctor->getSlot(shape->slot) : MagicValue(JS_GENERIC_MAGIC);
2946 JSObject *parent = ctor->getParent();
2947 JSObject *proto;
2948 if (pval.isObject()) {
2949 /* An object in ctor.prototype, let's use it as the new instance's proto. */
2950 proto = &pval.toObject();
2951 } else {
2952 /* A hole or a primitive: either way, we need to get Object.prototype. */
2953 if (!js_GetClassPrototype(cx, parent, JSProto_Object, &proto))
2954 return NULL;
2956 if (pval.isMagic(JS_GENERIC_MAGIC)) {
2958 * No ctor.prototype was set, so we inline-expand and optimize
2959 * fun_resolve's prototype creation code.
2961 proto = NewNativeClassInstance(cx, clasp, proto, parent);
2962 if (!proto)
2963 return NULL;
2964 JSFunction *fun = ctor->getFunctionPrivate();
2965 if (!fun->isNative() && !fun->isFunctionPrototype()) {
2966 if (!js_SetClassPrototype(cx, ctor, proto, JSPROP_ENUMERATE | JSPROP_PERMANENT))
2967 return NULL;
2969 } else {
2971 * A primitive value in .prototype means to use Object.prototype
2972 * for proto. See ES5 13.2.2 step 7.
2978 * FIXME: 561785 at least. Quasi-natives including XML objects prevent us
2979 * from easily or unconditionally calling NewNativeClassInstance here.
2981 gc::FinalizeKind kind = NewObjectGCKind(cx, clasp);
2982 return NewNonFunction<WithProto::Given>(cx, clasp, proto, parent, kind);
2985 JS_DEFINE_CALLINFO_3(extern, CONSTRUCTOR_RETRY, js_CreateThisFromTrace, CONTEXT, CLASS, OBJECT, 0,
2986 nanojit::ACCSET_STORE_ANY)
2988 #else /* !JS_TRACER */
2990 # define js_Object_trcinfo NULL
2992 #endif /* !JS_TRACER */
2995 * Given pc pointing after a property accessing bytecode, return true if the
2996 * access is "object-detecting" in the sense used by web scripts, e.g., when
2997 * checking whether document.all is defined.
2999 JS_REQUIRES_STACK JSBool
3000 Detecting(JSContext *cx, jsbytecode *pc)
3002 JSScript *script;
3003 jsbytecode *endpc;
3004 JSOp op;
3005 JSAtom *atom;
3007 script = cx->fp()->script();
3008 endpc = script->code + script->length;
3009 for (;; pc += js_CodeSpec[op].length) {
3010 JS_ASSERT_IF(!cx->fp()->hasImacropc(), script->code <= pc && pc < endpc);
3012 /* General case: a branch or equality op follows the access. */
3013 op = js_GetOpcode(cx, script, pc);
3014 if (js_CodeSpec[op].format & JOF_DETECTING)
3015 return JS_TRUE;
3017 switch (op) {
3018 case JSOP_NULL:
3020 * Special case #1: handle (document.all == null). Don't sweat
3021 * about JS1.2's revision of the equality operators here.
3023 if (++pc < endpc) {
3024 op = js_GetOpcode(cx, script, pc);
3025 return *pc == JSOP_EQ || *pc == JSOP_NE;
3027 return JS_FALSE;
3029 case JSOP_GETGNAME:
3030 case JSOP_NAME:
3032 * Special case #2: handle (document.all == undefined). Don't
3033 * worry about someone redefining undefined, which was added by
3034 * Edition 3, so is read/write for backward compatibility.
3036 GET_ATOM_FROM_BYTECODE(script, pc, 0, atom);
3037 if (atom == cx->runtime->atomState.typeAtoms[JSTYPE_VOID] &&
3038 (pc += js_CodeSpec[op].length) < endpc) {
3039 op = js_GetOpcode(cx, script, pc);
3040 return op == JSOP_EQ || op == JSOP_NE ||
3041 op == JSOP_STRICTEQ || op == JSOP_STRICTNE;
3043 return JS_FALSE;
3045 default:
3047 * At this point, anything but an extended atom index prefix means
3048 * we're not detecting.
3050 if (!(js_CodeSpec[op].format & JOF_INDEXBASE))
3051 return JS_FALSE;
3052 break;
3058 * Infer lookup flags from the currently executing bytecode. This does
3059 * not attempt to infer JSRESOLVE_WITH, because the current bytecode
3060 * does not indicate whether we are in a with statement. Return defaultFlags
3061 * if a currently executing bytecode cannot be determined.
3063 uintN
3064 js_InferFlags(JSContext *cx, uintN defaultFlags)
3066 #ifdef JS_TRACER
3067 if (JS_ON_TRACE(cx))
3068 return JS_TRACE_MONITOR(cx).bailExit->lookupFlags;
3069 #endif
3071 JS_ASSERT_NOT_ON_TRACE(cx);
3073 jsbytecode *pc;
3074 const JSCodeSpec *cs;
3075 uint32 format;
3076 uintN flags = 0;
3078 JSStackFrame *const fp = js_GetTopStackFrame(cx);
3079 if (!fp || !(pc = cx->regs->pc))
3080 return defaultFlags;
3081 cs = &js_CodeSpec[js_GetOpcode(cx, fp->script(), pc)];
3082 format = cs->format;
3083 if (JOF_MODE(format) != JOF_NAME)
3084 flags |= JSRESOLVE_QUALIFIED;
3085 if ((format & (JOF_SET | JOF_FOR)) || fp->isAssigning()) {
3086 flags |= JSRESOLVE_ASSIGNING;
3087 } else if (cs->length >= 0) {
3088 pc += cs->length;
3089 JSScript *script = cx->fp()->script();
3090 if (pc < script->code + script->length && Detecting(cx, pc))
3091 flags |= JSRESOLVE_DETECTING;
3093 if (format & JOF_DECLARING)
3094 flags |= JSRESOLVE_DECLARING;
3095 return flags;
3099 * ObjectOps and Class for with-statement stack objects.
3101 static JSBool
3102 with_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
3103 JSProperty **propp)
3105 /* Fixes bug 463997 */
3106 uintN flags = cx->resolveFlags;
3107 if (flags == JSRESOLVE_INFER)
3108 flags = js_InferFlags(cx, flags);
3109 flags |= JSRESOLVE_WITH;
3110 JSAutoResolveFlags rf(cx, flags);
3111 return obj->getProto()->lookupProperty(cx, id, objp, propp);
3114 static JSBool
3115 with_GetProperty(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp)
3117 return obj->getProto()->getProperty(cx, id, vp);
3120 static JSBool
3121 with_SetProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict)
3123 return obj->getProto()->setProperty(cx, id, vp, strict);
3126 static JSBool
3127 with_GetAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp)
3129 return obj->getProto()->getAttributes(cx, id, attrsp);
3132 static JSBool
3133 with_SetAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp)
3135 return obj->getProto()->setAttributes(cx, id, attrsp);
3138 static JSBool
3139 with_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool strict)
3141 return obj->getProto()->deleteProperty(cx, id, rval, strict);
3144 static JSBool
3145 with_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
3146 Value *statep, jsid *idp)
3148 return obj->getProto()->enumerate(cx, enum_op, statep, idp);
3151 static JSType
3152 with_TypeOf(JSContext *cx, JSObject *obj)
3154 return JSTYPE_OBJECT;
3157 static JSObject *
3158 with_ThisObject(JSContext *cx, JSObject *obj)
3160 return obj->getWithThis();
3163 Class js_WithClass = {
3164 "With",
3165 JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS,
3166 PropertyStub, /* addProperty */
3167 PropertyStub, /* delProperty */
3168 PropertyStub, /* getProperty */
3169 PropertyStub, /* setProperty */
3170 EnumerateStub,
3171 ResolveStub,
3172 ConvertStub,
3173 NULL, /* finalize */
3174 NULL, /* reserved */
3175 NULL, /* checkAccess */
3176 NULL, /* call */
3177 NULL, /* construct */
3178 NULL, /* xdrObject */
3179 NULL, /* hasInstance */
3180 NULL, /* mark */
3181 JS_NULL_CLASS_EXT,
3183 with_LookupProperty,
3184 NULL, /* defineProperty */
3185 with_GetProperty,
3186 with_SetProperty,
3187 with_GetAttributes,
3188 with_SetAttributes,
3189 with_DeleteProperty,
3190 with_Enumerate,
3191 with_TypeOf,
3192 NULL, /* trace */
3193 NULL, /* fix */
3194 with_ThisObject,
3195 NULL, /* clear */
3199 JS_REQUIRES_STACK JSObject *
3200 js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth)
3202 JSObject *obj;
3204 obj = js_NewGCObject(cx, FINALIZE_OBJECT2);
3205 if (!obj)
3206 return NULL;
3208 JSStackFrame *priv = js_FloatingFrameIfGenerator(cx, cx->fp());
3210 obj->init(cx, &js_WithClass, proto, parent, priv, false);
3211 obj->setMap(cx->runtime->emptyWithShape);
3212 OBJ_SET_BLOCK_DEPTH(cx, obj, depth);
3214 AutoObjectRooter tvr(cx, obj);
3215 JSObject *thisp = proto->thisObject(cx);
3216 if (!thisp)
3217 return NULL;
3219 assertSameCompartment(cx, obj, thisp);
3221 obj->setWithThis(thisp);
3222 return obj;
3225 JSObject *
3226 js_NewBlockObject(JSContext *cx)
3229 * Null obj's proto slot so that Object.prototype.* does not pollute block
3230 * scopes and to give the block object its own scope.
3232 JSObject *blockObj = js_NewGCObject(cx, FINALIZE_OBJECT2);
3233 if (!blockObj)
3234 return NULL;
3236 blockObj->init(cx, &js_BlockClass, NULL, NULL, NULL, false);
3237 blockObj->setMap(cx->runtime->emptyBlockShape);
3238 return blockObj;
3241 JSObject *
3242 js_CloneBlockObject(JSContext *cx, JSObject *proto, JSStackFrame *fp)
3244 JS_ASSERT(proto->isStaticBlock());
3246 size_t count = OBJ_BLOCK_COUNT(cx, proto);
3247 gc::FinalizeKind kind = gc::GetGCObjectKind(count + 1);
3249 JSObject *clone = js_NewGCObject(cx, kind);
3250 if (!clone)
3251 return NULL;
3253 JSStackFrame *priv = js_FloatingFrameIfGenerator(cx, fp);
3255 /* The caller sets parent on its own. */
3256 clone->init(cx, &js_BlockClass, proto, NULL, priv, false);
3258 clone->setMap(proto->map);
3259 if (!clone->ensureInstanceReservedSlots(cx, count + 1))
3260 return NULL;
3262 clone->setSlot(JSSLOT_BLOCK_DEPTH, proto->getSlot(JSSLOT_BLOCK_DEPTH));
3264 JS_ASSERT(clone->isClonedBlock());
3265 return clone;
3268 JS_REQUIRES_STACK JSBool
3269 js_PutBlockObject(JSContext *cx, JSBool normalUnwind)
3271 JSStackFrame *const fp = cx->fp();
3272 JSObject *obj = &fp->scopeChain();
3273 JS_ASSERT(obj->isClonedBlock());
3274 JS_ASSERT(obj->getPrivate() == js_FloatingFrameIfGenerator(cx, cx->fp()));
3276 /* Block objects should have all reserved slots allocated early. */
3277 uintN count = OBJ_BLOCK_COUNT(cx, obj);
3278 JS_ASSERT(obj->numSlots() >= JSSLOT_BLOCK_DEPTH + 1 + count);
3280 /* The block and its locals must be on the current stack for GC safety. */
3281 uintN depth = OBJ_BLOCK_DEPTH(cx, obj);
3282 JS_ASSERT(depth <= size_t(cx->regs->sp - fp->base()));
3283 JS_ASSERT(count <= size_t(cx->regs->sp - fp->base() - depth));
3285 /* See comments in CheckDestructuring from jsparse.cpp. */
3286 JS_ASSERT(count >= 1);
3288 if (normalUnwind) {
3289 uintN slot = JSSLOT_BLOCK_FIRST_FREE_SLOT;
3290 depth += fp->numFixed();
3291 memcpy(obj->getSlots() + slot, fp->slots() + depth, count * sizeof(Value));
3294 /* We must clear the private slot even with errors. */
3295 obj->setPrivate(NULL);
3296 fp->setScopeChainNoCallObj(*obj->getParent());
3297 return normalUnwind;
3300 static JSBool
3301 block_getProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
3304 * Block objects are never exposed to script, and the engine handles them
3305 * with care. So unlike other getters, this one can assert (rather than
3306 * check) certain invariants about obj.
3308 JS_ASSERT(obj->isClonedBlock());
3309 uintN index = (uintN) JSID_TO_INT(id);
3310 JS_ASSERT(index < OBJ_BLOCK_COUNT(cx, obj));
3312 JSStackFrame *fp = (JSStackFrame *) obj->getPrivate();
3313 if (fp) {
3314 fp = js_LiveFrameIfGenerator(fp);
3315 index += fp->numFixed() + OBJ_BLOCK_DEPTH(cx, obj);
3316 JS_ASSERT(index < fp->numSlots());
3317 *vp = fp->slots()[index];
3318 return true;
3321 /* Values are in slots immediately following the class-reserved ones. */
3322 JS_ASSERT(obj->getSlot(JSSLOT_FREE(&js_BlockClass) + index) == *vp);
3323 return true;
3326 static JSBool
3327 block_setProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
3329 JS_ASSERT(obj->isClonedBlock());
3330 uintN index = (uintN) JSID_TO_INT(id);
3331 JS_ASSERT(index < OBJ_BLOCK_COUNT(cx, obj));
3333 JSStackFrame *fp = (JSStackFrame *) obj->getPrivate();
3334 if (fp) {
3335 fp = js_LiveFrameIfGenerator(fp);
3336 index += fp->numFixed() + OBJ_BLOCK_DEPTH(cx, obj);
3337 JS_ASSERT(index < fp->numSlots());
3338 fp->slots()[index] = *vp;
3339 return true;
3343 * The value in *vp will be written back to the slot in obj that was
3344 * allocated when this let binding was defined.
3346 return true;
3349 const Shape *
3350 JSObject::defineBlockVariable(JSContext *cx, jsid id, intN index)
3352 JS_ASSERT(isStaticBlock());
3354 /* Use JSPROP_ENUMERATE to aid the disassembler. */
3355 uint32 slot = JSSLOT_FREE(&js_BlockClass) + index;
3356 const Shape *shape = addProperty(cx, id,
3357 block_getProperty, block_setProperty,
3358 slot, JSPROP_ENUMERATE | JSPROP_PERMANENT,
3359 Shape::HAS_SHORTID, index);
3360 if (!shape)
3361 return NULL;
3362 if (slot >= numSlots() && !growSlots(cx, slot + 1))
3363 return NULL;
3364 return shape;
3367 static size_t
3368 GetObjectSize(JSObject *obj)
3370 return (obj->isFunction() && !obj->getPrivate())
3371 ? sizeof(JSFunction)
3372 : sizeof(JSObject) + sizeof(js::Value) * obj->numFixedSlots();
3375 bool
3376 JSObject::copyPropertiesFrom(JSContext *cx, JSObject *obj)
3378 // If we're not native, then we cannot copy properties.
3379 JS_ASSERT(isNative() == obj->isNative());
3380 if (!isNative())
3381 return true;
3383 Vector<const Shape *> shapes(cx);
3384 for (Shape::Range r(obj->lastProperty()); !r.empty(); r.popFront()) {
3385 if (!shapes.append(&r.front()))
3386 return false;
3389 size_t n = shapes.length();
3390 while (n > 0) {
3391 const Shape *shape = shapes[--n];
3392 uintN attrs = shape->attributes();
3393 PropertyOp getter = shape->getter();
3394 if ((attrs & JSPROP_GETTER) && !cx->compartment->wrap(cx, &getter))
3395 return false;
3396 PropertyOp setter = shape->setter();
3397 if ((attrs & JSPROP_SETTER) && !cx->compartment->wrap(cx, &setter))
3398 return false;
3399 Value v = shape->hasSlot() ? obj->getSlot(shape->slot) : UndefinedValue();
3400 if (!cx->compartment->wrap(cx, &v))
3401 return false;
3402 if (!defineProperty(cx, shape->id, v, getter, setter, attrs))
3403 return false;
3405 return true;
3408 static bool
3409 CopySlots(JSContext *cx, JSObject *from, JSObject *to)
3411 JS_ASSERT(!from->isNative() && !to->isNative());
3412 size_t nslots = from->numSlots();
3413 if (to->ensureSlots(cx, nslots))
3414 return false;
3416 size_t n = 0;
3417 if (to->isWrapper() &&
3418 (JSWrapper::wrapperHandler(to)->flags() & JSWrapper::CROSS_COMPARTMENT)) {
3419 to->slots[0] = from->slots[0];
3420 to->slots[1] = from->slots[1];
3421 n = 2;
3424 for (; n < nslots; ++n) {
3425 Value v = from->slots[n];
3426 if (!cx->compartment->wrap(cx, &v))
3427 return false;
3428 to->slots[n] = v;
3430 return true;
3433 JSObject *
3434 JSObject::clone(JSContext *cx, JSObject *proto, JSObject *parent)
3437 * We can only clone native objects and proxies. Dense arrays are slowified if
3438 * we try to clone them.
3440 if (!isNative()) {
3441 if (isDenseArray()) {
3442 if (!makeDenseArraySlow(cx))
3443 return NULL;
3444 } else if (!isProxy()) {
3445 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3446 JSMSG_CANT_CLONE_OBJECT);
3447 return NULL;
3450 JSObject *clone = NewObject<WithProto::Given>(cx, getClass(),
3451 proto, parent,
3452 gc::FinalizeKind(finalizeKind()));
3453 if (!clone)
3454 return NULL;
3455 if (isNative()) {
3456 if (clone->isFunction() && (compartment() != clone->compartment())) {
3457 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3458 JSMSG_CANT_CLONE_OBJECT);
3459 return NULL;
3462 if (getClass()->flags & JSCLASS_HAS_PRIVATE)
3463 clone->setPrivate(getPrivate());
3464 } else {
3465 JS_ASSERT(isProxy());
3466 if (!CopySlots(cx, this, clone))
3467 return NULL;
3469 return clone;
3472 static void
3473 TradeGuts(JSObject *a, JSObject *b)
3475 JS_ASSERT(a->compartment() == b->compartment());
3476 JS_ASSERT(a->isFunction() == b->isFunction());
3479 * Regexp guts are more complicated -- we would need to migrate the
3480 * refcounted JIT code blob for them across compartments instead of just
3481 * swapping guts.
3483 JS_ASSERT(!a->isRegExp() && !b->isRegExp());
3485 bool aInline = !a->hasSlotsArray();
3486 bool bInline = !b->hasSlotsArray();
3488 /* Trade the guts of the objects. */
3489 const size_t size = GetObjectSize(a);
3490 if (size == GetObjectSize(b)) {
3492 * If the objects are the same size, then we make no assumptions about
3493 * whether they have dynamically allocated slots and instead just copy
3494 * them over wholesale.
3496 char tmp[tl::Max<sizeof(JSFunction), sizeof(JSObject_Slots16)>::result];
3497 JS_ASSERT(size <= sizeof(tmp));
3499 memcpy(tmp, a, size);
3500 memcpy(a, b, size);
3501 memcpy(b, tmp, size);
3503 /* Fixup pointers for inline slots on the objects. */
3504 if (aInline)
3505 b->slots = b->fixedSlots();
3506 if (bInline)
3507 a->slots = a->fixedSlots();
3508 } else {
3510 * If the objects are of differing sizes, then we only copy over the
3511 * JSObject portion (things like class, etc.) and leave it to
3512 * JSObject::clone to copy over the dynamic slots for us.
3514 if (a->isFunction()) {
3515 JSFunction tmp;
3516 memcpy(&tmp, a, sizeof tmp);
3517 memcpy(a, b, sizeof tmp);
3518 memcpy(b, &tmp, sizeof tmp);
3519 } else {
3520 JSObject tmp;
3521 memcpy(&tmp, a, sizeof tmp);
3522 memcpy(a, b, sizeof tmp);
3523 memcpy(b, &tmp, sizeof tmp);
3526 JS_ASSERT(!aInline);
3527 JS_ASSERT(!bInline);
3532 * Use this method with extreme caution. It trades the guts of two objects and updates
3533 * scope ownership. This operation is not thread-safe, just as fast array to slow array
3534 * transitions are inherently not thread-safe. Don't perform a swap operation on objects
3535 * shared across threads or, or bad things will happen. You have been warned.
3537 bool
3538 JSObject::swap(JSContext *cx, JSObject *other)
3541 * If we are swapping objects with a different number of builtin slots, force
3542 * both to not use their inline slots.
3544 if (GetObjectSize(this) != GetObjectSize(other)) {
3545 if (!hasSlotsArray()) {
3546 if (!allocSlots(cx, numSlots()))
3547 return false;
3549 if (!other->hasSlotsArray()) {
3550 if (!other->allocSlots(cx, other->numSlots()))
3551 return false;
3555 if (this->compartment() == other->compartment()) {
3556 TradeGuts(this, other);
3557 return true;
3560 JSObject *thisClone;
3561 JSObject *otherClone;
3563 AutoCompartment ac(cx, other);
3564 if (!ac.enter())
3565 return false;
3566 thisClone = this->clone(cx, other->getProto(), other->getParent());
3567 if (!thisClone || !thisClone->copyPropertiesFrom(cx, this))
3568 return false;
3571 AutoCompartment ac(cx, this);
3572 if (!ac.enter())
3573 return false;
3574 otherClone = other->clone(cx, other->getProto(), other->getParent());
3575 if (!otherClone || !otherClone->copyPropertiesFrom(cx, other))
3576 return false;
3578 TradeGuts(this, otherClone);
3579 TradeGuts(other, thisClone);
3581 return true;
3584 #if JS_HAS_XDR
3586 #define NO_PARENT_INDEX ((uint32)-1)
3588 uint32
3589 FindObjectIndex(JSObjectArray *array, JSObject *obj)
3591 size_t i;
3593 if (array) {
3594 i = array->length;
3595 do {
3597 if (array->vector[--i] == obj)
3598 return i;
3599 } while (i != 0);
3602 return NO_PARENT_INDEX;
3605 JSBool
3606 js_XDRBlockObject(JSXDRState *xdr, JSObject **objp)
3608 JSContext *cx;
3609 uint32 parentId;
3610 JSObject *obj, *parent;
3611 uintN depth, count;
3612 uint32 depthAndCount;
3613 const Shape *shape;
3615 cx = xdr->cx;
3616 #ifdef __GNUC__
3617 obj = NULL; /* quell GCC overwarning */
3618 #endif
3620 if (xdr->mode == JSXDR_ENCODE) {
3621 obj = *objp;
3622 parent = obj->getParent();
3623 parentId = JSScript::isValidOffset(xdr->script->objectsOffset)
3624 ? FindObjectIndex(xdr->script->objects(), parent)
3625 : NO_PARENT_INDEX;
3626 depth = (uint16)OBJ_BLOCK_DEPTH(cx, obj);
3627 count = (uint16)OBJ_BLOCK_COUNT(cx, obj);
3628 depthAndCount = (uint32)(depth << 16) | count;
3630 #ifdef __GNUC__ /* suppress bogus gcc warnings */
3631 else count = 0;
3632 #endif
3634 /* First, XDR the parent atomid. */
3635 if (!JS_XDRUint32(xdr, &parentId))
3636 return JS_FALSE;
3638 if (xdr->mode == JSXDR_DECODE) {
3639 obj = js_NewBlockObject(cx);
3640 if (!obj)
3641 return JS_FALSE;
3642 *objp = obj;
3645 * If there's a parent id, then get the parent out of our script's
3646 * object array. We know that we XDR block object in outer-to-inner
3647 * order, which means that getting the parent now will work.
3649 if (parentId == NO_PARENT_INDEX)
3650 parent = NULL;
3651 else
3652 parent = xdr->script->getObject(parentId);
3653 obj->setParent(parent);
3656 AutoObjectRooter tvr(cx, obj);
3658 if (!JS_XDRUint32(xdr, &depthAndCount))
3659 return false;
3661 if (xdr->mode == JSXDR_DECODE) {
3662 depth = (uint16)(depthAndCount >> 16);
3663 count = (uint16)depthAndCount;
3664 obj->setSlot(JSSLOT_BLOCK_DEPTH, Value(Int32Value(depth)));
3667 * XDR the block object's properties. We know that there are 'count'
3668 * properties to XDR, stored as id/shortid pairs.
3670 for (uintN i = 0; i < count; i++) {
3671 JSAtom *atom;
3672 uint16 shortid;
3674 /* XDR the real id, then the shortid. */
3675 if (!js_XDRAtom(xdr, &atom) || !JS_XDRUint16(xdr, &shortid))
3676 return false;
3678 if (!obj->defineBlockVariable(cx, ATOM_TO_JSID(atom), shortid))
3679 return false;
3681 } else {
3682 Vector<const Shape *, 8> shapes(cx);
3683 shapes.growByUninitialized(count);
3685 for (Shape::Range r(obj->lastProperty()); !r.empty(); r.popFront()) {
3686 shape = &r.front();
3687 shapes[shape->shortid] = shape;
3691 * XDR the block object's properties. We know that there are 'count'
3692 * properties to XDR, stored as id/shortid pairs.
3694 for (uintN i = 0; i < count; i++) {
3695 shape = shapes[i];
3696 JS_ASSERT(shape->getter() == block_getProperty);
3698 jsid propid = shape->id;
3699 JS_ASSERT(JSID_IS_ATOM(propid));
3700 JSAtom *atom = JSID_TO_ATOM(propid);
3702 uint16 shortid = uint16(shape->shortid);
3703 JS_ASSERT(shortid == i);
3705 /* XDR the real id, then the shortid. */
3706 if (!js_XDRAtom(xdr, &atom) || !JS_XDRUint16(xdr, &shortid))
3707 return false;
3710 return true;
3713 #endif
3715 Class js_BlockClass = {
3716 "Block",
3717 JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_IS_ANONYMOUS,
3718 PropertyStub, /* addProperty */
3719 PropertyStub, /* delProperty */
3720 PropertyStub, /* getProperty */
3721 PropertyStub, /* setProperty */
3722 EnumerateStub,
3723 ResolveStub,
3724 ConvertStub
3727 JSObject *
3728 js_InitObjectClass(JSContext *cx, JSObject *obj)
3730 JSObject *proto = js_InitClass(cx, obj, NULL, &js_ObjectClass, js_Object, 1,
3731 object_props, object_methods, NULL, object_static_methods);
3732 if (!proto)
3733 return NULL;
3735 /* ECMA (15.1.2.1) says 'eval' is a property of the global object. */
3736 jsid id = ATOM_TO_JSID(cx->runtime->atomState.evalAtom);
3737 if (!js_DefineFunction(cx, obj, id, eval, 1, JSFUN_STUB_GSOPS))
3738 return NULL;
3740 return proto;
3743 static bool
3744 DefineStandardSlot(JSContext *cx, JSObject *obj, JSProtoKey key, JSAtom *atom,
3745 const Value &v, uint32 attrs, bool &named)
3747 jsid id = ATOM_TO_JSID(atom);
3749 if (key != JSProto_Null) {
3751 * Initializing an actual standard class on a global object. If the
3752 * property is not yet present, force it into a new one bound to a
3753 * reserved slot. Otherwise, go through the normal property path.
3755 JS_ASSERT(obj->isGlobal());
3756 JS_ASSERT(obj->isNative());
3758 if (!obj->ensureClassReservedSlots(cx))
3759 return false;
3761 const Shape *shape = obj->nativeLookup(id);
3762 if (!shape) {
3763 uint32 slot = 2 * JSProto_LIMIT + key;
3764 if (!js_SetReservedSlot(cx, obj, slot, v))
3765 return false;
3766 if (!obj->addProperty(cx, id, PropertyStub, PropertyStub, slot, attrs, 0, 0))
3767 return false;
3769 named = true;
3770 return true;
3774 named = obj->defineProperty(cx, id, v, PropertyStub, PropertyStub, attrs);
3775 return named;
3778 JSObject *
3779 js_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto,
3780 Class *clasp, Native constructor, uintN nargs,
3781 JSPropertySpec *ps, JSFunctionSpec *fs,
3782 JSPropertySpec *static_ps, JSFunctionSpec *static_fs)
3784 JSAtom *atom;
3785 JSProtoKey key;
3786 JSFunction *fun;
3787 bool named = false;
3789 atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0);
3790 if (!atom)
3791 return NULL;
3794 * When initializing a standard class, if no parent_proto (grand-proto of
3795 * instances of the class, parent-proto of the class's prototype object)
3796 * is given, we must use Object.prototype if it is available. Otherwise,
3797 * we could look up the wrong binding for a class name in obj. Example:
3799 * String = Array;
3800 * print("hi there".join);
3802 * should print undefined, not Array.prototype.join. This is required by
3803 * ECMA-262, alas. It might have been better to make String readonly and
3804 * permanent in the global object, instead -- but that's too big a change
3805 * to swallow at this point.
3807 key = JSCLASS_CACHED_PROTO_KEY(clasp);
3808 if (key != JSProto_Null &&
3809 !parent_proto &&
3810 !js_GetClassPrototype(cx, obj, JSProto_Object, &parent_proto)) {
3811 return NULL;
3815 * Create a prototype object for this class.
3817 * FIXME: lazy standard (built-in) class initialization and even older
3818 * eager boostrapping code rely on all of these properties:
3820 * 1. NewObject attempting to compute a default prototype object when
3821 * passed null for proto; and
3823 * 2. NewObject tolerating no default prototype (null proto slot value)
3824 * due to this js_InitClass call coming from js_InitFunctionClass on an
3825 * otherwise-uninitialized global.
3827 * 3. NewObject allocating a JSFunction-sized GC-thing when clasp is
3828 * &js_FunctionClass, not a JSObject-sized (smaller) GC-thing.
3830 * The JS_NewObjectForGivenProto and JS_NewObject APIs also allow clasp to
3831 * be &js_FunctionClass (we could break compatibility easily). But fixing
3832 * (3) is not enough without addressing the bootstrapping dependency on (1)
3833 * and (2).
3835 JSObject *proto = NewObject<WithProto::Class>(cx, clasp, parent_proto, obj);
3836 if (!proto)
3837 return NULL;
3839 proto->syncSpecialEquality();
3841 /* After this point, control must exit via label bad or out. */
3842 AutoObjectRooter tvr(cx, proto);
3844 JSObject *ctor;
3845 if (!constructor) {
3847 * Lacking a constructor, name the prototype (e.g., Math) unless this
3848 * class (a) is anonymous, i.e. for internal use only; (b) the class
3849 * of obj (the global object) is has a reserved slot indexed by key;
3850 * and (c) key is not the null key.
3852 if (!(clasp->flags & JSCLASS_IS_ANONYMOUS) || !obj->isGlobal() || key == JSProto_Null) {
3853 uint32 attrs = (clasp->flags & JSCLASS_IS_ANONYMOUS)
3854 ? JSPROP_READONLY | JSPROP_PERMANENT
3855 : 0;
3856 if (!DefineStandardSlot(cx, obj, key, atom, ObjectValue(*proto), attrs, named))
3857 goto bad;
3860 ctor = proto;
3861 } else {
3862 fun = js_NewFunction(cx, NULL, constructor, nargs, JSFUN_CONSTRUCTOR, obj, atom);
3863 if (!fun)
3864 goto bad;
3866 AutoValueRooter tvr2(cx, ObjectValue(*fun));
3867 if (!DefineStandardSlot(cx, obj, key, atom, tvr2.value(), 0, named))
3868 goto bad;
3871 * Remember the class this function is a constructor for so that
3872 * we know to create an object of this class when we call the
3873 * constructor.
3875 FUN_CLASP(fun) = clasp;
3878 * Optionally construct the prototype object, before the class has
3879 * been fully initialized. Allow the ctor to replace proto with a
3880 * different object, as is done for operator new -- and as at least
3881 * XML support requires.
3883 ctor = FUN_OBJECT(fun);
3884 if (clasp->flags & JSCLASS_CONSTRUCT_PROTOTYPE) {
3885 Value rval;
3886 if (!InvokeConstructorWithGivenThis(cx, proto, ObjectOrNullValue(ctor),
3887 0, NULL, &rval)) {
3888 goto bad;
3890 if (rval.isObject() && &rval.toObject() != proto)
3891 proto = &rval.toObject();
3894 /* Connect constructor and prototype by named properties. */
3895 if (!js_SetClassPrototype(cx, ctor, proto,
3896 JSPROP_READONLY | JSPROP_PERMANENT)) {
3897 goto bad;
3900 /* Bootstrap Function.prototype (see also JS_InitStandardClasses). */
3901 if (ctor->getClass() == clasp)
3902 ctor->setProto(proto);
3905 /* Add properties and methods to the prototype and the constructor. */
3906 if ((ps && !JS_DefineProperties(cx, proto, ps)) ||
3907 (fs && !JS_DefineFunctions(cx, proto, fs)) ||
3908 (static_ps && !JS_DefineProperties(cx, ctor, static_ps)) ||
3909 (static_fs && !JS_DefineFunctions(cx, ctor, static_fs))) {
3910 goto bad;
3914 * Pre-brand the prototype and constructor if they have built-in methods.
3915 * This avoids extra shape guard branch exits in the tracejitted code.
3917 if (fs)
3918 proto->brand(cx);
3919 if (ctor != proto && static_fs)
3920 ctor->brand(cx);
3923 * Make sure proto's emptyShape is available to be shared by objects of
3924 * this class. JSObject::emptyShape is a one-slot cache. If we omit this,
3925 * some other class could snap it up. (The risk is particularly great for
3926 * Object.prototype.)
3928 * All callers of JSObject::initSharingEmptyShape depend on this.
3930 * FIXME: bug 592296 -- js_InitArrayClass should pass &js_SlowArrayClass
3931 * and make the Array.prototype slow from the start.
3933 JS_ASSERT_IF(proto->clasp != clasp,
3934 clasp == &js_ArrayClass && proto->clasp == &js_SlowArrayClass);
3935 if (!proto->getEmptyShape(cx, proto->clasp, FINALIZE_OBJECT0))
3936 goto bad;
3938 if (clasp->flags & (JSCLASS_FREEZE_PROTO|JSCLASS_FREEZE_CTOR)) {
3939 JS_ASSERT_IF(ctor == proto, !(clasp->flags & JSCLASS_FREEZE_CTOR));
3940 if (proto && (clasp->flags & JSCLASS_FREEZE_PROTO) && !proto->freeze(cx))
3941 goto bad;
3942 if (ctor && (clasp->flags & JSCLASS_FREEZE_CTOR) && !ctor->freeze(cx))
3943 goto bad;
3946 /* If this is a standard class, cache its prototype. */
3947 if (key != JSProto_Null && !js_SetClassObject(cx, obj, key, ctor, proto))
3948 goto bad;
3950 return proto;
3952 bad:
3953 if (named) {
3954 Value rval;
3955 obj->deleteProperty(cx, ATOM_TO_JSID(atom), &rval, false);
3957 return NULL;
3960 bool
3961 JSObject::allocSlots(JSContext *cx, size_t newcap)
3963 uint32 oldcap = numSlots();
3965 JS_ASSERT(newcap >= oldcap && !hasSlotsArray());
3967 if (newcap > NSLOTS_LIMIT) {
3968 if (!JS_ON_TRACE(cx))
3969 js_ReportAllocationOverflow(cx);
3970 return false;
3973 Value *tmpslots = (Value*) cx->malloc(newcap * sizeof(Value));
3974 if (!tmpslots)
3975 return false; /* Leave slots at inline buffer. */
3976 slots = tmpslots;
3977 capacity = newcap;
3979 /* Copy over anything from the inline buffer. */
3980 memcpy(slots, fixedSlots(), oldcap * sizeof(Value));
3981 ClearValueRange(slots + oldcap, newcap - oldcap, isDenseArray());
3982 return true;
3985 bool
3986 JSObject::growSlots(JSContext *cx, size_t newcap)
3989 * When an object with CAPACITY_DOUBLING_MAX or fewer slots needs to
3990 * grow, double its capacity, to add N elements in amortized O(N) time.
3992 * Above this limit, grow by 12.5% each time. Speed is still amortized
3993 * O(N), with a higher constant factor, and we waste less space.
3995 static const size_t CAPACITY_DOUBLING_MAX = 1024 * 1024;
3996 static const size_t CAPACITY_CHUNK = CAPACITY_DOUBLING_MAX / sizeof(Value);
3998 uint32 oldcap = numSlots();
3999 JS_ASSERT(oldcap < newcap);
4001 uint32 nextsize = (oldcap <= CAPACITY_DOUBLING_MAX)
4002 ? oldcap * 2
4003 : oldcap + (oldcap >> 3);
4005 uint32 actualCapacity = JS_MAX(newcap, nextsize);
4006 if (actualCapacity >= CAPACITY_CHUNK)
4007 actualCapacity = JS_ROUNDUP(actualCapacity, CAPACITY_CHUNK);
4008 else if (actualCapacity < SLOT_CAPACITY_MIN)
4009 actualCapacity = SLOT_CAPACITY_MIN;
4011 /* Don't let nslots get close to wrapping around uint32. */
4012 if (actualCapacity >= NSLOTS_LIMIT) {
4013 JS_ReportOutOfMemory(cx);
4014 return false;
4017 /* If nothing was allocated yet, treat it as initial allocation. */
4018 if (!hasSlotsArray())
4019 return allocSlots(cx, actualCapacity);
4021 Value *tmpslots = (Value*) cx->realloc(slots, oldcap * sizeof(Value), actualCapacity * sizeof(Value));
4022 if (!tmpslots)
4023 return false; /* Leave dslots as its old size. */
4024 slots = tmpslots;
4025 capacity = actualCapacity;
4027 /* Initialize the additional slots we added. */
4028 ClearValueRange(slots + oldcap, actualCapacity - oldcap, isDenseArray());
4029 return true;
4032 void
4033 JSObject::shrinkSlots(JSContext *cx, size_t newcap)
4035 uint32 oldcap = numSlots();
4036 JS_ASSERT(newcap <= oldcap);
4037 JS_ASSERT(newcap >= slotSpan());
4039 if (oldcap <= SLOT_CAPACITY_MIN || !hasSlotsArray()) {
4040 /* We won't shrink the slots any more. Clear excess holes. */
4041 ClearValueRange(slots + newcap, oldcap - newcap, isDenseArray());
4042 return;
4045 uint32 fill = newcap;
4046 if (newcap < SLOT_CAPACITY_MIN)
4047 newcap = SLOT_CAPACITY_MIN;
4048 if (newcap < numFixedSlots())
4049 newcap = numFixedSlots();
4051 Value *tmpslots = (Value*) cx->realloc(slots, newcap * sizeof(Value));
4052 if (!tmpslots)
4053 return; /* Leave slots at its old size. */
4054 slots = tmpslots;
4055 capacity = newcap;
4057 if (fill < newcap) {
4058 /* Clear any excess holes if we tried to shrink below SLOT_CAPACITY_MIN. */
4059 ClearValueRange(slots + fill, newcap - fill, isDenseArray());
4063 bool
4064 JSObject::ensureInstanceReservedSlots(JSContext *cx, size_t nreserved)
4066 JS_ASSERT_IF(isNative(),
4067 isBlock() || isCall() || (isFunction() && isBoundFunction()));
4069 uintN nslots = JSSLOT_FREE(clasp) + nreserved;
4070 return nslots <= numSlots() || allocSlots(cx, nslots);
4073 static JSObject *
4074 js_InitNullClass(JSContext *cx, JSObject *obj)
4076 JS_ASSERT(0);
4077 return NULL;
4080 #define JS_PROTO(name,code,init) extern JSObject *init(JSContext *, JSObject *);
4081 #include "jsproto.tbl"
4082 #undef JS_PROTO
4084 static JSObjectOp lazy_prototype_init[JSProto_LIMIT] = {
4085 #define JS_PROTO(name,code,init) init,
4086 #include "jsproto.tbl"
4087 #undef JS_PROTO
4090 namespace js {
4092 bool
4093 SetProto(JSContext *cx, JSObject *obj, JSObject *proto, bool checkForCycles)
4095 JS_ASSERT_IF(!checkForCycles, obj != proto);
4096 JS_ASSERT(obj->isExtensible());
4098 if (obj->isNative()) {
4099 if (!obj->ensureClassReservedSlots(cx))
4100 return false;
4104 * Regenerate property cache shape ids for all of the scopes along the
4105 * old prototype chain to invalidate their property cache entries, in
4106 * case any entries were filled by looking up through obj.
4108 JSObject *oldproto = obj;
4109 while (oldproto && oldproto->isNative()) {
4110 oldproto->protoShapeChange(cx);
4111 oldproto = oldproto->getProto();
4114 if (!proto || !checkForCycles) {
4115 obj->setProto(proto);
4116 } else if (!SetProtoCheckingForCycles(cx, obj, proto)) {
4117 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CYCLIC_VALUE, js_proto_str);
4118 return false;
4120 return true;
4125 JSBool
4126 js_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key,
4127 JSObject **objp)
4129 JSObject *cobj;
4130 JSResolvingKey rkey;
4131 JSResolvingEntry *rentry;
4132 uint32 generation;
4133 JSObjectOp init;
4134 Value v;
4136 obj = obj->getGlobal();
4137 if (!obj->isGlobal()) {
4138 *objp = NULL;
4139 return JS_TRUE;
4142 v = obj->getReservedSlot(key);
4143 if (v.isObject()) {
4144 *objp = &v.toObject();
4145 return JS_TRUE;
4148 rkey.obj = obj;
4149 rkey.id = ATOM_TO_JSID(cx->runtime->atomState.classAtoms[key]);
4150 if (!js_StartResolving(cx, &rkey, JSRESFLAG_LOOKUP, &rentry))
4151 return JS_FALSE;
4152 if (!rentry) {
4153 /* Already caching key in obj -- suppress recursion. */
4154 *objp = NULL;
4155 return JS_TRUE;
4157 generation = cx->resolvingTable->generation;
4159 JSBool ok = true;
4160 cobj = NULL;
4161 init = lazy_prototype_init[key];
4162 if (init) {
4163 if (!init(cx, obj)) {
4164 ok = JS_FALSE;
4165 } else {
4166 v = obj->getReservedSlot(key);
4167 if (v.isObject())
4168 cobj = &v.toObject();
4172 js_StopResolving(cx, &rkey, JSRESFLAG_LOOKUP, rentry, generation);
4173 *objp = cobj;
4174 return ok;
4177 JSBool
4178 js_SetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, JSObject *cobj, JSObject *proto)
4180 JS_ASSERT(!obj->getParent());
4181 if (!obj->isGlobal())
4182 return JS_TRUE;
4184 return js_SetReservedSlot(cx, obj, key, ObjectOrNullValue(cobj)) &&
4185 js_SetReservedSlot(cx, obj, JSProto_LIMIT + key, ObjectOrNullValue(proto));
4188 JSBool
4189 js_FindClassObject(JSContext *cx, JSObject *start, JSProtoKey protoKey,
4190 Value *vp, Class *clasp)
4192 JSStackFrame *fp;
4193 JSObject *obj, *cobj, *pobj;
4194 jsid id;
4195 JSProperty *prop;
4196 const Shape *shape;
4199 * Find the global object. Use cx->fp() directly to avoid falling off
4200 * trace; all JIT-elided stack frames have the same global object as
4201 * cx->fp().
4203 VOUCH_DOES_NOT_REQUIRE_STACK();
4204 if (!start && (fp = cx->maybefp()) != NULL)
4205 start = &fp->scopeChain();
4207 if (start) {
4208 /* Find the topmost object in the scope chain. */
4209 do {
4210 obj = start;
4211 start = obj->getParent();
4212 } while (start);
4213 } else {
4214 obj = cx->globalObject;
4215 if (!obj) {
4216 vp->setUndefined();
4217 return JS_TRUE;
4221 OBJ_TO_INNER_OBJECT(cx, obj);
4222 if (!obj)
4223 return JS_FALSE;
4225 if (protoKey != JSProto_Null) {
4226 JS_ASSERT(JSProto_Null < protoKey);
4227 JS_ASSERT(protoKey < JSProto_LIMIT);
4228 if (!js_GetClassObject(cx, obj, protoKey, &cobj))
4229 return JS_FALSE;
4230 if (cobj) {
4231 vp->setObject(*cobj);
4232 return JS_TRUE;
4234 id = ATOM_TO_JSID(cx->runtime->atomState.classAtoms[protoKey]);
4235 } else {
4236 JSAtom *atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0);
4237 if (!atom)
4238 return false;
4239 id = ATOM_TO_JSID(atom);
4242 JS_ASSERT(obj->isNative());
4243 if (js_LookupPropertyWithFlags(cx, obj, id, JSRESOLVE_CLASSNAME,
4244 &pobj, &prop) < 0) {
4245 return JS_FALSE;
4247 Value v = UndefinedValue();
4248 if (prop && pobj->isNative()) {
4249 shape = (Shape *) prop;
4250 if (pobj->containsSlot(shape->slot)) {
4251 v = pobj->nativeGetSlot(shape->slot);
4252 if (v.isPrimitive())
4253 v.setUndefined();
4256 *vp = v;
4257 return JS_TRUE;
4260 JSObject *
4261 js_ConstructObject(JSContext *cx, Class *clasp, JSObject *proto, JSObject *parent,
4262 uintN argc, Value *argv)
4264 AutoArrayRooter argtvr(cx, argc, argv);
4266 JSProtoKey protoKey = GetClassProtoKey(clasp);
4268 /* Protect constructor in case a crazy getter for .prototype uproots it. */
4269 AutoValueRooter tvr(cx);
4270 if (!js_FindClassObject(cx, parent, protoKey, tvr.addr(), clasp))
4271 return NULL;
4273 const Value &cval = tvr.value();
4274 if (tvr.value().isPrimitive()) {
4275 js_ReportIsNotFunction(cx, tvr.addr(), JSV2F_CONSTRUCT | JSV2F_SEARCH_STACK);
4276 return NULL;
4280 * If proto is NULL, set it to Constructor.prototype, just like JSOP_NEW
4281 * does, likewise for the new object's parent.
4283 JSObject *ctor = &cval.toObject();
4284 if (!parent)
4285 parent = ctor->getParent();
4286 if (!proto) {
4287 Value rval;
4288 if (!ctor->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom),
4289 &rval)) {
4290 return NULL;
4292 if (rval.isObjectOrNull())
4293 proto = rval.toObjectOrNull();
4296 JSObject *obj = NewObject<WithProto::Class>(cx, clasp, proto, parent);
4297 if (!obj)
4298 return NULL;
4300 obj->syncSpecialEquality();
4302 Value rval;
4303 if (!InvokeConstructorWithGivenThis(cx, obj, cval, argc, argv, &rval))
4304 return NULL;
4306 if (rval.isPrimitive())
4307 return obj;
4310 * If the instance's class differs from what was requested, throw a type
4311 * error. If the given class has both the JSCLASS_HAS_PRIVATE and the
4312 * JSCLASS_CONSTRUCT_PROTOTYPE flags, and the instance does not have its
4313 * private data set at this point, then the constructor was replaced and
4314 * we should throw a type error.
4316 obj = &rval.toObject();
4317 if (obj->getClass() != clasp ||
4318 (!(~clasp->flags & (JSCLASS_HAS_PRIVATE |
4319 JSCLASS_CONSTRUCT_PROTOTYPE)) &&
4320 !obj->getPrivate())) {
4321 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
4322 JSMSG_WRONG_CONSTRUCTOR, clasp->name);
4323 return NULL;
4325 return obj;
4328 bool
4329 JSObject::allocSlot(JSContext *cx, uint32 *slotp)
4331 uint32 slot = slotSpan();
4332 JS_ASSERT(slot >= JSSLOT_FREE(clasp));
4335 * If this object is in dictionary mode and it has a property table, try to
4336 * pull a free slot from the property table's slot-number freelist.
4338 if (inDictionaryMode() && lastProp->table) {
4339 uint32 &last = lastProp->table->freelist;
4340 if (last != SHAPE_INVALID_SLOT) {
4341 #ifdef DEBUG
4342 JS_ASSERT(last < slot);
4343 uint32 next = getSlot(last).toPrivateUint32();
4344 JS_ASSERT_IF(next != SHAPE_INVALID_SLOT, next < slot);
4345 #endif
4347 *slotp = last;
4349 Value &vref = getSlotRef(last);
4350 last = vref.toPrivateUint32();
4351 vref.setUndefined();
4352 return true;
4356 if (slot >= numSlots() && !growSlots(cx, slot + 1))
4357 return false;
4359 /* JSObject::growSlots or JSObject::freeSlot should set the free slots to void. */
4360 JS_ASSERT(getSlot(slot).isUndefined());
4361 *slotp = slot;
4362 return true;
4365 bool
4366 JSObject::freeSlot(JSContext *cx, uint32 slot)
4368 uint32 limit = slotSpan();
4369 JS_ASSERT(slot < limit);
4371 Value &vref = getSlotRef(slot);
4372 if (inDictionaryMode() && lastProp->table) {
4373 uint32 &last = lastProp->table->freelist;
4375 /* Can't afford to check the whole freelist, but let's check the head. */
4376 JS_ASSERT_IF(last != SHAPE_INVALID_SLOT, last < limit && last != slot);
4379 * Freeing a slot other than the last one mapped by this object's
4380 * shape (and not a reserved slot; see bug 595230): push the slot onto
4381 * the dictionary property table's freelist. We want to let the last
4382 * slot be freed by shrinking the dslots vector; see js_TraceObject.
4384 if (JSSLOT_FREE(clasp) <= slot && slot + 1 < limit) {
4385 JS_ASSERT_IF(last != SHAPE_INVALID_SLOT, last < slotSpan());
4386 vref.setPrivateUint32(last);
4387 last = slot;
4388 return true;
4391 vref.setUndefined();
4392 return false;
4395 /* JSBOXEDWORD_INT_MAX as a string */
4396 #define JSBOXEDWORD_INT_MAX_STRING "1073741823"
4399 * Convert string indexes that convert to int jsvals as ints to save memory.
4400 * Care must be taken to use this macro every time a property name is used, or
4401 * else double-sets, incorrect property cache misses, or other mistakes could
4402 * occur.
4404 jsid
4405 js_CheckForStringIndex(jsid id)
4407 if (!JSID_IS_ATOM(id))
4408 return id;
4410 JSAtom *atom = JSID_TO_ATOM(id);
4411 JSString *str = ATOM_TO_STRING(atom);
4412 const jschar *s = str->flatChars();
4413 jschar ch = *s;
4415 JSBool negative = (ch == '-');
4416 if (negative)
4417 ch = *++s;
4419 if (!JS7_ISDEC(ch))
4420 return id;
4422 size_t n = str->flatLength() - negative;
4423 if (n > sizeof(JSBOXEDWORD_INT_MAX_STRING) - 1)
4424 return id;
4426 const jschar *cp = s;
4427 const jschar *end = s + n;
4429 jsuint index = JS7_UNDEC(*cp++);
4430 jsuint oldIndex = 0;
4431 jsuint c = 0;
4433 if (index != 0) {
4434 while (JS7_ISDEC(*cp)) {
4435 oldIndex = index;
4436 c = JS7_UNDEC(*cp);
4437 index = 10 * index + c;
4438 cp++;
4443 * Non-integer indexes can't be represented as integers. Also, distinguish
4444 * index "-0" from "0", because JSBOXEDWORD_INT cannot.
4446 if (cp != end || (negative && index == 0))
4447 return id;
4449 if (negative) {
4450 if (oldIndex < -(JSID_INT_MIN / 10) ||
4451 (oldIndex == -(JSID_INT_MIN / 10) && c <= (-JSID_INT_MIN % 10)))
4453 id = INT_TO_JSID(-jsint(index));
4455 } else {
4456 if (oldIndex < JSID_INT_MAX / 10 ||
4457 (oldIndex == JSID_INT_MAX / 10 && c <= (JSID_INT_MAX % 10)))
4459 id = INT_TO_JSID(jsint(index));
4463 return id;
4466 static JSBool
4467 PurgeProtoChain(JSContext *cx, JSObject *obj, jsid id)
4469 const Shape *shape;
4471 while (obj) {
4472 if (!obj->isNative()) {
4473 obj = obj->getProto();
4474 continue;
4476 shape = obj->nativeLookup(id);
4477 if (shape) {
4478 PCMETER(JS_PROPERTY_CACHE(cx).pcpurges++);
4479 obj->shadowingShapeChange(cx, *shape);
4481 if (!obj->getParent()) {
4483 * All scope chains end in a global object, so this will change
4484 * the global shape. jstracer.cpp assumes that the global shape
4485 * never changes on trace, so we must deep-bail here.
4487 LeaveTrace(cx);
4489 return JS_TRUE;
4491 obj = obj->getProto();
4493 return JS_FALSE;
4496 void
4497 js_PurgeScopeChainHelper(JSContext *cx, JSObject *obj, jsid id)
4499 JS_ASSERT(obj->isDelegate());
4500 PurgeProtoChain(cx, obj->getProto(), id);
4503 * We must purge the scope chain only for Call objects as they are the only
4504 * kind of cacheable non-global object that can gain properties after outer
4505 * properties with the same names have been cached or traced. Call objects
4506 * may gain such properties via eval introducing new vars; see bug 490364.
4508 if (obj->isCall()) {
4509 while ((obj = obj->getParent()) != NULL) {
4510 if (PurgeProtoChain(cx, obj, id))
4511 break;
4516 const Shape *
4517 js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id,
4518 PropertyOp getter, PropertyOp setter, uint32 slot,
4519 uintN attrs, uintN flags, intN shortid)
4521 JS_ASSERT(!(flags & Shape::METHOD));
4524 * Purge the property cache of now-shadowed id in obj's scope chain. Do
4525 * this optimistically (assuming no failure below) before locking obj, so
4526 * we can lock the shadowed scope.
4528 js_PurgeScopeChain(cx, obj, id);
4530 if (!obj->ensureClassReservedSlots(cx))
4531 return NULL;
4533 /* Convert string indices to integers if appropriate. */
4534 id = js_CheckForStringIndex(id);
4535 return obj->putProperty(cx, id, getter, setter, slot, attrs, flags, shortid);
4538 const Shape *
4539 js_ChangeNativePropertyAttrs(JSContext *cx, JSObject *obj,
4540 const Shape *shape, uintN attrs, uintN mask,
4541 PropertyOp getter, PropertyOp setter)
4543 if (!obj->ensureClassReservedSlots(cx))
4544 return NULL;
4545 return obj->changeProperty(cx, shape, attrs, mask, getter, setter);
4548 JSBool
4549 js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, const Value *value,
4550 PropertyOp getter, PropertyOp setter, uintN attrs)
4552 return js_DefineNativeProperty(cx, obj, id, *value, getter, setter, attrs,
4553 0, 0, NULL);
4557 * Backward compatibility requires allowing addProperty hooks to mutate the
4558 * nominal initial value of a slotful property, while GC safety wants that
4559 * value to be stored before the call-out through the hook. Optimize to do
4560 * both while saving cycles for classes that stub their addProperty hook.
4562 static inline bool
4563 CallAddPropertyHook(JSContext *cx, Class *clasp, JSObject *obj, const Shape *shape, Value *vp)
4565 if (clasp->addProperty != PropertyStub) {
4566 Value nominal = *vp;
4568 if (!CallJSPropertyOp(cx, clasp->addProperty, obj, shape->id, vp))
4569 return false;
4570 if (*vp != nominal) {
4571 if (obj->containsSlot(shape->slot))
4572 obj->nativeSetSlot(shape->slot, *vp);
4575 return true;
4578 JSBool
4579 js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, const Value &value,
4580 PropertyOp getter, PropertyOp setter, uintN attrs,
4581 uintN flags, intN shortid, JSProperty **propp,
4582 uintN defineHow /* = 0 */)
4584 Class *clasp;
4585 const Shape *shape;
4586 JSBool added;
4587 Value valueCopy;
4589 JS_ASSERT((defineHow & ~(JSDNP_CACHE_RESULT | JSDNP_DONT_PURGE | JSDNP_SET_METHOD)) == 0);
4590 LeaveTraceIfGlobalObject(cx, obj);
4592 /* Convert string indices to integers if appropriate. */
4593 id = js_CheckForStringIndex(id);
4596 * If defining a getter or setter, we must check for its counterpart and
4597 * update the attributes and property ops. A getter or setter is really
4598 * only half of a property.
4600 shape = NULL;
4601 if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
4602 JSObject *pobj;
4603 JSProperty *prop;
4606 * If JS_THREADSAFE and id is found, js_LookupProperty returns with
4607 * shape non-null and pobj locked. If pobj == obj, the property is
4608 * already in obj and obj has its own (mutable) scope. So if we are
4609 * defining a getter whose setter was already defined, or vice versa,
4610 * finish the job via obj->changeProperty, and refresh the property
4611 * cache line for (obj, id) to map shape.
4613 if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
4614 return JS_FALSE;
4615 shape = (Shape *) prop;
4616 if (shape && pobj == obj && shape->isAccessorDescriptor()) {
4617 shape = obj->changeProperty(cx, shape, attrs,
4618 JSPROP_GETTER | JSPROP_SETTER,
4619 (attrs & JSPROP_GETTER)
4620 ? getter
4621 : shape->getter(),
4622 (attrs & JSPROP_SETTER)
4623 ? setter
4624 : shape->setter());
4626 if (!shape)
4627 return false;
4628 } else if (prop) {
4629 prop = NULL;
4630 shape = NULL;
4635 * Purge the property cache of any properties named by id that are about
4636 * to be shadowed in obj's scope chain unless it is known a priori that it
4637 * is not possible. We do this before locking obj to avoid nesting locks.
4639 if (!(defineHow & JSDNP_DONT_PURGE))
4640 js_PurgeScopeChain(cx, obj, id);
4643 * Check whether a readonly property or setter is being defined on a known
4644 * prototype object. See the comment in jscntxt.h before protoHazardShape's
4645 * member declaration.
4647 if (obj->isDelegate() && (attrs & (JSPROP_READONLY | JSPROP_SETTER)))
4648 cx->runtime->protoHazardShape = js_GenerateShape(cx, false);
4650 /* Use the object's class getter and setter by default. */
4651 clasp = obj->getClass();
4652 if (!(defineHow & JSDNP_SET_METHOD)) {
4653 if (!getter && !(attrs & JSPROP_GETTER))
4654 getter = clasp->getProperty;
4655 if (!setter && !(attrs & JSPROP_SETTER))
4656 setter = clasp->setProperty;
4659 /* Get obj's own scope if it has one, or create a new one for obj. */
4660 if (!obj->ensureClassReservedSlots(cx))
4661 return false;
4663 added = false;
4664 if (!shape) {
4665 /* Add a new property, or replace an existing one of the same id. */
4666 if (defineHow & JSDNP_SET_METHOD) {
4667 JS_ASSERT(clasp == &js_ObjectClass);
4668 JS_ASSERT(IsFunctionObject(value));
4669 JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
4670 JS_ASSERT(!getter && !setter);
4672 JSObject *funobj = &value.toObject();
4673 if (FUN_OBJECT(GET_FUNCTION_PRIVATE(cx, funobj)) == funobj) {
4674 flags |= Shape::METHOD;
4675 getter = CastAsPropertyOp(funobj);
4679 added = !obj->nativeContains(id);
4680 uint32 oldShape = obj->shape();
4681 shape = obj->putProperty(cx, id, getter, setter, SHAPE_INVALID_SLOT,
4682 attrs, flags, shortid);
4683 if (!shape)
4684 return false;
4687 * If shape is a method, the above call to putProperty suffices to
4688 * update the shape if necessary. But if scope->branded(), the shape
4689 * may not have changed and we may be overwriting a function-valued
4690 * property. See bug 560998.
4692 if (obj->shape() == oldShape && obj->branded() && shape->slot != SHAPE_INVALID_SLOT) {
4693 #ifdef DEBUG
4694 const Shape *newshape =
4695 #endif
4696 obj->methodWriteBarrier(cx, *shape, value);
4697 JS_ASSERT(newshape == shape);
4701 /* Store value before calling addProperty, in case the latter GC's. */
4702 if (obj->containsSlot(shape->slot)) {
4703 AbortRecordingIfUnexpectedGlobalWrite(cx, obj, shape->slot);
4704 obj->nativeSetSlot(shape->slot, value);
4707 /* XXXbe called with lock held */
4708 valueCopy = value;
4709 if (!CallAddPropertyHook(cx, clasp, obj, shape, &valueCopy)) {
4710 obj->removeProperty(cx, id);
4711 return false;
4714 if (defineHow & JSDNP_CACHE_RESULT) {
4715 JS_ASSERT_NOT_ON_TRACE(cx);
4716 if (added) {
4717 JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, 0, obj, shape, true);
4718 TRACE_1(AddProperty, obj);
4721 if (propp)
4722 *propp = (JSProperty *) shape;
4723 return true;
4725 #ifdef JS_TRACER
4726 error: // TRACE_1 jumps here on error.
4727 #endif
4728 return false;
4731 #define SCOPE_DEPTH_ACCUM(bs,val) \
4732 JS_SCOPE_DEPTH_METERING(JS_BASIC_STATS_ACCUM(bs, val))
4735 * Call obj's resolve hook. obj is a native object and the caller holds its
4736 * scope lock.
4738 * cx, start, id, and flags are the parameters initially passed to the ongoing
4739 * lookup; objp and propp are its out parameters. obj is an object along
4740 * start's prototype chain.
4742 * There are four possible outcomes:
4744 * - On failure, report an error or exception, unlock obj, and return false.
4746 * - If we are alrady resolving a property of *curobjp, set *recursedp = true,
4747 * unlock obj, and return true.
4749 * - If the resolve hook finds or defines the sought property, set *objp and
4750 * *propp appropriately, set *recursedp = false, and return true with *objp's
4751 * lock held.
4753 * - Otherwise no property was resolved. Set *propp = NULL and *recursedp = false
4754 * and return true.
4756 static JSBool
4757 CallResolveOp(JSContext *cx, JSObject *start, JSObject *obj, jsid id, uintN flags,
4758 JSObject **objp, JSProperty **propp, bool *recursedp)
4760 Class *clasp = obj->getClass();
4761 JSResolveOp resolve = clasp->resolve;
4764 * Avoid recursion on (obj, id) already being resolved on cx.
4766 * Once we have successfully added an entry for (obj, key) to
4767 * cx->resolvingTable, control must go through cleanup: before
4768 * returning. But note that JS_DHASH_ADD may find an existing
4769 * entry, in which case we bail to suppress runaway recursion.
4771 JSResolvingKey key = {obj, id};
4772 JSResolvingEntry *entry;
4773 if (!js_StartResolving(cx, &key, JSRESFLAG_LOOKUP, &entry))
4774 return false;
4775 if (!entry) {
4776 /* Already resolving id in obj -- suppress recursion. */
4777 *recursedp = true;
4778 return true;
4780 uint32 generation = cx->resolvingTable->generation;
4781 *recursedp = false;
4783 *propp = NULL;
4785 JSBool ok;
4786 const Shape *shape = NULL;
4787 if (clasp->flags & JSCLASS_NEW_RESOLVE) {
4788 JSNewResolveOp newresolve = (JSNewResolveOp)resolve;
4789 if (flags == JSRESOLVE_INFER)
4790 flags = js_InferFlags(cx, 0);
4791 JSObject *obj2 = (clasp->flags & JSCLASS_NEW_RESOLVE_GETS_START) ? start : NULL;
4794 /* Protect id and all atoms from a GC nested in resolve. */
4795 AutoKeepAtoms keep(cx->runtime);
4796 ok = newresolve(cx, obj, id, flags, &obj2);
4798 if (!ok)
4799 goto cleanup;
4801 if (obj2) {
4802 /* Resolved: lookup id again for backward compatibility. */
4803 if (!obj2->isNative()) {
4804 /* Whoops, newresolve handed back a foreign obj2. */
4805 JS_ASSERT(obj2 != obj);
4806 ok = obj2->lookupProperty(cx, id, objp, propp);
4807 if (!ok || *propp)
4808 goto cleanup;
4809 } else {
4811 * Require that obj2 not be empty now, as we do for old-style
4812 * resolve. If it doesn't, then id was not truly resolved, and
4813 * we'll find it in the proto chain, or miss it if obj2's proto
4814 * is not on obj's proto chain. That last case is a "too bad!"
4815 * case.
4817 if (!obj2->nativeEmpty())
4818 shape = obj2->nativeLookup(id);
4820 if (shape) {
4821 JS_ASSERT(!obj2->nativeEmpty());
4822 obj = obj2;
4825 } else {
4827 * Old resolve always requires id re-lookup if obj is not empty after
4828 * resolve returns.
4830 ok = resolve(cx, obj, id);
4831 if (!ok)
4832 goto cleanup;
4833 JS_ASSERT(obj->isNative());
4834 if (!obj->nativeEmpty())
4835 shape = obj->nativeLookup(id);
4838 cleanup:
4839 if (ok && shape) {
4840 *objp = obj;
4841 *propp = (JSProperty *) shape;
4843 js_StopResolving(cx, &key, JSRESFLAG_LOOKUP, entry, generation);
4844 return ok;
4847 static JS_ALWAYS_INLINE int
4848 js_LookupPropertyWithFlagsInline(JSContext *cx, JSObject *obj, jsid id, uintN flags,
4849 JSObject **objp, JSProperty **propp)
4851 /* We should not get string indices which aren't already integers here. */
4852 JS_ASSERT(id == js_CheckForStringIndex(id));
4854 /* Search scopes starting with obj and following the prototype link. */
4855 JSObject *start = obj;
4856 int protoIndex;
4857 for (protoIndex = 0; ; protoIndex++) {
4858 const Shape *shape = obj->nativeLookup(id);
4859 if (shape) {
4860 SCOPE_DEPTH_ACCUM(&cx->runtime->protoLookupDepthStats, protoIndex);
4861 *objp = obj;
4862 *propp = (JSProperty *) shape;
4863 return protoIndex;
4866 /* Try obj's class resolve hook if id was not found in obj's scope. */
4867 if (!shape && obj->getClass()->resolve != JS_ResolveStub) {
4868 bool recursed;
4869 if (!CallResolveOp(cx, start, obj, id, flags, objp, propp, &recursed))
4870 return -1;
4871 if (recursed)
4872 break;
4873 if (*propp) {
4874 /* Recalculate protoIndex in case it was resolved on some other object. */
4875 protoIndex = 0;
4876 for (JSObject *proto = start; proto && proto != *objp; proto = proto->getProto())
4877 protoIndex++;
4878 SCOPE_DEPTH_ACCUM(&cx->runtime->protoLookupDepthStats, protoIndex);
4879 return protoIndex;
4883 JSObject *proto = obj->getProto();
4884 if (!proto)
4885 break;
4886 if (!proto->isNative()) {
4887 if (!proto->lookupProperty(cx, id, objp, propp))
4888 return -1;
4889 #ifdef DEBUG
4891 * Non-native objects must have either non-native lookup results,
4892 * or else native results from the non-native's prototype chain.
4894 * See JSStackFrame::getValidCalleeObject, where we depend on this
4895 * fact to force a prototype-delegated joined method accessed via
4896 * arguments.callee through the delegating |this| object's method
4897 * read barrier.
4899 if (*propp && (*objp)->isNative()) {
4900 while ((proto = proto->getProto()) != *objp)
4901 JS_ASSERT(proto);
4903 #endif
4904 return protoIndex + 1;
4907 obj = proto;
4910 *objp = NULL;
4911 *propp = NULL;
4912 return protoIndex;
4915 JS_FRIEND_API(JSBool)
4916 js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
4917 JSProperty **propp)
4919 /* Convert string indices to integers if appropriate. */
4920 id = js_CheckForStringIndex(id);
4922 return js_LookupPropertyWithFlagsInline(cx, obj, id, cx->resolveFlags, objp, propp) >= 0;
4926 js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags,
4927 JSObject **objp, JSProperty **propp)
4929 /* Convert string indices to integers if appropriate. */
4930 id = js_CheckForStringIndex(id);
4932 return js_LookupPropertyWithFlagsInline(cx, obj, id, flags, objp, propp);
4935 PropertyCacheEntry *
4936 js_FindPropertyHelper(JSContext *cx, jsid id, JSBool cacheResult,
4937 JSObject **objp, JSObject **pobjp, JSProperty **propp)
4939 JSObject *scopeChain, *obj, *parent, *pobj;
4940 PropertyCacheEntry *entry;
4941 int scopeIndex, protoIndex;
4942 JSProperty *prop;
4944 JS_ASSERT_IF(cacheResult, !JS_ON_TRACE(cx));
4945 scopeChain = &js_GetTopStackFrame(cx)->scopeChain();
4947 /* Scan entries on the scope chain that we can cache across. */
4948 entry = JS_NO_PROP_CACHE_FILL;
4949 obj = scopeChain;
4950 parent = obj->getParent();
4951 for (scopeIndex = 0;
4952 parent
4953 ? js_IsCacheableNonGlobalScope(obj)
4954 : !obj->getOps()->lookupProperty;
4955 ++scopeIndex) {
4956 protoIndex =
4957 js_LookupPropertyWithFlags(cx, obj, id, cx->resolveFlags,
4958 &pobj, &prop);
4959 if (protoIndex < 0)
4960 return NULL;
4962 if (prop) {
4963 #ifdef DEBUG
4964 if (parent) {
4965 Class *clasp = obj->getClass();
4966 JS_ASSERT(pobj->isNative());
4967 JS_ASSERT(pobj->getClass() == clasp);
4968 if (clasp == &js_BlockClass) {
4970 * A block instance on the scope chain is immutable and it
4971 * shares its shapes with its compile-time prototype.
4973 JS_ASSERT(pobj == obj);
4974 JS_ASSERT(pobj->isClonedBlock());
4975 JS_ASSERT(protoIndex == 0);
4976 } else {
4977 /* Call and DeclEnvClass objects have no prototypes. */
4978 JS_ASSERT(!obj->getProto());
4979 JS_ASSERT(protoIndex == 0);
4981 } else {
4982 JS_ASSERT(obj->isNative());
4984 #endif
4986 * We must check if pobj is native as a global object can have
4987 * non-native prototype.
4989 if (cacheResult && pobj->isNative()) {
4990 entry = JS_PROPERTY_CACHE(cx).fill(cx, scopeChain, scopeIndex,
4991 protoIndex, pobj,
4992 (Shape *) prop);
4994 SCOPE_DEPTH_ACCUM(&cx->runtime->scopeSearchDepthStats, scopeIndex);
4995 goto out;
4998 if (!parent) {
4999 pobj = NULL;
5000 goto out;
5002 obj = parent;
5003 parent = obj->getParent();
5006 for (;;) {
5007 if (!obj->lookupProperty(cx, id, &pobj, &prop))
5008 return NULL;
5009 if (prop) {
5010 PCMETER(JS_PROPERTY_CACHE(cx).nofills++);
5011 goto out;
5015 * We conservatively assume that a resolve hook could mutate the scope
5016 * chain during JSObject::lookupProperty. So we read parent here again.
5018 parent = obj->getParent();
5019 if (!parent) {
5020 pobj = NULL;
5021 break;
5023 obj = parent;
5026 out:
5027 JS_ASSERT(!!pobj == !!prop);
5028 *objp = obj;
5029 *pobjp = pobj;
5030 *propp = prop;
5031 return entry;
5035 * On return, if |*pobjp| is a native object, then |*propp| is a |Shape *|.
5036 * Otherwise, its type and meaning depends on the host object's implementation.
5038 JS_FRIEND_API(JSBool)
5039 js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp,
5040 JSProperty **propp)
5042 return !!js_FindPropertyHelper(cx, id, false, objp, pobjp, propp);
5045 JSObject *
5046 js_FindIdentifierBase(JSContext *cx, JSObject *scopeChain, jsid id)
5049 * This function should not be called for a global object or from the
5050 * trace and should have a valid cache entry for native scopeChain.
5052 JS_ASSERT(scopeChain->getParent());
5053 JS_ASSERT(!JS_ON_TRACE(cx));
5055 JSObject *obj = scopeChain;
5058 * Loop over cacheable objects on the scope chain until we find a
5059 * property. We also stop when we reach the global object skipping any
5060 * farther checks or lookups. For details see the JSOP_BINDNAME case of
5061 * js_Interpret.
5063 * The test order here matters because js_IsCacheableNonGlobalScope
5064 * must not be passed a global object (i.e. one with null parent).
5066 for (int scopeIndex = 0;
5067 !obj->getParent() || js_IsCacheableNonGlobalScope(obj);
5068 scopeIndex++) {
5069 JSObject *pobj;
5070 JSProperty *prop;
5071 int protoIndex = js_LookupPropertyWithFlags(cx, obj, id,
5072 cx->resolveFlags,
5073 &pobj, &prop);
5074 if (protoIndex < 0)
5075 return NULL;
5076 if (prop) {
5077 if (!pobj->isNative()) {
5078 JS_ASSERT(!obj->getParent());
5079 return obj;
5081 JS_ASSERT_IF(obj->getParent(), pobj->getClass() == obj->getClass());
5082 #ifdef DEBUG
5083 PropertyCacheEntry *entry =
5084 #endif
5085 JS_PROPERTY_CACHE(cx).fill(cx, scopeChain, scopeIndex, protoIndex, pobj,
5086 (Shape *) prop);
5087 JS_ASSERT(entry);
5088 return obj;
5091 JSObject *parent = obj->getParent();
5092 if (!parent)
5093 return obj;
5094 obj = parent;
5097 /* Loop until we find a property or reach the global object. */
5098 do {
5099 JSObject *pobj;
5100 JSProperty *prop;
5101 if (!obj->lookupProperty(cx, id, &pobj, &prop))
5102 return NULL;
5103 if (prop)
5104 break;
5107 * We conservatively assume that a resolve hook could mutate the scope
5108 * chain during JSObject::lookupProperty. So we must check if parent is
5109 * not null here even if it wasn't before the lookup.
5111 JSObject *parent = obj->getParent();
5112 if (!parent)
5113 break;
5114 obj = parent;
5115 } while (obj->getParent());
5116 return obj;
5119 static JS_ALWAYS_INLINE JSBool
5120 js_NativeGetInline(JSContext *cx, JSObject *receiver, JSObject *obj, JSObject *pobj,
5121 const Shape *shape, uintN getHow, Value *vp)
5123 LeaveTraceIfGlobalObject(cx, pobj);
5125 uint32 slot;
5126 int32 sample;
5128 JS_ASSERT(pobj->isNative());
5130 slot = shape->slot;
5131 if (slot != SHAPE_INVALID_SLOT) {
5132 *vp = pobj->nativeGetSlot(slot);
5133 JS_ASSERT(!vp->isMagic());
5134 } else {
5135 vp->setUndefined();
5137 if (shape->hasDefaultGetter())
5138 return true;
5140 if (JS_UNLIKELY(shape->isMethod()) && (getHow & JSGET_NO_METHOD_BARRIER)) {
5141 JS_ASSERT(&shape->methodObject() == &vp->toObject());
5142 return true;
5145 sample = cx->runtime->propertyRemovals;
5147 AutoShapeRooter tvr(cx, shape);
5148 AutoObjectRooter tvr2(cx, pobj);
5149 if (!shape->get(cx, receiver, obj, pobj, vp))
5150 return false;
5153 if (pobj->containsSlot(slot) &&
5154 (JS_LIKELY(cx->runtime->propertyRemovals == sample) ||
5155 pobj->nativeContains(*shape))) {
5156 if (!pobj->methodWriteBarrier(cx, *shape, *vp))
5157 return false;
5158 pobj->nativeSetSlot(slot, *vp);
5161 return true;
5164 JSBool
5165 js_NativeGet(JSContext *cx, JSObject *obj, JSObject *pobj, const Shape *shape, uintN getHow,
5166 Value *vp)
5168 return js_NativeGetInline(cx, obj, obj, pobj, shape, getHow, vp);
5171 JSBool
5172 js_NativeSet(JSContext *cx, JSObject *obj, const Shape *shape, bool added, Value *vp)
5174 LeaveTraceIfGlobalObject(cx, obj);
5176 uint32 slot;
5177 int32 sample;
5179 JS_ASSERT(obj->isNative());
5181 slot = shape->slot;
5182 if (slot != SHAPE_INVALID_SLOT) {
5183 JS_ASSERT(obj->containsSlot(slot));
5185 /* If shape has a stub setter, keep obj locked and just store *vp. */
5186 if (shape->hasDefaultSetter()) {
5187 if (!added && !obj->methodWriteBarrier(cx, *shape, *vp))
5188 return false;
5189 AbortRecordingIfUnexpectedGlobalWrite(cx, obj, slot);
5190 obj->nativeSetSlot(slot, *vp);
5191 return true;
5193 } else {
5195 * Allow API consumers to create shared properties with stub setters.
5196 * Such properties effectively function as data descriptors which are
5197 * not writable, so attempting to set such a property should do nothing
5198 * or throw if we're in strict mode.
5200 if (!shape->hasGetterValue() && shape->hasDefaultSetter())
5201 return js_ReportGetterOnlyAssignment(cx);
5204 sample = cx->runtime->propertyRemovals;
5206 AutoShapeRooter tvr(cx, shape);
5207 if (!shape->set(cx, obj, vp))
5208 return false;
5210 JS_ASSERT_IF(!obj->inDictionaryMode(), shape->slot == slot);
5211 slot = shape->slot;
5214 if (obj->containsSlot(slot) &&
5215 (JS_LIKELY(cx->runtime->propertyRemovals == sample) ||
5216 obj->nativeContains(*shape))) {
5217 if (!added && !obj->methodWriteBarrier(cx, *shape, *vp))
5218 return false;
5219 AbortRecordingIfUnexpectedGlobalWrite(cx, obj, slot);
5220 obj->setSlot(slot, *vp);
5223 return true;
5226 static JS_ALWAYS_INLINE bool
5227 js_GetPropertyHelperWithShapeInline(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id,
5228 uintN getHow, Value *vp,
5229 const Shape **shapeOut, JSObject **holderOut)
5231 JSObject *aobj, *obj2;
5232 int protoIndex;
5233 JSProperty *prop;
5234 const Shape *shape;
5236 JS_ASSERT_IF(getHow & JSGET_CACHE_RESULT, !JS_ON_TRACE(cx));
5238 *shapeOut = NULL;
5240 /* Convert string indices to integers if appropriate. */
5241 id = js_CheckForStringIndex(id);
5243 aobj = js_GetProtoIfDenseArray(obj);
5244 /* This call site is hot -- use the always-inlined variant of js_LookupPropertyWithFlags(). */
5245 protoIndex = js_LookupPropertyWithFlagsInline(cx, aobj, id, cx->resolveFlags,
5246 &obj2, &prop);
5247 if (protoIndex < 0)
5248 return JS_FALSE;
5250 *holderOut = obj2;
5252 if (!prop) {
5253 vp->setUndefined();
5255 if (!CallJSPropertyOp(cx, obj->getClass()->getProperty, obj, id, vp))
5256 return JS_FALSE;
5258 PCMETER(getHow & JSGET_CACHE_RESULT && JS_PROPERTY_CACHE(cx).nofills++);
5261 * Give a strict warning if foo.bar is evaluated by a script for an
5262 * object foo with no property named 'bar'.
5264 jsbytecode *pc;
5265 if (vp->isUndefined() && ((pc = js_GetCurrentBytecodePC(cx)) != NULL)) {
5266 JSOp op;
5267 uintN flags;
5269 op = (JSOp) *pc;
5270 if (op == JSOP_TRAP) {
5271 JS_ASSERT_NOT_ON_TRACE(cx);
5272 op = JS_GetTrapOpcode(cx, cx->fp()->script(), pc);
5274 if (op == JSOP_GETXPROP) {
5275 flags = JSREPORT_ERROR;
5276 } else {
5277 if (!JS_HAS_STRICT_OPTION(cx) ||
5278 (op != JSOP_GETPROP && op != JSOP_GETELEM) ||
5279 js_CurrentPCIsInImacro(cx)) {
5280 return JS_TRUE;
5284 * XXX do not warn about missing __iterator__ as the function
5285 * may be called from JS_GetMethodById. See bug 355145.
5287 if (JSID_IS_ATOM(id, cx->runtime->atomState.iteratorAtom))
5288 return JS_TRUE;
5290 /* Do not warn about tests like (obj[prop] == undefined). */
5291 if (cx->resolveFlags == JSRESOLVE_INFER) {
5292 LeaveTrace(cx);
5293 pc += js_CodeSpec[op].length;
5294 if (Detecting(cx, pc))
5295 return JS_TRUE;
5296 } else if (cx->resolveFlags & JSRESOLVE_DETECTING) {
5297 return JS_TRUE;
5300 flags = JSREPORT_WARNING | JSREPORT_STRICT;
5303 /* Ok, bad undefined property reference: whine about it. */
5304 if (!js_ReportValueErrorFlags(cx, flags, JSMSG_UNDEFINED_PROP,
5305 JSDVG_IGNORE_STACK, IdToValue(id),
5306 NULL, NULL, NULL)) {
5307 return JS_FALSE;
5310 return JS_TRUE;
5313 if (!obj2->isNative()) {
5314 return obj2->isProxy()
5315 ? JSProxy::get(cx, obj2, receiver, id, vp)
5316 : obj2->getProperty(cx, id, vp);
5319 shape = (Shape *) prop;
5320 *shapeOut = shape;
5322 if (getHow & JSGET_CACHE_RESULT) {
5323 JS_ASSERT_NOT_ON_TRACE(cx);
5324 JS_PROPERTY_CACHE(cx).fill(cx, aobj, 0, protoIndex, obj2, shape);
5327 /* This call site is hot -- use the always-inlined variant of js_NativeGet(). */
5328 if (!js_NativeGetInline(cx, receiver, obj, obj2, shape, getHow, vp))
5329 return JS_FALSE;
5331 return JS_TRUE;
5334 bool
5335 js_GetPropertyHelperWithShape(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id,
5336 uint32 getHow, Value *vp,
5337 const Shape **shapeOut, JSObject **holderOut)
5339 return js_GetPropertyHelperWithShapeInline(cx, obj, receiver, id, getHow, vp,
5340 shapeOut, holderOut);
5343 static JS_ALWAYS_INLINE JSBool
5344 js_GetPropertyHelperInline(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id,
5345 uint32 getHow, Value *vp)
5347 const Shape *shape;
5348 JSObject *holder;
5349 return js_GetPropertyHelperWithShapeInline(cx, obj, receiver, id, getHow, vp, &shape, &holder);
5352 JSBool
5353 js_GetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uint32 getHow, Value *vp)
5355 return js_GetPropertyHelperInline(cx, obj, obj, id, getHow, vp);
5358 JSBool
5359 js_GetProperty(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp)
5361 /* This call site is hot -- use the always-inlined variant of js_GetPropertyHelper(). */
5362 return js_GetPropertyHelperInline(cx, obj, receiver, id, JSGET_METHOD_BARRIER, vp);
5365 JSBool
5366 js::GetPropertyDefault(JSContext *cx, JSObject *obj, jsid id, const Value &def, Value *vp)
5368 JSProperty *prop;
5369 JSObject *obj2;
5370 if (js_LookupPropertyWithFlags(cx, obj, id, JSRESOLVE_QUALIFIED, &obj2, &prop) < 0)
5371 return false;
5373 if (!prop) {
5374 *vp = def;
5375 return true;
5378 return js_GetProperty(cx, obj2, id, vp);
5381 JSBool
5382 js_GetMethod(JSContext *cx, JSObject *obj, jsid id, uintN getHow, Value *vp)
5384 JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED);
5386 PropertyIdOp op = obj->getOps()->getProperty;
5387 if (!op) {
5388 #if JS_HAS_XML_SUPPORT
5389 JS_ASSERT(!obj->isXML());
5390 #endif
5391 return js_GetPropertyHelper(cx, obj, id, getHow, vp);
5393 JS_ASSERT_IF(getHow & JSGET_CACHE_RESULT, obj->isDenseArray());
5394 #if JS_HAS_XML_SUPPORT
5395 if (obj->isXML())
5396 return js_GetXMLMethod(cx, obj, id, vp);
5397 #endif
5398 return op(cx, obj, obj, id, vp);
5401 JS_FRIEND_API(bool)
5402 js_CheckUndeclaredVarAssignment(JSContext *cx, JSString *propname)
5404 JSStackFrame *const fp = js_GetTopStackFrame(cx);
5405 if (!fp)
5406 return true;
5408 /* If neither cx nor the code is strict, then no check is needed. */
5409 if (!(fp->isScriptFrame() && fp->script()->strictModeCode) &&
5410 !JS_HAS_STRICT_OPTION(cx)) {
5411 return true;
5414 JSAutoByteString bytes(cx, propname);
5415 return !!bytes &&
5416 JS_ReportErrorFlagsAndNumber(cx,
5417 (JSREPORT_WARNING | JSREPORT_STRICT
5418 | JSREPORT_STRICT_MODE_ERROR),
5419 js_GetErrorMessage, NULL,
5420 JSMSG_UNDECLARED_VAR, bytes.ptr());
5423 bool
5424 JSObject::reportReadOnly(JSContext* cx, jsid id, uintN report)
5426 return js_ReportValueErrorFlags(cx, report, JSMSG_READ_ONLY,
5427 JSDVG_IGNORE_STACK, IdToValue(id), NULL,
5428 NULL, NULL);
5431 bool
5432 JSObject::reportNotConfigurable(JSContext* cx, jsid id, uintN report)
5434 return js_ReportValueErrorFlags(cx, report, JSMSG_CANT_DELETE,
5435 JSDVG_IGNORE_STACK, IdToValue(id), NULL,
5436 NULL, NULL);
5439 bool
5440 JSObject::reportNotExtensible(JSContext *cx, uintN report)
5442 return js_ReportValueErrorFlags(cx, report, JSMSG_OBJECT_NOT_EXTENSIBLE,
5443 JSDVG_IGNORE_STACK, ObjectValue(*this),
5444 NULL, NULL, NULL);
5447 JSBool
5448 js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow,
5449 Value *vp, JSBool strict)
5451 int protoIndex;
5452 JSObject *pobj;
5453 JSProperty *prop;
5454 const Shape *shape;
5455 uintN attrs, flags;
5456 intN shortid;
5457 Class *clasp;
5458 PropertyOp getter, setter;
5459 bool added;
5461 JS_ASSERT((defineHow &
5462 ~(JSDNP_CACHE_RESULT | JSDNP_SET_METHOD | JSDNP_UNQUALIFIED)) == 0);
5463 if (defineHow & JSDNP_CACHE_RESULT)
5464 JS_ASSERT_NOT_ON_TRACE(cx);
5466 /* Convert string indices to integers if appropriate. */
5467 id = js_CheckForStringIndex(id);
5469 protoIndex = js_LookupPropertyWithFlags(cx, obj, id, cx->resolveFlags,
5470 &pobj, &prop);
5471 if (protoIndex < 0)
5472 return JS_FALSE;
5473 if (prop) {
5474 if (!pobj->isNative()) {
5475 if (pobj->isProxy()) {
5476 AutoPropertyDescriptorRooter pd(cx);
5477 if (!JSProxy::getPropertyDescriptor(cx, pobj, id, true, &pd))
5478 return false;
5480 if (pd.attrs & JSPROP_SHARED)
5481 return CallSetter(cx, obj, id, pd.setter, pd.attrs, pd.shortid, vp);
5483 if (pd.attrs & JSPROP_READONLY) {
5484 if (strict)
5485 return obj->reportReadOnly(cx, id);
5486 if (JS_HAS_STRICT_OPTION(cx))
5487 return obj->reportReadOnly(cx, id, JSREPORT_STRICT | JSREPORT_WARNING);
5488 return true;
5492 prop = NULL;
5494 } else {
5495 /* We should never add properties to lexical blocks. */
5496 JS_ASSERT(!obj->isBlock());
5498 if (!obj->getParent() &&
5499 (defineHow & JSDNP_UNQUALIFIED) &&
5500 !js_CheckUndeclaredVarAssignment(cx, JSID_TO_STRING(id))) {
5501 return JS_FALSE;
5504 shape = (Shape *) prop;
5507 * Now either shape is null, meaning id was not found in obj or one of its
5508 * prototypes; or shape is non-null, meaning id was found directly in pobj.
5510 attrs = JSPROP_ENUMERATE;
5511 flags = 0;
5512 shortid = 0;
5513 clasp = obj->getClass();
5514 getter = clasp->getProperty;
5515 setter = clasp->setProperty;
5517 if (shape) {
5518 /* ES5 8.12.4 [[Put]] step 2. */
5519 if (shape->isAccessorDescriptor()) {
5520 if (shape->hasDefaultSetter())
5521 return js_ReportGetterOnlyAssignment(cx);
5522 } else {
5523 JS_ASSERT(shape->isDataDescriptor());
5525 if (!shape->writable()) {
5526 PCMETER((defineHow & JSDNP_CACHE_RESULT) && JS_PROPERTY_CACHE(cx).rofills++);
5528 /* Error in strict mode code, warn with strict option, otherwise do nothing. */
5529 if (strict)
5530 return obj->reportReadOnly(cx, id);
5531 if (JS_HAS_STRICT_OPTION(cx))
5532 return obj->reportReadOnly(cx, id, JSREPORT_STRICT | JSREPORT_WARNING);
5533 return JS_TRUE;
5537 attrs = shape->attributes();
5538 if (pobj != obj) {
5540 * We found id in a prototype object: prepare to share or shadow.
5542 if (!shape->shadowable()) {
5543 if (defineHow & JSDNP_CACHE_RESULT)
5544 JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, protoIndex, pobj, shape);
5546 if (shape->hasDefaultSetter() && !shape->hasGetterValue())
5547 return JS_TRUE;
5549 return shape->set(cx, obj, vp);
5553 * Preserve attrs except JSPROP_SHARED, getter, and setter when
5554 * shadowing any property that has no slot (is shared). We must
5555 * clear the shared attribute for the shadowing shape so that the
5556 * property in obj that it defines has a slot to retain the value
5557 * being set, in case the setter simply cannot operate on instances
5558 * of obj's class by storing the value in some class-specific
5559 * location.
5561 * A subset of slotless shared properties is the set of properties
5562 * with shortids, which must be preserved too. An old API requires
5563 * that the property's getter and setter receive the shortid, not
5564 * id, when they are called on the shadowing property that we are
5565 * about to create in obj.
5567 if (!shape->hasSlot()) {
5568 defineHow &= ~JSDNP_SET_METHOD;
5569 if (shape->hasShortID()) {
5570 flags = Shape::HAS_SHORTID;
5571 shortid = shape->shortid;
5573 attrs &= ~JSPROP_SHARED;
5574 getter = shape->getter();
5575 setter = shape->setter();
5576 } else {
5577 /* Restore attrs to the ECMA default for new properties. */
5578 attrs = JSPROP_ENUMERATE;
5582 * Forget we found the proto-property now that we've copied any
5583 * needed member values.
5585 shape = NULL;
5588 JS_ASSERT_IF(shape && shape->isMethod(), pobj->hasMethodBarrier());
5589 JS_ASSERT_IF(shape && shape->isMethod(),
5590 &pobj->getSlot(shape->slot).toObject() == &shape->methodObject());
5591 if (shape && (defineHow & JSDNP_SET_METHOD)) {
5593 * JSOP_SETMETHOD is assigning to an existing own property. If it
5594 * is an identical method property, do nothing. Otherwise downgrade
5595 * to ordinary assignment. Either way, do not fill the property
5596 * cache, as the interpreter has no fast path for these unusual
5597 * cases.
5599 bool identical = shape->isMethod() && &shape->methodObject() == &vp->toObject();
5600 if (!identical) {
5601 shape = obj->methodShapeChange(cx, *shape);
5602 if (!shape)
5603 return false;
5605 JSObject *funobj = &vp->toObject();
5606 JSFunction *fun = funobj->getFunctionPrivate();
5607 if (fun == funobj) {
5608 funobj = CloneFunctionObject(cx, fun, fun->parent);
5609 if (!funobj)
5610 return JS_FALSE;
5611 vp->setObject(*funobj);
5614 return identical || js_NativeSet(cx, obj, shape, false, vp);
5618 added = false;
5619 if (!shape) {
5620 if (!obj->isExtensible()) {
5621 /* Error in strict mode code, warn with strict option, otherwise do nothing. */
5622 if (strict)
5623 return obj->reportNotExtensible(cx);
5624 if (JS_HAS_STRICT_OPTION(cx))
5625 return obj->reportNotExtensible(cx, JSREPORT_STRICT | JSREPORT_WARNING);
5626 return JS_TRUE;
5630 * Purge the property cache of now-shadowed id in obj's scope chain.
5631 * Do this early, before locking obj to avoid nesting locks.
5633 js_PurgeScopeChain(cx, obj, id);
5635 /* Find or make a property descriptor with the right heritage. */
5636 if (!obj->ensureClassReservedSlots(cx))
5637 return JS_FALSE;
5640 * Check for Object class here to avoid defining a method on a class
5641 * with magic resolve, addProperty, getProperty, etc. hooks.
5643 if ((defineHow & JSDNP_SET_METHOD) && obj->canHaveMethodBarrier()) {
5644 JS_ASSERT(IsFunctionObject(*vp));
5645 JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
5647 JSObject *funobj = &vp->toObject();
5648 JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
5649 if (fun == funobj) {
5650 flags |= Shape::METHOD;
5651 getter = CastAsPropertyOp(funobj);
5655 shape = obj->putProperty(cx, id, getter, setter, SHAPE_INVALID_SLOT,
5656 attrs, flags, shortid);
5657 if (!shape)
5658 return JS_FALSE;
5660 if (defineHow & JSDNP_CACHE_RESULT)
5661 TRACE_1(AddProperty, obj);
5664 * Initialize the new property value (passed to setter) to undefined.
5665 * Note that we store before calling addProperty, to match the order
5666 * in js_DefineNativeProperty.
5668 if (obj->containsSlot(shape->slot))
5669 obj->nativeSetSlot(shape->slot, UndefinedValue());
5671 /* XXXbe called with obj locked */
5672 if (!CallAddPropertyHook(cx, clasp, obj, shape, vp)) {
5673 obj->removeProperty(cx, id);
5674 return JS_FALSE;
5676 added = true;
5679 if (defineHow & JSDNP_CACHE_RESULT)
5680 JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, 0, obj, shape, added);
5682 return js_NativeSet(cx, obj, shape, added, vp);
5684 #ifdef JS_TRACER
5685 error: // TRACE_1 jumps here in case of error.
5686 return JS_FALSE;
5687 #endif
5690 JSBool
5691 js_SetProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict)
5693 return js_SetPropertyHelper(cx, obj, id, 0, vp, strict);
5696 JSBool
5697 js_GetAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp)
5699 JSProperty *prop;
5700 if (!js_LookupProperty(cx, obj, id, &obj, &prop))
5701 return false;
5702 if (!prop) {
5703 *attrsp = 0;
5704 return true;
5706 if (!obj->isNative())
5707 return obj->getAttributes(cx, id, attrsp);
5709 const Shape *shape = (Shape *)prop;
5710 *attrsp = shape->attributes();
5711 return true;
5714 JSBool
5715 js_SetNativeAttributes(JSContext *cx, JSObject *obj, Shape *shape, uintN attrs)
5717 JS_ASSERT(obj->isNative());
5718 return !!js_ChangeNativePropertyAttrs(cx, obj, shape, attrs, 0,
5719 shape->getter(), shape->setter());
5722 JSBool
5723 js_SetAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp)
5725 JSProperty *prop;
5726 if (!js_LookupProperty(cx, obj, id, &obj, &prop))
5727 return false;
5728 if (!prop)
5729 return true;
5730 return obj->isNative()
5731 ? js_SetNativeAttributes(cx, obj, (Shape *) prop, *attrsp)
5732 : obj->setAttributes(cx, id, attrsp);
5735 JSBool
5736 js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool strict)
5738 JSObject *proto;
5739 JSProperty *prop;
5740 const Shape *shape;
5742 rval->setBoolean(true);
5744 /* Convert string indices to integers if appropriate. */
5745 id = js_CheckForStringIndex(id);
5747 if (!js_LookupProperty(cx, obj, id, &proto, &prop))
5748 return false;
5749 if (!prop || proto != obj) {
5751 * If the property was found in a native prototype, check whether it's
5752 * shared and permanent. Such a property stands for direct properties
5753 * in all delegating objects, matching ECMA semantics without bloating
5754 * each delegating object.
5756 if (prop && proto->isNative()) {
5757 shape = (Shape *)prop;
5758 if (shape->isSharedPermanent()) {
5759 if (strict)
5760 return obj->reportNotConfigurable(cx, id);
5761 rval->setBoolean(false);
5762 return true;
5767 * If no property, or the property comes unshared or impermanent from
5768 * a prototype, call the class's delProperty hook, passing rval as the
5769 * result parameter.
5771 return CallJSPropertyOp(cx, obj->getClass()->delProperty, obj, id, rval);
5774 shape = (Shape *)prop;
5775 if (!shape->configurable()) {
5776 if (strict)
5777 return obj->reportNotConfigurable(cx, id);
5778 rval->setBoolean(false);
5779 return true;
5782 if (!CallJSPropertyOp(cx, obj->getClass()->delProperty, obj, SHAPE_USERID(shape), rval))
5783 return false;
5785 if (obj->containsSlot(shape->slot)) {
5786 const Value &v = obj->nativeGetSlot(shape->slot);
5787 GC_POKE(cx, v);
5790 * Delete is rare enough that we can take the hit of checking for an
5791 * active cloned method function object that must be homed to a callee
5792 * slot on the active stack frame before this delete completes, in case
5793 * someone saved the clone and checks it against foo.caller for a foo
5794 * called from the active method.
5796 * We do not check suspended frames. They can't be reached via caller,
5797 * so the only way they could have the method's joined function object
5798 * as callee is through an API abusage. We break any such edge case.
5800 if (obj->hasMethodBarrier()) {
5801 JSObject *funobj;
5803 if (IsFunctionObject(v, &funobj)) {
5804 JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
5806 if (fun != funobj) {
5807 for (JSStackFrame *fp = cx->maybefp(); fp; fp = fp->prev()) {
5808 if (fp->isFunctionFrame() &&
5809 &fp->callee() == &fun->compiledFunObj() &&
5810 fp->thisValue().isObject())
5812 JSObject *tmp = &fp->thisValue().toObject();
5813 do {
5814 if (tmp == obj) {
5815 fp->calleeValue().setObject(*funobj);
5816 break;
5818 } while ((tmp = tmp->getProto()) != NULL);
5826 return obj->removeProperty(cx, id) && js_SuppressDeletedProperty(cx, obj, id);
5829 namespace js {
5831 JSObject *
5832 HasNativeMethod(JSObject *obj, jsid methodid, Native native)
5834 const Shape *shape = obj->nativeLookup(methodid);
5835 if (!shape || !shape->hasDefaultGetter() || !obj->containsSlot(shape->slot))
5836 return NULL;
5838 const Value &fval = obj->nativeGetSlot(shape->slot);
5839 JSObject *funobj;
5840 if (!IsFunctionObject(fval, &funobj) || funobj->getFunctionPrivate()->maybeNative() != native)
5841 return NULL;
5843 return funobj;
5847 * When we have an object of a builtin class, we don't quite know what its
5848 * valueOf/toString methods are, since these methods may have been overwritten
5849 * or shadowed. However, we can still do better than js_TryMethod by
5850 * hard-coding the necessary properties for us to find the native we expect.
5852 * TODO: a per-thread shape-based cache would be faster and simpler.
5854 static JS_ALWAYS_INLINE bool
5855 ClassMethodIsNative(JSContext *cx, JSObject *obj, Class *clasp, jsid methodid,
5856 Native native)
5858 JS_ASSERT(obj->getClass() == clasp);
5860 if (HasNativeMethod(obj, methodid, native))
5861 return true;
5863 JSObject *pobj = obj->getProto();
5864 return pobj && pobj->getClass() == clasp &&
5865 HasNativeMethod(pobj, methodid, native);
5868 bool
5869 DefaultValue(JSContext *cx, JSObject *obj, JSType hint, Value *vp)
5871 JS_ASSERT(hint != JSTYPE_OBJECT && hint != JSTYPE_FUNCTION);
5873 Value v = ObjectValue(*obj);
5874 if (hint == JSTYPE_STRING) {
5875 /* Optimize (new String(...)).toString(). */
5876 if (obj->getClass() == &js_StringClass &&
5877 ClassMethodIsNative(cx, obj,
5878 &js_StringClass,
5879 ATOM_TO_JSID(cx->runtime->atomState.toStringAtom),
5880 js_str_toString)) {
5881 *vp = obj->getPrimitiveThis();
5882 return true;
5885 if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0, NULL, &v))
5886 return false;
5887 if (!v.isPrimitive()) {
5888 if (!obj->getClass()->convert(cx, obj, hint, &v))
5889 return false;
5891 } else {
5892 /* Optimize (new String(...)).valueOf(). */
5893 Class *clasp = obj->getClass();
5894 if ((clasp == &js_StringClass &&
5895 ClassMethodIsNative(cx, obj, &js_StringClass,
5896 ATOM_TO_JSID(cx->runtime->atomState.valueOfAtom),
5897 js_str_toString)) ||
5898 (clasp == &js_NumberClass &&
5899 ClassMethodIsNative(cx, obj, &js_NumberClass,
5900 ATOM_TO_JSID(cx->runtime->atomState.valueOfAtom),
5901 js_num_valueOf))) {
5902 *vp = obj->getPrimitiveThis();
5903 return true;
5906 if (!obj->getClass()->convert(cx, obj, hint, &v))
5907 return false;
5908 if (v.isObject()) {
5909 JS_ASSERT(hint != TypeOfValue(cx, v));
5910 if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0, NULL, &v))
5911 return false;
5914 if (v.isObject()) {
5915 /* Avoid recursive death when decompiling in js_ReportValueError. */
5916 JSString *str;
5917 if (hint == JSTYPE_STRING) {
5918 str = JS_InternString(cx, obj->getClass()->name);
5919 if (!str)
5920 return false;
5921 } else {
5922 str = NULL;
5924 vp->setObject(*obj);
5925 js_ReportValueError2(cx, JSMSG_CANT_CONVERT_TO,
5926 JSDVG_SEARCH_STACK, *vp, str,
5927 (hint == JSTYPE_VOID)
5928 ? "primitive type"
5929 : JS_TYPE_STR(hint));
5930 return false;
5932 *vp = v;
5933 return true;
5936 } /* namespace js */
5938 JS_FRIEND_API(JSBool)
5939 js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, Value *statep, jsid *idp)
5941 /* If the class has a custom JSCLASS_NEW_ENUMERATE hook, call it. */
5942 Class *clasp = obj->getClass();
5943 JSEnumerateOp enumerate = clasp->enumerate;
5944 if (clasp->flags & JSCLASS_NEW_ENUMERATE) {
5945 JS_ASSERT(enumerate != JS_EnumerateStub);
5946 return ((NewEnumerateOp) enumerate)(cx, obj, enum_op, statep, idp);
5949 if (!enumerate(cx, obj))
5950 return false;
5952 /* Tell InitNativeIterator to treat us like a native object. */
5953 JS_ASSERT(enum_op == JSENUMERATE_INIT || enum_op == JSENUMERATE_INIT_ALL);
5954 statep->setMagic(JS_NATIVE_ENUMERATE);
5955 return true;
5958 namespace js {
5960 JSBool
5961 CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
5962 Value *vp, uintN *attrsp)
5964 JSBool writing;
5965 JSObject *pobj;
5966 JSProperty *prop;
5967 Class *clasp;
5968 const Shape *shape;
5969 JSSecurityCallbacks *callbacks;
5970 CheckAccessOp check;
5972 while (JS_UNLIKELY(obj->getClass() == &js_WithClass))
5973 obj = obj->getProto();
5975 writing = (mode & JSACC_WRITE) != 0;
5976 switch (mode & JSACC_TYPEMASK) {
5977 case JSACC_PROTO:
5978 pobj = obj;
5979 if (!writing)
5980 vp->setObjectOrNull(obj->getProto());
5981 *attrsp = JSPROP_PERMANENT;
5982 break;
5984 case JSACC_PARENT:
5985 JS_ASSERT(!writing);
5986 pobj = obj;
5987 vp->setObject(*obj->getParent());
5988 *attrsp = JSPROP_READONLY | JSPROP_PERMANENT;
5989 break;
5991 default:
5992 if (!obj->lookupProperty(cx, id, &pobj, &prop))
5993 return JS_FALSE;
5994 if (!prop) {
5995 if (!writing)
5996 vp->setUndefined();
5997 *attrsp = 0;
5998 pobj = obj;
5999 break;
6002 if (!pobj->isNative()) {
6003 if (!writing) {
6004 vp->setUndefined();
6005 *attrsp = 0;
6007 break;
6010 shape = (Shape *)prop;
6011 *attrsp = shape->attributes();
6012 if (!writing) {
6013 if (pobj->containsSlot(shape->slot))
6014 *vp = pobj->nativeGetSlot(shape->slot);
6015 else
6016 vp->setUndefined();
6021 * If obj's class has a stub (null) checkAccess hook, use the per-runtime
6022 * checkObjectAccess callback, if configured.
6024 * We don't want to require all classes to supply a checkAccess hook; we
6025 * need that hook only for certain classes used when precompiling scripts
6026 * and functions ("brutal sharing"). But for general safety of built-in
6027 * magic properties like __proto__, we route all access checks, even for
6028 * classes that stub out checkAccess, through the global checkObjectAccess
6029 * hook. This covers precompilation-based sharing and (possibly
6030 * unintended) runtime sharing across trust boundaries.
6032 clasp = pobj->getClass();
6033 check = clasp->checkAccess;
6034 if (!check) {
6035 callbacks = JS_GetSecurityCallbacks(cx);
6036 check = callbacks ? Valueify(callbacks->checkObjectAccess) : NULL;
6038 return !check || check(cx, pobj, id, mode, vp);
6043 JSType
6044 js_TypeOf(JSContext *cx, JSObject *obj)
6047 * ECMA 262, 11.4.3 says that any native object that implements
6048 * [[Call]] should be of type "function". However, RegExp is of
6049 * type "object", not "function", for Web compatibility.
6051 if (obj->isCallable()) {
6052 return (obj->getClass() != &js_RegExpClass)
6053 ? JSTYPE_FUNCTION
6054 : JSTYPE_OBJECT;
6057 return JSTYPE_OBJECT;
6060 bool
6061 js_IsDelegate(JSContext *cx, JSObject *obj, const Value &v)
6063 if (v.isPrimitive())
6064 return false;
6065 JSObject *obj2 = &v.toObject();
6066 while ((obj2 = obj2->getProto()) != NULL) {
6067 if (obj2 == obj)
6068 return true;
6070 return false;
6073 bool
6074 js::FindClassPrototype(JSContext *cx, JSObject *scopeobj, JSProtoKey protoKey,
6075 JSObject **protop, Class *clasp)
6077 Value v;
6078 if (!js_FindClassObject(cx, scopeobj, protoKey, &v, clasp))
6079 return false;
6081 if (IsFunctionObject(v)) {
6082 JSObject *ctor = &v.toObject();
6083 if (!ctor->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), &v))
6084 return false;
6087 *protop = v.isObject() ? &v.toObject() : NULL;
6088 return true;
6092 * The first part of this function has been hand-expanded and optimized into
6093 * NewBuiltinClassInstance in jsobjinlines.h.
6095 JSBool
6096 js_GetClassPrototype(JSContext *cx, JSObject *scopeobj, JSProtoKey protoKey,
6097 JSObject **protop, Class *clasp)
6099 VOUCH_DOES_NOT_REQUIRE_STACK();
6100 JS_ASSERT(JSProto_Null <= protoKey);
6101 JS_ASSERT(protoKey < JSProto_LIMIT);
6103 if (protoKey != JSProto_Null) {
6104 if (!scopeobj) {
6105 if (cx->hasfp())
6106 scopeobj = &cx->fp()->scopeChain();
6107 if (!scopeobj) {
6108 scopeobj = cx->globalObject;
6109 if (!scopeobj) {
6110 *protop = NULL;
6111 return true;
6115 scopeobj = scopeobj->getGlobal();
6116 if (scopeobj->isGlobal()) {
6117 const Value &v = scopeobj->getReservedSlot(JSProto_LIMIT + protoKey);
6118 if (v.isObject()) {
6119 *protop = &v.toObject();
6120 return true;
6125 return FindClassPrototype(cx, scopeobj, protoKey, protop, clasp);
6128 JSBool
6129 js_SetClassPrototype(JSContext *cx, JSObject *ctor, JSObject *proto, uintN attrs)
6132 * Use the given attributes for the prototype property of the constructor,
6133 * as user-defined constructors have a DontDelete prototype (which may be
6134 * reset), while native or "system" constructors have DontEnum | ReadOnly |
6135 * DontDelete.
6137 if (!ctor->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom),
6138 ObjectOrNullValue(proto), PropertyStub, PropertyStub, attrs)) {
6139 return JS_FALSE;
6143 * ECMA says that Object.prototype.constructor, or f.prototype.constructor
6144 * for a user-defined function f, is DontEnum.
6146 return proto->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.constructorAtom),
6147 ObjectOrNullValue(ctor), PropertyStub, PropertyStub, 0);
6150 JSBool
6151 js_PrimitiveToObject(JSContext *cx, Value *vp)
6153 Value v = *vp;
6154 JS_ASSERT(v.isPrimitive());
6156 Class *clasp;
6157 if (v.isNumber()) {
6158 clasp = &js_NumberClass;
6159 } else if (v.isString()) {
6160 clasp = &js_StringClass;
6161 } else {
6162 JS_ASSERT(v.isBoolean());
6163 clasp = &js_BooleanClass;
6166 JSObject *obj = NewBuiltinClassInstance(cx, clasp);
6167 if (!obj)
6168 return JS_FALSE;
6170 obj->setPrimitiveThis(v);
6171 vp->setObject(*obj);
6172 return JS_TRUE;
6175 JSBool
6176 js_ValueToObjectOrNull(JSContext *cx, const Value &v, JSObject **objp)
6178 JSObject *obj;
6180 if (v.isObjectOrNull()) {
6181 obj = v.toObjectOrNull();
6182 } else if (v.isUndefined()) {
6183 obj = NULL;
6184 } else {
6185 Value tmp = v;
6186 if (!js_PrimitiveToObject(cx, &tmp))
6187 return JS_FALSE;
6188 obj = &tmp.toObject();
6190 *objp = obj;
6191 return JS_TRUE;
6194 JSObject *
6195 js_ValueToNonNullObject(JSContext *cx, const Value &v)
6197 JSObject *obj;
6199 if (!js_ValueToObjectOrNull(cx, v, &obj))
6200 return NULL;
6201 if (!obj)
6202 js_ReportIsNullOrUndefined(cx, JSDVG_SEARCH_STACK, v, NULL);
6203 return obj;
6206 JSBool
6207 js_TryValueOf(JSContext *cx, JSObject *obj, JSType type, Value *rval)
6209 Value argv[1];
6211 argv[0].setString(ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[type]));
6212 return js_TryMethod(cx, obj, cx->runtime->atomState.valueOfAtom,
6213 1, argv, rval);
6216 JSBool
6217 js_TryMethod(JSContext *cx, JSObject *obj, JSAtom *atom,
6218 uintN argc, Value *argv, Value *rval)
6220 JS_CHECK_RECURSION(cx, return JS_FALSE);
6223 * Report failure only if an appropriate method was found, and calling it
6224 * returned failure. We propagate failure in this case to make exceptions
6225 * behave properly.
6227 JSErrorReporter older = JS_SetErrorReporter(cx, NULL);
6228 jsid id = ATOM_TO_JSID(atom);
6229 Value fval;
6230 JSBool ok = js_GetMethod(cx, obj, id, JSGET_NO_METHOD_BARRIER, &fval);
6231 JS_SetErrorReporter(cx, older);
6232 if (!ok)
6233 return false;
6235 if (fval.isPrimitive())
6236 return JS_TRUE;
6237 return ExternalInvoke(cx, obj, fval, argc, argv, rval);
6240 #if JS_HAS_XDR
6242 JSBool
6243 js_XDRObject(JSXDRState *xdr, JSObject **objp)
6245 JSContext *cx;
6246 JSAtom *atom;
6247 Class *clasp;
6248 uint32 classId, classDef;
6249 JSProtoKey protoKey;
6250 JSObject *proto;
6252 cx = xdr->cx;
6253 atom = NULL;
6254 if (xdr->mode == JSXDR_ENCODE) {
6255 clasp = (*objp)->getClass();
6256 classId = JS_XDRFindClassIdByName(xdr, clasp->name);
6257 classDef = !classId;
6258 if (classDef) {
6259 if (!JS_XDRRegisterClass(xdr, Jsvalify(clasp), &classId))
6260 return JS_FALSE;
6261 protoKey = JSCLASS_CACHED_PROTO_KEY(clasp);
6262 if (protoKey != JSProto_Null) {
6263 classDef |= (protoKey << 1);
6264 } else {
6265 atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0);
6266 if (!atom)
6267 return JS_FALSE;
6270 } else {
6271 clasp = NULL; /* quell GCC overwarning */
6272 classDef = 0;
6276 * XDR a flag word, which could be 0 for a class use, in which case no
6277 * name follows, only the id in xdr's class registry; 1 for a class def,
6278 * in which case the flag word is followed by the class name transferred
6279 * from or to atom; or a value greater than 1, an odd number that when
6280 * divided by two yields the JSProtoKey for class. In the last case, as
6281 * in the 0 classDef case, no name is transferred via atom.
6283 if (!JS_XDRUint32(xdr, &classDef))
6284 return JS_FALSE;
6285 if (classDef == 1 && !js_XDRAtom(xdr, &atom))
6286 return JS_FALSE;
6288 if (!JS_XDRUint32(xdr, &classId))
6289 return JS_FALSE;
6291 if (xdr->mode == JSXDR_DECODE) {
6292 if (classDef) {
6293 /* NB: we know that JSProto_Null is 0 here, for backward compat. */
6294 protoKey = (JSProtoKey) (classDef >> 1);
6295 if (!js_GetClassPrototype(cx, NULL, protoKey, &proto, clasp))
6296 return JS_FALSE;
6297 clasp = proto->getClass();
6298 if (!JS_XDRRegisterClass(xdr, Jsvalify(clasp), &classId))
6299 return JS_FALSE;
6300 } else {
6301 clasp = Valueify(JS_XDRFindClassById(xdr, classId));
6302 if (!clasp) {
6303 char numBuf[12];
6304 JS_snprintf(numBuf, sizeof numBuf, "%ld", (long)classId);
6305 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
6306 JSMSG_CANT_FIND_CLASS, numBuf);
6307 return JS_FALSE;
6312 if (!clasp->xdrObject) {
6313 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
6314 JSMSG_CANT_XDR_CLASS, clasp->name);
6315 return JS_FALSE;
6317 return clasp->xdrObject(xdr, objp);
6320 #endif /* JS_HAS_XDR */
6322 #ifdef JS_DUMP_SCOPE_METERS
6324 #include <stdio.h>
6326 JSBasicStats js_entry_count_bs = JS_INIT_STATIC_BASIC_STATS;
6328 static void
6329 MeterEntryCount(uintN count)
6331 JS_BASIC_STATS_ACCUM(&js_entry_count_bs, count);
6334 void
6335 js_DumpScopeMeters(JSRuntime *rt)
6337 static FILE *logfp;
6338 if (!logfp)
6339 logfp = fopen("/tmp/scope.stats", "a");
6342 double mean, sigma;
6344 mean = JS_MeanAndStdDevBS(&js_entry_count_bs, &sigma);
6346 fprintf(logfp, "scopes %u entries %g mean %g sigma %g max %u",
6347 js_entry_count_bs.num, js_entry_count_bs.sum, mean, sigma,
6348 js_entry_count_bs.max);
6351 JS_DumpHistogram(&js_entry_count_bs, logfp);
6352 JS_BASIC_STATS_INIT(&js_entry_count_bs);
6353 fflush(logfp);
6355 #endif
6357 #ifdef DEBUG
6358 void
6359 js_PrintObjectSlotName(JSTracer *trc, char *buf, size_t bufsize)
6361 JS_ASSERT(trc->debugPrinter == js_PrintObjectSlotName);
6363 JSObject *obj = (JSObject *)trc->debugPrintArg;
6364 uint32 slot = (uint32)trc->debugPrintIndex;
6366 const Shape *shape;
6367 if (obj->isNative()) {
6368 shape = obj->lastProperty();
6369 while (shape->previous() && shape->slot != slot)
6370 shape = shape->previous();
6371 if (shape->slot != slot)
6372 shape = NULL;
6373 } else {
6374 shape = NULL;
6377 if (!shape) {
6378 const char *slotname = NULL;
6379 if (obj->isGlobal()) {
6380 #define JS_PROTO(name,code,init) \
6381 if ((code) == slot) { slotname = js_##name##_str; goto found; }
6382 #include "jsproto.tbl"
6383 #undef JS_PROTO
6385 found:
6386 if (slotname)
6387 JS_snprintf(buf, bufsize, "CLASS_OBJECT(%s)", slotname);
6388 else
6389 JS_snprintf(buf, bufsize, "**UNKNOWN SLOT %ld**", (long)slot);
6390 } else {
6391 jsid id = shape->id;
6392 if (JSID_IS_INT(id)) {
6393 JS_snprintf(buf, bufsize, "%ld", (long)JSID_TO_INT(id));
6394 } else if (JSID_IS_ATOM(id)) {
6395 PutEscapedString(buf, bufsize, JSID_TO_ATOM(id), 0);
6396 } else {
6397 JS_snprintf(buf, bufsize, "**FINALIZED ATOM KEY**");
6401 #endif
6403 void
6404 js_TraceObject(JSTracer *trc, JSObject *obj)
6406 JS_ASSERT(obj->isNative());
6408 JSContext *cx = trc->context;
6409 if (obj->hasSlotsArray() && !obj->nativeEmpty() && IS_GC_MARKING_TRACER(trc)) {
6411 * Trim overlong dslots allocations from the GC, to avoid thrashing in
6412 * case of delete-happy code that settles down at a given population.
6413 * The !obj->nativeEmpty() guard above is due to the bug described by
6414 * the FIXME comment below.
6416 size_t slots = obj->slotSpan();
6417 if (obj->numSlots() != slots)
6418 obj->shrinkSlots(cx, slots);
6421 #ifdef JS_DUMP_SCOPE_METERS
6422 MeterEntryCount(obj->propertyCount);
6423 #endif
6425 obj->trace(trc);
6427 if (!JS_CLIST_IS_EMPTY(&cx->runtime->watchPointList))
6428 js_TraceWatchPoints(trc, obj);
6430 /* No one runs while the GC is running, so we can use LOCKED_... here. */
6431 Class *clasp = obj->getClass();
6432 if (clasp->mark) {
6433 if (clasp->flags & JSCLASS_MARK_IS_TRACE)
6434 ((JSTraceOp) clasp->mark)(trc, obj);
6435 else if (IS_GC_MARKING_TRACER(trc))
6436 (void) clasp->mark(cx, obj, trc);
6438 if (clasp->flags & JSCLASS_IS_GLOBAL) {
6439 JSCompartment *compartment = obj->getCompartment();
6440 compartment->marked = true;
6444 * NB: In case clasp->mark mutates something (which would be a bug, but we
6445 * want to be defensive), leave this code here -- don't move it up and
6446 * unify it with the |if (!traceScope)| section above.
6448 * FIXME: We minimize nslots against obj->slotSpan because native objects
6449 * such as Date instances may have failed to advance slotSpan to cover all
6450 * reserved slots (this Date issue may be a bug in JSObject::growSlots, but
6451 * the general problem occurs in other built-in class implementations).
6453 uint32 nslots = obj->numSlots();
6454 if (!obj->nativeEmpty() && obj->slotSpan() < nslots)
6455 nslots = obj->slotSpan();
6457 for (uint32 i = 0; i != nslots; ++i) {
6458 const Value &v = obj->getSlot(i);
6459 JS_SET_TRACING_DETAILS(trc, js_PrintObjectSlotName, obj, i);
6460 MarkValueRaw(trc, v);
6464 void
6465 js_ClearNative(JSContext *cx, JSObject *obj)
6468 * Clear obj of all obj's properties. FIXME: we do not clear reserved slots
6469 * lying below JSSLOT_FREE(clasp). JS_ClearScope does that.
6471 if (!obj->nativeEmpty()) {
6472 /* Now that we're done using real properties, clear obj. */
6473 obj->clear(cx);
6475 /* Clear slot values since obj->clear reset our shape to empty. */
6476 uint32 freeslot = JSSLOT_FREE(obj->getClass());
6477 uint32 n = obj->numSlots();
6478 for (uint32 i = freeslot; i < n; ++i)
6479 obj->setSlot(i, UndefinedValue());
6483 bool
6484 js_GetReservedSlot(JSContext *cx, JSObject *obj, uint32 slot, Value *vp)
6486 if (!obj->isNative()) {
6487 vp->setUndefined();
6488 return true;
6491 if (slot < obj->numSlots())
6492 *vp = obj->getSlot(slot);
6493 else
6494 vp->setUndefined();
6495 return true;
6498 bool
6499 js_SetReservedSlot(JSContext *cx, JSObject *obj, uint32 slot, const Value &v)
6501 if (!obj->isNative())
6502 return true;
6504 Class *clasp = obj->getClass();
6506 if (slot >= obj->numSlots()) {
6507 uint32 nslots = JSSLOT_FREE(clasp);
6508 JS_ASSERT(slot < nslots);
6509 if (!obj->allocSlots(cx, nslots))
6510 return false;
6513 obj->setSlot(slot, v);
6514 GC_POKE(cx, JS_NULL);
6515 return true;
6518 JSObject *
6519 JSObject::getGlobal() const
6521 JSObject *obj = const_cast<JSObject *>(this);
6522 while (JSObject *parent = obj->getParent())
6523 obj = parent;
6524 return obj;
6527 JSBool
6528 js_ReportGetterOnlyAssignment(JSContext *cx)
6530 return JS_ReportErrorFlagsAndNumber(cx,
6531 JSREPORT_WARNING | JSREPORT_STRICT |
6532 JSREPORT_STRICT_MODE_ERROR,
6533 js_GetErrorMessage, NULL,
6534 JSMSG_GETTER_ONLY);
6537 JS_FRIEND_API(JSBool)
6538 js_GetterOnlyPropertyStub(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
6540 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_GETTER_ONLY);
6541 return JS_FALSE;
6544 #ifdef DEBUG
6547 * Routines to print out values during debugging. These are FRIEND_API to help
6548 * the debugger find them and to support temporarily hacking js_Dump* calls
6549 * into other code.
6552 void
6553 dumpChars(const jschar *s, size_t n)
6555 size_t i;
6557 if (n == (size_t) -1) {
6558 while (s[++n]) ;
6561 fputc('"', stderr);
6562 for (i = 0; i < n; i++) {
6563 if (s[i] == '\n')
6564 fprintf(stderr, "\\n");
6565 else if (s[i] == '\t')
6566 fprintf(stderr, "\\t");
6567 else if (s[i] >= 32 && s[i] < 127)
6568 fputc(s[i], stderr);
6569 else if (s[i] <= 255)
6570 fprintf(stderr, "\\x%02x", (unsigned int) s[i]);
6571 else
6572 fprintf(stderr, "\\u%04x", (unsigned int) s[i]);
6574 fputc('"', stderr);
6577 JS_FRIEND_API(void)
6578 js_DumpChars(const jschar *s, size_t n)
6580 fprintf(stderr, "jschar * (%p) = ", (void *) s);
6581 dumpChars(s, n);
6582 fputc('\n', stderr);
6585 void
6586 dumpString(JSString *str)
6588 if (const jschar *chars = str->getChars(NULL))
6589 dumpChars(chars, str->length());
6590 else
6591 fprintf(stderr, "(oom in dumpString)");
6594 JS_FRIEND_API(void)
6595 js_DumpString(JSString *str)
6597 if (const jschar *chars = str->getChars(NULL)) {
6598 fprintf(stderr, "JSString* (%p) = jschar * (%p) = ",
6599 (void *) str, (void *) chars);
6600 dumpString(str);
6601 } else {
6602 fprintf(stderr, "(oom in JS_DumpString)");
6604 fputc('\n', stderr);
6607 JS_FRIEND_API(void)
6608 js_DumpAtom(JSAtom *atom)
6610 fprintf(stderr, "JSAtom* (%p) = ", (void *) atom);
6611 js_DumpString(ATOM_TO_STRING(atom));
6614 void
6615 dumpValue(const Value &v)
6617 if (v.isNull())
6618 fprintf(stderr, "null");
6619 else if (v.isUndefined())
6620 fprintf(stderr, "undefined");
6621 else if (v.isInt32())
6622 fprintf(stderr, "%d", v.toInt32());
6623 else if (v.isDouble())
6624 fprintf(stderr, "%g", v.toDouble());
6625 else if (v.isString())
6626 dumpString(v.toString());
6627 else if (v.isObject() && v.toObject().isFunction()) {
6628 JSObject *funobj = &v.toObject();
6629 JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
6630 if (fun->atom) {
6631 fputs("<function ", stderr);
6632 FileEscapedString(stderr, ATOM_TO_STRING(fun->atom), 0);
6633 } else {
6634 fputs("<unnamed function", stderr);
6636 if (fun->isInterpreted()) {
6637 JSScript *script = fun->script();
6638 fprintf(stderr, " (%s:%u)",
6639 script->filename ? script->filename : "", script->lineno);
6641 fprintf(stderr, " at %p (JSFunction at %p)>", (void *) funobj, (void *) fun);
6642 } else if (v.isObject()) {
6643 JSObject *obj = &v.toObject();
6644 Class *clasp = obj->getClass();
6645 fprintf(stderr, "<%s%s at %p>",
6646 clasp->name,
6647 (clasp == &js_ObjectClass) ? "" : " object",
6648 (void *) obj);
6649 } else if (v.isBoolean()) {
6650 if (v.toBoolean())
6651 fprintf(stderr, "true");
6652 else
6653 fprintf(stderr, "false");
6654 } else if (v.isMagic()) {
6655 fprintf(stderr, "<invalid");
6656 #ifdef DEBUG
6657 switch (v.whyMagic()) {
6658 case JS_ARRAY_HOLE: fprintf(stderr, " array hole"); break;
6659 case JS_ARGS_HOLE: fprintf(stderr, " args hole"); break;
6660 case JS_NATIVE_ENUMERATE: fprintf(stderr, " native enumeration"); break;
6661 case JS_NO_ITER_VALUE: fprintf(stderr, " no iter value"); break;
6662 case JS_GENERATOR_CLOSING: fprintf(stderr, " generator closing"); break;
6663 default: fprintf(stderr, " ?!"); break;
6665 #endif
6666 fprintf(stderr, ">");
6667 } else {
6668 fprintf(stderr, "unexpected value");
6672 JS_FRIEND_API(void)
6673 js_DumpValue(const Value &val)
6675 dumpValue(val);
6676 fputc('\n', stderr);
6679 JS_FRIEND_API(void)
6680 js_DumpId(jsid id)
6682 fprintf(stderr, "jsid %p = ", (void *) JSID_BITS(id));
6683 dumpValue(IdToValue(id));
6684 fputc('\n', stderr);
6687 static void
6688 DumpShape(const Shape &shape)
6690 jsid id = shape.id;
6691 uint8 attrs = shape.attributes();
6693 fprintf(stderr, " ");
6694 if (attrs & JSPROP_ENUMERATE) fprintf(stderr, "enumerate ");
6695 if (attrs & JSPROP_READONLY) fprintf(stderr, "readonly ");
6696 if (attrs & JSPROP_PERMANENT) fprintf(stderr, "permanent ");
6697 if (attrs & JSPROP_GETTER) fprintf(stderr, "getter ");
6698 if (attrs & JSPROP_SETTER) fprintf(stderr, "setter ");
6699 if (attrs & JSPROP_SHARED) fprintf(stderr, "shared ");
6700 if (shape.isAlias()) fprintf(stderr, "alias ");
6701 if (shape.isMethod()) fprintf(stderr, "method(%p) ", (void *) &shape.methodObject());
6703 if (JSID_IS_ATOM(id))
6704 dumpString(JSID_TO_STRING(id));
6705 else if (JSID_IS_INT(id))
6706 fprintf(stderr, "%d", (int) JSID_TO_INT(id));
6707 else
6708 fprintf(stderr, "unknown jsid %p", (void *) JSID_BITS(id));
6709 fprintf(stderr, ": slot %d", shape.slot);
6710 fprintf(stderr, "\n");
6713 JS_FRIEND_API(void)
6714 js_DumpObject(JSObject *obj)
6716 fprintf(stderr, "object %p\n", (void *) obj);
6717 Class *clasp = obj->getClass();
6718 fprintf(stderr, "class %p %s\n", (void *)clasp, clasp->name);
6720 fprintf(stderr, "flags:");
6721 uint32 flags = obj->flags;
6722 if (flags & JSObject::DELEGATE) fprintf(stderr, " delegate");
6723 if (flags & JSObject::SYSTEM) fprintf(stderr, " system");
6724 if (flags & JSObject::NOT_EXTENSIBLE) fprintf(stderr, " not extensible");
6725 if (flags & JSObject::BRANDED) fprintf(stderr, " branded");
6726 if (flags & JSObject::GENERIC) fprintf(stderr, " generic");
6727 if (flags & JSObject::METHOD_BARRIER) fprintf(stderr, " method_barrier");
6728 if (flags & JSObject::INDEXED) fprintf(stderr, " indexed");
6729 if (flags & JSObject::OWN_SHAPE) fprintf(stderr, " own_shape");
6730 if (flags & JSObject::HAS_EQUALITY) fprintf(stderr, " has_equality");
6732 bool anyFlags = flags != 0;
6733 if (obj->isNative()) {
6734 if (obj->inDictionaryMode()) {
6735 fprintf(stderr, " inDictionaryMode");
6736 anyFlags = true;
6738 if (obj->hasPropertyTable()) {
6739 fprintf(stderr, " hasPropertyTable");
6740 anyFlags = true;
6743 if (!anyFlags)
6744 fprintf(stderr, " none");
6745 fprintf(stderr, "\n");
6747 if (obj->isDenseArray()) {
6748 unsigned slots = JS_MIN(obj->getArrayLength(), obj->getDenseArrayCapacity());
6749 fprintf(stderr, "elements\n");
6750 for (unsigned i = 0; i < slots; i++) {
6751 fprintf(stderr, " %3d: ", i);
6752 dumpValue(obj->getDenseArrayElement(i));
6753 fprintf(stderr, "\n");
6754 fflush(stderr);
6756 return;
6759 if (obj->isNative()) {
6760 fprintf(stderr, "properties:\n");
6761 for (Shape::Range r = obj->lastProperty()->all(); !r.empty(); r.popFront())
6762 DumpShape(r.front());
6763 } else {
6764 if (!obj->isNative())
6765 fprintf(stderr, "not native\n");
6768 fprintf(stderr, "proto ");
6769 dumpValue(ObjectOrNullValue(obj->getProto()));
6770 fputc('\n', stderr);
6772 fprintf(stderr, "parent ");
6773 dumpValue(ObjectOrNullValue(obj->getParent()));
6774 fputc('\n', stderr);
6776 if (clasp->flags & JSCLASS_HAS_PRIVATE)
6777 fprintf(stderr, "private %p\n", obj->getPrivate());
6779 fprintf(stderr, "slots:\n");
6780 unsigned reservedEnd = JSCLASS_RESERVED_SLOTS(clasp);
6781 unsigned slots = obj->slotSpan();
6782 for (unsigned i = 0; i < slots; i++) {
6783 fprintf(stderr, " %3d ", i);
6784 if (i < reservedEnd)
6785 fprintf(stderr, "(reserved) ");
6786 fprintf(stderr, "= ");
6787 dumpValue(obj->getSlot(i));
6788 fputc('\n', stderr);
6790 fputc('\n', stderr);
6793 static void
6794 MaybeDumpObject(const char *name, JSObject *obj)
6796 if (obj) {
6797 fprintf(stderr, " %s: ", name);
6798 dumpValue(ObjectValue(*obj));
6799 fputc('\n', stderr);
6803 static void
6804 MaybeDumpValue(const char *name, const Value &v)
6806 if (!v.isNull()) {
6807 fprintf(stderr, " %s: ", name);
6808 dumpValue(v);
6809 fputc('\n', stderr);
6813 JS_FRIEND_API(void)
6814 js_DumpStackFrame(JSContext *cx, JSStackFrame *start)
6816 /* This should only called during live debugging. */
6817 VOUCH_DOES_NOT_REQUIRE_STACK();
6819 if (!start)
6820 start = cx->maybefp();
6821 FrameRegsIter i(cx);
6822 while (!i.done() && i.fp() != start)
6823 ++i;
6825 if (i.done()) {
6826 fprintf(stderr, "fp = %p not found in cx = %p\n", (void *)start, (void *)cx);
6827 return;
6830 for (; !i.done(); ++i) {
6831 JSStackFrame *const fp = i.fp();
6833 fprintf(stderr, "JSStackFrame at %p\n", (void *) fp);
6834 if (fp->isFunctionFrame()) {
6835 fprintf(stderr, "callee fun: ");
6836 dumpValue(ObjectValue(fp->callee()));
6837 } else {
6838 fprintf(stderr, "global frame, no callee");
6840 fputc('\n', stderr);
6842 if (fp->isScriptFrame()) {
6843 fprintf(stderr, "file %s line %u\n",
6844 fp->script()->filename, (unsigned) fp->script()->lineno);
6847 if (jsbytecode *pc = i.pc()) {
6848 if (!fp->isScriptFrame()) {
6849 fprintf(stderr, "*** pc && !script, skipping frame\n\n");
6850 continue;
6852 if (fp->hasImacropc()) {
6853 fprintf(stderr, " pc in imacro at %p\n called from ", pc);
6854 pc = fp->imacropc();
6855 } else {
6856 fprintf(stderr, " ");
6858 fprintf(stderr, "pc = %p\n", pc);
6859 fprintf(stderr, " current op: %s\n", js_CodeName[*pc]);
6861 Value *sp = i.sp();
6862 fprintf(stderr, " slots: %p\n", (void *) fp->slots());
6863 fprintf(stderr, " sp: %p = slots + %u\n", (void *) sp, (unsigned) (sp - fp->slots()));
6864 if (sp - fp->slots() < 10000) { // sanity
6865 for (Value *p = fp->slots(); p < sp; p++) {
6866 fprintf(stderr, " %p: ", (void *) p);
6867 dumpValue(*p);
6868 fputc('\n', stderr);
6871 if (fp->isFunctionFrame() && !fp->isEvalFrame()) {
6872 fprintf(stderr, " actuals: %p (%u) ", (void *) fp->actualArgs(), (unsigned) fp->numActualArgs());
6873 fprintf(stderr, " formals: %p (%u)\n", (void *) fp->formalArgs(), (unsigned) fp->numFormalArgs());
6875 MaybeDumpObject("callobj", fp->maybeCallObj());
6876 MaybeDumpObject("argsobj", fp->maybeArgsObj());
6877 if (!fp->isDummyFrame()) {
6878 MaybeDumpValue("this", fp->thisValue());
6879 fprintf(stderr, " rval: ");
6880 dumpValue(fp->returnValue());
6881 } else {
6882 fprintf(stderr, "dummy frame");
6884 fputc('\n', stderr);
6886 fprintf(stderr, " flags:");
6887 if (fp->isConstructing())
6888 fprintf(stderr, " constructing");
6889 if (fp->hasOverriddenArgs())
6890 fprintf(stderr, " overridden_args");
6891 if (fp->isAssigning())
6892 fprintf(stderr, " assigning");
6893 if (fp->isDebuggerFrame())
6894 fprintf(stderr, " debugger");
6895 if (fp->isEvalFrame())
6896 fprintf(stderr, " eval");
6897 if (fp->isYielding())
6898 fprintf(stderr, " yielding");
6899 if (fp->isGeneratorFrame())
6900 fprintf(stderr, " generator");
6901 fputc('\n', stderr);
6903 fprintf(stderr, " scopeChain: (JSObject *) %p\n", (void *) &fp->scopeChain());
6905 fputc('\n', stderr);
6909 #ifdef DEBUG
6910 bool
6911 IsSaneThisObject(JSObject &obj)
6913 Class *clasp = obj.getClass();
6914 return clasp != &js_CallClass &&
6915 clasp != &js_BlockClass &&
6916 clasp != &js_DeclEnvClass &&
6917 clasp != &js_WithClass;
6919 #endif
6921 #endif /* DEBUG */