Fix windows build breakage.
[mozilla-central.git] / js / src / jsobj.cpp
blobb71feb8e2c0254d906643faef45a709d90d3a027
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=79:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
15 * License.
17 * The Original Code is Mozilla Communicator client code, released
18 * March 31, 1998.
20 * The Initial Developer of the Original Code is
21 * Netscape Communications Corporation.
22 * Portions created by the Initial Developer are Copyright (C) 1998
23 * the Initial Developer. All Rights Reserved.
25 * Contributor(s):
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
42 * JS object implementation.
44 #include <stdlib.h>
45 #include <string.h>
46 #include "jstypes.h"
47 #include "jsstdint.h"
48 #include "jsarena.h" /* Added by JSIFY */
49 #include "jsbit.h"
50 #include "jsutil.h" /* Added by JSIFY */
51 #include "jshash.h" /* Added by JSIFY */
52 #include "jsdhash.h"
53 #include "jsprf.h"
54 #include "jsapi.h"
55 #include "jsarray.h"
56 #include "jsatom.h"
57 #include "jsbool.h"
58 #include "jsbuiltins.h"
59 #include "jscntxt.h"
60 #include "jsversion.h"
61 #include "jsemit.h"
62 #include "jsfun.h"
63 #include "jsgc.h"
64 #include "jsinterp.h"
65 #include "jsiter.h"
66 #include "jslock.h"
67 #include "jsnum.h"
68 #include "jsobj.h"
69 #include "jsopcode.h"
70 #include "jsparse.h"
71 #include "jsproxy.h"
72 #include "jsscope.h"
73 #include "jsscript.h"
74 #include "jsstaticcheck.h"
75 #include "jsstdint.h"
76 #include "jsstr.h"
77 #include "jstracer.h"
78 #include "jsdbgapi.h"
79 #include "json.h"
81 #include "jsinterpinlines.h"
82 #include "jsscopeinlines.h"
83 #include "jsscriptinlines.h"
84 #include "jsobjinlines.h"
86 #if JS_HAS_GENERATORS
87 #include "jsiter.h"
88 #endif
90 #if JS_HAS_XML_SUPPORT
91 #include "jsxml.h"
92 #endif
94 #if JS_HAS_XDR
95 #include "jsxdrapi.h"
96 #endif
98 #include "jsprobes.h"
99 #include "jsatominlines.h"
100 #include "jsobjinlines.h"
101 #include "jsscriptinlines.h"
103 #include "jsautooplen.h"
105 using namespace js;
107 JS_FRIEND_DATA(const JSObjectMap) JSObjectMap::sharedNonNative(JSObjectMap::SHAPELESS);
109 Class js_ObjectClass = {
110 js_Object_str,
111 JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
112 PropertyStub, /* addProperty */
113 PropertyStub, /* delProperty */
114 PropertyStub, /* getProperty */
115 PropertyStub, /* setProperty */
116 EnumerateStub,
117 ResolveStub,
118 ConvertStub
121 JS_FRIEND_API(JSObject *)
122 js_ObjectToOuterObject(JSContext *cx, JSObject *obj)
124 OBJ_TO_OUTER_OBJECT(cx, obj);
125 return obj;
128 #if JS_HAS_OBJ_PROTO_PROP
130 static JSBool
131 obj_getProto(JSContext *cx, JSObject *obj, jsid id, Value *vp);
133 static JSBool
134 obj_setProto(JSContext *cx, JSObject *obj, jsid id, Value *vp);
136 static JSPropertySpec object_props[] = {
137 {js_proto_str, 0, JSPROP_PERMANENT|JSPROP_SHARED, Jsvalify(obj_getProto), Jsvalify(obj_setProto)},
138 {0,0,0,0,0}
141 static JSBool
142 obj_getProto(JSContext *cx, JSObject *obj, jsid id, Value *vp)
144 /* Let CheckAccess get the slot's value, based on the access mode. */
145 uintN attrs;
146 id = ATOM_TO_JSID(cx->runtime->atomState.protoAtom);
147 return CheckAccess(cx, obj, id, JSACC_PROTO, vp, &attrs);
150 static JSBool
151 obj_setProto(JSContext *cx, JSObject *obj, jsid id, Value *vp)
153 if (!vp->isObjectOrNull())
154 return JS_TRUE;
156 JSObject *pobj = vp->toObjectOrNull();
157 if (pobj) {
159 * Innerize pobj here to avoid sticking unwanted properties on the
160 * outer object. This ensures that any with statements only grant
161 * access to the inner object.
163 OBJ_TO_INNER_OBJECT(cx, pobj);
164 if (!pobj)
165 return JS_FALSE;
168 uintN attrs;
169 id = ATOM_TO_JSID(cx->runtime->atomState.protoAtom);
170 if (!CheckAccess(cx, obj, id, JSAccessMode(JSACC_PROTO|JSACC_WRITE), vp, &attrs))
171 return JS_FALSE;
173 return SetProto(cx, obj, pobj, JS_TRUE);
176 #else /* !JS_HAS_OBJ_PROTO_PROP */
178 #define object_props NULL
180 #endif /* !JS_HAS_OBJ_PROTO_PROP */
182 static JSHashNumber
183 js_hash_object(const void *key)
185 return JSHashNumber(uintptr_t(key) >> JS_GCTHING_ALIGN);
188 static JSHashEntry *
189 MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap)
191 JSSharpObjectMap *map;
192 JSHashTable *table;
193 JSHashNumber hash;
194 JSHashEntry **hep, *he;
195 jsatomid sharpid;
196 JSIdArray *ida;
197 JSBool ok;
198 jsint i, length;
199 jsid id;
200 JSObject *obj2;
201 JSProperty *prop;
203 JS_CHECK_RECURSION(cx, return NULL);
205 map = &cx->sharpObjectMap;
206 JS_ASSERT(map->depth >= 1);
207 table = map->table;
208 hash = js_hash_object(obj);
209 hep = JS_HashTableRawLookup(table, hash, obj);
210 he = *hep;
211 if (!he) {
212 sharpid = 0;
213 he = JS_HashTableRawAdd(table, hep, hash, obj, (void *) sharpid);
214 if (!he) {
215 JS_ReportOutOfMemory(cx);
216 return NULL;
219 ida = JS_Enumerate(cx, obj);
220 if (!ida)
221 return NULL;
223 ok = JS_TRUE;
224 for (i = 0, length = ida->length; i < length; i++) {
225 id = ida->vector[i];
226 ok = obj->lookupProperty(cx, id, &obj2, &prop);
227 if (!ok)
228 break;
229 if (!prop)
230 continue;
231 bool hasGetter, hasSetter;
232 AutoValueRooter v(cx);
233 AutoValueRooter setter(cx);
234 if (obj2->isNative()) {
235 const Shape *shape = (Shape *) prop;
236 hasGetter = shape->hasGetterValue();
237 hasSetter = shape->hasSetterValue();
238 if (hasGetter)
239 v.set(shape->getterValue());
240 if (hasSetter)
241 setter.set(shape->setterValue());
242 JS_UNLOCK_OBJ(cx, obj2);
243 } else {
244 hasGetter = hasSetter = false;
246 if (hasSetter) {
247 /* Mark the getter, then set val to setter. */
248 if (hasGetter && v.value().isObject()) {
249 ok = !!MarkSharpObjects(cx, &v.value().toObject(), NULL);
250 if (!ok)
251 break;
253 v.set(setter.value());
254 } else if (!hasGetter) {
255 ok = obj->getProperty(cx, id, v.addr());
256 if (!ok)
257 break;
259 if (v.value().isObject() &&
260 !MarkSharpObjects(cx, &v.value().toObject(), NULL)) {
261 ok = JS_FALSE;
262 break;
265 if (!ok || !idap)
266 JS_DestroyIdArray(cx, ida);
267 if (!ok)
268 return NULL;
269 } else {
270 sharpid = uintptr_t(he->value);
271 if (sharpid == 0) {
272 sharpid = ++map->sharpgen << SHARP_ID_SHIFT;
273 he->value = (void *) sharpid;
275 ida = NULL;
277 if (idap)
278 *idap = ida;
279 return he;
282 JSHashEntry *
283 js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap,
284 jschar **sp)
286 JSSharpObjectMap *map;
287 JSHashTable *table;
288 JSIdArray *ida;
289 JSHashNumber hash;
290 JSHashEntry *he, **hep;
291 jsatomid sharpid;
292 char buf[20];
293 size_t len;
295 if (!JS_CHECK_OPERATION_LIMIT(cx))
296 return NULL;
298 /* Set to null in case we return an early error. */
299 *sp = NULL;
300 map = &cx->sharpObjectMap;
301 table = map->table;
302 if (!table) {
303 table = JS_NewHashTable(8, js_hash_object, JS_CompareValues,
304 JS_CompareValues, NULL, NULL);
305 if (!table) {
306 JS_ReportOutOfMemory(cx);
307 return NULL;
309 map->table = table;
310 JS_KEEP_ATOMS(cx->runtime);
313 /* From this point the control must flow either through out: or bad:. */
314 ida = NULL;
315 if (map->depth == 0) {
317 * Although MarkSharpObjects tries to avoid invoking getters,
318 * it ends up doing so anyway under some circumstances; for
319 * example, if a wrapped object has getters, the wrapper will
320 * prevent MarkSharpObjects from recognizing them as such.
321 * This could lead to js_LeaveSharpObject being called while
322 * MarkSharpObjects is still working.
324 * Increment map->depth while we call MarkSharpObjects, to
325 * ensure that such a call doesn't free the hash table we're
326 * still using.
328 ++map->depth;
329 he = MarkSharpObjects(cx, obj, &ida);
330 --map->depth;
331 if (!he)
332 goto bad;
333 JS_ASSERT((uintptr_t(he->value) & SHARP_BIT) == 0);
334 if (!idap) {
335 JS_DestroyIdArray(cx, ida);
336 ida = NULL;
338 } else {
339 hash = js_hash_object(obj);
340 hep = JS_HashTableRawLookup(table, hash, obj);
341 he = *hep;
344 * It's possible that the value of a property has changed from the
345 * first time the object's properties are traversed (when the property
346 * ids are entered into the hash table) to the second (when they are
347 * converted to strings), i.e., the JSObject::getProperty() call is not
348 * idempotent.
350 if (!he) {
351 he = JS_HashTableRawAdd(table, hep, hash, obj, NULL);
352 if (!he) {
353 JS_ReportOutOfMemory(cx);
354 goto bad;
356 sharpid = 0;
357 goto out;
361 sharpid = uintptr_t(he->value);
362 if (sharpid != 0) {
363 len = JS_snprintf(buf, sizeof buf, "#%u%c",
364 sharpid >> SHARP_ID_SHIFT,
365 (sharpid & SHARP_BIT) ? '#' : '=');
366 *sp = js_InflateString(cx, buf, &len);
367 if (!*sp) {
368 if (ida)
369 JS_DestroyIdArray(cx, ida);
370 goto bad;
374 out:
375 JS_ASSERT(he);
376 if ((sharpid & SHARP_BIT) == 0) {
377 if (idap && !ida) {
378 ida = JS_Enumerate(cx, obj);
379 if (!ida) {
380 if (*sp) {
381 cx->free(*sp);
382 *sp = NULL;
384 goto bad;
387 map->depth++;
390 if (idap)
391 *idap = ida;
392 return he;
394 bad:
395 /* Clean up the sharpObjectMap table on outermost error. */
396 if (map->depth == 0) {
397 JS_UNKEEP_ATOMS(cx->runtime);
398 map->sharpgen = 0;
399 JS_HashTableDestroy(map->table);
400 map->table = NULL;
402 return NULL;
405 void
406 js_LeaveSharpObject(JSContext *cx, JSIdArray **idap)
408 JSSharpObjectMap *map;
409 JSIdArray *ida;
411 map = &cx->sharpObjectMap;
412 JS_ASSERT(map->depth > 0);
413 if (--map->depth == 0) {
414 JS_UNKEEP_ATOMS(cx->runtime);
415 map->sharpgen = 0;
416 JS_HashTableDestroy(map->table);
417 map->table = NULL;
419 if (idap) {
420 ida = *idap;
421 if (ida) {
422 JS_DestroyIdArray(cx, ida);
423 *idap = NULL;
428 static intN
429 gc_sharp_table_entry_marker(JSHashEntry *he, intN i, void *arg)
431 JS_CALL_OBJECT_TRACER((JSTracer *)arg, (JSObject *)he->key,
432 "sharp table entry");
433 return JS_DHASH_NEXT;
436 void
437 js_TraceSharpMap(JSTracer *trc, JSSharpObjectMap *map)
439 JS_ASSERT(map->depth > 0);
440 JS_ASSERT(map->table);
443 * During recursive calls to MarkSharpObjects a non-native object or
444 * object with a custom getProperty method can potentially return an
445 * unrooted value or even cut from the object graph an argument of one of
446 * MarkSharpObjects recursive invocations. So we must protect map->table
447 * entries against GC.
449 * We can not simply use JSTempValueRooter to mark the obj argument of
450 * MarkSharpObjects during recursion as we have to protect *all* entries
451 * in JSSharpObjectMap including those that contains otherwise unreachable
452 * objects just allocated through custom getProperty. Otherwise newer
453 * allocations can re-use the address of an object stored in the hashtable
454 * confusing js_EnterSharpObject. So to address the problem we simply
455 * mark all objects from map->table.
457 * An alternative "proper" solution is to use JSTempValueRooter in
458 * MarkSharpObjects with code to remove during finalization entries
459 * with otherwise unreachable objects. But this is way too complex
460 * to justify spending efforts.
462 JS_HashTableEnumerateEntries(map->table, gc_sharp_table_entry_marker, trc);
465 #if JS_HAS_TOSOURCE
466 static JSBool
467 obj_toSource(JSContext *cx, uintN argc, Value *vp)
469 JSBool ok, outermost;
470 JSObject *obj;
471 JSHashEntry *he;
472 JSIdArray *ida;
473 jschar *chars, *ochars, *vsharp;
474 const jschar *idstrchars, *vchars;
475 size_t nchars, idstrlength, gsoplength, vlength, vsharplength, curlen;
476 const char *comma;
477 JSObject *obj2;
478 JSProperty *prop;
479 Value *val;
480 JSString *gsop[2];
481 JSString *idstr, *valstr, *str;
483 JS_CHECK_RECURSION(cx, return JS_FALSE);
485 Value localroot[4];
486 PodArrayZero(localroot);
487 AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(localroot), localroot);
489 /* If outermost, we need parentheses to be an expression, not a block. */
490 outermost = (cx->sharpObjectMap.depth == 0);
491 obj = ComputeThisFromVp(cx, vp);
492 if (!obj || !(he = js_EnterSharpObject(cx, obj, &ida, &chars))) {
493 ok = JS_FALSE;
494 goto out;
496 if (IS_SHARP(he)) {
498 * We didn't enter -- obj is already "sharp", meaning we've visited it
499 * already in our depth first search, and therefore chars contains a
500 * string of the form "#n#".
502 JS_ASSERT(!ida);
503 #if JS_HAS_SHARP_VARS
504 nchars = js_strlen(chars);
505 #else
506 chars[0] = '{';
507 chars[1] = '}';
508 chars[2] = 0;
509 nchars = 2;
510 #endif
511 goto make_string;
513 JS_ASSERT(ida);
514 ok = JS_TRUE;
516 if (!chars) {
517 /* If outermost, allocate 4 + 1 for "({})" and the terminator. */
518 chars = (jschar *) cx->runtime->malloc(((outermost ? 4 : 2) + 1) * sizeof(jschar));
519 nchars = 0;
520 if (!chars)
521 goto error;
522 if (outermost)
523 chars[nchars++] = '(';
524 } else {
525 /* js_EnterSharpObject returned a string of the form "#n=" in chars. */
526 MAKE_SHARP(he);
527 nchars = js_strlen(chars);
528 chars = (jschar *)
529 js_realloc((ochars = chars), (nchars + 2 + 1) * sizeof(jschar));
530 if (!chars) {
531 js_free(ochars);
532 goto error;
534 if (outermost) {
536 * No need for parentheses around the whole shebang, because #n=
537 * unambiguously begins an object initializer, and never a block
538 * statement.
540 outermost = JS_FALSE;
544 chars[nchars++] = '{';
546 comma = NULL;
549 * We have four local roots for cooked and raw value GC safety. Hoist the
550 * "localroot + 2" out of the loop using the val local, which refers to
551 * the raw (unconverted, "uncooked") values.
553 val = localroot + 2;
555 for (jsint i = 0, length = ida->length; i < length; i++) {
556 /* Get strings for id and value and GC-root them via vp. */
557 jsid id = ida->vector[i];
559 ok = obj->lookupProperty(cx, id, &obj2, &prop);
560 if (!ok)
561 goto error;
564 * Convert id to a value and then to a string. Decide early whether we
565 * prefer get/set or old getter/setter syntax.
567 idstr = js_ValueToString(cx, IdToValue(id));
568 if (!idstr) {
569 ok = JS_FALSE;
570 obj2->dropProperty(cx, prop);
571 goto error;
573 vp->setString(idstr); /* local root */
575 jsint valcnt = 0;
576 if (prop) {
577 bool doGet = true;
578 if (obj2->isNative()) {
579 const Shape *shape = (Shape *) prop;
580 unsigned attrs = shape->attributes();
581 if (attrs & JSPROP_GETTER) {
582 doGet = false;
583 val[valcnt] = shape->getterValue();
584 gsop[valcnt] = ATOM_TO_STRING(cx->runtime->atomState.getAtom);
585 valcnt++;
587 if (attrs & JSPROP_SETTER) {
588 doGet = false;
589 val[valcnt] = shape->setterValue();
590 gsop[valcnt] = ATOM_TO_STRING(cx->runtime->atomState.setAtom);
591 valcnt++;
593 JS_UNLOCK_OBJ(cx, obj2);
595 if (doGet) {
596 valcnt = 1;
597 gsop[0] = NULL;
598 ok = obj->getProperty(cx, id, &val[0]);
599 if (!ok)
600 goto error;
605 * If id is a string that's not an identifier, or if it's a negative
606 * integer, then it must be quoted.
608 bool idIsLexicalIdentifier = !!js_IsIdentifier(idstr);
609 if (JSID_IS_ATOM(id)
610 ? !idIsLexicalIdentifier
611 : (!JSID_IS_INT(id) || JSID_TO_INT(id) < 0)) {
612 idstr = js_QuoteString(cx, idstr, jschar('\''));
613 if (!idstr) {
614 ok = JS_FALSE;
615 goto error;
617 vp->setString(idstr); /* local root */
619 idstr->getCharsAndLength(idstrchars, idstrlength);
621 for (jsint j = 0; j < valcnt; j++) {
623 * Censor an accessor descriptor getter or setter part if it's
624 * undefined.
626 if (gsop[j] && val[j].isUndefined())
627 continue;
629 /* Convert val[j] to its canonical source form. */
630 valstr = js_ValueToSource(cx, val[j]);
631 if (!valstr) {
632 ok = JS_FALSE;
633 goto error;
635 localroot[j].setString(valstr); /* local root */
636 valstr->getCharsAndLength(vchars, vlength);
639 * If val[j] is a non-sharp object, and we're not serializing an
640 * accessor (ECMA syntax can't accommodate sharpened accessors),
641 * consider sharpening it.
643 vsharp = NULL;
644 vsharplength = 0;
645 #if JS_HAS_SHARP_VARS
646 if (!gsop[j] && val[j].isObject() && vchars[0] != '#') {
647 he = js_EnterSharpObject(cx, &val[j].toObject(), NULL, &vsharp);
648 if (!he) {
649 ok = JS_FALSE;
650 goto error;
652 if (IS_SHARP(he)) {
653 vchars = vsharp;
654 vlength = js_strlen(vchars);
655 } else {
656 if (vsharp) {
657 vsharplength = js_strlen(vsharp);
658 MAKE_SHARP(he);
660 js_LeaveSharpObject(cx, NULL);
663 #endif
666 * Remove '(function ' from the beginning of valstr and ')' from the
667 * end so that we can put "get" in front of the function definition.
669 if (gsop[j] && IsFunctionObject(val[j])) {
670 const jschar *start = vchars;
671 const jschar *end = vchars + vlength;
673 uint8 parenChomp = 0;
674 if (vchars[0] == '(') {
675 vchars++;
676 parenChomp = 1;
679 /* Try to jump "function" keyword. */
680 if (vchars)
681 vchars = js_strchr_limit(vchars, ' ', end);
684 * Jump over the function's name: it can't be encoded as part
685 * of an ECMA getter or setter.
687 if (vchars)
688 vchars = js_strchr_limit(vchars, '(', end);
690 if (vchars) {
691 if (*vchars == ' ')
692 vchars++;
693 vlength = end - vchars - parenChomp;
694 } else {
695 gsop[j] = NULL;
696 vchars = start;
700 #define SAFE_ADD(n) \
701 JS_BEGIN_MACRO \
702 size_t n_ = (n); \
703 curlen += n_; \
704 if (curlen < n_) \
705 goto overflow; \
706 JS_END_MACRO
708 curlen = nchars;
709 if (comma)
710 SAFE_ADD(2);
711 SAFE_ADD(idstrlength + 1);
712 if (gsop[j])
713 SAFE_ADD(gsop[j]->length() + 1);
714 SAFE_ADD(vsharplength);
715 SAFE_ADD(vlength);
716 /* Account for the trailing null. */
717 SAFE_ADD((outermost ? 2 : 1) + 1);
718 #undef SAFE_ADD
720 if (curlen > size_t(-1) / sizeof(jschar))
721 goto overflow;
723 /* Allocate 1 + 1 at end for closing brace and terminating 0. */
724 chars = (jschar *) js_realloc((ochars = chars), curlen * sizeof(jschar));
725 if (!chars) {
726 /* Save code space on error: let JS_free ignore null vsharp. */
727 cx->free(vsharp);
728 js_free(ochars);
729 goto error;
732 if (comma) {
733 chars[nchars++] = comma[0];
734 chars[nchars++] = comma[1];
736 comma = ", ";
738 if (gsop[j]) {
739 gsoplength = gsop[j]->length();
740 js_strncpy(&chars[nchars], gsop[j]->chars(),
741 gsoplength);
742 nchars += gsoplength;
743 chars[nchars++] = ' ';
745 js_strncpy(&chars[nchars], idstrchars, idstrlength);
746 nchars += idstrlength;
747 /* Extraneous space after id here will be extracted later */
748 chars[nchars++] = gsop[j] ? ' ' : ':';
750 if (vsharplength) {
751 js_strncpy(&chars[nchars], vsharp, vsharplength);
752 nchars += vsharplength;
754 js_strncpy(&chars[nchars], vchars, vlength);
755 nchars += vlength;
757 if (vsharp)
758 cx->free(vsharp);
762 chars[nchars++] = '}';
763 if (outermost)
764 chars[nchars++] = ')';
765 chars[nchars] = 0;
767 error:
768 js_LeaveSharpObject(cx, &ida);
770 if (!ok) {
771 if (chars)
772 js_free(chars);
773 goto out;
776 if (!chars) {
777 JS_ReportOutOfMemory(cx);
778 ok = JS_FALSE;
779 goto out;
781 make_string:
782 str = js_NewString(cx, chars, nchars);
783 if (!str) {
784 js_free(chars);
785 ok = JS_FALSE;
786 goto out;
788 vp->setString(str);
789 ok = JS_TRUE;
790 out:
791 return ok;
793 overflow:
794 cx->free(vsharp);
795 js_free(chars);
796 chars = NULL;
797 goto error;
799 #endif /* JS_HAS_TOSOURCE */
801 namespace js {
803 JSString *
804 obj_toStringHelper(JSContext *cx, JSObject *obj)
806 if (obj->isProxy())
807 return JSProxy::obj_toString(cx, obj);
809 const char *clazz = obj->wrappedObject(cx)->getClass()->name;
810 size_t nchars = 9 + strlen(clazz); /* 9 for "[object ]" */
811 jschar *chars = (jschar *) cx->malloc((nchars + 1) * sizeof(jschar));
812 if (!chars)
813 return NULL;
815 const char *prefix = "[object ";
816 nchars = 0;
817 while ((chars[nchars] = (jschar)*prefix) != 0)
818 nchars++, prefix++;
819 while ((chars[nchars] = (jschar)*clazz) != 0)
820 nchars++, clazz++;
821 chars[nchars++] = ']';
822 chars[nchars] = 0;
824 JSString *str = js_NewString(cx, chars, nchars);
825 if (!str)
826 cx->free(chars);
827 return str;
832 static JSBool
833 obj_toString(JSContext *cx, uintN argc, Value *vp)
835 JSObject *obj = ComputeThisFromVp(cx, vp);
836 if (!obj)
837 return false;
839 JSString *str = js::obj_toStringHelper(cx, obj);
840 if (!str)
841 return false;
843 vp->setString(str);
844 return true;
847 static JSBool
848 obj_toLocaleString(JSContext *cx, uintN argc, Value *vp)
850 if (!ComputeThisFromVp(cx, vp))
851 return JS_FALSE;
853 JSString *str = js_ValueToString(cx, vp[1]);
854 if (!str)
855 return JS_FALSE;
857 vp->setString(str);
858 return JS_TRUE;
861 static JSBool
862 obj_valueOf(JSContext *cx, uintN argc, Value *vp)
864 if (!ComputeThisFromVp(cx, vp))
865 return JS_FALSE;
866 *vp = vp[1];
867 return JS_TRUE;
871 * Check if CSP allows new Function() or eval() to run in the current
872 * principals.
874 JSBool
875 js_CheckContentSecurityPolicy(JSContext *cx)
877 JSSecurityCallbacks *callbacks = JS_GetSecurityCallbacks(cx);
879 // if there are callbacks, make sure that the CSP callback is installed and
880 // that it permits eval().
881 if (callbacks && callbacks->contentSecurityPolicyAllows)
882 return callbacks->contentSecurityPolicyAllows(cx);
884 return JS_TRUE;
888 * Check whether principals subsumes scopeobj's principals, and return true
889 * if so (or if scopeobj has no principals, for backward compatibility with
890 * the JS API, which does not require principals), and false otherwise.
892 JSBool
893 js_CheckPrincipalsAccess(JSContext *cx, JSObject *scopeobj,
894 JSPrincipals *principals, JSAtom *caller)
896 JSSecurityCallbacks *callbacks;
897 JSPrincipals *scopePrincipals;
898 const char *callerstr;
900 callbacks = JS_GetSecurityCallbacks(cx);
901 if (callbacks && callbacks->findObjectPrincipals) {
902 scopePrincipals = callbacks->findObjectPrincipals(cx, scopeobj);
903 if (!principals || !scopePrincipals ||
904 !principals->subsume(principals, scopePrincipals)) {
905 callerstr = js_AtomToPrintableString(cx, caller);
906 if (!callerstr)
907 return JS_FALSE;
908 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
909 JSMSG_BAD_INDIRECT_CALL, callerstr);
910 return JS_FALSE;
913 return JS_TRUE;
916 static JSObject *
917 CheckScopeChainValidity(JSContext *cx, JSObject *scopeobj, const char *caller)
919 JSObject *inner;
921 if (!scopeobj)
922 goto bad;
924 OBJ_TO_INNER_OBJECT(cx, scopeobj);
925 if (!scopeobj)
926 return NULL;
928 /* XXX This is an awful gross hack. */
929 inner = scopeobj;
930 while (scopeobj) {
931 JSObjectOp op = scopeobj->getClass()->ext.innerObject;
932 if (op && op(cx, scopeobj) != scopeobj)
933 goto bad;
934 scopeobj = scopeobj->getParent();
937 return inner;
939 bad:
940 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
941 JSMSG_BAD_INDIRECT_CALL, caller);
942 return NULL;
945 const char *
946 js_ComputeFilename(JSContext *cx, JSStackFrame *caller,
947 JSPrincipals *principals, uintN *linenop)
949 uint32 flags;
950 #ifdef DEBUG
951 JSSecurityCallbacks *callbacks = JS_GetSecurityCallbacks(cx);
952 #endif
954 JS_ASSERT(principals || !(callbacks && callbacks->findObjectPrincipals));
955 flags = JS_GetScriptFilenameFlags(caller->script());
956 if ((flags & JSFILENAME_PROTECTED) &&
957 principals &&
958 strcmp(principals->codebase, "[System Principal]")) {
959 *linenop = 0;
960 return principals->codebase;
963 jsbytecode *pc = caller->pc(cx);
964 if (pc && js_GetOpcode(cx, caller->script(), pc) == JSOP_EVAL) {
965 JS_ASSERT(js_GetOpcode(cx, caller->script(), pc + JSOP_EVAL_LENGTH) == JSOP_LINENO);
966 *linenop = GET_UINT16(pc + JSOP_EVAL_LENGTH);
967 } else {
968 *linenop = js_FramePCToLineNumber(cx, caller);
970 return caller->script()->filename;
973 #ifndef EVAL_CACHE_CHAIN_LIMIT
974 # define EVAL_CACHE_CHAIN_LIMIT 4
975 #endif
977 static inline JSScript **
978 EvalCacheHash(JSContext *cx, JSString *str)
980 const jschar *s;
981 size_t n;
982 uint32 h;
984 str->getCharsAndLength(s, n);
985 if (n > 100)
986 n = 100;
987 for (h = 0; n; s++, n--)
988 h = JS_ROTATE_LEFT32(h, 4) ^ *s;
990 h *= JS_GOLDEN_RATIO;
991 h >>= 32 - JS_EVAL_CACHE_SHIFT;
992 return &JS_SCRIPTS_TO_GC(cx)[h];
995 static JSBool
996 obj_eval(JSContext *cx, uintN argc, Value *vp)
998 if (argc < 1) {
999 vp->setUndefined();
1000 return JS_TRUE;
1003 JSStackFrame *caller = js_GetScriptedCaller(cx, NULL);
1004 if (!caller) {
1005 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1006 JSMSG_BAD_INDIRECT_CALL, js_eval_str);
1007 return JS_FALSE;
1010 jsbytecode *callerPC = caller->pc(cx);
1011 bool indirectCall = (callerPC && *callerPC != JSOP_EVAL);
1014 * This call to JSObject::wrappedObject is safe because of the security
1015 * checks we do below. However, the control flow below is confusing, so we
1016 * double check. There are two cases:
1017 * - Direct call: This object is never used. So unwrapping can't hurt.
1018 * - Indirect call: If this object isn't already the scope chain (which
1019 * we're guaranteed to be allowed to access) then we do a security
1020 * check.
1022 Value *argv = JS_ARGV(cx, vp);
1023 JSObject *obj = ComputeThisFromVp(cx, vp);
1024 if (!obj)
1025 return JS_FALSE;
1026 obj = obj->wrappedObject(cx);
1028 OBJ_TO_INNER_OBJECT(cx, obj);
1029 if (!obj)
1030 return JS_FALSE;
1033 * Ban indirect uses of eval (nonglobal.eval = eval; nonglobal.eval(....))
1034 * that attempt to use a non-global object as the scope object.
1037 JSObject *parent = obj->getParent();
1038 if (indirectCall || parent) {
1039 uintN flags = parent
1040 ? JSREPORT_ERROR
1041 : JSREPORT_STRICT | JSREPORT_WARNING;
1042 if (!JS_ReportErrorFlagsAndNumber(cx, flags, js_GetErrorMessage, NULL,
1043 JSMSG_BAD_INDIRECT_CALL,
1044 js_eval_str)) {
1045 return JS_FALSE;
1050 if (!argv[0].isString()) {
1051 *vp = argv[0];
1052 return JS_TRUE;
1056 * We once supported a second argument to eval to use as the scope chain
1057 * when evaluating the code string. Warn when such uses are seen so that
1058 * authors will know that support for eval(s, o) has been removed.
1060 if (argc > 1 && !caller->script()->warnedAboutTwoArgumentEval) {
1061 static const char TWO_ARGUMENT_WARNING[] =
1062 "Support for eval(code, scopeObject) has been removed. "
1063 "Use |with (scopeObject) eval(code);| instead.";
1064 if (!JS_ReportWarning(cx, TWO_ARGUMENT_WARNING))
1065 return JS_FALSE;
1066 caller->script()->warnedAboutTwoArgumentEval = true;
1069 /* From here on, control must exit through label out with ok set. */
1070 MUST_FLOW_THROUGH("out");
1071 uintN staticLevel = caller->script()->staticLevel + 1;
1074 * Bring fp->scopeChain up to date. We're either going to use
1075 * it (direct call) or save it and restore it (indirect call).
1077 JSObject *callerScopeChain = js_GetScopeChain(cx, caller);
1078 if (!callerScopeChain)
1079 return JS_FALSE;
1081 JSObject *scopeobj = NULL;
1083 #if JS_HAS_EVAL_THIS_SCOPE
1085 * If we see an indirect call, then run eval in the global scope. We do
1086 * this so the compiler can make assumptions about what bindings may or
1087 * may not exist in the current frame if it doesn't see 'eval'.
1089 if (indirectCall) {
1090 /* Pretend that we're top level. */
1091 staticLevel = 0;
1093 if (!js_CheckPrincipalsAccess(cx, obj,
1094 js_StackFramePrincipals(cx, caller),
1095 cx->runtime->atomState.evalAtom)) {
1096 return JS_FALSE;
1099 /* NB: We know inner is a global object here. */
1100 JS_ASSERT(!obj->getParent());
1101 scopeobj = obj;
1102 } else {
1104 * Compile using the caller's current scope object.
1106 * NB: This means that the C API must not be used to call eval.
1108 JS_ASSERT_IF(caller->isFunctionFrame(), caller->hasCallObj());
1109 scopeobj = callerScopeChain;
1111 #endif
1113 /* Ensure we compile this eval with the right object in the scope chain. */
1114 JSObject *result = CheckScopeChainValidity(cx, scopeobj, js_eval_str);
1115 JS_ASSERT_IF(result, result == scopeobj);
1116 if (!result)
1117 return JS_FALSE;
1119 // CSP check: is eval() allowed at all?
1120 // report errors via CSP is done in the script security mgr.
1121 if (!js_CheckContentSecurityPolicy(cx)) {
1122 JS_ReportError(cx, "call to eval() blocked by CSP");
1123 return JS_FALSE;
1126 JSObject *callee = &vp[0].toObject();
1127 JSPrincipals *principals = js_EvalFramePrincipals(cx, callee, caller);
1128 uintN line;
1129 const char *file = js_ComputeFilename(cx, caller, principals, &line);
1131 JSString *str = argv[0].toString();
1132 JSScript *script = NULL;
1134 const jschar *chars;
1135 size_t length;
1136 str->getCharsAndLength(chars, length);
1139 * If the eval string starts with '(' and ends with ')', it may be JSON.
1140 * Try the JSON parser first because it's much faster. If the eval string
1141 * isn't JSON, JSON parsing will probably fail quickly, so little time
1142 * will be lost.
1144 if (length > 2 && chars[0] == '(' && chars[length-1] == ')') {
1145 JSONParser *jp = js_BeginJSONParse(cx, vp, /* suppressErrors = */true);
1146 JSBool ok = jp != NULL;
1147 if (ok) {
1148 /* Run JSON-parser on string inside ( and ). */
1149 ok = js_ConsumeJSONText(cx, jp, chars+1, length-2);
1150 ok &= js_FinishJSONParse(cx, jp, NullValue());
1151 if (ok)
1152 return JS_TRUE;
1157 * Cache local eval scripts indexed by source qualified by scope.
1159 * An eval cache entry should never be considered a hit unless its
1160 * strictness matches that of the new eval code. The existing code takes
1161 * care of this, because hits are qualified by the function from which
1162 * eval was called, whose strictness doesn't change. Scripts produced by
1163 * calls to eval from global code are not cached.
1165 JSScript **bucket = EvalCacheHash(cx, str);
1166 if (!indirectCall && caller->isFunctionFrame()) {
1167 uintN count = 0;
1168 JSScript **scriptp = bucket;
1170 EVAL_CACHE_METER(probe);
1171 JSVersion version = cx->findVersion();
1172 while ((script = *scriptp) != NULL) {
1173 if (script->savedCallerFun &&
1174 script->staticLevel == staticLevel &&
1175 script->version == version &&
1176 (script->principals == principals ||
1177 (principals->subsume(principals, script->principals) &&
1178 script->principals->subsume(script->principals, principals)))) {
1180 * Get the prior (cache-filling) eval's saved caller function.
1181 * See Compiler::compileScript in jsparse.cpp.
1183 JSFunction *fun = script->getFunction(0);
1185 if (fun == caller->fun()) {
1187 * Get the source string passed for safekeeping in the
1188 * atom map by the prior eval to Compiler::compileScript.
1190 JSString *src = ATOM_TO_STRING(script->atomMap.vector[0]);
1192 if (src == str || js_EqualStrings(src, str)) {
1194 * Source matches, qualify by comparing scopeobj to the
1195 * COMPILE_N_GO-memoized parent of the first literal
1196 * function or regexp object if any. If none, then this
1197 * script has no compiled-in dependencies on the prior
1198 * eval's scopeobj.
1200 JSObjectArray *objarray = script->objects();
1201 int i = 1;
1203 if (objarray->length == 1) {
1204 if (script->regexpsOffset != 0) {
1205 objarray = script->regexps();
1206 i = 0;
1207 } else {
1208 EVAL_CACHE_METER(noscope);
1209 i = -1;
1212 if (i < 0 ||
1213 objarray->vector[i]->getParent() == scopeobj) {
1214 JS_ASSERT(staticLevel == script->staticLevel);
1215 EVAL_CACHE_METER(hit);
1216 *scriptp = script->u.nextToGC;
1217 script->u.nextToGC = NULL;
1218 break;
1224 if (++count == EVAL_CACHE_CHAIN_LIMIT) {
1225 script = NULL;
1226 break;
1228 EVAL_CACHE_METER(step);
1229 scriptp = &script->u.nextToGC;
1234 * We can't have a callerFrame (down in js_Execute's terms) if we're in
1235 * global code. This includes indirect eval and direct eval called with a
1236 * scope object parameter.
1238 JSStackFrame *callerFrame = (staticLevel != 0) ? caller : NULL;
1239 if (!script) {
1240 uint32 tcflags = TCF_COMPILE_N_GO | TCF_NEED_MUTABLE_SCRIPT | TCF_COMPILE_FOR_EVAL;
1241 script = Compiler::compileScript(cx, scopeobj, callerFrame,
1242 principals, tcflags,
1243 chars, length,
1244 NULL, file, line, str, staticLevel);
1245 if (!script)
1246 return JS_FALSE;
1250 * Belt-and-braces: check that the lesser of eval's principals and the
1251 * caller's principals has access to scopeobj.
1253 JSBool ok = js_CheckPrincipalsAccess(cx, scopeobj, principals,
1254 cx->runtime->atomState.evalAtom) &&
1255 Execute(cx, scopeobj, script, callerFrame, JSFRAME_EVAL, vp);
1257 script->u.nextToGC = *bucket;
1258 *bucket = script;
1259 #ifdef CHECK_SCRIPT_OWNER
1260 script->owner = NULL;
1261 #endif
1263 return ok;
1266 #if JS_HAS_OBJ_WATCHPOINT
1268 static JSBool
1269 obj_watch_handler(JSContext *cx, JSObject *obj, jsid id, jsval old,
1270 jsval *nvp, void *closure)
1272 JSObject *callable;
1273 JSSecurityCallbacks *callbacks;
1274 JSStackFrame *caller;
1275 JSPrincipals *subject, *watcher;
1276 JSResolvingKey key;
1277 JSResolvingEntry *entry;
1278 uint32 generation;
1279 Value argv[3];
1280 JSBool ok;
1282 callable = (JSObject *) closure;
1284 callbacks = JS_GetSecurityCallbacks(cx);
1285 if (callbacks && callbacks->findObjectPrincipals) {
1286 /* Skip over any obj_watch_* frames between us and the real subject. */
1287 caller = js_GetScriptedCaller(cx, NULL);
1288 if (caller) {
1290 * Only call the watch handler if the watcher is allowed to watch
1291 * the currently executing script.
1293 watcher = callbacks->findObjectPrincipals(cx, callable);
1294 subject = js_StackFramePrincipals(cx, caller);
1296 if (watcher && subject && !watcher->subsume(watcher, subject)) {
1297 /* Silently don't call the watch handler. */
1298 return JS_TRUE;
1303 /* Avoid recursion on (obj, id) already being watched on cx. */
1304 key.obj = obj;
1305 key.id = id;
1306 if (!js_StartResolving(cx, &key, JSRESFLAG_WATCH, &entry))
1307 return JS_FALSE;
1308 if (!entry)
1309 return JS_TRUE;
1310 generation = cx->resolvingTable->generation;
1312 argv[0] = IdToValue(id);
1313 argv[1] = Valueify(old);
1314 argv[2] = Valueify(*nvp);
1315 ok = ExternalInvoke(cx, obj, ObjectOrNullValue(callable), 3, argv, Valueify(nvp));
1316 js_StopResolving(cx, &key, JSRESFLAG_WATCH, entry, generation);
1317 return ok;
1320 static JSBool
1321 obj_watch(JSContext *cx, uintN argc, Value *vp)
1323 if (argc <= 1) {
1324 js_ReportMissingArg(cx, *vp, 1);
1325 return JS_FALSE;
1328 JSObject *callable = js_ValueToCallableObject(cx, &vp[3], 0);
1329 if (!callable)
1330 return JS_FALSE;
1332 /* Compute the unique int/atom symbol id needed by js_LookupProperty. */
1333 jsid propid;
1334 if (!ValueToId(cx, vp[2], &propid))
1335 return JS_FALSE;
1337 JSObject *obj = ComputeThisFromVp(cx, vp);
1338 Value tmp;
1339 uintN attrs;
1340 if (!obj || !CheckAccess(cx, obj, propid, JSACC_WATCH, &tmp, &attrs))
1341 return JS_FALSE;
1343 vp->setUndefined();
1345 if (attrs & JSPROP_READONLY)
1346 return JS_TRUE;
1347 if (obj->isDenseArray() && !obj->makeDenseArraySlow(cx))
1348 return JS_FALSE;
1349 return JS_SetWatchPoint(cx, obj, propid, obj_watch_handler, callable);
1352 static JSBool
1353 obj_unwatch(JSContext *cx, uintN argc, Value *vp)
1355 JSObject *obj = ComputeThisFromVp(cx, vp);
1356 if (!obj)
1357 return JS_FALSE;
1358 vp->setUndefined();
1359 jsid id;
1360 if (argc != 0) {
1361 if (!ValueToId(cx, vp[2], &id))
1362 return JS_FALSE;
1363 } else {
1364 id = JSID_VOID;
1366 return JS_ClearWatchPoint(cx, obj, id, NULL, NULL);
1369 #endif /* JS_HAS_OBJ_WATCHPOINT */
1372 * Prototype and property query methods, to complement the 'in' and
1373 * 'instanceof' operators.
1376 /* Proposed ECMA 15.2.4.5. */
1377 static JSBool
1378 obj_hasOwnProperty(JSContext *cx, uintN argc, Value *vp)
1380 JSObject *obj = ComputeThisFromVp(cx, vp);
1381 return obj &&
1382 js_HasOwnPropertyHelper(cx, obj->getOps()->lookupProperty, argc, vp);
1385 JSBool
1386 js_HasOwnPropertyHelper(JSContext *cx, JSLookupPropOp lookup, uintN argc,
1387 Value *vp)
1389 jsid id;
1390 if (!ValueToId(cx, argc != 0 ? vp[2] : UndefinedValue(), &id))
1391 return JS_FALSE;
1393 JSObject *obj = ComputeThisFromVp(cx, vp);
1394 JSObject *obj2;
1395 JSProperty *prop;
1396 if (!obj)
1397 return false;
1398 if (obj->isProxy()) {
1399 bool has;
1400 if (!JSProxy::hasOwn(cx, obj, id, &has))
1401 return false;
1402 vp->setBoolean(has);
1403 return true;
1405 if (!js_HasOwnProperty(cx, lookup, obj, id, &obj2, &prop))
1406 return JS_FALSE;
1407 if (prop) {
1408 vp->setBoolean(true);
1409 obj2->dropProperty(cx, prop);
1410 } else {
1411 vp->setBoolean(false);
1413 return JS_TRUE;
1416 JSBool
1417 js_HasOwnProperty(JSContext *cx, JSLookupPropOp lookup, JSObject *obj, jsid id,
1418 JSObject **objp, JSProperty **propp)
1420 JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_DETECTING);
1421 if (!(lookup ? lookup : js_LookupProperty)(cx, obj, id, objp, propp))
1422 return false;
1423 if (!*propp)
1424 return true;
1426 if (*objp == obj)
1427 return true;
1429 Class *clasp = (*objp)->getClass();
1430 JSObject *outer = NULL;
1431 if (JSObjectOp op = (*objp)->getClass()->ext.outerObject) {
1432 outer = op(cx, *objp);
1433 if (!outer)
1434 return false;
1437 if (outer != *objp) {
1438 if ((*objp)->isNative() && obj->getClass() == clasp) {
1440 * The combination of JSPROP_SHARED and JSPROP_PERMANENT in a
1441 * delegated property makes that property appear to be direct in
1442 * all delegating instances of the same native class. This hack
1443 * avoids bloating every function instance with its own 'length'
1444 * (AKA 'arity') property. But it must not extend across class
1445 * boundaries, to avoid making hasOwnProperty lie (bug 320854).
1447 * It's not really a hack, of course: a permanent property can't
1448 * be deleted, and JSPROP_SHARED means "don't allocate a slot in
1449 * any instance, prototype or delegating". Without a slot, and
1450 * without the ability to remove and recreate (with differences)
1451 * the property, there is no way to tell whether it is directly
1452 * owned, or indirectly delegated.
1454 Shape *shape = reinterpret_cast<Shape *>(*propp);
1455 if (shape->isSharedPermanent())
1456 return true;
1459 (*objp)->dropProperty(cx, *propp);
1460 *propp = NULL;
1462 return true;
1465 /* Proposed ECMA 15.2.4.6. */
1466 static JSBool
1467 obj_isPrototypeOf(JSContext *cx, uintN argc, Value *vp)
1469 JSObject *obj = ComputeThisFromVp(cx, vp);
1470 if (!obj)
1471 return JS_FALSE;
1472 const Value &v = argc != 0 ? vp[2] : UndefinedValue();
1473 vp->setBoolean(js_IsDelegate(cx, obj, v));
1474 return JS_TRUE;
1477 /* Proposed ECMA 15.2.4.7. */
1478 static JSBool
1479 obj_propertyIsEnumerable(JSContext *cx, uintN argc, Value *vp)
1481 jsid id;
1482 if (!ValueToId(cx, argc != 0 ? vp[2] : UndefinedValue(), &id))
1483 return JS_FALSE;
1485 JSObject *obj = ComputeThisFromVp(cx, vp);
1486 return obj && js_PropertyIsEnumerable(cx, obj, id, vp);
1489 JSBool
1490 js_PropertyIsEnumerable(JSContext *cx, JSObject *obj, jsid id, Value *vp)
1492 JSObject *pobj;
1493 JSProperty *prop;
1494 if (!obj->lookupProperty(cx, id, &pobj, &prop))
1495 return JS_FALSE;
1497 if (!prop) {
1498 vp->setBoolean(false);
1499 return JS_TRUE;
1503 * XXX ECMA spec error compatible: return false unless hasOwnProperty.
1504 * The ECMA spec really should be fixed so propertyIsEnumerable and the
1505 * for..in loop agree on whether prototype properties are enumerable,
1506 * obviously by fixing this method (not by breaking the for..in loop!).
1508 * We check here for shared permanent prototype properties, which should
1509 * be treated as if they are local to obj. They are an implementation
1510 * technique used to satisfy ECMA requirements; users should not be able
1511 * to distinguish a shared permanent proto-property from a local one.
1513 bool shared;
1514 uintN attrs;
1515 if (pobj->isNative()) {
1516 Shape *shape = (Shape *) prop;
1517 shared = shape->isSharedPermanent();
1518 attrs = shape->attributes();
1519 JS_UNLOCK_OBJ(cx, pobj);
1520 } else {
1521 shared = false;
1522 if (!pobj->getAttributes(cx, id, &attrs))
1523 return false;
1525 if (pobj != obj && !shared) {
1526 vp->setBoolean(false);
1527 return true;
1529 vp->setBoolean((attrs & JSPROP_ENUMERATE) != 0);
1530 return true;
1533 #if OLD_GETTER_SETTER_METHODS
1535 const char js_defineGetter_str[] = "__defineGetter__";
1536 const char js_defineSetter_str[] = "__defineSetter__";
1537 const char js_lookupGetter_str[] = "__lookupGetter__";
1538 const char js_lookupSetter_str[] = "__lookupSetter__";
1540 JS_FRIEND_API(JSBool)
1541 js_obj_defineGetter(JSContext *cx, uintN argc, Value *vp)
1543 if (argc <= 1 || !js_IsCallable(vp[3])) {
1544 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1545 JSMSG_BAD_GETTER_OR_SETTER,
1546 js_getter_str);
1547 return JS_FALSE;
1549 PropertyOp getter = CastAsPropertyOp(&vp[3].toObject());
1551 jsid id;
1552 if (!ValueToId(cx, vp[2], &id))
1553 return JS_FALSE;
1554 JSObject *obj = ComputeThisFromVp(cx, vp);
1555 if (!obj || !CheckRedeclaration(cx, obj, id, JSPROP_GETTER, NULL, NULL))
1556 return JS_FALSE;
1558 * Getters and setters are just like watchpoints from an access
1559 * control point of view.
1561 Value junk;
1562 uintN attrs;
1563 if (!CheckAccess(cx, obj, id, JSACC_WATCH, &junk, &attrs))
1564 return JS_FALSE;
1565 vp->setUndefined();
1566 return obj->defineProperty(cx, id, UndefinedValue(), getter, PropertyStub,
1567 JSPROP_ENUMERATE | JSPROP_GETTER | JSPROP_SHARED);
1570 JS_FRIEND_API(JSBool)
1571 js_obj_defineSetter(JSContext *cx, uintN argc, Value *vp)
1573 if (argc <= 1 || !js_IsCallable(vp[3])) {
1574 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1575 JSMSG_BAD_GETTER_OR_SETTER,
1576 js_setter_str);
1577 return JS_FALSE;
1579 PropertyOp setter = CastAsPropertyOp(&vp[3].toObject());
1581 jsid id;
1582 if (!ValueToId(cx, vp[2], &id))
1583 return JS_FALSE;
1584 JSObject *obj = ComputeThisFromVp(cx, vp);
1585 if (!obj || !CheckRedeclaration(cx, obj, id, JSPROP_SETTER, NULL, NULL))
1586 return JS_FALSE;
1588 * Getters and setters are just like watchpoints from an access
1589 * control point of view.
1591 Value junk;
1592 uintN attrs;
1593 if (!CheckAccess(cx, obj, id, JSACC_WATCH, &junk, &attrs))
1594 return JS_FALSE;
1595 vp->setUndefined();
1596 return obj->defineProperty(cx, id, UndefinedValue(), PropertyStub, setter,
1597 JSPROP_ENUMERATE | JSPROP_SETTER | JSPROP_SHARED);
1600 static JSBool
1601 obj_lookupGetter(JSContext *cx, uintN argc, Value *vp)
1603 jsid id;
1604 if (!ValueToId(cx, argc != 0 ? vp[2] : UndefinedValue(), &id))
1605 return JS_FALSE;
1606 JSObject *obj = ComputeThisFromVp(cx, vp);
1607 JSObject *pobj;
1608 JSProperty *prop;
1609 if (!obj || !obj->lookupProperty(cx, id, &pobj, &prop))
1610 return JS_FALSE;
1611 vp->setUndefined();
1612 if (prop) {
1613 if (pobj->isNative()) {
1614 Shape *shape = (Shape *) prop;
1615 if (shape->hasGetterValue())
1616 *vp = shape->getterValue();
1617 JS_UNLOCK_OBJ(cx, pobj);
1620 return JS_TRUE;
1623 static JSBool
1624 obj_lookupSetter(JSContext *cx, uintN argc, Value *vp)
1626 jsid id;
1627 if (!ValueToId(cx, argc != 0 ? vp[2] : UndefinedValue(), &id))
1628 return JS_FALSE;
1629 JSObject *obj = ComputeThisFromVp(cx, vp);
1630 JSObject *pobj;
1631 JSProperty *prop;
1632 if (!obj || !obj->lookupProperty(cx, id, &pobj, &prop))
1633 return JS_FALSE;
1634 vp->setUndefined();
1635 if (prop) {
1636 if (pobj->isNative()) {
1637 Shape *shape = (Shape *) prop;
1638 if (shape->hasSetterValue())
1639 *vp = shape->setterValue();
1640 JS_UNLOCK_OBJ(cx, pobj);
1643 return JS_TRUE;
1645 #endif /* OLD_GETTER_SETTER_METHODS */
1647 JSBool
1648 obj_getPrototypeOf(JSContext *cx, uintN argc, Value *vp)
1650 if (argc == 0) {
1651 js_ReportMissingArg(cx, *vp, 0);
1652 return JS_FALSE;
1655 if (vp[2].isPrimitive()) {
1656 char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, vp[2], NULL);
1657 if (!bytes)
1658 return JS_FALSE;
1659 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1660 JSMSG_UNEXPECTED_TYPE, bytes, "not an object");
1661 JS_free(cx, bytes);
1662 return JS_FALSE;
1665 JSObject *obj = &vp[2].toObject();
1666 uintN attrs;
1667 return CheckAccess(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.protoAtom),
1668 JSACC_PROTO, vp, &attrs);
1671 extern JSBool
1672 js_NewPropertyDescriptorObject(JSContext *cx, jsid id, uintN attrs,
1673 const Value &getter, const Value &setter,
1674 const Value &value, Value *vp)
1676 /* We have our own property, so start creating the descriptor. */
1677 JSObject *desc = NewBuiltinClassInstance(cx, &js_ObjectClass);
1678 if (!desc)
1679 return false;
1680 vp->setObject(*desc); /* Root and return. */
1682 const JSAtomState &atomState = cx->runtime->atomState;
1683 if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
1684 if (!desc->defineProperty(cx, ATOM_TO_JSID(atomState.getAtom), getter,
1685 PropertyStub, PropertyStub, JSPROP_ENUMERATE) ||
1686 !desc->defineProperty(cx, ATOM_TO_JSID(atomState.setAtom), setter,
1687 PropertyStub, PropertyStub, JSPROP_ENUMERATE)) {
1688 return false;
1690 } else {
1691 if (!desc->defineProperty(cx, ATOM_TO_JSID(atomState.valueAtom), value,
1692 PropertyStub, PropertyStub, JSPROP_ENUMERATE) ||
1693 !desc->defineProperty(cx, ATOM_TO_JSID(atomState.writableAtom),
1694 BooleanValue((attrs & JSPROP_READONLY) == 0),
1695 PropertyStub, PropertyStub, JSPROP_ENUMERATE)) {
1696 return false;
1700 return desc->defineProperty(cx, ATOM_TO_JSID(atomState.enumerableAtom),
1701 BooleanValue((attrs & JSPROP_ENUMERATE) != 0),
1702 PropertyStub, PropertyStub, JSPROP_ENUMERATE) &&
1703 desc->defineProperty(cx, ATOM_TO_JSID(atomState.configurableAtom),
1704 BooleanValue((attrs & JSPROP_PERMANENT) == 0),
1705 PropertyStub, PropertyStub, JSPROP_ENUMERATE);
1708 JSBool
1709 js_GetOwnPropertyDescriptor(JSContext *cx, JSObject *obj, jsid id, Value *vp)
1711 if (obj->isProxy()) {
1712 if (!JSProxy::getOwnPropertyDescriptor(cx, obj, id, vp))
1713 return false;
1716 JSObject *pobj;
1717 JSProperty *prop;
1718 if (!js_HasOwnProperty(cx, obj->getOps()->lookupProperty, obj, id, &pobj, &prop))
1719 return false;
1720 if (!prop) {
1721 vp->setUndefined();
1722 return true;
1725 Value roots[] = { UndefinedValue(), UndefinedValue(), UndefinedValue() };
1726 AutoArrayRooter tvr(cx, JS_ARRAY_LENGTH(roots), roots);
1727 unsigned attrs;
1728 bool doGet = true;
1729 if (pobj->isNative()) {
1730 Shape *shape = (Shape *) prop;
1731 attrs = shape->attributes();
1732 if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
1733 doGet = false;
1734 if (attrs & JSPROP_GETTER)
1735 roots[0] = shape->getterValue();
1736 if (attrs & JSPROP_SETTER)
1737 roots[1] = shape->setterValue();
1739 JS_UNLOCK_OBJ(cx, pobj);
1740 } else if (!pobj->getAttributes(cx, id, &attrs)) {
1741 return false;
1744 if (doGet && !obj->getProperty(cx, id, &roots[2]))
1745 return false;
1747 return js_NewPropertyDescriptorObject(cx, id,
1748 attrs,
1749 roots[0], /* getter */
1750 roots[1], /* setter */
1751 roots[2], /* value */
1752 vp);
1755 static bool
1756 GetFirstArgumentAsObject(JSContext *cx, uintN argc, Value *vp, const char *method, JSObject **objp)
1758 if (argc == 0) {
1759 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
1760 method, "0", "s");
1761 return false;
1764 const Value &v = vp[2];
1765 if (v.isPrimitive()) {
1766 char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NULL);
1767 if (!bytes)
1768 return false;
1769 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_UNEXPECTED_TYPE,
1770 bytes, "not an object");
1771 JS_free(cx, bytes);
1772 return false;
1775 *objp = &v.toObject();
1776 return true;
1779 static JSBool
1780 obj_getOwnPropertyDescriptor(JSContext *cx, uintN argc, Value *vp)
1782 JSObject *obj;
1783 if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.getOwnPropertyDescriptor", &obj))
1784 return JS_FALSE;
1785 AutoIdRooter nameidr(cx);
1786 if (!ValueToId(cx, argc >= 2 ? vp[3] : UndefinedValue(), nameidr.addr()))
1787 return JS_FALSE;
1788 return js_GetOwnPropertyDescriptor(cx, obj, nameidr.id(), vp);
1791 static JSBool
1792 obj_keys(JSContext *cx, uintN argc, Value *vp)
1794 JSObject *obj;
1795 if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.keys", &obj))
1796 return JS_FALSE;
1798 AutoIdVector props(cx);
1799 if (!GetPropertyNames(cx, obj, JSITER_OWNONLY, props))
1800 return JS_FALSE;
1802 AutoValueVector vals(cx);
1803 vals.resize(props.length());
1804 for (size_t i = 0, len = props.length(); i < len; i++) {
1805 jsid id = props[i];
1806 if (JSID_IS_STRING(id)) {
1807 vals[i].setString(JSID_TO_STRING(id));
1808 } else {
1809 JS_ASSERT(JSID_IS_INT(id));
1810 JSString *str = js_IntToString(cx, JSID_TO_INT(id));
1811 if (!str)
1812 return JS_FALSE;
1813 vals[i].setString(str);
1817 JS_ASSERT(props.length() <= UINT32_MAX);
1818 JSObject *aobj = js_NewArrayObject(cx, jsuint(vals.length()), vals.begin());
1819 if (!aobj)
1820 return JS_FALSE;
1821 vp->setObject(*aobj);
1823 return JS_TRUE;
1826 static JSBool
1827 HasProperty(JSContext* cx, JSObject* obj, jsid id, Value* vp, JSBool* answerp)
1829 if (!JS_HasPropertyById(cx, obj, id, answerp))
1830 return JS_FALSE;
1831 if (!*answerp) {
1832 vp->setUndefined();
1833 return JS_TRUE;
1835 return JS_GetPropertyById(cx, obj, id, Jsvalify(vp));
1838 PropDesc::PropDesc()
1839 : pd(UndefinedValue()),
1840 id(INT_TO_JSID(0)),
1841 value(UndefinedValue()),
1842 get(UndefinedValue()),
1843 set(UndefinedValue()),
1844 attrs(0),
1845 hasGet(false),
1846 hasSet(false),
1847 hasValue(false),
1848 hasWritable(false),
1849 hasEnumerable(false),
1850 hasConfigurable(false)
1854 bool
1855 PropDesc::initialize(JSContext* cx, jsid id, const Value &origval)
1857 Value v = origval;
1858 this->id = id;
1860 /* 8.10.5 step 1 */
1861 if (v.isPrimitive()) {
1862 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_NONNULL_OBJECT);
1863 return false;
1865 JSObject* desc = &v.toObject();
1867 /* Make a copy of the descriptor. We might need it later. */
1868 pd = v;
1870 /* Start with the proper defaults. */
1871 attrs = JSPROP_PERMANENT | JSPROP_READONLY;
1873 JSBool hasProperty;
1875 /* 8.10.5 step 3 */
1876 if (!HasProperty(cx, desc, ATOM_TO_JSID(cx->runtime->atomState.enumerableAtom), &v,
1877 &hasProperty)) {
1878 return false;
1880 if (hasProperty) {
1881 hasEnumerable = JS_TRUE;
1882 if (js_ValueToBoolean(v))
1883 attrs |= JSPROP_ENUMERATE;
1886 /* 8.10.5 step 4 */
1887 if (!HasProperty(cx, desc, ATOM_TO_JSID(cx->runtime->atomState.configurableAtom), &v,
1888 &hasProperty)) {
1889 return false;
1891 if (hasProperty) {
1892 hasConfigurable = JS_TRUE;
1893 if (js_ValueToBoolean(v))
1894 attrs &= ~JSPROP_PERMANENT;
1897 /* 8.10.5 step 5 */
1898 if (!HasProperty(cx, desc, ATOM_TO_JSID(cx->runtime->atomState.valueAtom), &v, &hasProperty))
1899 return false;
1900 if (hasProperty) {
1901 hasValue = true;
1902 value = v;
1905 /* 8.10.6 step 6 */
1906 if (!HasProperty(cx, desc, ATOM_TO_JSID(cx->runtime->atomState.writableAtom), &v, &hasProperty))
1907 return false;
1908 if (hasProperty) {
1909 hasWritable = JS_TRUE;
1910 if (js_ValueToBoolean(v))
1911 attrs &= ~JSPROP_READONLY;
1914 /* 8.10.7 step 7 */
1915 if (!HasProperty(cx, desc, ATOM_TO_JSID(cx->runtime->atomState.getAtom), &v, &hasProperty))
1916 return false;
1917 if (hasProperty) {
1918 if ((v.isPrimitive() || !js_IsCallable(v)) && !v.isUndefined()) {
1919 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_GET_SET_FIELD,
1920 js_getter_str);
1921 return false;
1923 hasGet = true;
1924 get = v;
1925 attrs |= JSPROP_GETTER | JSPROP_SHARED;
1928 /* 8.10.7 step 8 */
1929 if (!HasProperty(cx, desc, ATOM_TO_JSID(cx->runtime->atomState.setAtom), &v, &hasProperty))
1930 return false;
1931 if (hasProperty) {
1932 if ((v.isPrimitive() || !js_IsCallable(v)) && !v.isUndefined()) {
1933 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_GET_SET_FIELD,
1934 js_setter_str);
1935 return false;
1937 hasSet = true;
1938 set = v;
1939 attrs |= JSPROP_SETTER | JSPROP_SHARED;
1942 /* 8.10.7 step 9 */
1943 if ((hasGet || hasSet) && (hasValue || hasWritable)) {
1944 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INVALID_DESCRIPTOR);
1945 return false;
1948 return true;
1951 static JSBool
1952 Reject(JSContext *cx, uintN errorNumber, bool throwError, jsid id, bool *rval)
1954 if (throwError) {
1955 jsid idstr;
1956 if (!js_ValueToStringId(cx, IdToValue(id), &idstr))
1957 return JS_FALSE;
1958 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, errorNumber,
1959 JS_GetStringBytes(JSID_TO_STRING(idstr)));
1960 return JS_FALSE;
1963 *rval = false;
1964 return JS_TRUE;
1967 static JSBool
1968 Reject(JSContext *cx, uintN errorNumber, bool throwError, bool *rval)
1970 if (throwError) {
1971 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, errorNumber);
1972 return JS_FALSE;
1975 *rval = false;
1976 return JS_TRUE;
1979 static JSBool
1980 Reject(JSContext *cx, JSObject *obj, JSProperty *prop, uintN errorNumber, bool throwError,
1981 jsid id, bool *rval)
1983 obj->dropProperty(cx, prop);
1984 return Reject(cx, errorNumber, throwError, id, rval);
1987 static JSBool
1988 DefinePropertyOnObject(JSContext *cx, JSObject *obj, const PropDesc &desc,
1989 bool throwError, bool *rval)
1991 /* 8.12.9 step 1. */
1992 JSProperty *current;
1993 JSObject *obj2;
1994 JS_ASSERT(!obj->getOps()->lookupProperty);
1995 if (!js_HasOwnProperty(cx, NULL, obj, desc.id, &obj2, &current))
1996 return JS_FALSE;
1998 JS_ASSERT(!obj->getOps()->defineProperty);
2000 /* 8.12.9 steps 2-4. */
2001 if (!current) {
2002 if (obj->sealed())
2003 return Reject(cx, JSMSG_OBJECT_NOT_EXTENSIBLE, throwError, rval);
2005 *rval = true;
2007 if (desc.isGenericDescriptor() || desc.isDataDescriptor()) {
2008 JS_ASSERT(!obj->getOps()->defineProperty);
2009 return js_DefineProperty(cx, obj, desc.id, &desc.value,
2010 PropertyStub, PropertyStub, desc.attrs);
2013 JS_ASSERT(desc.isAccessorDescriptor());
2016 * Getters and setters are just like watchpoints from an access
2017 * control point of view.
2019 Value dummy;
2020 uintN dummyAttrs;
2021 if (!CheckAccess(cx, obj, desc.id, JSACC_WATCH, &dummy, &dummyAttrs))
2022 return JS_FALSE;
2024 Value tmp = UndefinedValue();
2025 return js_DefineProperty(cx, obj, desc.id, &tmp,
2026 desc.getter(), desc.setter(), desc.attrs);
2029 /* 8.12.9 steps 5-6 (note 5 is merely a special case of 6). */
2030 Value v = UndefinedValue();
2033 * In the special case of shared permanent properties, the "own" property
2034 * can be found on a different object. In that case the returned property
2035 * might not be native, except: the shared permanent property optimization
2036 * is not applied if the objects have different classes (bug 320854), as
2037 * must be enforced by js_HasOwnProperty for the Shape cast below to be
2038 * safe.
2040 JS_ASSERT(obj->getClass() == obj2->getClass());
2042 const Shape *shape = reinterpret_cast<Shape *>(current);
2043 do {
2044 if (desc.isAccessorDescriptor()) {
2045 if (!shape->isAccessorDescriptor())
2046 break;
2048 if (desc.hasGet &&
2049 !SameValue(desc.getterValue(), shape->getterOrUndefined(), cx)) {
2050 break;
2053 if (desc.hasSet &&
2054 !SameValue(desc.setterValue(), shape->setterOrUndefined(), cx)) {
2055 break;
2057 } else {
2059 * Determine the current value of the property once, if the current
2060 * value might actually need to be used or preserved later. NB: we
2061 * guard on whether the current property is a data descriptor to
2062 * avoid calling a getter; we won't need the value if it's not a
2063 * data descriptor.
2065 if (shape->isDataDescriptor()) {
2067 * Non-standard: if the property is non-configurable and is
2068 * represented by a native getter or setter, don't permit
2069 * redefinition. We expose properties with native getter/setter
2070 * as though they were data properties, for the most part, but
2071 * in this particular case we must worry about integrity
2072 * concerns for JSAPI users who expected that
2073 * permanent+getter/setter means precisely controlled behavior.
2074 * If we permitted such redefinitions, such a property could be
2075 * "fixed" to some specific previous value, no longer varying
2076 * according to the intent of the native getter/setter for the
2077 * property.
2079 * Other engines expose properties of this nature using ECMA
2080 * getter/setter pairs, but we can't because we use them even
2081 * for properties which ECMA specifies as being true data
2082 * descriptors ([].length, Function.length, /regex/.lastIndex,
2083 * &c.). Longer-term perhaps we should convert such properties
2084 * to use data descriptors (at which point representing a
2085 * descriptor with native getter/setter as an accessor
2086 * descriptor would be fine) and take a small memory hit, but
2087 * for now we'll simply forbid their redefinition.
2089 if (!shape->configurable() &&
2090 (!shape->hasDefaultGetter() || !shape->hasDefaultSetter())) {
2091 return Reject(cx, obj2, current, JSMSG_CANT_REDEFINE_UNCONFIGURABLE_PROP,
2092 throwError, desc.id, rval);
2095 if (!js_NativeGet(cx, obj, obj2, shape, JSGET_NO_METHOD_BARRIER, &v)) {
2096 /* current was dropped when the failure occurred. */
2097 return JS_FALSE;
2101 if (desc.isDataDescriptor()) {
2102 if (!shape->isDataDescriptor())
2103 break;
2105 if (desc.hasValue && !SameValue(desc.value, v, cx))
2106 break;
2107 if (desc.hasWritable && desc.writable() != shape->writable())
2108 break;
2109 } else {
2110 /* The only fields in desc will be handled below. */
2111 JS_ASSERT(desc.isGenericDescriptor());
2115 if (desc.hasConfigurable && desc.configurable() != shape->configurable())
2116 break;
2117 if (desc.hasEnumerable && desc.enumerable() != shape->enumerable())
2118 break;
2120 /* The conditions imposed by step 5 or step 6 apply. */
2121 obj2->dropProperty(cx, current);
2122 *rval = true;
2123 return JS_TRUE;
2124 } while (0);
2126 /* 8.12.9 step 7. */
2127 if (!shape->configurable()) {
2129 * Since [[Configurable]] defaults to false, we don't need to check
2130 * whether it was specified. We can't do likewise for [[Enumerable]]
2131 * because its putative value is used in a comparison -- a comparison
2132 * whose result must always be false per spec if the [[Enumerable]]
2133 * field is not present. Perfectly pellucid logic, eh?
2135 JS_ASSERT_IF(!desc.hasConfigurable, !desc.configurable());
2136 if (desc.configurable() ||
2137 (desc.hasEnumerable && desc.enumerable() != shape->enumerable())) {
2138 return Reject(cx, obj2, current, JSMSG_CANT_REDEFINE_UNCONFIGURABLE_PROP, throwError,
2139 desc.id, rval);
2143 if (desc.isGenericDescriptor()) {
2144 /* 8.12.9 step 8, no validation required */
2145 } else if (desc.isDataDescriptor() != shape->isDataDescriptor()) {
2146 /* 8.12.9 step 9. */
2147 if (!shape->configurable()) {
2148 return Reject(cx, obj2, current, JSMSG_CANT_REDEFINE_UNCONFIGURABLE_PROP,
2149 throwError, desc.id, rval);
2151 } else if (desc.isDataDescriptor()) {
2152 /* 8.12.9 step 10. */
2153 JS_ASSERT(shape->isDataDescriptor());
2154 if (!shape->configurable() && !shape->writable()) {
2155 if ((desc.hasWritable && desc.writable()) ||
2156 (desc.hasValue && !SameValue(desc.value, v, cx))) {
2157 return Reject(cx, obj2, current, JSMSG_CANT_REDEFINE_UNCONFIGURABLE_PROP,
2158 throwError, desc.id, rval);
2161 } else {
2162 /* 8.12.9 step 11. */
2163 JS_ASSERT(desc.isAccessorDescriptor() && shape->isAccessorDescriptor());
2164 if (!shape->configurable()) {
2165 if ((desc.hasSet &&
2166 !SameValue(desc.setterValue(), shape->setterOrUndefined(), cx)) ||
2167 (desc.hasGet &&
2168 !SameValue(desc.getterValue(), shape->getterOrUndefined(), cx))) {
2169 return Reject(cx, obj2, current, JSMSG_CANT_REDEFINE_UNCONFIGURABLE_PROP,
2170 throwError, desc.id, rval);
2175 /* 8.12.9 step 12. */
2176 uintN attrs;
2177 PropertyOp getter, setter;
2178 if (desc.isGenericDescriptor()) {
2179 uintN changed = 0;
2180 if (desc.hasConfigurable)
2181 changed |= JSPROP_PERMANENT;
2182 if (desc.hasEnumerable)
2183 changed |= JSPROP_ENUMERATE;
2185 attrs = (shape->attributes() & ~changed) | (desc.attrs & changed);
2186 if (shape->isMethod()) {
2187 JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
2188 getter = setter = PropertyStub;
2189 } else {
2190 getter = shape->getter();
2191 setter = shape->setter();
2193 } else if (desc.isDataDescriptor()) {
2194 uintN unchanged = 0;
2195 if (!desc.hasConfigurable)
2196 unchanged |= JSPROP_PERMANENT;
2197 if (!desc.hasEnumerable)
2198 unchanged |= JSPROP_ENUMERATE;
2199 if (!desc.hasWritable)
2200 unchanged |= JSPROP_READONLY;
2202 if (desc.hasValue)
2203 v = desc.value;
2204 attrs = (desc.attrs & ~unchanged) | (shape->attributes() & unchanged);
2205 getter = setter = PropertyStub;
2206 } else {
2207 JS_ASSERT(desc.isAccessorDescriptor());
2210 * Getters and setters are just like watchpoints from an access
2211 * control point of view.
2213 Value dummy;
2214 if (!CheckAccess(cx, obj2, desc.id, JSACC_WATCH, &dummy, &attrs)) {
2215 obj2->dropProperty(cx, current);
2216 return JS_FALSE;
2219 JS_ASSERT_IF(shape->isMethod(), !(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
2221 /* 8.12.9 step 12. */
2222 uintN changed = 0;
2223 if (desc.hasConfigurable)
2224 changed |= JSPROP_PERMANENT;
2225 if (desc.hasEnumerable)
2226 changed |= JSPROP_ENUMERATE;
2227 if (desc.hasGet)
2228 changed |= JSPROP_GETTER | JSPROP_SHARED;
2229 if (desc.hasSet)
2230 changed |= JSPROP_SETTER | JSPROP_SHARED;
2232 attrs = (desc.attrs & changed) | (shape->attributes() & ~changed);
2233 if (desc.hasGet) {
2234 getter = desc.getter();
2235 } else {
2236 getter = (shape->isMethod() || (shape->hasDefaultGetter() && !shape->hasGetterValue()))
2237 ? PropertyStub
2238 : shape->getter();
2240 if (desc.hasSet) {
2241 setter = desc.setter();
2242 } else {
2243 setter = (shape->hasDefaultSetter() && !shape->hasSetterValue())
2244 ? PropertyStub
2245 : shape->setter();
2249 *rval = true;
2250 obj2->dropProperty(cx, current);
2251 return js_DefineProperty(cx, obj, desc.id, &v, getter, setter, attrs);
2254 static JSBool
2255 DefinePropertyOnArray(JSContext *cx, JSObject *obj, const PropDesc &desc,
2256 bool throwError, bool *rval)
2259 * We probably should optimize dense array property definitions where
2260 * the descriptor describes a traditional array property (enumerable,
2261 * configurable, writable, numeric index or length without altering its
2262 * attributes). Such definitions are probably unlikely, so we don't bother
2263 * for now.
2265 if (obj->isDenseArray() && !obj->makeDenseArraySlow(cx))
2266 return JS_FALSE;
2268 jsuint oldLen = obj->getArrayLength();
2270 if (JSID_IS_ATOM(desc.id, cx->runtime->atomState.lengthAtom)) {
2272 * Our optimization of storage of the length property of arrays makes
2273 * it very difficult to properly implement defining the property. For
2274 * now simply throw an exception (NB: not merely Reject) on any attempt
2275 * to define the "length" property, rather than attempting to implement
2276 * some difficult-for-authors-to-grasp subset of that functionality.
2278 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_DEFINE_ARRAY_LENGTH_UNSUPPORTED);
2279 return JS_FALSE;
2282 uint32 index;
2283 if (js_IdIsIndex(desc.id, &index)) {
2285 // Disabled until we support defining "length":
2286 if (index >= oldLen && lengthPropertyNotWritable())
2287 return ThrowTypeError(cx, JSMSG_CANT_APPEND_PROPERTIES_TO_UNWRITABLE_LENGTH_ARRAY);
2289 if (!DefinePropertyOnObject(cx, obj, desc, false, rval))
2290 return JS_FALSE;
2291 if (!*rval)
2292 return Reject(cx, JSMSG_CANT_DEFINE_ARRAY_INDEX, throwError, rval);
2294 if (index >= oldLen) {
2295 JS_ASSERT(index != UINT32_MAX);
2296 obj->setArrayLength(index + 1);
2299 *rval = true;
2300 return JS_TRUE;
2303 return DefinePropertyOnObject(cx, obj, desc, throwError, rval);
2306 static JSBool
2307 DefineProperty(JSContext *cx, JSObject *obj, const PropDesc &desc, bool throwError,
2308 bool *rval)
2310 if (obj->isArray())
2311 return DefinePropertyOnArray(cx, obj, desc, throwError, rval);
2313 if (obj->getOps()->lookupProperty) {
2314 if (obj->isProxy())
2315 return JSProxy::defineProperty(cx, obj, desc.id, desc.pd);
2316 return Reject(cx, JSMSG_OBJECT_NOT_EXTENSIBLE, throwError, rval);
2319 return DefinePropertyOnObject(cx, obj, desc, throwError, rval);
2322 JSBool
2323 js_DefineOwnProperty(JSContext *cx, JSObject *obj, jsid id,
2324 const Value &descriptor, JSBool *bp)
2326 AutoPropDescArrayRooter descs(cx);
2327 PropDesc *desc = descs.append();
2328 if (!desc || !desc->initialize(cx, id, descriptor))
2329 return false;
2331 bool rval;
2332 if (!DefineProperty(cx, obj, *desc, true, &rval))
2333 return false;
2334 *bp = !!rval;
2335 return true;
2338 /* ES5 15.2.3.6: Object.defineProperty(O, P, Attributes) */
2339 static JSBool
2340 obj_defineProperty(JSContext* cx, uintN argc, Value* vp)
2342 /* 15.2.3.6 steps 1 and 5. */
2343 JSObject *obj;
2344 if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.defineProperty", &obj))
2345 return JS_FALSE;
2346 vp->setObject(*obj);
2348 /* 15.2.3.6 step 2. */
2349 AutoIdRooter nameidr(cx);
2350 if (!ValueToId(cx, argc >= 2 ? vp[3] : UndefinedValue(), nameidr.addr()))
2351 return JS_FALSE;
2353 /* 15.2.3.6 step 3. */
2354 const Value &descval = argc >= 3 ? vp[4] : UndefinedValue();
2356 /* 15.2.3.6 step 4 */
2357 JSBool junk;
2358 return js_DefineOwnProperty(cx, obj, nameidr.id(), descval, &junk);
2361 static bool
2362 DefineProperties(JSContext *cx, JSObject *obj, JSObject *props)
2364 AutoIdArray ida(cx, JS_Enumerate(cx, props));
2365 if (!ida)
2366 return false;
2368 AutoPropDescArrayRooter descs(cx);
2369 size_t len = ida.length();
2370 for (size_t i = 0; i < len; i++) {
2371 jsid id = ida[i];
2372 PropDesc* desc = descs.append();
2373 AutoValueRooter tvr(cx);
2374 if (!desc ||
2375 !JS_GetPropertyById(cx, props, id, tvr.jsval_addr()) ||
2376 !desc->initialize(cx, id, tvr.value())) {
2377 return false;
2381 bool dummy;
2382 for (size_t i = 0; i < len; i++) {
2383 if (!DefineProperty(cx, obj, descs[i], true, &dummy))
2384 return false;
2387 return true;
2390 extern JSBool
2391 js_PopulateObject(JSContext *cx, JSObject *newborn, JSObject *props)
2393 return DefineProperties(cx, newborn, props);
2396 /* ES5 15.2.3.7: Object.defineProperties(O, Properties) */
2397 static JSBool
2398 obj_defineProperties(JSContext* cx, uintN argc, Value* vp)
2400 /* 15.2.3.6 steps 1 and 5. */
2401 JSObject *obj;
2402 if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.defineProperties", &obj))
2403 return false;
2404 vp->setObject(*obj);
2406 if (argc < 2) {
2407 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
2408 "Object.defineProperties", "0", "s");
2409 return false;
2412 JSObject* props = js_ValueToNonNullObject(cx, vp[3]);
2413 if (!props)
2414 return false;
2415 vp[3].setObject(*props);
2417 return DefineProperties(cx, obj, props);
2420 /* ES5 15.2.3.5: Object.create(O [, Properties]) */
2421 static JSBool
2422 obj_create(JSContext *cx, uintN argc, Value *vp)
2424 if (argc == 0) {
2425 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
2426 "Object.create", "0", "s");
2427 return JS_FALSE;
2430 const Value &v = vp[2];
2431 if (!v.isObjectOrNull()) {
2432 char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NULL);
2433 if (!bytes)
2434 return JS_FALSE;
2435 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_UNEXPECTED_TYPE,
2436 bytes, "not an object or null");
2437 JS_free(cx, bytes);
2438 return JS_FALSE;
2442 * Use the callee's global as the parent of the new object to avoid dynamic
2443 * scoping (i.e., using the caller's global).
2445 JSObject *obj = NewNonFunction<WithProto::Given>(cx, &js_ObjectClass, v.toObjectOrNull(),
2446 vp->toObject().getGlobal());
2447 if (!obj)
2448 return JS_FALSE;
2449 vp->setObject(*obj); /* Root and prepare for eventual return. */
2451 /* 15.2.3.5 step 4. */
2452 if (argc > 1 && !vp[3].isUndefined()) {
2453 if (vp[3].isPrimitive()) {
2454 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_NONNULL_OBJECT);
2455 return JS_FALSE;
2458 JSObject *props = &vp[3].toObject();
2459 AutoIdArray ida(cx, JS_Enumerate(cx, props));
2460 if (!ida)
2461 return JS_FALSE;
2463 AutoPropDescArrayRooter descs(cx);
2464 size_t len = ida.length();
2465 for (size_t i = 0; i < len; i++) {
2466 jsid id = ida[i];
2467 PropDesc *desc = descs.append();
2468 if (!desc || !JS_GetPropertyById(cx, props, id, Jsvalify(&vp[1])) ||
2469 !desc->initialize(cx, id, vp[1])) {
2470 return JS_FALSE;
2474 bool dummy;
2475 for (size_t i = 0; i < len; i++) {
2476 if (!DefineProperty(cx, obj, descs[i], true, &dummy))
2477 return JS_FALSE;
2481 /* 5. Return obj. */
2482 return JS_TRUE;
2485 static JSBool
2486 obj_getOwnPropertyNames(JSContext *cx, uintN argc, Value *vp)
2488 JSObject *obj;
2489 if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.getOwnPropertyNames", &obj))
2490 return false;
2492 AutoIdVector keys(cx);
2493 if (!GetPropertyNames(cx, obj, JSITER_OWNONLY | JSITER_HIDDEN, keys))
2494 return false;
2496 AutoValueVector vals(cx);
2497 if (!vals.resize(keys.length()))
2498 return false;
2500 for (size_t i = 0, len = keys.length(); i < len; i++) {
2501 jsid id = keys[i];
2502 if (JSID_IS_INT(id)) {
2503 JSString *str = js_ValueToString(cx, Int32Value(JSID_TO_INT(id)));
2504 if (!str)
2505 return false;
2506 vals[i].setString(str);
2507 } else if (JSID_IS_ATOM(id)) {
2508 vals[i].setString(JSID_TO_STRING(id));
2509 } else {
2510 vals[i].setObject(*JSID_TO_OBJECT(id));
2514 JSObject *aobj = js_NewArrayObject(cx, vals.length(), vals.begin());
2515 if (!aobj)
2516 return false;
2518 vp->setObject(*aobj);
2519 return true;
2523 #if JS_HAS_OBJ_WATCHPOINT
2524 const char js_watch_str[] = "watch";
2525 const char js_unwatch_str[] = "unwatch";
2526 #endif
2527 const char js_hasOwnProperty_str[] = "hasOwnProperty";
2528 const char js_isPrototypeOf_str[] = "isPrototypeOf";
2529 const char js_propertyIsEnumerable_str[] = "propertyIsEnumerable";
2531 static JSFunctionSpec object_methods[] = {
2532 #if JS_HAS_TOSOURCE
2533 JS_FN(js_toSource_str, obj_toSource, 0,0),
2534 #endif
2535 JS_FN(js_toString_str, obj_toString, 0,0),
2536 JS_FN(js_toLocaleString_str, obj_toLocaleString, 0,0),
2537 JS_FN(js_valueOf_str, obj_valueOf, 0,0),
2538 #if JS_HAS_OBJ_WATCHPOINT
2539 JS_FN(js_watch_str, obj_watch, 2,0),
2540 JS_FN(js_unwatch_str, obj_unwatch, 1,0),
2541 #endif
2542 JS_FN(js_hasOwnProperty_str, obj_hasOwnProperty, 1,0),
2543 JS_FN(js_isPrototypeOf_str, obj_isPrototypeOf, 1,0),
2544 JS_FN(js_propertyIsEnumerable_str, obj_propertyIsEnumerable, 1,0),
2545 #if OLD_GETTER_SETTER_METHODS
2546 JS_FN(js_defineGetter_str, js_obj_defineGetter, 2,0),
2547 JS_FN(js_defineSetter_str, js_obj_defineSetter, 2,0),
2548 JS_FN(js_lookupGetter_str, obj_lookupGetter, 1,0),
2549 JS_FN(js_lookupSetter_str, obj_lookupSetter, 1,0),
2550 #endif
2551 JS_FS_END
2554 static JSFunctionSpec object_static_methods[] = {
2555 JS_FN("getPrototypeOf", obj_getPrototypeOf, 1,0),
2556 JS_FN("getOwnPropertyDescriptor", obj_getOwnPropertyDescriptor,2,0),
2557 JS_FN("keys", obj_keys, 1,0),
2558 JS_FN("defineProperty", obj_defineProperty, 3,0),
2559 JS_FN("defineProperties", obj_defineProperties, 2,0),
2560 JS_FN("create", obj_create, 2,0),
2561 JS_FN("getOwnPropertyNames", obj_getOwnPropertyNames, 1,0),
2562 JS_FS_END
2565 JSBool
2566 js_Object(JSContext *cx, uintN argc, Value *vp)
2568 JSObject *obj;
2569 if (argc == 0) {
2570 /* Trigger logic below to construct a blank object. */
2571 obj = NULL;
2572 } else {
2573 /* If argv[0] is null or undefined, obj comes back null. */
2574 if (!js_ValueToObjectOrNull(cx, vp[2], &obj))
2575 return JS_FALSE;
2577 if (!obj) {
2578 /* Make an object whether this was called with 'new' or not. */
2579 JS_ASSERT(!argc || vp[2].isNull() || vp[2].isUndefined());
2580 obj = NewBuiltinClassInstance(cx, &js_ObjectClass);
2581 if (!obj)
2582 return JS_FALSE;
2584 vp->setObject(*obj);
2585 return JS_TRUE;
2588 JSObject*
2589 js_NewInstance(JSContext *cx, JSObject *callee)
2591 Class *clasp = callee->getClass();
2593 Class *newclasp = &js_ObjectClass;
2594 if (clasp == &js_FunctionClass) {
2595 JSFunction *fun = callee->getFunctionPrivate();
2596 if (fun->isNative() && fun->u.n.clasp)
2597 newclasp = fun->u.n.clasp;
2600 Value protov;
2601 if (!callee->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), &protov))
2602 return NULL;
2604 JSObject *proto = protov.isObjectOrNull() ? protov.toObjectOrNull() : NULL;
2605 JSObject *parent = callee->getParent();
2606 return NewObject<WithProto::Class>(cx, newclasp, proto, parent);
2609 #ifdef JS_TRACER
2611 static JS_ALWAYS_INLINE JSObject*
2612 NewObjectWithClassProto(JSContext *cx, Class *clasp, JSObject *proto,
2613 const Value &privateSlotValue)
2615 JS_ASSERT(clasp->isNative());
2617 JSObject* obj = js_NewGCObject(cx);
2618 if (!obj)
2619 return NULL;
2621 obj->initSharingEmptyShape(clasp, proto, proto->getParent(), privateSlotValue, cx);
2622 return obj;
2625 JSObject* FASTCALL
2626 js_Object_tn(JSContext* cx, JSObject* proto)
2628 JS_ASSERT(!(js_ObjectClass.flags & JSCLASS_HAS_PRIVATE));
2630 return NewObjectWithClassProto(cx, &js_ObjectClass, proto, UndefinedValue());
2633 JS_DEFINE_TRCINFO_1(js_Object,
2634 (2, (extern, CONSTRUCTOR_RETRY, js_Object_tn, CONTEXT, CALLEE_PROTOTYPE, 0,
2635 nanojit::ACCSET_STORE_ANY)))
2637 JSObject* FASTCALL
2638 js_NonEmptyObject(JSContext* cx, JSObject* proto)
2640 JS_ASSERT(!(js_ObjectClass.flags & JSCLASS_HAS_PRIVATE));
2642 JSObject *obj = NewObjectWithClassProto(cx, &js_ObjectClass, proto, UndefinedValue());
2643 return (obj && obj->ensureClassReservedSlotsForEmptyObject(cx)) ? obj : NULL;
2646 JS_DEFINE_CALLINFO_2(extern, CONSTRUCTOR_RETRY, js_NonEmptyObject, CONTEXT, CALLEE_PROTOTYPE, 0,
2647 nanojit::ACCSET_STORE_ANY)
2649 JSObject* FASTCALL
2650 js_String_tn(JSContext* cx, JSObject* proto, JSString* str)
2652 JS_ASSERT(JS_ON_TRACE(cx));
2653 return NewObjectWithClassProto(cx, &js_StringClass, proto, StringValue(str));
2655 JS_DEFINE_CALLINFO_3(extern, OBJECT, js_String_tn, CONTEXT, CALLEE_PROTOTYPE, STRING, 0,
2656 nanojit::ACCSET_STORE_ANY)
2658 JSObject* FASTCALL
2659 js_NewInstanceFromTrace(JSContext *cx, Class *clasp, JSObject *ctor)
2661 JS_ASSERT(JS_ON_TRACE(cx));
2662 JS_ASSERT(ctor->isFunction());
2663 #ifdef JS_THREADSAFE
2664 if (ctor->title.ownercx != cx)
2665 return NULL;
2666 #endif
2668 if (!ctor->ensureClassReservedSlots(cx))
2669 return NULL;
2671 jsid classPrototypeId = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom);
2672 const Shape *shape = ctor->nativeLookup(classPrototypeId);
2673 Value pval = shape ? ctor->getSlot(shape->slot) : MagicValue(JS_GENERIC_MAGIC);
2675 JSObject *parent = ctor->getParent();
2676 JSObject *proto;
2677 if (pval.isObject()) {
2678 /* An object in ctor.prototype, let's use it as the new instance's proto. */
2679 proto = &pval.toObject();
2680 } else {
2681 /* A hole or a primitive: either way, we need to get Object.prototype. */
2682 if (!js_GetClassPrototype(cx, parent, JSProto_Object, &proto))
2683 return NULL;
2685 if (pval.isMagic(JS_GENERIC_MAGIC)) {
2687 * No ctor.prototype was set, so we inline-expand and optimize
2688 * fun_resolve's prototype creation code.
2690 proto = NewNativeClassInstance(cx, clasp, proto, parent);
2691 if (!proto)
2692 return NULL;
2693 if (!js_SetClassPrototype(cx, ctor, proto, JSPROP_ENUMERATE | JSPROP_PERMANENT))
2694 return NULL;
2695 } else {
2697 * A primitive value in .prototype means to use Object.prototype
2698 * for proto. See ES5 13.2.2 step 7.
2704 * FIXME: 561785 at least. Quasi-natives including XML objects prevent us
2705 * from easily or unconditionally calling NewNativeClassInstance here.
2707 return NewNonFunction<WithProto::Given>(cx, clasp, proto, parent);
2710 JS_DEFINE_CALLINFO_3(extern, CONSTRUCTOR_RETRY, js_NewInstanceFromTrace, CONTEXT, CLASS, OBJECT, 0,
2711 nanojit::ACCSET_STORE_ANY)
2713 #else /* !JS_TRACER */
2715 # define js_Object_trcinfo NULL
2717 #endif /* !JS_TRACER */
2720 * Given pc pointing after a property accessing bytecode, return true if the
2721 * access is "object-detecting" in the sense used by web scripts, e.g., when
2722 * checking whether document.all is defined.
2724 JS_REQUIRES_STACK JSBool
2725 Detecting(JSContext *cx, jsbytecode *pc)
2727 JSScript *script;
2728 jsbytecode *endpc;
2729 JSOp op;
2730 JSAtom *atom;
2732 script = cx->fp()->script();
2733 endpc = script->code + script->length;
2734 for (;; pc += js_CodeSpec[op].length) {
2735 JS_ASSERT_IF(!cx->fp()->hasImacropc(), script->code <= pc && pc < endpc);
2737 /* General case: a branch or equality op follows the access. */
2738 op = js_GetOpcode(cx, script, pc);
2739 if (js_CodeSpec[op].format & JOF_DETECTING)
2740 return JS_TRUE;
2742 switch (op) {
2743 case JSOP_NULL:
2745 * Special case #1: handle (document.all == null). Don't sweat
2746 * about JS1.2's revision of the equality operators here.
2748 if (++pc < endpc) {
2749 op = js_GetOpcode(cx, script, pc);
2750 return *pc == JSOP_EQ || *pc == JSOP_NE;
2752 return JS_FALSE;
2754 case JSOP_GETGNAME:
2755 case JSOP_NAME:
2757 * Special case #2: handle (document.all == undefined). Don't
2758 * worry about someone redefining undefined, which was added by
2759 * Edition 3, so is read/write for backward compatibility.
2761 GET_ATOM_FROM_BYTECODE(script, pc, 0, atom);
2762 if (atom == cx->runtime->atomState.typeAtoms[JSTYPE_VOID] &&
2763 (pc += js_CodeSpec[op].length) < endpc) {
2764 op = js_GetOpcode(cx, script, pc);
2765 return op == JSOP_EQ || op == JSOP_NE ||
2766 op == JSOP_STRICTEQ || op == JSOP_STRICTNE;
2768 return JS_FALSE;
2770 default:
2772 * At this point, anything but an extended atom index prefix means
2773 * we're not detecting.
2775 if (!(js_CodeSpec[op].format & JOF_INDEXBASE))
2776 return JS_FALSE;
2777 break;
2783 * Infer lookup flags from the currently executing bytecode. This does
2784 * not attempt to infer JSRESOLVE_WITH, because the current bytecode
2785 * does not indicate whether we are in a with statement. Return defaultFlags
2786 * if a currently executing bytecode cannot be determined.
2788 uintN
2789 js_InferFlags(JSContext *cx, uintN defaultFlags)
2791 #ifdef JS_TRACER
2792 if (JS_ON_TRACE(cx))
2793 return cx->bailExit->lookupFlags;
2794 #endif
2796 JS_ASSERT_NOT_ON_TRACE(cx);
2798 jsbytecode *pc;
2799 const JSCodeSpec *cs;
2800 uint32 format;
2801 uintN flags = 0;
2803 JSStackFrame *const fp = js_GetTopStackFrame(cx);
2804 if (!fp || !(pc = cx->regs->pc))
2805 return defaultFlags;
2806 cs = &js_CodeSpec[js_GetOpcode(cx, fp->script(), pc)];
2807 format = cs->format;
2808 if (JOF_MODE(format) != JOF_NAME)
2809 flags |= JSRESOLVE_QUALIFIED;
2810 if ((format & (JOF_SET | JOF_FOR)) || fp->isAssigning()) {
2811 flags |= JSRESOLVE_ASSIGNING;
2812 } else if (cs->length >= 0) {
2813 pc += cs->length;
2814 JSScript *script = cx->fp()->script();
2815 if (pc < script->code + script->length && Detecting(cx, pc))
2816 flags |= JSRESOLVE_DETECTING;
2818 if (format & JOF_DECLARING)
2819 flags |= JSRESOLVE_DECLARING;
2820 return flags;
2824 * ObjectOps and Class for with-statement stack objects.
2826 static JSBool
2827 with_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
2828 JSProperty **propp)
2830 /* Fixes bug 463997 */
2831 uintN flags = cx->resolveFlags;
2832 if (flags == JSRESOLVE_INFER)
2833 flags = js_InferFlags(cx, flags);
2834 flags |= JSRESOLVE_WITH;
2835 JSAutoResolveFlags rf(cx, flags);
2836 return obj->getProto()->lookupProperty(cx, id, objp, propp);
2839 static JSBool
2840 with_GetProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
2842 return obj->getProto()->getProperty(cx, id, vp);
2845 static JSBool
2846 with_SetProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict)
2848 return obj->getProto()->setProperty(cx, id, vp, strict);
2851 static JSBool
2852 with_GetAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp)
2854 return obj->getProto()->getAttributes(cx, id, attrsp);
2857 static JSBool
2858 with_SetAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp)
2860 return obj->getProto()->setAttributes(cx, id, attrsp);
2863 static JSBool
2864 with_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool strict)
2866 return obj->getProto()->deleteProperty(cx, id, rval, strict);
2869 static JSBool
2870 with_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
2871 Value *statep, jsid *idp)
2873 return obj->getProto()->enumerate(cx, enum_op, statep, idp);
2876 static JSType
2877 with_TypeOf(JSContext *cx, JSObject *obj)
2879 return JSTYPE_OBJECT;
2882 static JSObject *
2883 with_ThisObject(JSContext *cx, JSObject *obj)
2885 return obj->getWithThis();
2888 Class js_WithClass = {
2889 "With",
2890 JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS,
2891 PropertyStub, /* addProperty */
2892 PropertyStub, /* delProperty */
2893 PropertyStub, /* getProperty */
2894 PropertyStub, /* setProperty */
2895 EnumerateStub,
2896 ResolveStub,
2897 ConvertStub,
2898 NULL, /* finalize */
2899 NULL, /* reserved */
2900 NULL, /* checkAccess */
2901 NULL, /* call */
2902 NULL, /* construct */
2903 NULL, /* xdrObject */
2904 NULL, /* hasInstance */
2905 NULL, /* mark */
2906 JS_NULL_CLASS_EXT,
2908 with_LookupProperty,
2909 NULL, /* defineProperty */
2910 with_GetProperty,
2911 with_SetProperty,
2912 with_GetAttributes,
2913 with_SetAttributes,
2914 with_DeleteProperty,
2915 with_Enumerate,
2916 with_TypeOf,
2917 NULL, /* trace */
2918 with_ThisObject,
2919 NULL, /* clear */
2923 JS_REQUIRES_STACK JSObject *
2924 js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth)
2926 JSObject *obj;
2928 obj = js_NewGCObject(cx);
2929 if (!obj)
2930 return NULL;
2932 obj->init(&js_WithClass, proto, parent, js_FloatingFrameIfGenerator(cx, cx->fp()), cx);
2933 obj->setMap(cx->runtime->emptyWithShape);
2934 OBJ_SET_BLOCK_DEPTH(cx, obj, depth);
2936 AutoObjectRooter tvr(cx, obj);
2937 JSObject *thisp = proto->thisObject(cx);
2938 if (!thisp)
2939 return NULL;
2941 obj->setWithThis(thisp);
2942 return obj;
2945 JSObject *
2946 js_NewBlockObject(JSContext *cx)
2949 * Null obj's proto slot so that Object.prototype.* does not pollute block
2950 * scopes and to give the block object its own scope.
2952 JSObject *blockObj = js_NewGCObject(cx);
2953 if (!blockObj)
2954 return NULL;
2956 blockObj->init(&js_BlockClass, NULL, NULL, NullValue(), cx);
2957 blockObj->setMap(cx->runtime->emptyBlockShape);
2958 return blockObj;
2961 JSObject *
2962 js_CloneBlockObject(JSContext *cx, JSObject *proto, JSStackFrame *fp)
2964 JS_ASSERT(proto->isStaticBlock());
2966 JSObject *clone = js_NewGCObject(cx);
2967 if (!clone)
2968 return NULL;
2970 JSStackFrame *priv = js_FloatingFrameIfGenerator(cx, fp);
2972 /* The caller sets parent on its own. */
2973 clone->init(&js_BlockClass, proto, NULL, priv, cx);
2974 clone->fslots[JSSLOT_BLOCK_DEPTH] = proto->fslots[JSSLOT_BLOCK_DEPTH];
2976 clone->setMap(proto->map);
2977 if (!clone->ensureInstanceReservedSlots(cx, OBJ_BLOCK_COUNT(cx, proto)))
2978 return NULL;
2980 JS_ASSERT(clone->isClonedBlock());
2981 return clone;
2984 JS_REQUIRES_STACK JSBool
2985 js_PutBlockObject(JSContext *cx, JSBool normalUnwind)
2987 /* Blocks have one fixed slot available for the first local.*/
2988 JS_STATIC_ASSERT(JS_INITIAL_NSLOTS == JSSLOT_BLOCK_DEPTH + 2);
2990 JSStackFrame *const fp = cx->fp();
2991 JSObject *obj = &fp->scopeChain();
2992 JS_ASSERT(obj->isClonedBlock());
2993 JS_ASSERT(obj->getPrivate() == js_FloatingFrameIfGenerator(cx, cx->fp()));
2995 /* Block objects should have all reserved slots allocated early. */
2996 uintN count = OBJ_BLOCK_COUNT(cx, obj);
2997 JS_ASSERT(obj->numSlots() == JSSLOT_BLOCK_DEPTH + 1 + count);
2999 /* The block and its locals must be on the current stack for GC safety. */
3000 uintN depth = OBJ_BLOCK_DEPTH(cx, obj);
3001 JS_ASSERT(depth <= size_t(cx->regs->sp - fp->base()));
3002 JS_ASSERT(count <= size_t(cx->regs->sp - fp->base() - depth));
3004 /* See comments in CheckDestructuring from jsparse.cpp. */
3005 JS_ASSERT(count >= 1);
3007 if (normalUnwind) {
3008 uintN slot = JSSLOT_BLOCK_DEPTH + 1;
3009 uintN flen = JS_MIN(count, JS_INITIAL_NSLOTS - slot);
3010 uintN stop = slot + flen;
3012 depth += fp->numFixed();
3013 while (slot < stop)
3014 obj->fslots[slot++] = fp->slots()[depth++];
3015 count -= flen;
3016 if (count != 0)
3017 memcpy(obj->dslots, fp->slots() + depth, count * sizeof(Value));
3020 /* We must clear the private slot even with errors. */
3021 obj->setPrivate(NULL);
3022 fp->setScopeChainNoCallObj(*obj->getParent());
3023 return normalUnwind;
3026 static JSBool
3027 block_getProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
3030 * Block objects are never exposed to script, and the engine handles them
3031 * with care. So unlike other getters, this one can assert (rather than
3032 * check) certain invariants about obj.
3034 JS_ASSERT(obj->isClonedBlock());
3035 uintN index = (uintN) JSID_TO_INT(id);
3036 JS_ASSERT(index < OBJ_BLOCK_COUNT(cx, obj));
3038 JSStackFrame *fp = (JSStackFrame *) obj->getPrivate();
3039 if (fp) {
3040 fp = js_LiveFrameIfGenerator(fp);
3041 index += fp->numFixed() + OBJ_BLOCK_DEPTH(cx, obj);
3042 JS_ASSERT(index < fp->numSlots());
3043 *vp = fp->slots()[index];
3044 return true;
3047 /* Values are in slots immediately following the class-reserved ones. */
3048 JS_ASSERT(obj->getSlot(JSSLOT_FREE(&js_BlockClass) + index) == *vp);
3049 return true;
3052 static JSBool
3053 block_setProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
3055 JS_ASSERT(obj->isClonedBlock());
3056 uintN index = (uintN) JSID_TO_INT(id);
3057 JS_ASSERT(index < OBJ_BLOCK_COUNT(cx, obj));
3059 JSStackFrame *fp = (JSStackFrame *) obj->getPrivate();
3060 if (fp) {
3061 fp = js_LiveFrameIfGenerator(fp);
3062 index += fp->numFixed() + OBJ_BLOCK_DEPTH(cx, obj);
3063 JS_ASSERT(index < fp->numSlots());
3064 fp->slots()[index] = *vp;
3065 return true;
3069 * The value in *vp will be written back to the slot in obj that was
3070 * allocated when this let binding was defined.
3072 return true;
3075 const Shape *
3076 JSObject::defineBlockVariable(JSContext *cx, jsid id, intN index)
3078 JS_ASSERT(isStaticBlock());
3080 /* Use JSPROP_ENUMERATE to aid the disassembler. */
3081 uint32 slot = JSSLOT_FREE(&js_BlockClass) + index;
3082 const Shape *shape = addProperty(cx, id,
3083 block_getProperty, block_setProperty,
3084 slot, JSPROP_ENUMERATE | JSPROP_PERMANENT,
3085 Shape::HAS_SHORTID, index);
3086 if (!shape)
3087 return NULL;
3088 if (slot >= numSlots() && !growSlots(cx, slot + 1))
3089 return NULL;
3090 return shape;
3093 static size_t
3094 GetObjectSize(JSObject *obj)
3096 return (obj->isFunction() && !obj->getPrivate())
3097 ? sizeof(JSFunction)
3098 : sizeof(JSObject);
3102 * Use this method with extreme caution. It trades the guts of two objects and updates
3103 * scope ownership. This operation is not thread-safe, just as fast array to slow array
3104 * transitions are inherently not thread-safe. Don't perform a swap operation on objects
3105 * shared across threads or, or bad things will happen. You have been warned.
3107 void
3108 JSObject::swap(JSObject *other)
3110 size_t size = GetObjectSize(this);
3111 JS_ASSERT(size == GetObjectSize(other));
3113 /* Trade the guts of the objects. */
3114 char tmp[tl::Max<sizeof(JSFunction), sizeof(JSObject)>::result];
3115 memcpy(tmp, this, size);
3116 memcpy(this, other, size);
3117 memcpy(other, tmp, size);
3120 #if JS_HAS_XDR
3122 #define NO_PARENT_INDEX ((uint32)-1)
3124 uint32
3125 FindObjectIndex(JSObjectArray *array, JSObject *obj)
3127 size_t i;
3129 if (array) {
3130 i = array->length;
3131 do {
3133 if (array->vector[--i] == obj)
3134 return i;
3135 } while (i != 0);
3138 return NO_PARENT_INDEX;
3141 JSBool
3142 js_XDRBlockObject(JSXDRState *xdr, JSObject **objp)
3144 JSContext *cx;
3145 uint32 parentId;
3146 JSObject *obj, *parent;
3147 uintN depth, count;
3148 uint32 depthAndCount;
3149 const Shape *shape;
3151 cx = xdr->cx;
3152 #ifdef __GNUC__
3153 obj = NULL; /* quell GCC overwarning */
3154 #endif
3156 if (xdr->mode == JSXDR_ENCODE) {
3157 obj = *objp;
3158 parent = obj->getParent();
3159 parentId = (xdr->script->objectsOffset == 0)
3160 ? NO_PARENT_INDEX
3161 : FindObjectIndex(xdr->script->objects(), parent);
3162 depth = (uint16)OBJ_BLOCK_DEPTH(cx, obj);
3163 count = (uint16)OBJ_BLOCK_COUNT(cx, obj);
3164 depthAndCount = (uint32)(depth << 16) | count;
3166 #ifdef __GNUC__ /* suppress bogus gcc warnings */
3167 else count = 0;
3168 #endif
3170 /* First, XDR the parent atomid. */
3171 if (!JS_XDRUint32(xdr, &parentId))
3172 return JS_FALSE;
3174 if (xdr->mode == JSXDR_DECODE) {
3175 obj = js_NewBlockObject(cx);
3176 if (!obj)
3177 return JS_FALSE;
3178 *objp = obj;
3181 * If there's a parent id, then get the parent out of our script's
3182 * object array. We know that we XDR block object in outer-to-inner
3183 * order, which means that getting the parent now will work.
3185 if (parentId == NO_PARENT_INDEX)
3186 parent = NULL;
3187 else
3188 parent = xdr->script->getObject(parentId);
3189 obj->setParent(parent);
3192 AutoObjectRooter tvr(cx, obj);
3194 if (!JS_XDRUint32(xdr, &depthAndCount))
3195 return false;
3197 if (xdr->mode == JSXDR_DECODE) {
3198 depth = (uint16)(depthAndCount >> 16);
3199 count = (uint16)depthAndCount;
3200 obj->setSlot(JSSLOT_BLOCK_DEPTH, Value(Int32Value(depth)));
3203 * XDR the block object's properties. We know that there are 'count'
3204 * properties to XDR, stored as id/shortid pairs.
3206 for (uintN i = 0; i < count; i++) {
3207 JSAtom *atom;
3208 uint16 shortid;
3210 /* XDR the real id, then the shortid. */
3211 if (!js_XDRAtom(xdr, &atom) || !JS_XDRUint16(xdr, &shortid))
3212 return false;
3214 if (!obj->defineBlockVariable(cx, ATOM_TO_JSID(atom), shortid))
3215 return false;
3217 } else {
3218 Vector<const Shape *, 8> shapes(cx);
3219 shapes.growByUninitialized(count);
3221 for (Shape::Range r(obj->lastProperty()); !r.empty(); r.popFront()) {
3222 shape = &r.front();
3223 shapes[shape->shortid] = shape;
3227 * XDR the block object's properties. We know that there are 'count'
3228 * properties to XDR, stored as id/shortid pairs.
3230 for (uintN i = 0; i < count; i++) {
3231 shape = shapes[i];
3232 JS_ASSERT(shape->getter() == block_getProperty);
3234 jsid propid = shape->id;
3235 JS_ASSERT(JSID_IS_ATOM(propid));
3236 JSAtom *atom = JSID_TO_ATOM(propid);
3238 uint16 shortid = uint16(shape->shortid);
3239 JS_ASSERT(shortid == i);
3241 /* XDR the real id, then the shortid. */
3242 if (!js_XDRAtom(xdr, &atom) || !JS_XDRUint16(xdr, &shortid))
3243 return false;
3246 return true;
3249 #endif
3251 Class js_BlockClass = {
3252 "Block",
3253 JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_IS_ANONYMOUS,
3254 PropertyStub, /* addProperty */
3255 PropertyStub, /* delProperty */
3256 PropertyStub, /* getProperty */
3257 PropertyStub, /* setProperty */
3258 EnumerateStub,
3259 ResolveStub,
3260 ConvertStub
3263 JSObject *
3264 js_InitObjectClass(JSContext *cx, JSObject *obj)
3266 JSObject *proto = js_InitClass(cx, obj, NULL, &js_ObjectClass, js_Object, 1,
3267 object_props, object_methods, NULL, object_static_methods);
3268 if (!proto)
3269 return NULL;
3271 /* ECMA (15.1.2.1) says 'eval' is a property of the global object. */
3272 if (!js_DefineFunction(cx, obj, cx->runtime->atomState.evalAtom, obj_eval, 1,
3273 JSFUN_STUB_GSOPS)) {
3274 return NULL;
3277 return proto;
3280 static bool
3281 DefineStandardSlot(JSContext *cx, JSObject *obj, JSProtoKey key, JSAtom *atom,
3282 const Value &v, uint32 attrs, bool &named)
3284 jsid id = ATOM_TO_JSID(atom);
3286 if (key != JSProto_Null) {
3288 * Initializing an actual standard class on a global object. If the
3289 * property is not yet present, force it into a new one bound to a
3290 * reserved slot. Otherwise, go through the normal property path.
3292 JS_ASSERT(obj->getClass()->flags & JSCLASS_IS_GLOBAL);
3293 JS_ASSERT(obj->isNative());
3295 JS_LOCK_OBJ(cx, obj);
3296 if (!obj->ensureClassReservedSlots(cx)) {
3297 JS_UNLOCK_OBJ(cx, obj);
3298 return false;
3301 const Shape *shape = obj->nativeLookup(id);
3302 if (!shape) {
3303 uint32 index = 2 * JSProto_LIMIT + key;
3304 if (!js_SetReservedSlot(cx, obj, index, v)) {
3305 JS_UNLOCK_OBJ(cx, obj);
3306 return false;
3309 uint32 slot = JSSLOT_START(obj->getClass()) + index;
3310 shape = obj->addProperty(cx, id, PropertyStub, PropertyStub, slot, attrs, 0, 0);
3312 JS_UNLOCK_OBJ(cx, obj);
3313 if (!shape)
3314 return false;
3316 named = true;
3317 return true;
3319 JS_UNLOCK_OBJ(cx, obj);
3322 named = obj->defineProperty(cx, id, v, PropertyStub, PropertyStub, attrs);
3323 return named;
3326 JSObject *
3327 js_InitClass(JSContext *cx, JSObject *obj, JSObject *parent_proto,
3328 Class *clasp, Native constructor, uintN nargs,
3329 JSPropertySpec *ps, JSFunctionSpec *fs,
3330 JSPropertySpec *static_ps, JSFunctionSpec *static_fs)
3332 JSAtom *atom;
3333 JSProtoKey key;
3334 JSFunction *fun;
3335 bool named = false;
3337 atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0);
3338 if (!atom)
3339 return NULL;
3342 * When initializing a standard class, if no parent_proto (grand-proto of
3343 * instances of the class, parent-proto of the class's prototype object)
3344 * is given, we must use Object.prototype if it is available. Otherwise,
3345 * we could look up the wrong binding for a class name in obj. Example:
3347 * String = Array;
3348 * print("hi there".join);
3350 * should print undefined, not Array.prototype.join. This is required by
3351 * ECMA-262, alas. It might have been better to make String readonly and
3352 * permanent in the global object, instead -- but that's too big a change
3353 * to swallow at this point.
3355 key = JSCLASS_CACHED_PROTO_KEY(clasp);
3356 if (key != JSProto_Null &&
3357 !parent_proto &&
3358 !js_GetClassPrototype(cx, obj, JSProto_Object, &parent_proto)) {
3359 return NULL;
3363 * Create a prototype object for this class.
3365 * FIXME: lazy standard (built-in) class initialization and even older
3366 * eager boostrapping code rely on all of these properties:
3368 * 1. NewObject attempting to compute a default prototype object when
3369 * passed null for proto; and
3371 * 2. NewObject tolerating no default prototype (null proto slot value)
3372 * due to this js_InitClass call coming from js_InitFunctionClass on an
3373 * otherwise-uninitialized global.
3375 * 3. NewObject allocating a JSFunction-sized GC-thing when clasp is
3376 * &js_FunctionClass, not a JSObject-sized (smaller) GC-thing.
3378 * The JS_NewObjectForGivenProto and JS_NewObject APIs also allow clasp to
3379 * be &js_FunctionClass (we could break compatibility easily). But fixing
3380 * (3) is not enough without addressing the bootstrapping dependency on (1)
3381 * and (2).
3383 JSObject *proto = NewObject<WithProto::Class>(cx, clasp, parent_proto, obj);
3384 if (!proto)
3385 return NULL;
3387 /* After this point, control must exit via label bad or out. */
3388 AutoObjectRooter tvr(cx, proto);
3390 JSObject *ctor;
3391 if (!constructor) {
3393 * Lacking a constructor, name the prototype (e.g., Math) unless this
3394 * class (a) is anonymous, i.e. for internal use only; (b) the class
3395 * of obj (the global object) is has a reserved slot indexed by key;
3396 * and (c) key is not the null key.
3398 if (!(clasp->flags & JSCLASS_IS_ANONYMOUS) ||
3399 !(obj->getClass()->flags & JSCLASS_IS_GLOBAL) ||
3400 key == JSProto_Null)
3402 uint32 attrs = (clasp->flags & JSCLASS_IS_ANONYMOUS)
3403 ? JSPROP_READONLY | JSPROP_PERMANENT
3404 : 0;
3405 if (!DefineStandardSlot(cx, obj, key, atom, ObjectValue(*proto), attrs, named))
3406 goto bad;
3409 ctor = proto;
3410 } else {
3411 fun = js_NewFunction(cx, NULL, constructor, nargs, JSFUN_CONSTRUCTOR, obj, atom);
3412 if (!fun)
3413 goto bad;
3415 AutoValueRooter tvr2(cx, ObjectValue(*fun));
3416 if (!DefineStandardSlot(cx, obj, key, atom, tvr2.value(), 0, named))
3417 goto bad;
3420 * Remember the class this function is a constructor for so that
3421 * we know to create an object of this class when we call the
3422 * constructor.
3424 FUN_CLASP(fun) = clasp;
3427 * Optionally construct the prototype object, before the class has
3428 * been fully initialized. Allow the ctor to replace proto with a
3429 * different object, as is done for operator new -- and as at least
3430 * XML support requires.
3432 ctor = FUN_OBJECT(fun);
3433 if (clasp->flags & JSCLASS_CONSTRUCT_PROTOTYPE) {
3434 Value rval;
3435 if (!InvokeConstructorWithGivenThis(cx, proto, ObjectOrNullValue(ctor),
3436 0, NULL, &rval)) {
3437 goto bad;
3439 if (rval.isObject() && &rval.toObject() != proto)
3440 proto = &rval.toObject();
3443 /* Connect constructor and prototype by named properties. */
3444 if (!js_SetClassPrototype(cx, ctor, proto,
3445 JSPROP_READONLY | JSPROP_PERMANENT)) {
3446 goto bad;
3449 /* Bootstrap Function.prototype (see also JS_InitStandardClasses). */
3450 if (ctor->getClass() == clasp)
3451 ctor->setProto(proto);
3454 /* Add properties and methods to the prototype and the constructor. */
3455 if ((ps && !JS_DefineProperties(cx, proto, ps)) ||
3456 (fs && !JS_DefineFunctions(cx, proto, fs)) ||
3457 (static_ps && !JS_DefineProperties(cx, ctor, static_ps)) ||
3458 (static_fs && !JS_DefineFunctions(cx, ctor, static_fs))) {
3459 goto bad;
3463 * Make sure proto's emptyShape is available to be shared by objects of
3464 * this class. JSObject::emptyShape is a one-slot cache. If we omit this,
3465 * some other class could snap it up. (The risk is particularly great for
3466 * Object.prototype.)
3468 * All callers of JSObject::initSharingEmptyShape depend on this.
3470 * FIXME: bug 592296 -- js_InitArrayClass should pass &js_SlowArrayClass
3471 * and make the Array.prototype slow from the start.
3473 JS_ASSERT_IF(proto->clasp != clasp,
3474 clasp == &js_ArrayClass && proto->clasp == &js_SlowArrayClass);
3475 if (!proto->getEmptyShape(cx, proto->clasp))
3476 goto bad;
3478 /* If this is a standard class, cache its prototype. */
3479 if (key != JSProto_Null && !js_SetClassObject(cx, obj, key, ctor, proto))
3480 goto bad;
3482 return proto;
3484 bad:
3485 if (named) {
3486 Value rval;
3487 obj->deleteProperty(cx, ATOM_TO_JSID(atom), &rval, false);
3489 return NULL;
3492 bool
3493 JSObject::allocSlots(JSContext *cx, size_t nslots)
3495 JS_ASSERT(!dslots);
3496 JS_ASSERT(nslots > JS_INITIAL_NSLOTS);
3498 size_t nwords = slotsToDynamicWords(nslots);
3499 dslots = (Value*) cx->malloc(nwords * sizeof(Value));
3500 if (!dslots)
3501 return false;
3503 dslots++;
3504 dslots[-1].setPrivateUint32(nslots);
3505 SetValueRangeToUndefined(dslots, nslots - JS_INITIAL_NSLOTS);
3506 return true;
3509 bool
3510 JSObject::growSlots(JSContext *cx, size_t nslots)
3513 * Minimal number of dynamic slots to allocate.
3515 const size_t MIN_DYNAMIC_WORDS = 4;
3518 * The limit to switch to linear allocation strategy from the power of 2
3519 * growth no to waste too much memory.
3521 const size_t LINEAR_GROWTH_STEP = JS_BIT(16);
3523 /* If we are allocating fslots, there is nothing to do. */
3524 if (nslots <= JS_INITIAL_NSLOTS)
3525 return true;
3527 /* Don't let nslots get close to wrapping around uint32. */
3528 if (nslots >= NSLOTS_LIMIT) {
3529 JS_ReportOutOfMemory(cx);
3530 return false;
3533 size_t nwords = slotsToDynamicWords(nslots);
3536 * Round up nslots so the number of bytes in dslots array is power
3537 * of 2 to ensure exponential grouth.
3539 uintN log;
3540 if (nwords <= MIN_DYNAMIC_WORDS) {
3541 nwords = MIN_DYNAMIC_WORDS;
3542 } else if (nwords < LINEAR_GROWTH_STEP) {
3543 JS_CEILING_LOG2(log, nwords);
3544 nwords = JS_BIT(log);
3545 } else {
3546 nwords = JS_ROUNDUP(nwords, LINEAR_GROWTH_STEP);
3548 nslots = dynamicWordsToSlots(nwords);
3551 * If nothing was allocated yet, treat it as initial allocation (but with
3552 * the exponential growth algorithm applied).
3554 if (!dslots)
3555 return allocSlots(cx, nslots);
3557 size_t oldnslots = dslots[-1].toPrivateUint32();
3559 Value *tmpdslots = (Value*) cx->realloc(dslots - 1, nwords * sizeof(Value));
3560 if (!tmpdslots)
3561 return false; /* leave dslots at its old size */
3563 dslots = tmpdslots;
3564 dslots++;
3565 dslots[-1].setPrivateUint32(nslots);
3567 /* Initialize the additional slots we added. */
3568 JS_ASSERT(nslots > oldnslots);
3569 Value *beg = dslots + (oldnslots - JS_INITIAL_NSLOTS);
3570 Value *end = dslots + (nslots - JS_INITIAL_NSLOTS);
3571 SetValueRangeToUndefined(beg, end);
3573 return true;
3576 void
3577 JSObject::shrinkSlots(JSContext *cx, size_t nslots)
3579 /* Nothing to shrink? */
3580 if (!dslots)
3581 return;
3583 JS_ASSERT(dslots[-1].toPrivateUint32() > JS_INITIAL_NSLOTS);
3584 JS_ASSERT(nslots <= dslots[-1].toPrivateUint32());
3586 if (nslots <= JS_INITIAL_NSLOTS) {
3587 freeSlotsArray(cx);
3588 dslots = NULL;
3589 } else {
3590 size_t nwords = slotsToDynamicWords(nslots);
3591 Value *tmpdslots = (Value*) cx->realloc(dslots - 1, nwords * sizeof(Value));
3592 if (!tmpdslots)
3593 return; /* leave dslots at its old size */
3595 dslots = tmpdslots;
3596 dslots++;
3597 dslots[-1].setPrivateUint32(nslots);
3601 bool
3602 JSObject::ensureInstanceReservedSlots(JSContext *cx, size_t nreserved)
3604 JS_ASSERT_IF(isNative(),
3605 isBlock() || isCall() || (isFunction() && getFunctionPrivate()->isBound()));
3607 uintN nslots = JSSLOT_FREE(clasp) + nreserved;
3608 return nslots <= numSlots() || allocSlots(cx, nslots);
3611 static JSObject *
3612 js_InitNullClass(JSContext *cx, JSObject *obj)
3614 JS_ASSERT(0);
3615 return NULL;
3618 #define JS_PROTO(name,code,init) extern JSObject *init(JSContext *, JSObject *);
3619 #include "jsproto.tbl"
3620 #undef JS_PROTO
3622 static JSObjectOp lazy_prototype_init[JSProto_LIMIT] = {
3623 #define JS_PROTO(name,code,init) init,
3624 #include "jsproto.tbl"
3625 #undef JS_PROTO
3628 namespace js {
3630 bool
3631 SetProto(JSContext *cx, JSObject *obj, JSObject *proto, bool checkForCycles)
3633 JS_ASSERT_IF(!checkForCycles, obj != proto);
3635 if (obj->isNative()) {
3636 JS_LOCK_OBJ(cx, obj);
3637 bool ok = obj->ensureClassReservedSlots(cx);
3638 JS_UNLOCK_OBJ(cx, obj);
3639 if (!ok)
3640 return false;
3644 * Regenerate property cache shape ids for all of the scopes along the
3645 * old prototype chain to invalidate their property cache entries, in
3646 * case any entries were filled by looking up through obj.
3648 JSObject *oldproto = obj;
3649 while (oldproto && oldproto->isNative()) {
3650 JS_LOCK_OBJ(cx, oldproto);
3651 oldproto->protoShapeChange(cx);
3652 JSObject *tmp = oldproto->getProto();
3653 JS_UNLOCK_OBJ(cx, oldproto);
3654 oldproto = tmp;
3657 if (!proto || !checkForCycles) {
3658 obj->setProto(proto);
3659 } else if (!SetProtoCheckingForCycles(cx, obj, proto)) {
3660 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CYCLIC_VALUE, js_proto_str);
3661 return false;
3663 return true;
3668 JSBool
3669 js_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key,
3670 JSObject **objp)
3672 JSObject *tmp, *cobj;
3673 JSResolvingKey rkey;
3674 JSResolvingEntry *rentry;
3675 uint32 generation;
3676 JSObjectOp init;
3677 Value v;
3679 while ((tmp = obj->getParent()) != NULL)
3680 obj = tmp;
3681 if (!(obj->getClass()->flags & JSCLASS_IS_GLOBAL)) {
3682 *objp = NULL;
3683 return JS_TRUE;
3686 v = obj->getReservedSlot(key);
3687 if (v.isObject()) {
3688 *objp = &v.toObject();
3689 return JS_TRUE;
3692 rkey.obj = obj;
3693 rkey.id = ATOM_TO_JSID(cx->runtime->atomState.classAtoms[key]);
3694 if (!js_StartResolving(cx, &rkey, JSRESFLAG_LOOKUP, &rentry))
3695 return JS_FALSE;
3696 if (!rentry) {
3697 /* Already caching key in obj -- suppress recursion. */
3698 *objp = NULL;
3699 return JS_TRUE;
3701 generation = cx->resolvingTable->generation;
3703 JSBool ok = true;
3704 cobj = NULL;
3705 init = lazy_prototype_init[key];
3706 if (init) {
3707 if (!init(cx, obj)) {
3708 ok = JS_FALSE;
3709 } else {
3710 v = obj->getReservedSlot(key);
3711 if (v.isObject())
3712 cobj = &v.toObject();
3716 js_StopResolving(cx, &rkey, JSRESFLAG_LOOKUP, rentry, generation);
3717 *objp = cobj;
3718 return ok;
3721 JSBool
3722 js_SetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, JSObject *cobj, JSObject *proto)
3724 JS_ASSERT(!obj->getParent());
3725 if (!(obj->getClass()->flags & JSCLASS_IS_GLOBAL))
3726 return JS_TRUE;
3728 return js_SetReservedSlot(cx, obj, key, ObjectOrNullValue(cobj)) &&
3729 js_SetReservedSlot(cx, obj, JSProto_LIMIT + key, ObjectOrNullValue(proto));
3732 JSBool
3733 js_FindClassObject(JSContext *cx, JSObject *start, JSProtoKey protoKey,
3734 Value *vp, Class *clasp)
3736 JSStackFrame *fp;
3737 JSObject *obj, *cobj, *pobj;
3738 jsid id;
3739 JSProperty *prop;
3740 const Shape *shape;
3743 * Find the global object. Use cx->fp() directly to avoid falling off
3744 * trace; all JIT-elided stack frames have the same global object as
3745 * cx->fp().
3747 VOUCH_DOES_NOT_REQUIRE_STACK();
3748 if (!start && (fp = cx->maybefp()) != NULL)
3749 start = &fp->scopeChain();
3751 if (start) {
3752 /* Find the topmost object in the scope chain. */
3753 do {
3754 obj = start;
3755 start = obj->getParent();
3756 } while (start);
3757 } else {
3758 obj = cx->globalObject;
3759 if (!obj) {
3760 vp->setUndefined();
3761 return JS_TRUE;
3765 OBJ_TO_INNER_OBJECT(cx, obj);
3766 if (!obj)
3767 return JS_FALSE;
3769 if (protoKey != JSProto_Null) {
3770 JS_ASSERT(JSProto_Null < protoKey);
3771 JS_ASSERT(protoKey < JSProto_LIMIT);
3772 if (!js_GetClassObject(cx, obj, protoKey, &cobj))
3773 return JS_FALSE;
3774 if (cobj) {
3775 vp->setObject(*cobj);
3776 return JS_TRUE;
3778 id = ATOM_TO_JSID(cx->runtime->atomState.classAtoms[protoKey]);
3779 } else {
3780 JSAtom *atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0);
3781 if (!atom)
3782 return false;
3783 id = ATOM_TO_JSID(atom);
3786 JS_ASSERT(obj->isNative());
3787 if (js_LookupPropertyWithFlags(cx, obj, id, JSRESOLVE_CLASSNAME,
3788 &pobj, &prop) < 0) {
3789 return JS_FALSE;
3791 Value v = UndefinedValue();
3792 if (prop && pobj->isNative()) {
3793 shape = (Shape *) prop;
3794 if (pobj->containsSlot(shape->slot)) {
3795 v = pobj->lockedGetSlot(shape->slot);
3796 if (v.isPrimitive())
3797 v.setUndefined();
3799 JS_UNLOCK_OBJ(cx, pobj);
3801 *vp = v;
3802 return JS_TRUE;
3805 JSObject *
3806 js_ConstructObject(JSContext *cx, Class *clasp, JSObject *proto, JSObject *parent,
3807 uintN argc, Value *argv)
3809 AutoArrayRooter argtvr(cx, argc, argv);
3811 JSProtoKey protoKey = GetClassProtoKey(clasp);
3813 /* Protect constructor in case a crazy getter for .prototype uproots it. */
3814 AutoValueRooter tvr(cx);
3815 if (!js_FindClassObject(cx, parent, protoKey, tvr.addr(), clasp))
3816 return NULL;
3818 const Value &cval = tvr.value();
3819 if (tvr.value().isPrimitive()) {
3820 js_ReportIsNotFunction(cx, tvr.addr(), JSV2F_CONSTRUCT | JSV2F_SEARCH_STACK);
3821 return NULL;
3825 * If proto is NULL, set it to Constructor.prototype, just like JSOP_NEW
3826 * does, likewise for the new object's parent.
3828 JSObject *ctor = &cval.toObject();
3829 if (!parent)
3830 parent = ctor->getParent();
3831 if (!proto) {
3832 Value rval;
3833 if (!ctor->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom),
3834 &rval)) {
3835 return NULL;
3837 if (rval.isObjectOrNull())
3838 proto = rval.toObjectOrNull();
3841 JSObject *obj = NewObject<WithProto::Class>(cx, clasp, proto, parent);
3842 if (!obj)
3843 return NULL;
3845 Value rval;
3846 if (!InvokeConstructorWithGivenThis(cx, obj, cval, argc, argv, &rval))
3847 return NULL;
3849 if (rval.isPrimitive())
3850 return obj;
3853 * If the instance's class differs from what was requested, throw a type
3854 * error. If the given class has both the JSCLASS_HAS_PRIVATE and the
3855 * JSCLASS_CONSTRUCT_PROTOTYPE flags, and the instance does not have its
3856 * private data set at this point, then the constructor was replaced and
3857 * we should throw a type error.
3859 obj = &rval.toObject();
3860 if (obj->getClass() != clasp ||
3861 (!(~clasp->flags & (JSCLASS_HAS_PRIVATE |
3862 JSCLASS_CONSTRUCT_PROTOTYPE)) &&
3863 !obj->getPrivate())) {
3864 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3865 JSMSG_WRONG_CONSTRUCTOR, clasp->name);
3866 return NULL;
3868 return obj;
3871 bool
3872 JSObject::allocSlot(JSContext *cx, uint32 *slotp)
3874 uint32 slot = slotSpan();
3875 JS_ASSERT(slot >= JSSLOT_FREE(clasp));
3878 * If this object is in dictionary mode and it has a property table, try to
3879 * pull a free slot from the property table's slot-number freelist.
3881 if (inDictionaryMode() && lastProp->table) {
3882 uint32 &last = lastProp->table->freelist;
3883 if (last != SHAPE_INVALID_SLOT) {
3884 #ifdef DEBUG
3885 JS_ASSERT(last < slot);
3886 uint32 next = getSlot(last).toPrivateUint32();
3887 JS_ASSERT_IF(next != SHAPE_INVALID_SLOT, next < slot);
3888 #endif
3890 *slotp = last;
3892 Value &vref = getSlotRef(last);
3893 last = vref.toPrivateUint32();
3894 vref.setUndefined();
3895 return true;
3899 if (slot >= numSlots() && !growSlots(cx, slot + 1))
3900 return false;
3902 /* JSObject::growSlots or JSObject::freeSlot should set the free slots to void. */
3903 JS_ASSERT(getSlot(slot).isUndefined());
3904 *slotp = slot;
3905 return true;
3908 void
3909 JSObject::freeSlot(JSContext *cx, uint32 slot)
3911 uint32 limit = slotSpan();
3912 JS_ASSERT(slot < limit);
3914 Value &vref = getSlotRef(slot);
3915 if (inDictionaryMode() && lastProp->table) {
3916 uint32 &last = lastProp->table->freelist;
3918 /* Can't afford to check the whole freelist, but let's check the head. */
3919 JS_ASSERT_IF(last != SHAPE_INVALID_SLOT, last < limit && last != slot);
3922 * Freeing a slot other than the last one mapped by this object's
3923 * shape: push the slot onto the dictionary table's freelist. We want
3924 * to let the last slot be freed by shrinking the dslots vector; see
3925 * js_TraceObject.
3927 if (slot + 1 < limit) {
3928 JS_ASSERT_IF(last != SHAPE_INVALID_SLOT, last < slotSpan());
3929 vref.setPrivateUint32(last);
3930 last = slot;
3931 return;
3934 vref.setUndefined();
3937 /* JSBOXEDWORD_INT_MAX as a string */
3938 #define JSBOXEDWORD_INT_MAX_STRING "1073741823"
3941 * Convert string indexes that convert to int jsvals as ints to save memory.
3942 * Care must be taken to use this macro every time a property name is used, or
3943 * else double-sets, incorrect property cache misses, or other mistakes could
3944 * occur.
3946 jsid
3947 js_CheckForStringIndex(jsid id)
3949 if (!JSID_IS_ATOM(id))
3950 return id;
3952 JSAtom *atom = JSID_TO_ATOM(id);
3953 JSString *str = ATOM_TO_STRING(atom);
3954 const jschar *s = str->flatChars();
3955 jschar ch = *s;
3957 JSBool negative = (ch == '-');
3958 if (negative)
3959 ch = *++s;
3961 if (!JS7_ISDEC(ch))
3962 return id;
3964 size_t n = str->flatLength() - negative;
3965 if (n > sizeof(JSBOXEDWORD_INT_MAX_STRING) - 1)
3966 return id;
3968 const jschar *cp = s;
3969 const jschar *end = s + n;
3971 jsuint index = JS7_UNDEC(*cp++);
3972 jsuint oldIndex = 0;
3973 jsuint c = 0;
3975 if (index != 0) {
3976 while (JS7_ISDEC(*cp)) {
3977 oldIndex = index;
3978 c = JS7_UNDEC(*cp);
3979 index = 10 * index + c;
3980 cp++;
3985 * Non-integer indexes can't be represented as integers. Also, distinguish
3986 * index "-0" from "0", because JSBOXEDWORD_INT cannot.
3988 if (cp != end || (negative && index == 0))
3989 return id;
3991 if (oldIndex < JSID_INT_MAX / 10 ||
3992 (oldIndex == JSID_INT_MAX / 10 && c <= (JSID_INT_MAX % 10))) {
3993 if (negative)
3994 index = 0 - index;
3995 id = INT_TO_JSID((jsint)index);
3998 return id;
4001 static JSBool
4002 PurgeProtoChain(JSContext *cx, JSObject *obj, jsid id)
4004 const Shape *shape;
4006 while (obj) {
4007 if (!obj->isNative()) {
4008 obj = obj->getProto();
4009 continue;
4011 JS_LOCK_OBJ(cx, obj);
4012 shape = obj->nativeLookup(id);
4013 if (shape) {
4014 PCMETER(JS_PROPERTY_CACHE(cx).pcpurges++);
4015 obj->shadowingShapeChange(cx, *shape);
4016 JS_UNLOCK_OBJ(cx, obj);
4018 if (!obj->getParent()) {
4020 * All scope chains end in a global object, so this will change
4021 * the global shape. jstracer.cpp assumes that the global shape
4022 * never changes on trace, so we must deep-bail here.
4024 LeaveTrace(cx);
4026 return JS_TRUE;
4028 #ifdef JS_THREADSAFE
4029 JSObject *pobj = obj;
4030 #endif
4031 obj = obj->getProto();
4032 JS_UNLOCK_OBJ(cx, pobj);
4034 return JS_FALSE;
4037 void
4038 js_PurgeScopeChainHelper(JSContext *cx, JSObject *obj, jsid id)
4040 JS_ASSERT(obj->isDelegate());
4041 PurgeProtoChain(cx, obj->getProto(), id);
4044 * We must purge the scope chain only for Call objects as they are the only
4045 * kind of cacheable non-global object that can gain properties after outer
4046 * properties with the same names have been cached or traced. Call objects
4047 * may gain such properties via eval introducing new vars; see bug 490364.
4049 if (obj->isCall()) {
4050 while ((obj = obj->getParent()) != NULL) {
4051 if (PurgeProtoChain(cx, obj, id))
4052 break;
4057 const Shape *
4058 js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id,
4059 PropertyOp getter, PropertyOp setter, uint32 slot,
4060 uintN attrs, uintN flags, intN shortid)
4062 const Shape *shape;
4064 JS_ASSERT(!(flags & Shape::METHOD));
4067 * Purge the property cache of now-shadowed id in obj's scope chain. Do
4068 * this optimistically (assuming no failure below) before locking obj, so
4069 * we can lock the shadowed scope.
4071 js_PurgeScopeChain(cx, obj, id);
4073 JS_LOCK_OBJ(cx, obj);
4074 if (!obj->ensureClassReservedSlots(cx)) {
4075 shape = NULL;
4076 } else {
4077 /* Convert string indices to integers if appropriate. */
4078 id = js_CheckForStringIndex(id);
4079 shape = obj->putProperty(cx, id, getter, setter, slot, attrs, flags, shortid);
4081 JS_UNLOCK_OBJ(cx, obj);
4082 return shape;
4085 const Shape *
4086 js_ChangeNativePropertyAttrs(JSContext *cx, JSObject *obj,
4087 const Shape *shape, uintN attrs, uintN mask,
4088 PropertyOp getter, PropertyOp setter)
4090 JS_LOCK_OBJ(cx, obj);
4091 shape = obj->ensureClassReservedSlots(cx)
4092 ? obj->changeProperty(cx, shape, attrs, mask, getter, setter)
4093 : NULL;
4094 JS_UNLOCK_OBJ(cx, obj);
4095 return shape;
4098 JSBool
4099 js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, const Value *value,
4100 PropertyOp getter, PropertyOp setter, uintN attrs)
4102 return js_DefineNativeProperty(cx, obj, id, *value, getter, setter, attrs,
4103 0, 0, NULL);
4107 * Backward compatibility requires allowing addProperty hooks to mutate the
4108 * nominal initial value of a slotful property, while GC safety wants that
4109 * value to be stored before the call-out through the hook. Optimize to do
4110 * both while saving cycles for classes that stub their addProperty hook.
4112 static inline bool
4113 CallAddPropertyHook(JSContext *cx, Class *clasp, JSObject *obj, const Shape *shape, Value *vp)
4115 if (clasp->addProperty != PropertyStub) {
4116 Value nominal = *vp;
4118 if (!CallJSPropertyOp(cx, clasp->addProperty, obj, SHAPE_USERID(shape), vp))
4119 return false;
4120 if (*vp != nominal) {
4121 if (obj->containsSlot(shape->slot))
4122 obj->lockedSetSlot(shape->slot, *vp);
4125 return true;
4128 JSBool
4129 js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, const Value &value,
4130 PropertyOp getter, PropertyOp setter, uintN attrs,
4131 uintN flags, intN shortid, JSProperty **propp,
4132 uintN defineHow /* = 0 */)
4134 Class *clasp;
4135 const Shape *shape;
4136 JSBool added;
4137 Value valueCopy;
4139 JS_ASSERT((defineHow & ~(JSDNP_CACHE_RESULT | JSDNP_DONT_PURGE | JSDNP_SET_METHOD)) == 0);
4140 LeaveTraceIfGlobalObject(cx, obj);
4142 /* Convert string indices to integers if appropriate. */
4143 id = js_CheckForStringIndex(id);
4146 * If defining a getter or setter, we must check for its counterpart and
4147 * update the attributes and property ops. A getter or setter is really
4148 * only half of a property.
4150 shape = NULL;
4151 if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
4152 JSObject *pobj;
4153 JSProperty *prop;
4156 * If JS_THREADSAFE and id is found, js_LookupProperty returns with
4157 * shape non-null and pobj locked. If pobj == obj, the property is
4158 * already in obj and obj has its own (mutable) scope. So if we are
4159 * defining a getter whose setter was already defined, or vice versa,
4160 * finish the job via obj->changeProperty, and refresh the property
4161 * cache line for (obj, id) to map shape.
4163 if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
4164 return JS_FALSE;
4165 shape = (Shape *) prop;
4166 if (shape && pobj == obj && shape->isAccessorDescriptor()) {
4167 shape = obj->changeProperty(cx, shape, attrs,
4168 JSPROP_GETTER | JSPROP_SETTER,
4169 (attrs & JSPROP_GETTER)
4170 ? getter
4171 : shape->getter(),
4172 (attrs & JSPROP_SETTER)
4173 ? setter
4174 : shape->setter());
4176 /* NB: obj == pobj, so we can share unlock code at the bottom. */
4177 if (!shape)
4178 goto error;
4179 } else if (prop) {
4180 pobj->dropProperty(cx, prop);
4181 prop = NULL;
4182 shape = NULL;
4187 * Purge the property cache of any properties named by id that are about
4188 * to be shadowed in obj's scope chain unless it is known a priori that it
4189 * is not possible. We do this before locking obj to avoid nesting locks.
4191 if (!(defineHow & JSDNP_DONT_PURGE))
4192 js_PurgeScopeChain(cx, obj, id);
4195 * Check whether a readonly property or setter is being defined on a known
4196 * prototype object. See the comment in jscntxt.h before protoHazardShape's
4197 * member declaration.
4199 if (obj->isDelegate() && (attrs & (JSPROP_READONLY | JSPROP_SETTER)))
4200 cx->runtime->protoHazardShape = js_GenerateShape(cx, false);
4202 /* Lock if object locking is required by this implementation. */
4203 JS_LOCK_OBJ(cx, obj);
4205 /* Use the object's class getter and setter by default. */
4206 clasp = obj->getClass();
4207 if (!(defineHow & JSDNP_SET_METHOD)) {
4208 if (!getter && !(attrs & JSPROP_GETTER))
4209 getter = clasp->getProperty;
4210 if (!setter && !(attrs & JSPROP_SETTER))
4211 setter = clasp->setProperty;
4214 /* Get obj's own scope if it has one, or create a new one for obj. */
4215 if (!obj->ensureClassReservedSlots(cx))
4216 goto error;
4218 added = false;
4219 if (!shape) {
4220 /* Add a new property, or replace an existing one of the same id. */
4221 if (defineHow & JSDNP_SET_METHOD) {
4222 JS_ASSERT(clasp == &js_ObjectClass);
4223 JS_ASSERT(IsFunctionObject(value));
4224 JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
4225 JS_ASSERT(!getter && !setter);
4227 JSObject *funobj = &value.toObject();
4228 if (FUN_OBJECT(GET_FUNCTION_PRIVATE(cx, funobj)) == funobj) {
4229 flags |= Shape::METHOD;
4230 getter = CastAsPropertyOp(funobj);
4234 added = !obj->nativeContains(id);
4235 uint32 oldShape = obj->shape();
4236 shape = obj->putProperty(cx, id, getter, setter, SHAPE_INVALID_SLOT,
4237 attrs, flags, shortid);
4238 if (!shape)
4239 goto error;
4242 * If shape is a method, the above call to putProperty suffices to
4243 * update the shape if necessary. But if scope->branded(), the shape
4244 * may not have changed and we may be overwriting a function-valued
4245 * property. See bug 560998.
4247 if (obj->shape() == oldShape && obj->branded() && shape->slot != SHAPE_INVALID_SLOT)
4248 obj->methodWriteBarrier(cx, shape->slot, value);
4251 /* Store value before calling addProperty, in case the latter GC's. */
4252 if (obj->containsSlot(shape->slot))
4253 obj->lockedSetSlot(shape->slot, value);
4255 /* XXXbe called with lock held */
4256 valueCopy = value;
4257 if (!CallAddPropertyHook(cx, clasp, obj, shape, &valueCopy)) {
4258 obj->removeProperty(cx, id);
4259 goto error;
4262 if (defineHow & JSDNP_CACHE_RESULT) {
4263 #ifdef JS_TRACER
4264 JS_ASSERT_NOT_ON_TRACE(cx);
4265 PropertyCacheEntry *entry =
4266 #endif
4267 JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, 0, obj, shape, added);
4268 TRACE_2(SetPropHit, entry, shape);
4270 if (propp)
4271 *propp = (JSProperty *) shape;
4272 else
4273 JS_UNLOCK_OBJ(cx, obj);
4274 return JS_TRUE;
4276 error: // TRACE_2 jumps here on error, as well.
4277 JS_UNLOCK_OBJ(cx, obj);
4278 return JS_FALSE;
4281 JS_FRIEND_API(JSBool)
4282 js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
4283 JSProperty **propp)
4285 return js_LookupPropertyWithFlags(cx, obj, id, cx->resolveFlags,
4286 objp, propp) >= 0;
4289 #define SCOPE_DEPTH_ACCUM(bs,val) \
4290 JS_SCOPE_DEPTH_METERING(JS_BASIC_STATS_ACCUM(bs, val))
4293 * Call obj's resolve hook. obj is a native object and the caller holds its
4294 * scope lock.
4296 * cx, start, id, and flags are the parameters initially passed to the ongoing
4297 * lookup; objp and propp are its out parameters. obj is an object along
4298 * start's prototype chain.
4300 * There are four possible outcomes:
4302 * - On failure, report an error or exception, unlock obj, and return false.
4304 * - If we are alrady resolving a property of *curobjp, set *recursedp = true,
4305 * unlock obj, and return true.
4307 * - If the resolve hook finds or defines the sought property, set *objp and
4308 * *propp appropriately, set *recursedp = false, and return true with *objp's
4309 * lock held.
4311 * - Otherwise no property was resolved. Set *propp = NULL and *recursedp = false
4312 * and return true.
4314 static JSBool
4315 CallResolveOp(JSContext *cx, JSObject *start, JSObject *obj, jsid id, uintN flags,
4316 JSObject **objp, JSProperty **propp, bool *recursedp)
4318 Class *clasp = obj->getClass();
4319 JSResolveOp resolve = clasp->resolve;
4322 * Avoid recursion on (obj, id) already being resolved on cx.
4324 * Once we have successfully added an entry for (obj, key) to
4325 * cx->resolvingTable, control must go through cleanup: before
4326 * returning. But note that JS_DHASH_ADD may find an existing
4327 * entry, in which case we bail to suppress runaway recursion.
4329 JSResolvingKey key = {obj, id};
4330 JSResolvingEntry *entry;
4331 if (!js_StartResolving(cx, &key, JSRESFLAG_LOOKUP, &entry)) {
4332 JS_UNLOCK_OBJ(cx, obj);
4333 return false;
4335 if (!entry) {
4336 /* Already resolving id in obj -- suppress recursion. */
4337 JS_UNLOCK_OBJ(cx, obj);
4338 *recursedp = true;
4339 return true;
4341 uint32 generation = cx->resolvingTable->generation;
4342 *recursedp = false;
4344 *propp = NULL;
4346 JSBool ok;
4347 const Shape *shape = NULL;
4348 if (clasp->flags & JSCLASS_NEW_RESOLVE) {
4349 JSNewResolveOp newresolve = (JSNewResolveOp)resolve;
4350 if (flags == JSRESOLVE_INFER)
4351 flags = js_InferFlags(cx, 0);
4352 JSObject *obj2 = (clasp->flags & JSCLASS_NEW_RESOLVE_GETS_START) ? start : NULL;
4353 JS_UNLOCK_OBJ(cx, obj);
4356 /* Protect id and all atoms from a GC nested in resolve. */
4357 AutoKeepAtoms keep(cx->runtime);
4358 ok = newresolve(cx, obj, id, flags, &obj2);
4360 if (!ok)
4361 goto cleanup;
4363 JS_LOCK_OBJ(cx, obj);
4364 if (obj2) {
4365 /* Resolved: juggle locks and lookup id again. */
4366 if (obj2 != obj) {
4367 JS_UNLOCK_OBJ(cx, obj);
4368 if (obj2->isNative())
4369 JS_LOCK_OBJ(cx, obj2);
4371 if (!obj2->isNative()) {
4372 /* Whoops, newresolve handed back a foreign obj2. */
4373 JS_ASSERT(obj2 != obj);
4374 ok = obj2->lookupProperty(cx, id, objp, propp);
4375 if (!ok || *propp)
4376 goto cleanup;
4377 JS_LOCK_OBJ(cx, obj2);
4378 } else {
4380 * Require that obj2 not be empty now, as we do for old-style
4381 * resolve. If it doesn't, then id was not truly resolved, and
4382 * we'll find it in the proto chain, or miss it if obj2's proto
4383 * is not on obj's proto chain. That last case is a "too bad!"
4384 * case.
4386 if (!obj2->nativeEmpty())
4387 shape = obj2->nativeLookup(id);
4389 if (shape) {
4390 JS_ASSERT(!obj2->nativeEmpty());
4391 obj = obj2;
4392 } else if (obj2 != obj) {
4393 if (obj2->isNative())
4394 JS_UNLOCK_OBJ(cx, obj2);
4395 JS_LOCK_OBJ(cx, obj);
4398 } else {
4400 * Old resolve always requires id re-lookup if obj is not empty after
4401 * resolve returns.
4403 JS_UNLOCK_OBJ(cx, obj);
4404 ok = resolve(cx, obj, id);
4405 if (!ok)
4406 goto cleanup;
4407 JS_LOCK_OBJ(cx, obj);
4408 JS_ASSERT(obj->isNative());
4409 if (!obj->nativeEmpty())
4410 shape = obj->nativeLookup(id);
4413 cleanup:
4414 if (ok && shape) {
4415 *objp = obj;
4416 *propp = (JSProperty *) shape;
4418 js_StopResolving(cx, &key, JSRESFLAG_LOOKUP, entry, generation);
4419 return ok;
4423 js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags,
4424 JSObject **objp, JSProperty **propp)
4426 /* Convert string indices to integers if appropriate. */
4427 id = js_CheckForStringIndex(id);
4429 /* Search scopes starting with obj and following the prototype link. */
4430 JSObject *start = obj;
4431 int protoIndex;
4432 for (protoIndex = 0; ; protoIndex++) {
4433 JS_LOCK_OBJ(cx, obj);
4434 const Shape *shape = obj->nativeLookup(id);
4435 if (shape) {
4436 SCOPE_DEPTH_ACCUM(&cx->runtime->protoLookupDepthStats, protoIndex);
4437 *objp = obj;
4438 *propp = (JSProperty *) shape;
4439 return protoIndex;
4442 /* Try obj's class resolve hook if id was not found in obj's scope. */
4443 if (!shape && obj->getClass()->resolve != JS_ResolveStub) {
4444 bool recursed;
4445 if (!CallResolveOp(cx, start, obj, id, flags, objp, propp, &recursed))
4446 return -1;
4447 if (recursed)
4448 break;
4449 if (*propp) {
4450 /* Recalculate protoIndex in case it was resolved on some other object. */
4451 protoIndex = 0;
4452 for (JSObject *proto = start; proto && proto != *objp; proto = proto->getProto())
4453 protoIndex++;
4454 SCOPE_DEPTH_ACCUM(&cx->runtime->protoLookupDepthStats, protoIndex);
4455 return protoIndex;
4459 JSObject *proto = obj->getProto();
4460 JS_UNLOCK_OBJ(cx, obj);
4461 if (!proto)
4462 break;
4463 if (!proto->isNative()) {
4464 if (!proto->lookupProperty(cx, id, objp, propp))
4465 return -1;
4466 return protoIndex + 1;
4469 obj = proto;
4472 *objp = NULL;
4473 *propp = NULL;
4474 return protoIndex;
4477 PropertyCacheEntry *
4478 js_FindPropertyHelper(JSContext *cx, jsid id, JSBool cacheResult,
4479 JSObject **objp, JSObject **pobjp, JSProperty **propp)
4481 JSObject *scopeChain, *obj, *parent, *pobj;
4482 PropertyCacheEntry *entry;
4483 int scopeIndex, protoIndex;
4484 JSProperty *prop;
4486 JS_ASSERT_IF(cacheResult, !JS_ON_TRACE(cx));
4487 scopeChain = &js_GetTopStackFrame(cx)->scopeChain();
4489 /* Scan entries on the scope chain that we can cache across. */
4490 entry = JS_NO_PROP_CACHE_FILL;
4491 obj = scopeChain;
4492 parent = obj->getParent();
4493 for (scopeIndex = 0;
4494 parent
4495 ? js_IsCacheableNonGlobalScope(obj)
4496 : !obj->getOps()->lookupProperty;
4497 ++scopeIndex) {
4498 protoIndex =
4499 js_LookupPropertyWithFlags(cx, obj, id, cx->resolveFlags,
4500 &pobj, &prop);
4501 if (protoIndex < 0)
4502 return NULL;
4504 if (prop) {
4505 #ifdef DEBUG
4506 if (parent) {
4507 Class *clasp = obj->getClass();
4508 JS_ASSERT(pobj->isNative());
4509 JS_ASSERT(pobj->getClass() == clasp);
4510 if (clasp == &js_BlockClass) {
4512 * A block instance on the scope chain is immutable and it
4513 * shares its shapes with its compile-time prototype.
4515 JS_ASSERT(pobj == obj);
4516 JS_ASSERT(pobj->isClonedBlock());
4517 JS_ASSERT(protoIndex == 0);
4518 } else {
4519 /* Call and DeclEnvClass objects have no prototypes. */
4520 JS_ASSERT(!obj->getProto());
4521 JS_ASSERT(protoIndex == 0);
4523 } else {
4524 JS_ASSERT(obj->isNative());
4526 #endif
4528 * We must check if pobj is native as a global object can have
4529 * non-native prototype.
4531 if (cacheResult && pobj->isNative()) {
4532 entry = JS_PROPERTY_CACHE(cx).fill(cx, scopeChain, scopeIndex,
4533 protoIndex, pobj,
4534 (Shape *) prop);
4536 SCOPE_DEPTH_ACCUM(&cx->runtime->scopeSearchDepthStats, scopeIndex);
4537 goto out;
4540 if (!parent) {
4541 pobj = NULL;
4542 goto out;
4544 obj = parent;
4545 parent = obj->getParent();
4548 for (;;) {
4549 if (!obj->lookupProperty(cx, id, &pobj, &prop))
4550 return NULL;
4551 if (prop) {
4552 PCMETER(JS_PROPERTY_CACHE(cx).nofills++);
4553 goto out;
4557 * We conservatively assume that a resolve hook could mutate the scope
4558 * chain during JSObject::lookupProperty. So we read parent here again.
4560 parent = obj->getParent();
4561 if (!parent) {
4562 pobj = NULL;
4563 break;
4565 obj = parent;
4568 out:
4569 JS_ASSERT(!!pobj == !!prop);
4570 *objp = obj;
4571 *pobjp = pobj;
4572 *propp = prop;
4573 return entry;
4576 JS_FRIEND_API(JSBool)
4577 js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp,
4578 JSProperty **propp)
4580 return !!js_FindPropertyHelper(cx, id, false, objp, pobjp, propp);
4583 JSObject *
4584 js_FindIdentifierBase(JSContext *cx, JSObject *scopeChain, jsid id)
4587 * This function should not be called for a global object or from the
4588 * trace and should have a valid cache entry for native scopeChain.
4590 JS_ASSERT(scopeChain->getParent());
4591 JS_ASSERT(!JS_ON_TRACE(cx));
4593 JSObject *obj = scopeChain;
4596 * Loop over cacheable objects on the scope chain until we find a
4597 * property. We also stop when we reach the global object skipping any
4598 * farther checks or lookups. For details see the JSOP_BINDNAME case of
4599 * js_Interpret.
4601 * The test order here matters because js_IsCacheableNonGlobalScope
4602 * must not be passed a global object (i.e. one with null parent).
4604 for (int scopeIndex = 0;
4605 !obj->getParent() || js_IsCacheableNonGlobalScope(obj);
4606 scopeIndex++) {
4607 JSObject *pobj;
4608 JSProperty *prop;
4609 int protoIndex = js_LookupPropertyWithFlags(cx, obj, id,
4610 cx->resolveFlags,
4611 &pobj, &prop);
4612 if (protoIndex < 0)
4613 return NULL;
4614 if (prop) {
4615 if (!pobj->isNative()) {
4616 JS_ASSERT(!obj->getParent());
4617 return obj;
4619 JS_ASSERT_IF(obj->getParent(), pobj->getClass() == obj->getClass());
4620 #ifdef DEBUG
4621 PropertyCacheEntry *entry =
4622 #endif
4623 JS_PROPERTY_CACHE(cx).fill(cx, scopeChain, scopeIndex, protoIndex, pobj,
4624 (Shape *) prop);
4625 JS_ASSERT(entry);
4626 JS_UNLOCK_OBJ(cx, pobj);
4627 return obj;
4630 JSObject *parent = obj->getParent();
4631 if (!parent)
4632 return obj;
4633 obj = parent;
4636 /* Loop until we find a property or reach the global object. */
4637 do {
4638 JSObject *pobj;
4639 JSProperty *prop;
4640 if (!obj->lookupProperty(cx, id, &pobj, &prop))
4641 return NULL;
4642 if (prop) {
4643 pobj->dropProperty(cx, prop);
4644 break;
4648 * We conservatively assume that a resolve hook could mutate the scope
4649 * chain during JSObject::lookupProperty. So we must check if parent is
4650 * not null here even if it wasn't before the lookup.
4652 JSObject *parent = obj->getParent();
4653 if (!parent)
4654 break;
4655 obj = parent;
4656 } while (obj->getParent());
4657 return obj;
4660 JSBool
4661 js_NativeGet(JSContext *cx, JSObject *obj, JSObject *pobj, const Shape *shape, uintN getHow,
4662 Value *vp)
4664 LeaveTraceIfGlobalObject(cx, pobj);
4666 uint32 slot;
4667 int32 sample;
4669 JS_ASSERT(pobj->isNative());
4670 JS_ASSERT(JS_IS_OBJ_LOCKED(cx, pobj));
4672 slot = shape->slot;
4673 if (slot != SHAPE_INVALID_SLOT)
4674 *vp = pobj->lockedGetSlot(slot);
4675 else
4676 vp->setUndefined();
4677 if (shape->hasDefaultGetter())
4678 return true;
4680 if (JS_UNLIKELY(shape->isMethod()) && (getHow & JSGET_NO_METHOD_BARRIER)) {
4681 JS_ASSERT(&shape->methodObject() == &vp->toObject());
4682 return true;
4685 sample = cx->runtime->propertyRemovals;
4686 JS_UNLOCK_OBJ(cx, pobj);
4688 AutoShapeRooter tvr(cx, shape);
4689 AutoObjectRooter tvr2(cx, pobj);
4690 if (!shape->get(cx, obj, pobj, vp))
4691 return false;
4693 JS_LOCK_OBJ(cx, pobj);
4695 if (pobj->containsSlot(slot) &&
4696 (JS_LIKELY(cx->runtime->propertyRemovals == sample) ||
4697 pobj->nativeContains(*shape))) {
4698 if (!pobj->methodWriteBarrier(cx, *shape, *vp)) {
4699 JS_UNLOCK_OBJ(cx, pobj);
4700 return false;
4702 pobj->lockedSetSlot(slot, *vp);
4705 return true;
4708 JSBool
4709 js_NativeSet(JSContext *cx, JSObject *obj, const Shape *shape, bool added, Value *vp)
4711 LeaveTraceIfGlobalObject(cx, obj);
4713 uint32 slot;
4714 int32 sample;
4716 JS_ASSERT(obj->isNative());
4717 JS_ASSERT(JS_IS_OBJ_LOCKED(cx, obj));
4719 slot = shape->slot;
4720 if (slot != SHAPE_INVALID_SLOT) {
4721 OBJ_CHECK_SLOT(obj, slot);
4723 /* If shape has a stub setter, keep obj locked and just store *vp. */
4724 if (shape->hasDefaultSetter()) {
4725 if (!added && !obj->methodWriteBarrier(cx, *shape, *vp)) {
4726 JS_UNLOCK_OBJ(cx, obj);
4727 return false;
4729 obj->lockedSetSlot(slot, *vp);
4730 return true;
4732 } else {
4734 * Allow API consumers to create shared properties with stub setters.
4735 * Such properties effectively function as data descriptors which are
4736 * not writable, so attempting to set such a property should do nothing
4737 * or throw if we're in strict mode.
4739 if (!shape->hasGetterValue() && shape->hasDefaultSetter())
4740 return js_ReportGetterOnlyAssignment(cx);
4743 sample = cx->runtime->propertyRemovals;
4744 JS_UNLOCK_OBJ(cx, obj);
4746 AutoShapeRooter tvr(cx, shape);
4747 if (!shape->set(cx, obj, vp))
4748 return false;
4751 JS_LOCK_OBJ(cx, obj);
4752 if (obj->containsSlot(slot) &&
4753 (JS_LIKELY(cx->runtime->propertyRemovals == sample) ||
4754 obj->nativeContains(*shape))) {
4755 if (!added && !obj->methodWriteBarrier(cx, *shape, *vp)) {
4756 JS_UNLOCK_OBJ(cx, obj);
4757 return false;
4759 obj->lockedSetSlot(slot, *vp);
4762 return true;
4765 static JS_ALWAYS_INLINE bool
4766 js_GetPropertyHelperWithShapeInline(JSContext *cx, JSObject *obj, jsid id,
4767 uintN getHow, Value *vp,
4768 const Shape **shapeOut, JSObject **holderOut)
4770 JSObject *aobj, *obj2;
4771 int protoIndex;
4772 JSProperty *prop;
4773 const Shape *shape;
4775 JS_ASSERT_IF(getHow & JSGET_CACHE_RESULT, !JS_ON_TRACE(cx));
4777 *shapeOut = NULL;
4779 /* Convert string indices to integers if appropriate. */
4780 id = js_CheckForStringIndex(id);
4782 aobj = js_GetProtoIfDenseArray(obj);
4783 protoIndex = js_LookupPropertyWithFlags(cx, aobj, id, cx->resolveFlags,
4784 &obj2, &prop);
4785 if (protoIndex < 0)
4786 return JS_FALSE;
4788 *holderOut = obj2;
4790 if (!prop) {
4791 vp->setUndefined();
4793 if (!CallJSPropertyOp(cx, obj->getClass()->getProperty, obj, id, vp))
4794 return JS_FALSE;
4796 PCMETER(getHow & JSGET_CACHE_RESULT && JS_PROPERTY_CACHE(cx).nofills++);
4799 * Give a strict warning if foo.bar is evaluated by a script for an
4800 * object foo with no property named 'bar'.
4802 jsbytecode *pc;
4803 if (vp->isUndefined() && ((pc = js_GetCurrentBytecodePC(cx)) != NULL)) {
4804 JSOp op;
4805 uintN flags;
4807 op = (JSOp) *pc;
4808 if (op == JSOP_TRAP) {
4809 JS_ASSERT_NOT_ON_TRACE(cx);
4810 op = JS_GetTrapOpcode(cx, cx->fp()->script(), pc);
4812 if (op == JSOP_GETXPROP) {
4813 flags = JSREPORT_ERROR;
4814 } else {
4815 if (!JS_HAS_STRICT_OPTION(cx) ||
4816 (op != JSOP_GETPROP && op != JSOP_GETELEM) ||
4817 js_CurrentPCIsInImacro(cx)) {
4818 return JS_TRUE;
4822 * XXX do not warn about missing __iterator__ as the function
4823 * may be called from JS_GetMethodById. See bug 355145.
4825 if (JSID_IS_ATOM(id, cx->runtime->atomState.iteratorAtom))
4826 return JS_TRUE;
4828 /* Do not warn about tests like (obj[prop] == undefined). */
4829 if (cx->resolveFlags == JSRESOLVE_INFER) {
4830 LeaveTrace(cx);
4831 pc += js_CodeSpec[op].length;
4832 if (Detecting(cx, pc))
4833 return JS_TRUE;
4834 } else if (cx->resolveFlags & JSRESOLVE_DETECTING) {
4835 return JS_TRUE;
4838 flags = JSREPORT_WARNING | JSREPORT_STRICT;
4841 /* Ok, bad undefined property reference: whine about it. */
4842 if (!js_ReportValueErrorFlags(cx, flags, JSMSG_UNDEFINED_PROP,
4843 JSDVG_IGNORE_STACK, IdToValue(id),
4844 NULL, NULL, NULL)) {
4845 return JS_FALSE;
4848 return JS_TRUE;
4851 if (!obj2->isNative())
4852 return obj2->getProperty(cx, id, vp);
4854 shape = (Shape *) prop;
4855 *shapeOut = shape;
4857 if (getHow & JSGET_CACHE_RESULT) {
4858 JS_ASSERT_NOT_ON_TRACE(cx);
4859 JS_PROPERTY_CACHE(cx).fill(cx, aobj, 0, protoIndex, obj2, shape);
4862 if (!js_NativeGet(cx, obj, obj2, shape, getHow, vp))
4863 return JS_FALSE;
4865 JS_UNLOCK_OBJ(cx, obj2);
4866 return JS_TRUE;
4869 extern bool
4870 js_GetPropertyHelperWithShape(JSContext *cx, JSObject *obj, jsid id,
4871 uint32 getHow, Value *vp,
4872 const Shape **shapeOut, JSObject **holderOut)
4874 return js_GetPropertyHelperWithShapeInline(cx, obj, id, getHow, vp, shapeOut, holderOut);
4877 extern JSBool
4878 js_GetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uint32 getHow, Value *vp)
4880 const Shape *shape;
4881 JSObject *holder;
4882 return js_GetPropertyHelperWithShapeInline(cx, obj, id, getHow, vp, &shape, &holder);
4885 JSBool
4886 js_GetProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
4888 return js_GetPropertyHelper(cx, obj, id, JSGET_METHOD_BARRIER, vp);
4891 JSBool
4892 js_GetMethod(JSContext *cx, JSObject *obj, jsid id, uintN getHow, Value *vp)
4894 JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED);
4896 PropertyIdOp op = obj->getOps()->getProperty;
4897 if (!op) {
4898 #if JS_HAS_XML_SUPPORT
4899 JS_ASSERT(!obj->isXML());
4900 #endif
4901 return js_GetPropertyHelper(cx, obj, id, getHow, vp);
4903 JS_ASSERT_IF(getHow & JSGET_CACHE_RESULT, obj->isDenseArray());
4904 #if JS_HAS_XML_SUPPORT
4905 if (obj->isXML())
4906 return js_GetXMLMethod(cx, obj, id, vp);
4907 #endif
4908 return op(cx, obj, id, vp);
4911 JS_FRIEND_API(bool)
4912 js_CheckUndeclaredVarAssignment(JSContext *cx, JSString *propname)
4914 JSStackFrame *const fp = js_GetTopStackFrame(cx);
4915 if (!fp)
4916 return true;
4918 /* If neither cx nor the code is strict, then no check is needed. */
4919 if (!(fp->isScriptFrame() && fp->script()->strictModeCode) &&
4920 !JS_HAS_STRICT_OPTION(cx)) {
4921 return true;
4924 const char *bytes = js_GetStringBytes(cx, propname);
4925 return bytes &&
4926 JS_ReportErrorFlagsAndNumber(cx,
4927 (JSREPORT_WARNING | JSREPORT_STRICT
4928 | JSREPORT_STRICT_MODE_ERROR),
4929 js_GetErrorMessage, NULL,
4930 JSMSG_UNDECLARED_VAR, bytes);
4933 namespace js {
4935 JSBool
4936 ReportReadOnly(JSContext* cx, jsid id, uintN flags)
4938 return js_ReportValueErrorFlags(cx, flags, JSMSG_READ_ONLY,
4939 JSDVG_IGNORE_STACK, IdToValue(id), NULL,
4940 NULL, NULL);
4943 JSBool
4944 ReportNotConfigurable(JSContext* cx, jsid id, uintN flags)
4946 return js_ReportValueErrorFlags(cx, flags, JSMSG_CANT_DELETE,
4947 JSDVG_IGNORE_STACK, IdToValue(id), NULL,
4948 NULL, NULL);
4954 * Note: all non-error exits in this function must notify the tracer using
4955 * SetPropHit when called from the interpreter, which is detected by testing
4956 * (defineHow & JSDNP_CACHE_RESULT).
4958 JSBool
4959 js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow,
4960 Value *vp, JSBool strict)
4962 int protoIndex;
4963 JSObject *pobj;
4964 JSProperty *prop;
4965 const Shape *shape;
4966 uintN attrs, flags;
4967 intN shortid;
4968 Class *clasp;
4969 PropertyOp getter, setter;
4970 bool added;
4972 JS_ASSERT((defineHow &
4973 ~(JSDNP_CACHE_RESULT | JSDNP_SET_METHOD | JSDNP_UNQUALIFIED)) == 0);
4974 if (defineHow & JSDNP_CACHE_RESULT)
4975 JS_ASSERT_NOT_ON_TRACE(cx);
4977 /* Convert string indices to integers if appropriate. */
4978 id = js_CheckForStringIndex(id);
4980 /* Check for a sealed object first (now that id has been normalized). */
4981 if (obj->sealed())
4982 return ReportReadOnly(cx, id, JSREPORT_ERROR);
4984 protoIndex = js_LookupPropertyWithFlags(cx, obj, id, cx->resolveFlags,
4985 &pobj, &prop);
4986 if (protoIndex < 0)
4987 return JS_FALSE;
4988 if (prop) {
4989 if (!pobj->isNative())
4990 prop = NULL;
4991 } else {
4992 /* We should never add properties to lexical blocks. */
4993 JS_ASSERT(!obj->isBlock());
4995 if (!obj->getParent() &&
4996 (defineHow & JSDNP_UNQUALIFIED) &&
4997 !js_CheckUndeclaredVarAssignment(cx, JSID_TO_STRING(id))) {
4998 return JS_FALSE;
5001 shape = (Shape *) prop;
5004 * Now either shape is null, meaning id was not found in obj or one of its
5005 * prototypes; or shape is non-null, meaning id was found directly in pobj.
5006 * If JS_THREADSAFE and shape is non-null, then pobj is locked, and shape
5007 * is held: we must JSObject::dropProperty or else JS_UNLOCK_OBJ before we
5008 * return (the two are equivalent for native objects; we use JS_UNLOCK_OBJ
5009 * because it is cheaper).
5011 attrs = JSPROP_ENUMERATE;
5012 flags = 0;
5013 shortid = 0;
5014 clasp = obj->getClass();
5015 getter = clasp->getProperty;
5016 setter = clasp->setProperty;
5018 if (shape) {
5019 /* ES5 8.12.4 [[Put]] step 2. */
5020 if (shape->isAccessorDescriptor()) {
5021 if (shape->hasDefaultSetter()) {
5022 JS_UNLOCK_OBJ(cx, pobj);
5023 if (defineHow & JSDNP_CACHE_RESULT)
5024 TRACE_2(SetPropHit, JS_NO_PROP_CACHE_FILL, shape);
5025 return js_ReportGetterOnlyAssignment(cx);
5027 } else {
5028 JS_ASSERT(shape->isDataDescriptor());
5030 if (!shape->writable()) {
5031 JS_UNLOCK_OBJ(cx, pobj);
5033 PCMETER((defineHow & JSDNP_CACHE_RESULT) && JS_PROPERTY_CACHE(cx).rofills++);
5034 if (defineHow & JSDNP_CACHE_RESULT) {
5035 JS_ASSERT_NOT_ON_TRACE(cx);
5036 TRACE_2(SetPropHit, JS_NO_PROP_CACHE_FILL, shape);
5039 /* Error in strict mode code, warn with strict option, otherwise do nothing. */
5040 if (strict)
5041 return ReportReadOnly(cx, id, 0);
5042 if (JS_HAS_STRICT_OPTION(cx))
5043 return ReportReadOnly(cx, id, JSREPORT_STRICT | JSREPORT_WARNING);
5044 return JS_TRUE;
5046 #ifdef JS_TRACER
5047 error: // TRACE_2 jumps here in case of error.
5048 return JS_FALSE;
5049 #endif
5052 if (pobj->sealed() && !shape->hasSlot()) {
5053 JS_UNLOCK_OBJ(cx, pobj);
5054 return ReportReadOnly(cx, id, JSREPORT_ERROR);
5057 attrs = shape->attributes();
5058 if (pobj != obj) {
5060 * We found id in a prototype object: prepare to share or shadow.
5062 * NB: Thanks to the immutable, garbage-collected property tree
5063 * maintained by jsscope.c in cx->runtime, we needn't worry about
5064 * shape going away behind our back after we've unlocked pobj.
5066 JS_UNLOCK_OBJ(cx, pobj);
5068 /* Don't clone a prototype property that doesn't have a slot. */
5069 if (!shape->hasSlot()) {
5070 if (defineHow & JSDNP_CACHE_RESULT) {
5071 #ifdef JS_TRACER
5072 JS_ASSERT_NOT_ON_TRACE(cx);
5073 PropertyCacheEntry *entry =
5074 #endif
5075 JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, protoIndex, pobj, shape);
5076 TRACE_2(SetPropHit, entry, shape);
5079 if (shape->hasDefaultSetter() && !shape->hasGetterValue())
5080 return JS_TRUE;
5082 return shape->set(cx, obj, vp);
5085 /* Restore attrs to the ECMA default for new properties. */
5086 attrs = JSPROP_ENUMERATE;
5089 * Preserve the shortid, getter, and setter when shadowing any
5090 * property that has a shortid. An old API convention requires
5091 * that the property's getter and setter functions receive the
5092 * shortid, not id, when they are called on the shadow we are
5093 * about to create in obj.
5095 if (shape->hasShortID()) {
5096 flags = Shape::HAS_SHORTID;
5097 shortid = shape->shortid;
5098 getter = shape->getter();
5099 setter = shape->setter();
5103 * Forget we found the proto-property now that we've copied any
5104 * needed member values.
5106 shape = NULL;
5109 if (shape) {
5110 if (shape->isMethod()) {
5111 JS_ASSERT(pobj->hasMethodBarrier());
5112 } else if ((defineHow & JSDNP_SET_METHOD) && obj->canHaveMethodBarrier()) {
5113 JS_ASSERT(IsFunctionObject(*vp));
5114 JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
5116 JSObject *funobj = &vp->toObject();
5117 JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
5118 if (fun == funobj) {
5119 funobj = CloneFunctionObject(cx, fun, fun->parent);
5120 if (!funobj)
5121 return JS_FALSE;
5122 vp->setObject(*funobj);
5128 added = false;
5129 if (!shape) {
5131 * Purge the property cache of now-shadowed id in obj's scope chain.
5132 * Do this early, before locking obj to avoid nesting locks.
5134 js_PurgeScopeChain(cx, obj, id);
5136 /* Find or make a property descriptor with the right heritage. */
5137 JS_LOCK_OBJ(cx, obj);
5138 if (!obj->ensureClassReservedSlots(cx)) {
5139 JS_UNLOCK_OBJ(cx, obj);
5140 return JS_FALSE;
5144 * Check for Object class here to avoid defining a method on a class
5145 * with magic resolve, addProperty, getProperty, etc. hooks.
5147 if ((defineHow & JSDNP_SET_METHOD) && obj->canHaveMethodBarrier()) {
5148 JS_ASSERT(IsFunctionObject(*vp));
5149 JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
5151 JSObject *funobj = &vp->toObject();
5152 JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
5153 if (fun == funobj) {
5154 flags |= Shape::METHOD;
5155 getter = CastAsPropertyOp(funobj);
5159 shape = obj->putProperty(cx, id, getter, setter, SHAPE_INVALID_SLOT,
5160 attrs, flags, shortid);
5161 if (!shape) {
5162 JS_UNLOCK_OBJ(cx, obj);
5163 return JS_FALSE;
5167 * Initialize the new property value (passed to setter) to undefined.
5168 * Note that we store before calling addProperty, to match the order
5169 * in js_DefineNativeProperty.
5171 if (obj->containsSlot(shape->slot))
5172 obj->lockedSetSlot(shape->slot, UndefinedValue());
5174 /* XXXbe called with obj locked */
5175 if (!CallAddPropertyHook(cx, clasp, obj, shape, vp)) {
5176 obj->removeProperty(cx, id);
5177 JS_UNLOCK_OBJ(cx, obj);
5178 return JS_FALSE;
5180 added = true;
5183 if (defineHow & JSDNP_CACHE_RESULT) {
5184 #ifdef JS_TRACER
5185 JS_ASSERT_NOT_ON_TRACE(cx);
5186 PropertyCacheEntry *entry =
5187 #endif
5188 JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, 0, obj, shape, added);
5189 TRACE_2(SetPropHit, entry, shape);
5192 if (!js_NativeSet(cx, obj, shape, added, vp))
5193 return NULL;
5195 JS_UNLOCK_OBJ(cx, obj);
5196 return JS_TRUE;
5199 JSBool
5200 js_SetProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool strict)
5202 return js_SetPropertyHelper(cx, obj, id, 0, vp, strict);
5205 JSBool
5206 js_GetAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp)
5208 JSProperty *prop;
5209 if (!js_LookupProperty(cx, obj, id, &obj, &prop))
5210 return false;
5211 if (!prop) {
5212 *attrsp = 0;
5213 return true;
5215 if (!obj->isNative())
5216 return obj->getAttributes(cx, id, attrsp);
5218 const Shape *shape = (Shape *)prop;
5219 *attrsp = shape->attributes();
5220 JS_UNLOCK_OBJ(cx, obj);
5221 return true;
5224 JSBool
5225 js_SetNativeAttributes(JSContext *cx, JSObject *obj, Shape *shape, uintN attrs)
5227 JS_ASSERT(obj->isNative());
5228 bool ok = !!js_ChangeNativePropertyAttrs(cx, obj, shape, attrs, 0,
5229 shape->getter(), shape->setter());
5230 JS_UNLOCK_OBJ(cx, obj);
5231 return ok;
5234 JSBool
5235 js_SetAttributes(JSContext *cx, JSObject *obj, jsid id, uintN *attrsp)
5237 JSProperty *prop;
5238 if (!js_LookupProperty(cx, obj, id, &obj, &prop))
5239 return false;
5240 if (!prop)
5241 return true;
5242 return obj->isNative()
5243 ? js_SetNativeAttributes(cx, obj, (Shape *) prop, *attrsp)
5244 : obj->setAttributes(cx, id, attrsp);
5247 JSBool
5248 js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool strict)
5250 JSObject *proto;
5251 JSProperty *prop;
5252 const Shape *shape;
5253 JSBool ok;
5255 rval->setBoolean(true);
5257 /* Convert string indices to integers if appropriate. */
5258 id = js_CheckForStringIndex(id);
5260 if (!js_LookupProperty(cx, obj, id, &proto, &prop))
5261 return false;
5262 if (!prop || proto != obj) {
5264 * If the property was found in a native prototype, check whether it's
5265 * shared and permanent. Such a property stands for direct properties
5266 * in all delegating objects, matching ECMA semantics without bloating
5267 * each delegating object.
5269 if (prop && proto->isNative()) {
5270 shape = (Shape *)prop;
5271 if (shape->isSharedPermanent()) {
5272 JS_UNLOCK_OBJ(cx, proto);
5273 if (strict)
5274 return ReportNotConfigurable(cx, id, 0);
5275 rval->setBoolean(false);
5276 return true;
5278 JS_UNLOCK_OBJ(cx, proto);
5282 * If no property, or the property comes unshared or impermanent from
5283 * a prototype, call the class's delProperty hook, passing rval as the
5284 * result parameter.
5286 return CallJSPropertyOp(cx, obj->getClass()->delProperty, obj, id, rval);
5289 shape = (Shape *)prop;
5290 if (!shape->configurable()) {
5291 JS_UNLOCK_OBJ(cx, obj);
5292 if (strict)
5293 return ReportNotConfigurable(cx, id, 0);
5294 rval->setBoolean(false);
5295 return true;
5298 /* XXXbe called with obj locked */
5299 if (!CallJSPropertyOp(cx, obj->getClass()->delProperty, obj, SHAPE_USERID(shape), rval)) {
5300 JS_UNLOCK_OBJ(cx, obj);
5301 return false;
5304 if (obj->containsSlot(shape->slot)) {
5305 const Value &v = obj->lockedGetSlot(shape->slot);
5306 GC_POKE(cx, v);
5309 * Delete is rare enough that we can take the hit of checking for an
5310 * active cloned method function object that must be homed to a callee
5311 * slot on the active stack frame before this delete completes, in case
5312 * someone saved the clone and checks it against foo.caller for a foo
5313 * called from the active method.
5315 * We do not check suspended frames. They can't be reached via caller,
5316 * so the only way they could have the method's joined function object
5317 * as callee is through an API abusage. We break any such edge case.
5319 if (obj->hasMethodBarrier()) {
5320 JSObject *funobj;
5322 if (IsFunctionObject(v, &funobj)) {
5323 JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
5325 if (fun != funobj) {
5326 for (JSStackFrame *fp = cx->maybefp(); fp; fp = fp->prev()) {
5327 if (fp->isFunctionFrame() &&
5328 &fp->callee() == &fun->compiledFunObj() &&
5329 fp->thisValue().isObject() &&
5330 &fp->thisValue().toObject() == obj) {
5331 fp->calleeValue().setObject(*funobj);
5339 ok = obj->removeProperty(cx, id);
5340 JS_UNLOCK_OBJ(cx, obj);
5342 return ok && js_SuppressDeletedProperty(cx, obj, id);
5345 namespace js {
5347 JSBool
5348 DefaultValue(JSContext *cx, JSObject *obj, JSType hint, Value *vp)
5350 JS_ASSERT(hint != JSTYPE_OBJECT && hint != JSTYPE_FUNCTION);
5352 Value v = ObjectValue(*obj);
5353 if (hint == JSTYPE_STRING) {
5355 * Optimize for String objects with standard toString methods. Support
5356 * new String(...) instances whether mutated to have their own scope or
5357 * not, as well as direct String.prototype references.
5359 if (obj->getClass() == &js_StringClass) {
5360 jsid toStringId = ATOM_TO_JSID(cx->runtime->atomState.toStringAtom);
5362 JS_LOCK_OBJ(cx, obj);
5363 JSObject *lockedobj = obj;
5364 const Shape *shape = obj->nativeLookup(toStringId);
5365 JSObject *pobj = obj;
5367 if (!shape) {
5368 pobj = obj->getProto();
5370 if (pobj && pobj->getClass() == &js_StringClass) {
5371 JS_UNLOCK_OBJ(cx, obj);
5372 JS_LOCK_OBJ(cx, pobj);
5373 lockedobj = pobj;
5374 shape = pobj->nativeLookup(toStringId);
5378 if (shape && shape->hasDefaultGetter() && pobj->containsSlot(shape->slot)) {
5379 const Value &fval = pobj->lockedGetSlot(shape->slot);
5381 JSObject *funobj;
5382 if (IsFunctionObject(fval, &funobj)) {
5383 JSFunction *fun = funobj->getFunctionPrivate();
5384 if (fun->maybeNative() == js_str_toString) {
5385 JS_UNLOCK_OBJ(cx, lockedobj);
5386 *vp = obj->getPrimitiveThis();
5387 return JS_TRUE;
5391 JS_UNLOCK_OBJ(cx, lockedobj);
5395 * Propagate the exception if js_TryMethod finds an appropriate
5396 * method, and calling that method returned failure.
5398 if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom,
5399 0, NULL, &v)) {
5400 return JS_FALSE;
5403 if (!v.isPrimitive()) {
5404 if (!obj->getClass()->convert(cx, obj, hint, &v))
5405 return JS_FALSE;
5407 } else {
5408 if (!obj->getClass()->convert(cx, obj, hint, &v))
5409 return JS_FALSE;
5410 if (v.isObject()) {
5411 JS_ASSERT(hint != TypeOfValue(cx, v));
5412 if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0, NULL, &v))
5413 return JS_FALSE;
5416 if (v.isObject()) {
5417 /* Avoid recursive death when decompiling in js_ReportValueError. */
5418 JSString *str;
5419 if (hint == JSTYPE_STRING) {
5420 str = JS_InternString(cx, obj->getClass()->name);
5421 if (!str)
5422 return JS_FALSE;
5423 } else {
5424 str = NULL;
5426 vp->setObject(*obj);
5427 js_ReportValueError2(cx, JSMSG_CANT_CONVERT_TO,
5428 JSDVG_SEARCH_STACK, *vp, str,
5429 (hint == JSTYPE_VOID)
5430 ? "primitive type"
5431 : JS_TYPE_STR(hint));
5432 return JS_FALSE;
5434 *vp = v;
5435 return JS_TRUE;
5438 } /* namespace js */
5440 JS_FRIEND_API(JSBool)
5441 js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op, Value *statep, jsid *idp)
5443 /* If the class has a custom JSCLASS_NEW_ENUMERATE hook, call it. */
5444 Class *clasp = obj->getClass();
5445 JSEnumerateOp enumerate = clasp->enumerate;
5446 if (clasp->flags & JSCLASS_NEW_ENUMERATE) {
5447 JS_ASSERT(enumerate != JS_EnumerateStub);
5448 return ((NewEnumerateOp) enumerate)(cx, obj, enum_op, statep, idp);
5451 if (!enumerate(cx, obj))
5452 return false;
5454 /* Tell InitNativeIterator to treat us like a native object. */
5455 JS_ASSERT(enum_op == JSENUMERATE_INIT || enum_op == JSENUMERATE_INIT_ALL);
5456 statep->setMagic(JS_NATIVE_ENUMERATE);
5457 return true;
5460 namespace js {
5462 JSBool
5463 CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
5464 Value *vp, uintN *attrsp)
5466 JSBool writing;
5467 JSObject *pobj;
5468 JSProperty *prop;
5469 Class *clasp;
5470 const Shape *shape;
5471 JSSecurityCallbacks *callbacks;
5472 CheckAccessOp check;
5474 while (JS_UNLIKELY(obj->getClass() == &js_WithClass))
5475 obj = obj->getProto();
5477 writing = (mode & JSACC_WRITE) != 0;
5478 switch (mode & JSACC_TYPEMASK) {
5479 case JSACC_PROTO:
5480 pobj = obj;
5481 if (!writing)
5482 vp->setObjectOrNull(obj->getProto());
5483 *attrsp = JSPROP_PERMANENT;
5484 break;
5486 case JSACC_PARENT:
5487 JS_ASSERT(!writing);
5488 pobj = obj;
5489 vp->setObject(*obj->getParent());
5490 *attrsp = JSPROP_READONLY | JSPROP_PERMANENT;
5491 break;
5493 default:
5494 if (!obj->lookupProperty(cx, id, &pobj, &prop))
5495 return JS_FALSE;
5496 if (!prop) {
5497 if (!writing)
5498 vp->setUndefined();
5499 *attrsp = 0;
5500 pobj = obj;
5501 break;
5504 if (!pobj->isNative()) {
5505 if (!writing) {
5506 vp->setUndefined();
5507 *attrsp = 0;
5509 break;
5512 shape = (Shape *)prop;
5513 *attrsp = shape->attributes();
5514 if (!writing) {
5515 if (pobj->containsSlot(shape->slot))
5516 *vp = pobj->lockedGetSlot(shape->slot);
5517 else
5518 vp->setUndefined();
5520 JS_UNLOCK_OBJ(cx, pobj);
5524 * If obj's class has a stub (null) checkAccess hook, use the per-runtime
5525 * checkObjectAccess callback, if configured.
5527 * We don't want to require all classes to supply a checkAccess hook; we
5528 * need that hook only for certain classes used when precompiling scripts
5529 * and functions ("brutal sharing"). But for general safety of built-in
5530 * magic properties like __proto__, we route all access checks, even for
5531 * classes that stub out checkAccess, through the global checkObjectAccess
5532 * hook. This covers precompilation-based sharing and (possibly
5533 * unintended) runtime sharing across trust boundaries.
5535 clasp = pobj->getClass();
5536 check = clasp->checkAccess;
5537 if (!check) {
5538 callbacks = JS_GetSecurityCallbacks(cx);
5539 check = callbacks ? Valueify(callbacks->checkObjectAccess) : NULL;
5541 return !check || check(cx, pobj, id, mode, vp);
5546 JSType
5547 js_TypeOf(JSContext *cx, JSObject *obj)
5550 * Unfortunately we have wrappers that are native objects and thus don't
5551 * overwrite js_TypeOf (i.e. XPCCrossOriginWrapper), so we have to
5552 * unwrap here.
5554 obj = obj->wrappedObject(cx);
5557 * ECMA 262, 11.4.3 says that any native object that implements
5558 * [[Call]] should be of type "function". However, RegExp is of
5559 * type "object", not "function", for Web compatibility.
5561 if (obj->isCallable()) {
5562 return (obj->getClass() != &js_RegExpClass)
5563 ? JSTYPE_FUNCTION
5564 : JSTYPE_OBJECT;
5567 return JSTYPE_OBJECT;
5570 #ifdef JS_THREADSAFE
5571 void
5572 js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop)
5574 JS_UNLOCK_OBJ(cx, obj);
5576 #endif
5578 bool
5579 js_IsDelegate(JSContext *cx, JSObject *obj, const Value &v)
5581 if (v.isPrimitive())
5582 return false;
5583 JSObject *obj2 = v.toObject().wrappedObject(cx);
5584 while ((obj2 = obj2->getProto()) != NULL) {
5585 if (obj2 == obj)
5586 return true;
5588 return false;
5591 bool
5592 js::FindClassPrototype(JSContext *cx, JSObject *scopeobj, JSProtoKey protoKey,
5593 JSObject **protop, Class *clasp)
5595 Value v;
5596 if (!js_FindClassObject(cx, scopeobj, protoKey, &v, clasp))
5597 return false;
5599 if (IsFunctionObject(v)) {
5600 JSObject *ctor = &v.toObject();
5601 if (!ctor->getProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom), &v))
5602 return false;
5605 *protop = v.isObject() ? &v.toObject() : NULL;
5606 return true;
5610 * The first part of this function has been hand-expanded and optimized into
5611 * NewBuiltinClassInstance in jsobjinlines.h.
5613 JSBool
5614 js_GetClassPrototype(JSContext *cx, JSObject *scopeobj, JSProtoKey protoKey,
5615 JSObject **protop, Class *clasp)
5617 VOUCH_DOES_NOT_REQUIRE_STACK();
5618 JS_ASSERT(JSProto_Null <= protoKey);
5619 JS_ASSERT(protoKey < JSProto_LIMIT);
5621 if (protoKey != JSProto_Null) {
5622 if (!scopeobj) {
5623 if (cx->hasfp())
5624 scopeobj = &cx->fp()->scopeChain();
5625 if (!scopeobj) {
5626 scopeobj = cx->globalObject;
5627 if (!scopeobj) {
5628 *protop = NULL;
5629 return true;
5633 scopeobj = scopeobj->getGlobal();
5634 if (scopeobj->getClass()->flags & JSCLASS_IS_GLOBAL) {
5635 const Value &v = scopeobj->getReservedSlot(JSProto_LIMIT + protoKey);
5636 if (v.isObject()) {
5637 *protop = &v.toObject();
5638 return true;
5643 return FindClassPrototype(cx, scopeobj, protoKey, protop, clasp);
5647 * For shared precompilation of function objects, we support cloning on entry
5648 * to an execution context in which the function declaration or expression
5649 * should be processed as if it were not precompiled, where the precompiled
5650 * function's scope chain does not match the execution context's. The cloned
5651 * function object carries its execution-context scope in its parent slot; it
5652 * links to the precompiled function (the "clone-parent") via its proto slot.
5654 * Note that this prototype-based delegation leaves an unchecked access path
5655 * from the clone to the clone-parent's 'constructor' property. If the clone
5656 * lives in a less privileged or shared scope than the clone-parent, this is
5657 * a security hole, a sharing hazard, or both. Therefore we check all such
5658 * accesses with the following getter/setter pair, which we use when defining
5659 * 'constructor' in f.prototype for all function objects f.
5661 static JSBool
5662 CheckCtorGetAccess(JSContext *cx, JSObject *obj, jsid id, Value *vp)
5664 JSAtom *atom = cx->runtime->atomState.constructorAtom;
5665 JS_ASSERT(id == ATOM_TO_JSID(atom));
5666 uintN attrs;
5667 return CheckAccess(cx, obj, ATOM_TO_JSID(atom), JSACC_READ, vp, &attrs);
5670 static JSBool
5671 CheckCtorSetAccess(JSContext *cx, JSObject *obj, jsid id, Value *vp)
5673 JSAtom *atom = cx->runtime->atomState.constructorAtom;
5674 JS_ASSERT(id == ATOM_TO_JSID(atom));
5675 uintN attrs;
5676 return CheckAccess(cx, obj, ATOM_TO_JSID(atom), JSACC_WRITE, vp, &attrs);
5679 JSBool
5680 js_SetClassPrototype(JSContext *cx, JSObject *ctor, JSObject *proto, uintN attrs)
5683 * Use the given attributes for the prototype property of the constructor,
5684 * as user-defined constructors have a DontDelete prototype (which may be
5685 * reset), while native or "system" constructors have DontEnum | ReadOnly |
5686 * DontDelete.
5688 if (!ctor->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom),
5689 ObjectOrNullValue(proto), PropertyStub, PropertyStub, attrs)) {
5690 return JS_FALSE;
5694 * ECMA says that Object.prototype.constructor, or f.prototype.constructor
5695 * for a user-defined function f, is DontEnum.
5697 return proto->defineProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.constructorAtom),
5698 ObjectOrNullValue(ctor), CheckCtorGetAccess, CheckCtorSetAccess, 0);
5701 JSBool
5702 js_PrimitiveToObject(JSContext *cx, Value *vp)
5704 Value v = *vp;
5705 JS_ASSERT(v.isPrimitive());
5707 Class *clasp;
5708 if (v.isNumber())
5709 clasp = &js_NumberClass;
5710 else if (v.isString())
5711 clasp = &js_StringClass;
5712 else
5713 clasp = &js_BooleanClass;
5715 JSObject *obj = NewBuiltinClassInstance(cx, clasp);
5716 if (!obj)
5717 return JS_FALSE;
5719 obj->setPrimitiveThis(v);
5720 vp->setObject(*obj);
5721 return JS_TRUE;
5724 JSBool
5725 js_ValueToObjectOrNull(JSContext *cx, const Value &v, JSObject **objp)
5727 JSObject *obj;
5729 if (v.isObjectOrNull()) {
5730 obj = v.toObjectOrNull();
5731 } else if (v.isUndefined()) {
5732 obj = NULL;
5733 } else {
5734 Value tmp = v;
5735 if (!js_PrimitiveToObject(cx, &tmp))
5736 return JS_FALSE;
5737 obj = &tmp.toObject();
5739 *objp = obj;
5740 return JS_TRUE;
5743 JSObject *
5744 js_ValueToNonNullObject(JSContext *cx, const Value &v)
5746 JSObject *obj;
5748 if (!js_ValueToObjectOrNull(cx, v, &obj))
5749 return NULL;
5750 if (!obj)
5751 js_ReportIsNullOrUndefined(cx, JSDVG_SEARCH_STACK, v, NULL);
5752 return obj;
5755 JSBool
5756 js_TryValueOf(JSContext *cx, JSObject *obj, JSType type, Value *rval)
5758 Value argv[1];
5760 argv[0].setString(ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[type]));
5761 return js_TryMethod(cx, obj, cx->runtime->atomState.valueOfAtom,
5762 1, argv, rval);
5765 JSBool
5766 js_TryMethod(JSContext *cx, JSObject *obj, JSAtom *atom,
5767 uintN argc, Value *argv, Value *rval)
5769 JS_CHECK_RECURSION(cx, return JS_FALSE);
5772 * Report failure only if an appropriate method was found, and calling it
5773 * returned failure. We propagate failure in this case to make exceptions
5774 * behave properly.
5776 JSErrorReporter older = JS_SetErrorReporter(cx, NULL);
5777 jsid id = ATOM_TO_JSID(atom);
5778 Value fval;
5779 JSBool ok = js_GetMethod(cx, obj, id, JSGET_NO_METHOD_BARRIER, &fval);
5780 JS_SetErrorReporter(cx, older);
5781 if (!ok)
5782 return false;
5784 if (fval.isPrimitive())
5785 return JS_TRUE;
5786 return ExternalInvoke(cx, obj, fval, argc, argv, rval);
5789 #if JS_HAS_XDR
5791 JSBool
5792 js_XDRObject(JSXDRState *xdr, JSObject **objp)
5794 JSContext *cx;
5795 JSAtom *atom;
5796 Class *clasp;
5797 uint32 classId, classDef;
5798 JSProtoKey protoKey;
5799 JSObject *proto;
5801 cx = xdr->cx;
5802 atom = NULL;
5803 if (xdr->mode == JSXDR_ENCODE) {
5804 clasp = (*objp)->getClass();
5805 classId = JS_XDRFindClassIdByName(xdr, clasp->name);
5806 classDef = !classId;
5807 if (classDef) {
5808 if (!JS_XDRRegisterClass(xdr, Jsvalify(clasp), &classId))
5809 return JS_FALSE;
5810 protoKey = JSCLASS_CACHED_PROTO_KEY(clasp);
5811 if (protoKey != JSProto_Null) {
5812 classDef |= (protoKey << 1);
5813 } else {
5814 atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0);
5815 if (!atom)
5816 return JS_FALSE;
5819 } else {
5820 clasp = NULL; /* quell GCC overwarning */
5821 classDef = 0;
5825 * XDR a flag word, which could be 0 for a class use, in which case no
5826 * name follows, only the id in xdr's class registry; 1 for a class def,
5827 * in which case the flag word is followed by the class name transferred
5828 * from or to atom; or a value greater than 1, an odd number that when
5829 * divided by two yields the JSProtoKey for class. In the last case, as
5830 * in the 0 classDef case, no name is transferred via atom.
5832 if (!JS_XDRUint32(xdr, &classDef))
5833 return JS_FALSE;
5834 if (classDef == 1 && !js_XDRAtom(xdr, &atom))
5835 return JS_FALSE;
5837 if (!JS_XDRUint32(xdr, &classId))
5838 return JS_FALSE;
5840 if (xdr->mode == JSXDR_DECODE) {
5841 if (classDef) {
5842 /* NB: we know that JSProto_Null is 0 here, for backward compat. */
5843 protoKey = (JSProtoKey) (classDef >> 1);
5844 if (!js_GetClassPrototype(cx, NULL, protoKey, &proto, clasp))
5845 return JS_FALSE;
5846 clasp = proto->getClass();
5847 if (!JS_XDRRegisterClass(xdr, Jsvalify(clasp), &classId))
5848 return JS_FALSE;
5849 } else {
5850 clasp = Valueify(JS_XDRFindClassById(xdr, classId));
5851 if (!clasp) {
5852 char numBuf[12];
5853 JS_snprintf(numBuf, sizeof numBuf, "%ld", (long)classId);
5854 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
5855 JSMSG_CANT_FIND_CLASS, numBuf);
5856 return JS_FALSE;
5861 if (!clasp->xdrObject) {
5862 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
5863 JSMSG_CANT_XDR_CLASS, clasp->name);
5864 return JS_FALSE;
5866 return clasp->xdrObject(xdr, objp);
5869 #endif /* JS_HAS_XDR */
5871 #ifdef JS_DUMP_SCOPE_METERS
5873 #include <stdio.h>
5875 JSBasicStats js_entry_count_bs = JS_INIT_STATIC_BASIC_STATS;
5877 static void
5878 MeterEntryCount(uintN count)
5880 JS_BASIC_STATS_ACCUM(&js_entry_count_bs, count);
5883 void
5884 js_DumpScopeMeters(JSRuntime *rt)
5886 static FILE *logfp;
5887 if (!logfp)
5888 logfp = fopen("/tmp/scope.stats", "a");
5891 double mean, sigma;
5893 mean = JS_MeanAndStdDevBS(&js_entry_count_bs, &sigma);
5895 fprintf(logfp, "scopes %u entries %g mean %g sigma %g max %u",
5896 js_entry_count_bs.num, js_entry_count_bs.sum, mean, sigma,
5897 js_entry_count_bs.max);
5900 JS_DumpHistogram(&js_entry_count_bs, logfp);
5901 JS_BASIC_STATS_INIT(&js_entry_count_bs);
5902 fflush(logfp);
5904 #endif
5906 #ifdef DEBUG
5907 void
5908 js_PrintObjectSlotName(JSTracer *trc, char *buf, size_t bufsize)
5910 JS_ASSERT(trc->debugPrinter == js_PrintObjectSlotName);
5912 JSObject *obj = (JSObject *)trc->debugPrintArg;
5913 uint32 slot = (uint32)trc->debugPrintIndex;
5914 JS_ASSERT(slot >= JSSLOT_START(obj->getClass()));
5916 const Shape *shape;
5917 if (obj->isNative()) {
5918 shape = obj->lastProperty();
5919 while (shape->previous() && shape->slot != slot)
5920 shape = shape->previous();
5921 } else {
5922 shape = NULL;
5925 if (!shape) {
5926 const char *slotname = NULL;
5927 Class *clasp = obj->getClass();
5928 if (clasp->flags & JSCLASS_IS_GLOBAL) {
5929 uint32 key = slot - JSSLOT_START(clasp);
5930 #define JS_PROTO(name,code,init) \
5931 if ((code) == key) { slotname = js_##name##_str; goto found; }
5932 #include "jsproto.tbl"
5933 #undef JS_PROTO
5935 found:
5936 if (slotname)
5937 JS_snprintf(buf, bufsize, "CLASS_OBJECT(%s)", slotname);
5938 else
5939 JS_snprintf(buf, bufsize, "**UNKNOWN SLOT %ld**", (long)slot);
5940 } else {
5941 jsid id = shape->id;
5942 if (JSID_IS_INT(id)) {
5943 JS_snprintf(buf, bufsize, "%ld", (long)JSID_TO_INT(id));
5944 } else if (JSID_IS_ATOM(id)) {
5945 js_PutEscapedString(buf, bufsize, JSID_TO_STRING(id), 0);
5946 } else {
5947 JS_snprintf(buf, bufsize, "**FINALIZED ATOM KEY**");
5951 #endif
5953 void
5954 js_TraceObject(JSTracer *trc, JSObject *obj)
5956 JS_ASSERT(obj->isNative());
5958 JSContext *cx = trc->context;
5959 if (!obj->nativeEmpty() && IS_GC_MARKING_TRACER(trc)) {
5961 * Trim overlong dslots allocations from the GC, to avoid thrashing in
5962 * case of delete-happy code that settles down at a given population.
5963 * The !obj->nativeEmpty() guard above is due to the bug described by
5964 * the FIXME comment below.
5966 size_t slots = obj->slotSpan();
5967 if (obj->numSlots() != slots)
5968 obj->shrinkSlots(cx, slots);
5971 #ifdef JS_DUMP_SCOPE_METERS
5972 MeterEntryCount(obj->propertyCount);
5973 #endif
5975 obj->trace(trc);
5977 if (!JS_CLIST_IS_EMPTY(&cx->runtime->watchPointList))
5978 js_TraceWatchPoints(trc, obj);
5980 /* No one runs while the GC is running, so we can use LOCKED_... here. */
5981 Class *clasp = obj->getClass();
5982 if (clasp->mark) {
5983 if (clasp->flags & JSCLASS_MARK_IS_TRACE)
5984 ((JSTraceOp) clasp->mark)(trc, obj);
5985 else if (IS_GC_MARKING_TRACER(trc))
5986 (void) clasp->mark(cx, obj, trc);
5988 if (clasp->flags & JSCLASS_IS_GLOBAL) {
5989 JSCompartment *compartment = obj->getCompartment(cx);
5990 compartment->marked = true;
5994 * NB: In case clasp->mark mutates something (which would be a bug, but we
5995 * want to be defensive), leave this code here -- don't move it up and
5996 * unify it with the |if (!traceScope)| section above.
5998 * FIXME: We minimize nslots against obj->slotSpan because native objects
5999 * such as Date instances may have failed to advance slotSpan to cover all
6000 * reserved slots (this Date issue may be a bug in JSObject::growSlots, but
6001 * the general problem occurs in other built-in class implementations).
6003 uint32 nslots = obj->numSlots();
6004 if (!obj->nativeEmpty() && obj->slotSpan() < nslots)
6005 nslots = obj->slotSpan();
6006 JS_ASSERT(nslots >= JSSLOT_START(clasp));
6008 for (uint32 i = JSSLOT_START(clasp); i != nslots; ++i) {
6009 const Value &v = obj->getSlot(i);
6010 JS_SET_TRACING_DETAILS(trc, js_PrintObjectSlotName, obj, i);
6011 MarkValueRaw(trc, v);
6015 void
6016 js_ClearNative(JSContext *cx, JSObject *obj)
6019 * Clear obj of all obj's properties. FIXME: we do not clear reserved slots
6020 * lying below JSSLOT_FREE(clasp). JS_ClearScope does that.
6022 JS_LOCK_OBJ(cx, obj);
6023 if (!obj->nativeEmpty()) {
6024 /* Now that we're done using real properties, clear obj. */
6025 obj->clear(cx);
6027 /* Clear slot values since obj->clear reset our shape to empty. */
6028 uint32 freeslot = JSSLOT_FREE(obj->getClass());
6029 uint32 n = obj->numSlots();
6030 for (uint32 i = freeslot; i < n; ++i)
6031 obj->setSlot(i, UndefinedValue());
6033 JS_UNLOCK_OBJ(cx, obj);
6036 bool
6037 js_GetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, Value *vp)
6039 if (!obj->isNative()) {
6040 vp->setUndefined();
6041 return true;
6044 uint32 slot = JSSLOT_START(obj->getClass()) + index;
6045 JS_LOCK_OBJ(cx, obj);
6046 if (slot < obj->numSlots())
6047 *vp = obj->getSlot(slot);
6048 else
6049 vp->setUndefined();
6050 JS_UNLOCK_OBJ(cx, obj);
6051 return true;
6054 bool
6055 js_SetReservedSlot(JSContext *cx, JSObject *obj, uint32 index, const Value &v)
6057 if (!obj->isNative())
6058 return true;
6060 Class *clasp = obj->getClass();
6061 uint32 slot = JSSLOT_START(clasp) + index;
6063 JS_LOCK_OBJ(cx, obj);
6064 if (slot >= obj->numSlots()) {
6065 uint32 nslots = JSSLOT_FREE(clasp);
6066 JS_ASSERT(slot < nslots);
6067 if (!obj->allocSlots(cx, nslots)) {
6068 JS_UNLOCK_OBJ(cx, obj);
6069 return false;
6073 obj->setSlot(slot, v);
6074 GC_POKE(cx, JS_NULL);
6075 JS_UNLOCK_OBJ(cx, obj);
6076 return true;
6079 JSObject *
6080 JSObject::wrappedObject(JSContext *cx) const
6082 if (JSObjectOp op = getClass()->ext.wrappedObject) {
6083 if (JSObject *obj = op(cx, const_cast<JSObject *>(this)))
6084 return obj;
6086 return const_cast<JSObject *>(this);
6089 JSObject *
6090 JSObject::getGlobal() const
6092 JSObject *obj = const_cast<JSObject *>(this);
6093 while (JSObject *parent = obj->getParent())
6094 obj = parent;
6095 return obj;
6098 JSBool
6099 js_ReportGetterOnlyAssignment(JSContext *cx)
6101 return JS_ReportErrorFlagsAndNumber(cx,
6102 JSREPORT_WARNING | JSREPORT_STRICT |
6103 JSREPORT_STRICT_MODE_ERROR,
6104 js_GetErrorMessage, NULL,
6105 JSMSG_GETTER_ONLY);
6108 JSCompartment *
6109 JSObject::getCompartment(JSContext *cx)
6111 JSObject *obj = getGlobal();
6113 Class *clasp = obj->getClass();
6114 if (!(clasp->flags & JSCLASS_IS_GLOBAL)) {
6115 #if JS_HAS_XML_SUPPORT
6116 // The magic AnyName object is runtime-wide.
6117 if (clasp == &js_AnyNameClass)
6118 return cx->runtime->defaultCompartment;
6120 // The magic function namespace object is runtime-wide.
6121 if (clasp == &js_NamespaceClass &&
6122 obj->getNameURI() == ATOM_TO_JSVAL(cx->runtime->
6123 atomState.functionNamespaceURIAtom)) {
6124 return cx->runtime->defaultCompartment;
6126 #endif
6129 * Script objects and compile-time Function, Block, RegExp objects
6130 * are not parented.
6132 if (clasp == &js_FunctionClass || clasp == &js_BlockClass || clasp == &js_RegExpClass ||
6133 clasp == &js_ScriptClass) {
6134 // This is a bogus answer, but it'll do for now.
6135 return cx->runtime->defaultCompartment;
6137 JS_NOT_REACHED("non-global object at end of scope chain");
6139 const Value &v = obj->getReservedSlot(JSRESERVED_GLOBAL_COMPARTMENT);
6140 return (JSCompartment *)v.toPrivate();
6143 JS_FRIEND_API(JSBool)
6144 js_GetterOnlyPropertyStub(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
6146 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_GETTER_ONLY);
6147 return JS_FALSE;
6150 #ifdef DEBUG
6153 * Routines to print out values during debugging. These are FRIEND_API to help
6154 * the debugger find them and to support temporarily hacking js_Dump* calls
6155 * into other code.
6158 void
6159 dumpChars(const jschar *s, size_t n)
6161 size_t i;
6163 if (n == (size_t) -1) {
6164 while (s[++n]) ;
6167 fputc('"', stderr);
6168 for (i = 0; i < n; i++) {
6169 if (s[i] == '\n')
6170 fprintf(stderr, "\\n");
6171 else if (s[i] == '\t')
6172 fprintf(stderr, "\\t");
6173 else if (s[i] >= 32 && s[i] < 127)
6174 fputc(s[i], stderr);
6175 else if (s[i] <= 255)
6176 fprintf(stderr, "\\x%02x", (unsigned int) s[i]);
6177 else
6178 fprintf(stderr, "\\u%04x", (unsigned int) s[i]);
6180 fputc('"', stderr);
6183 JS_FRIEND_API(void)
6184 js_DumpChars(const jschar *s, size_t n)
6186 fprintf(stderr, "jschar * (%p) = ", (void *) s);
6187 dumpChars(s, n);
6188 fputc('\n', stderr);
6191 void
6192 dumpString(JSString *str)
6194 dumpChars(str->chars(), str->length());
6197 JS_FRIEND_API(void)
6198 js_DumpString(JSString *str)
6200 fprintf(stderr, "JSString* (%p) = jschar * (%p) = ",
6201 (void *) str, (void *) str->chars());
6202 dumpString(str);
6203 fputc('\n', stderr);
6206 JS_FRIEND_API(void)
6207 js_DumpAtom(JSAtom *atom)
6209 fprintf(stderr, "JSAtom* (%p) = ", (void *) atom);
6210 js_DumpString(ATOM_TO_STRING(atom));
6213 void
6214 dumpValue(const Value &v)
6216 if (v.isNull())
6217 fprintf(stderr, "null");
6218 else if (v.isUndefined())
6219 fprintf(stderr, "undefined");
6220 else if (v.isInt32())
6221 fprintf(stderr, "%d", v.toInt32());
6222 else if (v.isDouble())
6223 fprintf(stderr, "%g", v.toDouble());
6224 else if (v.isString())
6225 dumpString(v.toString());
6226 else if (v.isObject() && v.toObject().isFunction()) {
6227 JSObject *funobj = &v.toObject();
6228 JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
6229 fprintf(stderr, "<%s %s at %p (JSFunction at %p)>",
6230 fun->atom ? "function" : "unnamed",
6231 fun->atom ? JS_GetStringBytes(ATOM_TO_STRING(fun->atom)) : "function",
6232 (void *) funobj,
6233 (void *) fun);
6234 } else if (v.isObject()) {
6235 JSObject *obj = &v.toObject();
6236 Class *clasp = obj->getClass();
6237 fprintf(stderr, "<%s%s at %p>",
6238 clasp->name,
6239 (clasp == &js_ObjectClass) ? "" : " object",
6240 (void *) obj);
6241 } else if (v.isBoolean()) {
6242 if (v.toBoolean())
6243 fprintf(stderr, "true");
6244 else
6245 fprintf(stderr, "false");
6246 } else if (v.isMagic()) {
6247 fprintf(stderr, "<invalid");
6248 #ifdef DEBUG
6249 switch (v.whyMagic()) {
6250 case JS_ARRAY_HOLE: fprintf(stderr, " array hole"); break;
6251 case JS_ARGS_HOLE: fprintf(stderr, " args hole"); break;
6252 case JS_NATIVE_ENUMERATE: fprintf(stderr, " native enumeration"); break;
6253 case JS_NO_ITER_VALUE: fprintf(stderr, " no iter value"); break;
6254 case JS_GENERATOR_CLOSING: fprintf(stderr, " generator closing"); break;
6255 default: fprintf(stderr, " ?!"); break;
6257 #endif
6258 fprintf(stderr, ">");
6259 } else {
6260 fprintf(stderr, "unexpected value");
6264 JS_FRIEND_API(void)
6265 js_DumpValue(const Value &val)
6267 dumpValue(val);
6268 fputc('\n', stderr);
6271 JS_FRIEND_API(void)
6272 js_DumpId(jsid id)
6274 fprintf(stderr, "jsid %p = ", (void *) JSID_BITS(id));
6275 dumpValue(IdToValue(id));
6276 fputc('\n', stderr);
6279 static void
6280 DumpShape(const Shape &shape)
6282 jsid id = shape.id;
6283 uint8 attrs = shape.attributes();
6285 fprintf(stderr, " ");
6286 if (attrs & JSPROP_ENUMERATE) fprintf(stderr, "enumerate ");
6287 if (attrs & JSPROP_READONLY) fprintf(stderr, "readonly ");
6288 if (attrs & JSPROP_PERMANENT) fprintf(stderr, "permanent ");
6289 if (attrs & JSPROP_GETTER) fprintf(stderr, "getter ");
6290 if (attrs & JSPROP_SETTER) fprintf(stderr, "setter ");
6291 if (attrs & JSPROP_SHARED) fprintf(stderr, "shared ");
6292 if (shape.isAlias()) fprintf(stderr, "alias ");
6293 if (JSID_IS_ATOM(id))
6294 dumpString(JSID_TO_STRING(id));
6295 else if (JSID_IS_INT(id))
6296 fprintf(stderr, "%d", (int) JSID_TO_INT(id));
6297 else
6298 fprintf(stderr, "unknown jsid %p", (void *) JSID_BITS(id));
6299 fprintf(stderr, ": slot %d", shape.slot);
6300 fprintf(stderr, "\n");
6303 JS_FRIEND_API(void)
6304 js_DumpObject(JSObject *obj)
6306 uint32 i, slots;
6307 Class *clasp;
6308 jsuint reservedEnd;
6310 fprintf(stderr, "object %p\n", (void *) obj);
6311 clasp = obj->getClass();
6312 fprintf(stderr, "class %p %s\n", (void *)clasp, clasp->name);
6314 fprintf(stderr, "flags:");
6315 uint32 flags = obj->flags;
6316 if (flags & JSObject::DELEGATE) fprintf(stderr, " delegate");
6317 if (flags & JSObject::SYSTEM) fprintf(stderr, " system");
6318 if (flags & JSObject::SEALED) fprintf(stderr, " sealed");
6319 if (flags & JSObject::BRANDED) fprintf(stderr, " branded");
6320 if (flags & JSObject::GENERIC) fprintf(stderr, " generic");
6321 if (flags & JSObject::METHOD_BARRIER) fprintf(stderr, " method_barrier");
6322 if (flags & JSObject::INDEXED) fprintf(stderr, " indexed");
6323 if (flags & JSObject::OWN_SHAPE) fprintf(stderr, " own_shape");
6324 bool anyFlags = flags != 0;
6325 if (obj->isNative()) {
6326 if (obj->inDictionaryMode()) {
6327 fprintf(stderr, " inDictionaryMode");
6328 anyFlags = true;
6330 if (obj->hasPropertyTable()) {
6331 fprintf(stderr, " hasPropertyTable");
6332 anyFlags = true;
6335 if (!anyFlags)
6336 fprintf(stderr, " none");
6337 fprintf(stderr, "\n");
6339 if (obj->isDenseArray()) {
6340 slots = JS_MIN(obj->getArrayLength(), obj->getDenseArrayCapacity());
6341 fprintf(stderr, "elements\n");
6342 for (i = 0; i < slots; i++) {
6343 fprintf(stderr, " %3d: ", i);
6344 dumpValue(obj->getDenseArrayElement(i));
6345 fprintf(stderr, "\n");
6346 fflush(stderr);
6348 return;
6351 if (obj->isNative()) {
6352 if (obj->sealed())
6353 fprintf(stderr, "sealed\n");
6355 fprintf(stderr, "properties:\n");
6356 for (Shape::Range r = obj->lastProperty()->all(); !r.empty(); r.popFront())
6357 DumpShape(r.front());
6358 } else {
6359 if (!obj->isNative())
6360 fprintf(stderr, "not native\n");
6363 fprintf(stderr, "proto ");
6364 dumpValue(ObjectOrNullValue(obj->getProto()));
6365 fputc('\n', stderr);
6367 fprintf(stderr, "parent ");
6368 dumpValue(ObjectOrNullValue(obj->getParent()));
6369 fputc('\n', stderr);
6371 i = JSSLOT_PRIVATE;
6372 if (clasp->flags & JSCLASS_HAS_PRIVATE) {
6373 i = JSSLOT_PRIVATE + 1;
6374 fprintf(stderr, "private %p\n", obj->getPrivate());
6377 fprintf(stderr, "slots:\n");
6378 reservedEnd = i + JSCLASS_RESERVED_SLOTS(clasp);
6379 slots = obj->slotSpan();
6380 for (; i < slots; i++) {
6381 fprintf(stderr, " %3d ", i);
6382 if (i < reservedEnd)
6383 fprintf(stderr, "(reserved) ");
6384 fprintf(stderr, "= ");
6385 dumpValue(obj->getSlot(i));
6386 fputc('\n', stderr);
6388 fputc('\n', stderr);
6391 static void
6392 MaybeDumpObject(const char *name, JSObject *obj)
6394 if (obj) {
6395 fprintf(stderr, " %s: ", name);
6396 dumpValue(ObjectValue(*obj));
6397 fputc('\n', stderr);
6401 static void
6402 MaybeDumpValue(const char *name, const Value &v)
6404 if (!v.isNull()) {
6405 fprintf(stderr, " %s: ", name);
6406 dumpValue(v);
6407 fputc('\n', stderr);
6411 JS_FRIEND_API(void)
6412 js_DumpStackFrame(JSContext *cx, JSStackFrame *start)
6414 /* This should only called during live debugging. */
6415 VOUCH_DOES_NOT_REQUIRE_STACK();
6417 if (!start)
6418 start = cx->maybefp();
6419 FrameRegsIter i(cx);
6420 while (!i.done() && i.fp() != start)
6421 ++i;
6423 if (i.done()) {
6424 fprintf(stderr, "fp = %p not found in cx = %p\n", (void *)start, (void *)cx);
6425 return;
6428 for (; !i.done(); ++i) {
6429 JSStackFrame *const fp = i.fp();
6431 fprintf(stderr, "JSStackFrame at %p\n", (void *) fp);
6432 if (fp->isFunctionFrame()) {
6433 fprintf(stderr, "callee fun: ");
6434 dumpValue(ObjectValue(fp->callee()));
6435 } else {
6436 fprintf(stderr, "global frame, no callee");
6438 fputc('\n', stderr);
6440 if (fp->isScriptFrame()) {
6441 fprintf(stderr, "file %s line %u\n",
6442 fp->script()->filename, (unsigned) fp->script()->lineno);
6445 if (jsbytecode *pc = i.pc()) {
6446 if (!fp->isScriptFrame()) {
6447 fprintf(stderr, "*** pc && !script, skipping frame\n\n");
6448 continue;
6450 if (fp->hasImacropc()) {
6451 fprintf(stderr, " pc in imacro at %p\n called from ", pc);
6452 pc = fp->imacropc();
6453 } else {
6454 fprintf(stderr, " ");
6456 fprintf(stderr, "pc = %p\n", pc);
6457 fprintf(stderr, " current op: %s\n", js_CodeName[*pc]);
6459 Value *sp = i.sp();
6460 fprintf(stderr, " slots: %p\n", (void *) fp->slots());
6461 fprintf(stderr, " sp: %p = slots + %u\n", (void *) sp, (unsigned) (sp - fp->slots()));
6462 if (sp - fp->slots() < 10000) { // sanity
6463 for (Value *p = fp->slots(); p < sp; p++) {
6464 fprintf(stderr, " %p: ", (void *) p);
6465 dumpValue(*p);
6466 fputc('\n', stderr);
6469 if (fp->isFunctionFrame() && !fp->isEvalFrame()) {
6470 fprintf(stderr, " actuals: %p (%u) ", (void *) fp->actualArgs(), (unsigned) fp->numActualArgs());
6471 fprintf(stderr, " formals: %p (%u)\n", (void *) fp->formalArgs(), (unsigned) fp->numFormalArgs());
6473 MaybeDumpObject("callobj", fp->maybeCallObj());
6474 MaybeDumpObject("argsobj", fp->maybeArgsObj());
6475 MaybeDumpValue("this", fp->thisValue());
6476 fprintf(stderr, " rval: ");
6477 dumpValue(fp->returnValue());
6478 fputc('\n', stderr);
6480 fprintf(stderr, " flags:");
6481 if (fp->isConstructing())
6482 fprintf(stderr, " constructing");
6483 if (fp->hasOverriddenArgs())
6484 fprintf(stderr, " overridden_args");
6485 if (fp->isAssigning())
6486 fprintf(stderr, " assigning");
6487 if (fp->isDebuggerFrame())
6488 fprintf(stderr, " debugger");
6489 if (fp->isEvalFrame())
6490 fprintf(stderr, " eval");
6491 if (fp->isYielding())
6492 fprintf(stderr, " yielding");
6493 if (fp->isGeneratorFrame())
6494 fprintf(stderr, " generator");
6495 fputc('\n', stderr);
6497 fprintf(stderr, " scopeChain: (JSObject *) %p\n", (void *) &fp->scopeChain());
6498 if (fp->hasBlockChain())
6499 fprintf(stderr, " blockChain: (JSObject *) %p\n", (void *) fp->blockChain());
6501 fputc('\n', stderr);
6505 #ifdef DEBUG
6506 bool
6507 IsSaneThisObject(JSObject &obj)
6509 Class *clasp = obj.getClass();
6510 return clasp != &js_CallClass &&
6511 clasp != &js_BlockClass &&
6512 clasp != &js_DeclEnvClass &&
6513 clasp != &js_WithClass;
6515 #endif
6517 #endif /* DEBUG */