1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 *
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 *
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
10 *
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
15 *
16 * The Original Code is Mozilla Communicator client code, released
17 * March 31, 1998.
18 *
19 * The Initial Developer of the Original Code is
20 * Netscape Communications Corporation.
21 * Portions created by the Initial Developer are Copyright (C) 1998
22 * the Initial Developer. All Rights Reserved.
23 *
24 * Contributor(s):
25 *
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
37 *
38 * ***** END LICENSE BLOCK ***** */
39
40 /*
41 * JS object implementation.
42 */
43 #include "jsstddef.h"
44 #include <stdlib.h>
45 #include <string.h>
46 #include "jstypes.h"
47 #include "jsarena.h" /* Added by JSIFY */
48 #include "jsutil.h" /* Added by JSIFY */
49 #include "jshash.h" /* Added by JSIFY */
50 #include "jsdhash.h"
51 #include "jsprf.h"
52 #include "jsapi.h"
53 #include "jsarray.h"
54 #include "jsatom.h"
55 #include "jsbool.h"
56 #include "jscntxt.h"
57 #include "jsconfig.h"
58 #include "jsfun.h"
59 #include "jsgc.h"
60 #include "jsinterp.h"
61 #include "jslock.h"
62 #include "jsnum.h"
63 #include "jsobj.h"
64 #include "jsscope.h"
65 #include "jsscript.h"
66 #include "jsstr.h"
67 #include "jsopcode.h"
68
69 #include "jsdbgapi.h" /* whether or not JS_HAS_OBJ_WATCHPOINT */
70
71 #ifdef JS_THREADSAFE
72 #define NATIVE_DROP_PROPERTY js_DropProperty
73
74 extern void
75 js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop);
76 #else
77 #define NATIVE_DROP_PROPERTY NULL
78 #endif
79
80 #ifdef XP_MAC
81 #pragma export on
82 #endif
83
84 JS_FRIEND_DATA(JSObjectOps) js_ObjectOps = {
85 js_NewObjectMap, js_DestroyObjectMap,
86 #if defined JS_THREADSAFE && defined DEBUG
87 _js_LookupProperty, js_DefineProperty,
88 #else
89 js_LookupProperty, js_DefineProperty,
90 #endif
91 js_GetProperty, js_SetProperty,
92 js_GetAttributes, js_SetAttributes,
93 js_DeleteProperty, js_DefaultValue,
94 js_Enumerate, js_CheckAccess,
95 NULL, NATIVE_DROP_PROPERTY,
96 js_Call, js_Construct,
97 NULL, js_HasInstance,
98 js_SetProtoOrParent, js_SetProtoOrParent,
99 js_Mark, js_Clear,
100 js_GetRequiredSlot, js_SetRequiredSlot
101 };
102
103 #ifdef XP_MAC
104 #pragma export off
105 #endif
106
107 JSClass js_ObjectClass = {
108 js_Object_str,
109 0,
110 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
111 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
112 JSCLASS_NO_OPTIONAL_MEMBERS
113 };
114
115 #if JS_HAS_OBJ_PROTO_PROP
116
117 static JSBool
118 obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
119
120 static JSBool
121 obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
122
123 static JSBool
124 obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
125
126 static JSPropertySpec object_props[] = {
127 /* These two must come first; see object_props[slot].name usage below. */
128 {js_proto_str, JSSLOT_PROTO, JSPROP_PERMANENT|JSPROP_SHARED,
129 obj_getSlot, obj_setSlot},
130 {js_parent_str,JSSLOT_PARENT,JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED,
131 obj_getSlot, obj_setSlot},
132 {js_count_str, 0, JSPROP_PERMANENT,obj_getCount, obj_getCount},
133 {0,0,0,0,0}
134 };
135
136 /* NB: JSSLOT_PROTO and JSSLOT_PARENT are already indexes into object_props. */
137 #define JSSLOT_COUNT 2
138
139 static JSBool
ReportStrictSlot(JSContext * cx,uint32 slot)140 ReportStrictSlot(JSContext *cx, uint32 slot)
141 {
142 if (slot == JSSLOT_PROTO)
143 return JS_TRUE;
144 return JS_ReportErrorFlagsAndNumber(cx,
145 JSREPORT_WARNING | JSREPORT_STRICT,
146 js_GetErrorMessage, NULL,
147 JSMSG_DEPRECATED_USAGE,
148 object_props[slot].name);
149 }
150
151 static JSBool
obj_getSlot(JSContext * cx,JSObject * obj,jsval id,jsval * vp)152 obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
153 {
154 uint32 slot;
155 JSAccessMode mode;
156 uintN attrs;
157
158 slot = (uint32) JSVAL_TO_INT(id);
159 if (id == INT_TO_JSVAL(JSSLOT_PROTO)) {
160 id = (jsid)cx->runtime->atomState.protoAtom;
161 mode = JSACC_PROTO;
162 } else {
163 id = (jsid)cx->runtime->atomState.parentAtom;
164 mode = JSACC_PARENT;
165 }
166 if (!OBJ_CHECK_ACCESS(cx, obj, id, mode, vp, &attrs))
167 return JS_FALSE;
168 *vp = OBJ_GET_SLOT(cx, obj, slot);
169 return JS_TRUE;
170 }
171
172 static JSBool
obj_setSlot(JSContext * cx,JSObject * obj,jsval id,jsval * vp)173 obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
174 {
175 JSObject *pobj;
176 uint32 slot;
177 uintN attrs;
178
179 if (!JSVAL_IS_OBJECT(*vp))
180 return JS_TRUE;
181 pobj = JSVAL_TO_OBJECT(*vp);
182 slot = (uint32) JSVAL_TO_INT(id);
183 if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, slot))
184 return JS_FALSE;
185
186 /* __parent__ is readonly and permanent, only __proto__ may be set. */
187 if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_PROTO | JSACC_WRITE, vp, &attrs))
188 return JS_FALSE;
189
190 return js_SetProtoOrParent(cx, obj, slot, pobj);
191 }
192
193 static JSBool
obj_getCount(JSContext * cx,JSObject * obj,jsval id,jsval * vp)194 obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
195 {
196 jsval iter_state;
197 jsid num_properties;
198 JSBool ok;
199
200 if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, JSSLOT_COUNT))
201 return JS_FALSE;
202
203 /* Get the number of properties to enumerate. */
204 iter_state = JSVAL_NULL;
205 ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, &num_properties);
206 if (!ok)
207 goto out;
208
209 if (!JSVAL_IS_INT(num_properties)) {
210 JS_ASSERT(0);
211 *vp = JSVAL_ZERO;
212 goto out;
213 }
214 *vp = num_properties;
215
216 out:
217 if (iter_state != JSVAL_NULL)
218 ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_DESTROY, &iter_state, 0);
219 return ok;
220 }
221
222 #else /* !JS_HAS_OBJ_PROTO_PROP */
223
224 #define object_props NULL
225
226 #endif /* !JS_HAS_OBJ_PROTO_PROP */
227
228 JSBool
js_SetProtoOrParent(JSContext * cx,JSObject * obj,uint32 slot,JSObject * pobj)229 js_SetProtoOrParent(JSContext *cx, JSObject *obj, uint32 slot, JSObject *pobj)
230 {
231 JSRuntime *rt;
232 JSObject *obj2, *oldproto;
233 JSScope *scope, *newscope;
234
235 /*
236 * Serialize all proto and parent setting in order to detect cycles.
237 * We nest locks in this function, and only here, in the following orders:
238 *
239 * (1) rt->setSlotLock < pobj's scope lock;
240 * rt->setSlotLock < pobj's proto-or-parent's scope lock;
241 * rt->setSlotLock < pobj's grand-proto-or-parent's scope lock;
242 * etc...
243 * (2) rt->setSlotLock < obj's scope lock < pobj's scope lock.
244 *
245 * We avoid AB-BA deadlock by restricting obj from being on pobj's parent
246 * or proto chain (pobj may already be on obj's parent or proto chain; it
247 * could be moving up or down). We finally order obj with respect to pobj
248 * at the bottom of this routine (just before releasing rt->setSlotLock),
249 * by making pobj be obj's prototype or parent.
250 *
251 * After we have set the slot and released rt->setSlotLock, another call
252 * to js_SetProtoOrParent could nest locks according to the first order
253 * list above, but it cannot deadlock with any other thread. For there
254 * to be a deadlock, other parts of the engine would have to nest scope
255 * locks in the opposite order. XXXbe ensure they don't!
256 */
257 rt = cx->runtime;
258 #ifdef JS_THREADSAFE
259
260 JS_ACQUIRE_LOCK(rt->setSlotLock);
261 while (rt->setSlotBusy) {
262 jsrefcount saveDepth;
263
264 /* Take pains to avoid nesting rt->gcLock inside rt->setSlotLock! */
265 JS_RELEASE_LOCK(rt->setSlotLock);
266 saveDepth = JS_SuspendRequest(cx);
267 JS_ACQUIRE_LOCK(rt->setSlotLock);
268 if (rt->setSlotBusy)
269 JS_WAIT_CONDVAR(rt->setSlotDone, JS_NO_TIMEOUT);
270 JS_RELEASE_LOCK(rt->setSlotLock);
271 JS_ResumeRequest(cx, saveDepth);
272 JS_ACQUIRE_LOCK(rt->setSlotLock);
273 }
274 rt->setSlotBusy = JS_TRUE;
275 JS_RELEASE_LOCK(rt->setSlotLock);
276
277 #define SET_SLOT_DONE(rt) \
278 JS_BEGIN_MACRO \
279 JS_ACQUIRE_LOCK((rt)->setSlotLock); \
280 (rt)->setSlotBusy = JS_FALSE; \
281 JS_NOTIFY_ALL_CONDVAR((rt)->setSlotDone); \
282 JS_RELEASE_LOCK((rt)->setSlotLock); \
283 JS_END_MACRO
284
285 #else
286
287 #define SET_SLOT_DONE(rt) /* nothing */
288
289 #endif
290
291 obj2 = pobj;
292 while (obj2) {
293 if (obj2 == obj) {
294 SET_SLOT_DONE(rt);
295 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
296 JSMSG_CYCLIC_VALUE,
297 #if JS_HAS_OBJ_PROTO_PROP
298 object_props[slot].name
299 #else
300 (slot == JSSLOT_PROTO) ? js_proto_str
301 : js_parent_str
302 #endif
303 );
304 return JS_FALSE;
305 }
306 obj2 = JSVAL_TO_OBJECT(OBJ_GET_SLOT(cx, obj2, slot));
307 }
308
309 if (slot == JSSLOT_PROTO && OBJ_IS_NATIVE(obj)) {
310 /* Check to see whether obj shares its prototype's scope. */
311 JS_LOCK_OBJ(cx, obj);
312 scope = OBJ_SCOPE(obj);
313 oldproto = JSVAL_TO_OBJECT(LOCKED_OBJ_GET_SLOT(obj, JSSLOT_PROTO));
314 if (oldproto && OBJ_SCOPE(oldproto) == scope) {
315 /* Either obj needs a new empty scope, or it should share pobj's. */
316 if (!pobj ||
317 !OBJ_IS_NATIVE(pobj) ||
318 OBJ_GET_CLASS(cx, pobj) != LOCKED_OBJ_GET_CLASS(oldproto)) {
319 /*
320 * With no proto and no scope of its own, obj is truly empty.
321 *
322 * If pobj is not native, obj needs its own empty scope -- it
323 * should not continue to share oldproto's scope once oldproto
324 * is not on obj's prototype chain. That would put properties
325 * from oldproto's scope ahead of properties defined by pobj,
326 * in lookup order.
327 *
328 * If pobj's class differs from oldproto's, we may need a new
329 * scope to handle differences in private and reserved slots,
330 * so we suboptimally but safely make one.
331 */
332 scope = js_GetMutableScope(cx, obj);
333 if (!scope) {
334 JS_UNLOCK_OBJ(cx, obj);
335 SET_SLOT_DONE(rt);
336 return JS_FALSE;
337 }
338 } else if (OBJ_SCOPE(pobj) != scope) {
339 #ifdef JS_THREADSAFE
340 /*
341 * We are about to nest scope locks. Help jslock.c:ShareScope
342 * keep scope->u.count balanced for the JS_UNLOCK_SCOPE, while
343 * avoiding deadlock, by recording scope in rt->setSlotScope.
344 */
345 if (scope->ownercx) {
346 JS_ASSERT(scope->ownercx == cx);
347 rt->setSlotScope = scope;
348 }
349 #endif
350
351 /* We can't deadlock because we checked for cycles above (2). */
352 JS_LOCK_OBJ(cx, pobj);
353 newscope = (JSScope *) js_HoldObjectMap(cx, pobj->map);
354 obj->map = &newscope->map;
355 js_DropObjectMap(cx, &scope->map, obj);
356 JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope);
357 scope = newscope;
358 #ifdef JS_THREADSAFE
359 rt->setSlotScope = NULL;
360 #endif
361 }
362 }
363 LOCKED_OBJ_SET_SLOT(obj, JSSLOT_PROTO, OBJECT_TO_JSVAL(pobj));
364 JS_UNLOCK_SCOPE(cx, scope);
365 } else {
366 OBJ_SET_SLOT(cx, obj, slot, OBJECT_TO_JSVAL(pobj));
367 }
368
369 SET_SLOT_DONE(rt);
370 return JS_TRUE;
371
372 #undef SET_SLOT_DONE
373 }
374
375 JS_STATIC_DLL_CALLBACK(JSHashNumber)
js_hash_object(const void * key)376 js_hash_object(const void *key)
377 {
378 return (JSHashNumber)key >> JSVAL_TAGBITS;
379 }
380
381 static JSHashEntry *
MarkSharpObjects(JSContext * cx,JSObject * obj,JSIdArray ** idap)382 MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap)
383 {
384 JSSharpObjectMap *map;
385 JSHashTable *table;
386 JSHashNumber hash;
387 JSHashEntry **hep, *he;
388 jsatomid sharpid;
389 JSIdArray *ida;
390 JSBool ok;
391 jsint i, length;
392 jsid id;
393 #if JS_HAS_GETTER_SETTER
394 JSObject *obj2;
395 JSProperty *prop;
396 uintN attrs;
397 #endif
398 jsval val;
399
400 map = &cx->sharpObjectMap;
401 table = map->table;
402 hash = js_hash_object(obj);
403 hep = JS_HashTableRawLookup(table, hash, obj);
404 he = *hep;
405 if (!he) {
406 sharpid = 0;
407 he = JS_HashTableRawAdd(table, hep, hash, obj, (void *)sharpid);
408 if (!he) {
409 JS_ReportOutOfMemory(cx);
410 return NULL;
411 }
412 ida = JS_Enumerate(cx, obj);
413 if (!ida)
414 return NULL;
415 ok = JS_TRUE;
416 for (i = 0, length = ida->length; i < length; i++) {
417 id = ida->vector[i];
418 #if JS_HAS_GETTER_SETTER
419 ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
420 if (!ok)
421 break;
422 if (prop) {
423 ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs);
424 if (ok) {
425 if (OBJ_IS_NATIVE(obj2) &&
426 (attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
427 val = JSVAL_NULL;
428 if (attrs & JSPROP_GETTER)
429 val = (jsval) ((JSScopeProperty*)prop)->getter;
430 if (attrs & JSPROP_SETTER) {
431 if (val != JSVAL_NULL) {
432 /* Mark the getter, then set val to setter. */
433 ok = (MarkSharpObjects(cx, JSVAL_TO_OBJECT(val),
434 NULL)
435 != NULL);
436 }
437 val = (jsval) ((JSScopeProperty*)prop)->setter;
438 }
439 } else {
440 ok = OBJ_GET_PROPERTY(cx, obj, id, &val);
441 }
442 }
443 OBJ_DROP_PROPERTY(cx, obj2, prop);
444 }
445 #else
446 ok = OBJ_GET_PROPERTY(cx, obj, id, &val);
447 #endif
448 if (!ok)
449 break;
450 if (!JSVAL_IS_PRIMITIVE(val) &&
451 !MarkSharpObjects(cx, JSVAL_TO_OBJECT(val), NULL)) {
452 ok = JS_FALSE;
453 break;
454 }
455 }
456 if (!ok || !idap)
457 JS_DestroyIdArray(cx, ida);
458 if (!ok)
459 return NULL;
460 } else {
461 sharpid = (jsatomid) he->value;
462 if (sharpid == 0) {
463 sharpid = ++map->sharpgen << SHARP_ID_SHIFT;
464 he->value = (void *) sharpid;
465 }
466 ida = NULL;
467 }
468 if (idap)
469 *idap = ida;
470 return he;
471 }
472
473 JSHashEntry *
js_EnterSharpObject(JSContext * cx,JSObject * obj,JSIdArray ** idap,jschar ** sp)474 js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap,
475 jschar **sp)
476 {
477 JSSharpObjectMap *map;
478 JSHashTable *table;
479 JSIdArray *ida;
480 JSHashNumber hash;
481 JSHashEntry *he, **hep;
482 jsatomid sharpid;
483 char buf[20];
484 size_t len;
485
486 /* Set to null in case we return an early error. */
487 *sp = NULL;
488 map = &cx->sharpObjectMap;
489 table = map->table;
490 if (!table) {
491 table = JS_NewHashTable(8, js_hash_object, JS_CompareValues,
492 JS_CompareValues, NULL, NULL);
493 if (!table) {
494 JS_ReportOutOfMemory(cx);
495 return NULL;
496 }
497 map->table = table;
498 }
499
500 ida = NULL;
501 if (map->depth == 0) {
502 he = MarkSharpObjects(cx, obj, &ida);
503 if (!he)
504 goto bad;
505 JS_ASSERT((((jsatomid) he->value) & SHARP_BIT) == 0);
506 if (!idap) {
507 JS_DestroyIdArray(cx, ida);
508 ida = NULL;
509 }
510 } else {
511 hash = js_hash_object(obj);
512 hep = JS_HashTableRawLookup(table, hash, obj);
513 he = *hep;
514
515 /*
516 * It's possible that the value of a property has changed from the
517 * first time the object's properties are traversed (when the property
518 * ids are entered into the hash table) to the second (when they are
519 * converted to strings), i.e., the OBJ_GET_PROPERTY() call is not
520 * idempotent.
521 */
522 if (!he) {
523 he = JS_HashTableRawAdd(table, hep, hash, obj, NULL);
524 if (!he) {
525 JS_ReportOutOfMemory(cx);
526 goto bad;
527 }
528 *sp = NULL;
529 sharpid = 0;
530 goto out;
531 }
532 }
533
534 sharpid = (jsatomid) he->value;
535 if (sharpid == 0) {
536 *sp = NULL;
537 } else {
538 len = JS_snprintf(buf, sizeof buf, "#%u%c",
539 sharpid >> SHARP_ID_SHIFT,
540 (sharpid & SHARP_BIT) ? '#' : '=');
541 *sp = js_InflateString(cx, buf, len);
542 if (!*sp) {
543 if (ida)
544 JS_DestroyIdArray(cx, ida);
545 goto bad;
546 }
547 }
548
549 out:
550 JS_ASSERT(he);
551 if ((sharpid & SHARP_BIT) == 0) {
552 if (idap && !ida) {
553 ida = JS_Enumerate(cx, obj);
554 if (!ida) {
555 if (*sp) {
556 JS_free(cx, *sp);
557 *sp = NULL;
558 }
559 goto bad;
560 }
561 }
562 map->depth++;
563 }
564
565 if (idap)
566 *idap = ida;
567 return he;
568
569 bad:
570 /* Clean up the sharpObjectMap table on outermost error. */
571 if (map->depth == 0) {
572 map->sharpgen = 0;
573 JS_HashTableDestroy(map->table);
574 map->table = NULL;
575 }
576 return NULL;
577 }
578
579 void
js_LeaveSharpObject(JSContext * cx,JSIdArray ** idap)580 js_LeaveSharpObject(JSContext *cx, JSIdArray **idap)
581 {
582 JSSharpObjectMap *map;
583 JSIdArray *ida;
584
585 map = &cx->sharpObjectMap;
586 JS_ASSERT(map->depth > 0);
587 if (--map->depth == 0) {
588 map->sharpgen = 0;
589 JS_HashTableDestroy(map->table);
590 map->table = NULL;
591 }
592 if (idap) {
593 ida = *idap;
594 if (ida) {
595 JS_DestroyIdArray(cx, ida);
596 *idap = NULL;
597 }
598 }
599 }
600
601 #define OBJ_TOSTRING_EXTRA 3 /* for 3 local GC roots */
602
603 #if JS_HAS_INITIALIZERS || JS_HAS_TOSOURCE
604 JSBool
js_obj_toSource(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)605 js_obj_toSource(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
606 jsval *rval)
607 {
608 JSBool ok, outermost;
609 JSHashEntry *he;
610 JSIdArray *ida;
611 jschar *chars, *ochars, *vsharp;
612 const jschar *idstrchars, *vchars;
613 size_t nchars, idstrlength, gsoplength, vlength, vsharplength;
614 char *comma;
615 jsint i, j, length, valcnt;
616 jsid id;
617 #if JS_HAS_GETTER_SETTER
618 JSObject *obj2;
619 JSProperty *prop;
620 uintN attrs;
621 #endif
622 jsval val[2];
623 JSString *gsop[2];
624 JSAtom *atom;
625 JSString *idstr, *valstr, *str;
626 int stackDummy;
627
628 if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
629 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OVER_RECURSED);
630 return JS_FALSE;
631 }
632
633 /*
634 * obj_toString for 1.2 calls toSource, and doesn't want the extra parens
635 * on the outside.
636 */
637 outermost = (cx->version != JSVERSION_1_2 && cx->sharpObjectMap.depth == 0);
638 he = js_EnterSharpObject(cx, obj, &ida, &chars);
639 if (!he)
640 return JS_FALSE;
641 if (IS_SHARP(he)) {
642 /*
643 * We didn't enter -- obj is already "sharp", meaning we've visited it
644 * already in our depth first search, and therefore chars contains a
645 * string of the form "#n#".
646 */
647 JS_ASSERT(!ida);
648 #if JS_HAS_SHARP_VARS
649 nchars = js_strlen(chars);
650 #else
651 chars[0] = '{';
652 chars[1] = '}';
653 chars[2] = 0;
654 nchars = 2;
655 #endif
656 goto make_string;
657 }
658 JS_ASSERT(ida);
659 ok = JS_TRUE;
660
661 if (!chars) {
662 /* If outermost, allocate 4 + 1 for "({})" and the terminator. */
663 chars = (jschar *) malloc(((outermost ? 4 : 2) + 1) * sizeof(jschar));
664 nchars = 0;
665 if (!chars)
666 goto error;
667 if (outermost)
668 chars[nchars++] = '(';
669 } else {
670 /* js_EnterSharpObject returned a string of the form "#n=" in chars. */
671 MAKE_SHARP(he);
672 nchars = js_strlen(chars);
673 chars = (jschar *)
674 realloc((ochars = chars), (nchars + 2 + 1) * sizeof(jschar));
675 if (!chars) {
676 free(ochars);
677 goto error;
678 }
679 if (outermost) {
680 /*
681 * No need for parentheses around the whole shebang, because #n=
682 * unambiguously begins an object initializer, and never a block
683 * statement.
684 */
685 outermost = JS_FALSE;
686 }
687 }
688
689 #ifdef DUMP_CALL_TABLE
690 if (cx->options & JSOPTION_LOGCALL_TOSOURCE) {
691 const char *classname = OBJ_GET_CLASS(cx, obj)->name;
692 size_t classnchars = strlen(classname);
693 static const char classpropid[] = "C";
694 const char *cp;
695 size_t onchars = nchars;
696
697 /* 2 for ': ', 2 quotes around classname, 2 for ', ' after. */
698 classnchars += sizeof classpropid - 1 + 2 + 2;
699 if (ida->length)
700 classnchars += 2;
701
702 /* 2 for the braces, 1 for the terminator */
703 chars = (jschar *)
704 realloc((ochars = chars),
705 (nchars + classnchars + 2 + 1) * sizeof(jschar));
706 if (!chars) {
707 free(ochars);
708 goto error;
709 }
710
711 chars[nchars++] = '{'; /* 1 from the 2 braces */
712 for (cp = classpropid; *cp; cp++)
713 chars[nchars++] = (jschar) *cp;
714 chars[nchars++] = ':';
715 chars[nchars++] = ' '; /* 2 for ': ' */
716 chars[nchars++] = '"';
717 for (cp = classname; *cp; cp++)
718 chars[nchars++] = (jschar) *cp;
719 chars[nchars++] = '"'; /* 2 quotes */
720 if (ida->length) {
721 chars[nchars++] = ',';
722 chars[nchars++] = ' '; /* 2 for ', ' */
723 }
724
725 JS_ASSERT(nchars - onchars == 1 + classnchars);
726 } else
727 #endif
728 chars[nchars++] = '{';
729
730 comma = NULL;
731
732 for (i = 0, length = ida->length; i < length; i++) {
733 /* Get strings for id and value and GC-root them via argv. */
734 id = ida->vector[i];
735
736 #if JS_HAS_GETTER_SETTER
737
738 ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
739 if (!ok)
740 goto error;
741 valcnt = 0;
742 if (prop) {
743 ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs);
744 if (!ok) {
745 OBJ_DROP_PROPERTY(cx, obj2, prop);
746 goto error;
747 }
748 if (OBJ_IS_NATIVE(obj2) &&
749 (attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
750 if (attrs & JSPROP_GETTER) {
751 val[valcnt] = (jsval) ((JSScopeProperty *)prop)->getter;
752 #ifdef OLD_GETTER_SETTER
753 gsop[valcnt] =
754 ATOM_TO_STRING(cx->runtime->atomState.getterAtom);
755 #else
756 gsop[valcnt] =
757 ATOM_TO_STRING(cx->runtime->atomState.getAtom);
758 #endif
759 valcnt++;
760 }
761 if (attrs & JSPROP_SETTER) {
762 val[valcnt] = (jsval) ((JSScopeProperty *)prop)->setter;
763 #ifdef OLD_GETTER_SETTER
764 gsop[valcnt] =
765 ATOM_TO_STRING(cx->runtime->atomState.setterAtom);
766 #else
767 gsop[valcnt] =
768 ATOM_TO_STRING(cx->runtime->atomState.setAtom);
769 #endif
770 valcnt++;
771 }
772 } else {
773 valcnt = 1;
774 gsop[0] = NULL;
775 ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]);
776 }
777 OBJ_DROP_PROPERTY(cx, obj2, prop);
778 }
779
780 #else /* !JS_HAS_GETTER_SETTER */
781
782 valcnt = 1;
783 gsop[0] = NULL;
784 ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]);
785
786 #endif /* !JS_HAS_GETTER_SETTER */
787
788 if (!ok)
789 goto error;
790
791 /* Convert id to a jsval and then to a string. */
792 atom = JSVAL_IS_INT(id) ? NULL : (JSAtom *)id;
793 id = ID_TO_VALUE(id);
794 idstr = js_ValueToString(cx, id);
795 if (!idstr) {
796 ok = JS_FALSE;
797 goto error;
798 }
799 argv[0] = STRING_TO_JSVAL(idstr);
800
801 /*
802 * If id is a string that's a reserved identifier, or else id is not
803 * an identifier at all, then it needs to be quoted. Also, negative
804 * integer ids must be quoted.
805 */
806 if (atom
807 ? (ATOM_KEYWORD(atom) || !js_IsIdentifier(idstr))
808 : JSVAL_TO_INT(id) < 0) {
809 idstr = js_QuoteString(cx, idstr, (jschar)'\'');
810 if (!idstr) {
811 ok = JS_FALSE;
812 goto error;
813 }
814 argv[0] = STRING_TO_JSVAL(idstr);
815 }
816 idstrchars = JSSTRING_CHARS(idstr);
817 idstrlength = JSSTRING_LENGTH(idstr);
818
819 for (j = 0; j < valcnt; j++) {
820 /* Convert val[j] to its canonical source form. */
821 valstr = js_ValueToSource(cx, val[j]);
822 if (!valstr) {
823 ok = JS_FALSE;
824 goto error;
825 }
826 argv[1+j] = STRING_TO_JSVAL(valstr);
827 vchars = JSSTRING_CHARS(valstr);
828 vlength = JSSTRING_LENGTH(valstr);
829
830 #ifndef OLD_GETTER_SETTER
831 /* Remove 'function ' from beginning of valstr. */
832 if (gsop[j]) {
833 int n = strlen(js_function_str) + 1;
834 vchars += n;
835 vlength -= n;
836 }
837 #endif
838
839 /* If val[j] is a non-sharp object, consider sharpening it. */
840 vsharp = NULL;
841 vsharplength = 0;
842 #if JS_HAS_SHARP_VARS
843 if (!JSVAL_IS_PRIMITIVE(val[j]) && vchars[0] != '#') {
844 he = js_EnterSharpObject(cx, JSVAL_TO_OBJECT(val[j]), NULL,
845 &vsharp);
846 if (!he) {
847 ok = JS_FALSE;
848 goto error;
849 }
850 if (IS_SHARP(he)) {
851 vchars = vsharp;
852 vlength = js_strlen(vchars);
853 } else {
854 if (vsharp) {
855 vsharplength = js_strlen(vsharp);
856 MAKE_SHARP(he);
857 }
858 js_LeaveSharpObject(cx, NULL);
859 }
860 }
861 #endif
862
863 /* Allocate 1 + 1 at end for closing brace and terminating 0. */
864 chars = (jschar *)
865 realloc((ochars = chars),
866 (nchars + (comma ? 2 : 0) +
867 idstrlength + 1 +
868 (gsop[j] ? 1 + JSSTRING_LENGTH(gsop[j]) : 0) +
869 vsharplength + vlength +
870 (outermost ? 2 : 1) + 1) * sizeof(jschar));
871 if (!chars) {
872 /* Save code space on error: let JS_free ignore null vsharp. */
873 JS_free(cx, vsharp);
874 free(ochars);
875 goto error;
876 }
877
878 if (comma) {
879 chars[nchars++] = comma[0];
880 chars[nchars++] = comma[1];
881 }
882 comma = ", ";
883
884 #ifdef OLD_GETTER_SETTER
885 js_strncpy(&chars[nchars], idstrchars, idstrlength);
886 nchars += idstrlength;
887 if (gsop[j]) {
888 chars[nchars++] = ' ';
889 gsoplength = JSSTRING_LENGTH(gsop[j]);
890 js_strncpy(&chars[nchars], JSSTRING_CHARS(gsop[j]), gsoplength);
891 nchars += gsoplength;
892 }
893 chars[nchars++] = ':';
894 #else
895 if (gsop[j]) {
896 gsoplength = JSSTRING_LENGTH(gsop[j]);
897 js_strncpy(&chars[nchars], JSSTRING_CHARS(gsop[j]), gsoplength);
898 nchars += gsoplength;
899 chars[nchars++] = ' ';
900 }
901 js_strncpy(&chars[nchars], idstrchars, idstrlength);
902 nchars += idstrlength;
903 if (!gsop[j])
904 chars[nchars++] = ':';
905 #endif
906 if (vsharplength) {
907 js_strncpy(&chars[nchars], vsharp, vsharplength);
908 nchars += vsharplength;
909 }
910 js_strncpy(&chars[nchars], vchars, vlength);
911 nchars += vlength;
912
913 if (vsharp)
914 JS_free(cx, vsharp);
915 #ifdef DUMP_CALL_TABLE
916 if (outermost && nchars >= js_LogCallToSourceLimit)
917 break;
918 #endif
919 }
920 }
921
922 chars[nchars++] = '}';
923 if (outermost)
924 chars[nchars++] = ')';
925 chars[nchars] = 0;
926
927 error:
928 js_LeaveSharpObject(cx, &ida);
929
930 if (!ok) {
931 if (chars)
932 free(chars);
933 return ok;
934 }
935
936 if (!chars) {
937 JS_ReportOutOfMemory(cx);
938 return JS_FALSE;
939 }
940 make_string:
941 str = js_NewString(cx, chars, nchars, 0);
942 if (!str) {
943 free(chars);
944 return JS_FALSE;
945 }
946 *rval = STRING_TO_JSVAL(str);
947 return JS_TRUE;
948 }
949 #endif /* JS_HAS_INITIALIZERS || JS_HAS_TOSOURCE */
950
951 JSBool
js_obj_toString(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)952 js_obj_toString(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
953 jsval *rval)
954 {
955 jschar *chars;
956 size_t nchars;
957 const char *clazz, *prefix;
958 JSString *str;
959
960 #if JS_HAS_INITIALIZERS
961 if (cx->version == JSVERSION_1_2)
962 return js_obj_toSource(cx, obj, argc, argv, rval);
963 #endif
964
965 clazz = OBJ_GET_CLASS(cx, obj)->name;
966 nchars = 9 + strlen(clazz); /* 9 for "[object ]" */
967 chars = (jschar *) JS_malloc(cx, (nchars + 1) * sizeof(jschar));
968 if (!chars)
969 return JS_FALSE;
970
971 prefix = "[object ";
972 nchars = 0;
973 while ((chars[nchars] = (jschar)*prefix) != 0)
974 nchars++, prefix++;
975 while ((chars[nchars] = (jschar)*clazz) != 0)
976 nchars++, clazz++;
977 chars[nchars++] = ']';
978 chars[nchars] = 0;
979
980 str = js_NewString(cx, chars, nchars, 0);
981 if (!str) {
982 JS_free(cx, chars);
983 return JS_FALSE;
984 }
985 *rval = STRING_TO_JSVAL(str);
986 return JS_TRUE;
987 }
988
989 static JSBool
obj_valueOf(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)990 obj_valueOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
991 {
992 *rval = OBJECT_TO_JSVAL(obj);
993 return JS_TRUE;
994 }
995
996 static JSBool
obj_eval(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)997 obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
998 {
999 JSStackFrame *fp, *caller;
1000 JSBool indirectCall;
1001 JSObject *scopeobj;
1002 JSString *str;
1003 const char *file;
1004 uintN line;
1005 JSPrincipals *principals;
1006 JSScript *script;
1007 JSBool ok;
1008 #if JS_HAS_EVAL_THIS_SCOPE
1009 JSObject *callerScopeChain = NULL, *callerVarObj = NULL;
1010 JSBool setCallerScopeChain = JS_FALSE, setCallerVarObj = JS_FALSE;
1011 #endif
1012
1013 fp = cx->fp;
1014 caller = JS_GetScriptedCaller(cx, fp);
1015 indirectCall = (caller && caller->pc && *caller->pc != JSOP_EVAL);
1016
1017 if (JSVERSION_IS_ECMA(cx->version) &&
1018 indirectCall &&
1019 !JS_ReportErrorFlagsAndNumber(cx,
1020 JSREPORT_WARNING | JSREPORT_STRICT,
1021 js_GetErrorMessage, NULL,
1022 JSMSG_BAD_INDIRECT_CALL,
1023 js_eval_str)) {
1024 return JS_FALSE;
1025 }
1026
1027 if (!JSVAL_IS_STRING(argv[0])) {
1028 *rval = argv[0];
1029 return JS_TRUE;
1030 }
1031
1032 #if JS_HAS_SCRIPT_OBJECT
1033 /*
1034 * Script.prototype.compile/exec and Object.prototype.eval all take an
1035 * optional trailing argument that overrides the scope object.
1036 */
1037 scopeobj = NULL;
1038 if (argc >= 2) {
1039 if (!js_ValueToObject(cx, argv[1], &scopeobj))
1040 return JS_FALSE;
1041 argv[1] = OBJECT_TO_JSVAL(scopeobj);
1042 }
1043 if (!scopeobj)
1044 #endif
1045 {
1046 #if JS_HAS_EVAL_THIS_SCOPE
1047 /* If obj.eval(str), emulate 'with (obj) eval(str)' in the caller. */
1048 if (indirectCall) {
1049 callerScopeChain = caller->scopeChain;
1050 if (obj != callerScopeChain) {
1051 scopeobj = js_NewObject(cx, &js_WithClass, obj,
1052 callerScopeChain);
1053 if (!scopeobj)
1054 return JS_FALSE;
1055
1056 /* Set fp->scopeChain too, for the compiler. */
1057 caller->scopeChain = fp->scopeChain = scopeobj;
1058 setCallerScopeChain = JS_TRUE;
1059 }
1060
1061 callerVarObj = caller->varobj;
1062 if (obj != callerVarObj) {
1063 /* Set fp->varobj too, for the compiler. */
1064 caller->varobj = fp->varobj = obj;
1065 setCallerVarObj = JS_TRUE;
1066 }
1067 }
1068 /* From here on, control must exit through label out with ok set. */
1069 #endif
1070
1071 #if JS_BUG_EVAL_THIS_SCOPE
1072 /* An old version used the object in which eval was found for scope. */
1073 scopeobj = obj;
1074 #else
1075 /* Compile using caller's current scope object. */
1076 if (caller)
1077 scopeobj = caller->scopeChain;
1078 #endif
1079 }
1080
1081 str = JSVAL_TO_STRING(argv[0]);
1082 if (caller) {
1083 file = caller->script->filename;
1084 line = js_PCToLineNumber(cx, caller->script, caller->pc);
1085 principals = JS_EvalFramePrincipals(cx, fp, caller);
1086 } else {
1087 file = NULL;
1088 line = 0;
1089 principals = NULL;
1090 }
1091
1092 fp->flags |= JSFRAME_EVAL;
1093 script = JS_CompileUCScriptForPrincipals(cx, scopeobj, principals,
1094 JSSTRING_CHARS(str),
1095 JSSTRING_LENGTH(str),
1096 file, line);
1097 if (!script) {
1098 ok = JS_FALSE;
1099 goto out;
1100 }
1101
1102 #if !JS_BUG_EVAL_THIS_SCOPE
1103 #if JS_HAS_SCRIPT_OBJECT
1104 if (argc < 2)
1105 #endif
1106 {
1107 /* Execute using caller's new scope object (might be a Call object). */
1108 if (caller)
1109 scopeobj = caller->scopeChain;
1110 }
1111 #endif
1112 ok = js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval);
1113 JS_DestroyScript(cx, script);
1114
1115 out:
1116 #if JS_HAS_EVAL_THIS_SCOPE
1117 /* Restore OBJ_GET_PARENT(scopeobj) not callerScopeChain in case of Call. */
1118 if (setCallerScopeChain)
1119 caller->scopeChain = callerScopeChain;
1120 if (setCallerVarObj)
1121 caller->varobj = callerVarObj;
1122 #endif
1123 return ok;
1124 }
1125
1126 #if JS_HAS_OBJ_WATCHPOINT
1127
1128 static JSBool
obj_watch_handler(JSContext * cx,JSObject * obj,jsval id,jsval old,jsval * nvp,void * closure)1129 obj_watch_handler(JSContext *cx, JSObject *obj, jsval id, jsval old, jsval *nvp,
1130 void *closure)
1131 {
1132 JSResolvingKey key;
1133 JSResolvingEntry *entry;
1134 uint32 generation;
1135 JSObject *funobj;
1136 jsval argv[3];
1137 JSBool ok;
1138
1139 /* Avoid recursion on (obj, id) already being watched on cx. */
1140 key.obj = obj;
1141 key.id = id;
1142 if (!js_StartResolving(cx, &key, JSRESFLAG_WATCH, &entry))
1143 return JS_FALSE;
1144 if (!entry)
1145 return JS_TRUE;
1146 generation = cx->resolvingTable->generation;
1147
1148 funobj = (JSObject *) closure;
1149 argv[0] = id;
1150 argv[1] = old;
1151 argv[2] = *nvp;
1152 ok = js_InternalCall(cx, obj, OBJECT_TO_JSVAL(funobj), 3, argv, nvp);
1153 js_StopResolving(cx, &key, JSRESFLAG_WATCH, entry, generation);
1154 return ok;
1155 }
1156
1157 static JSBool
obj_watch(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1158 obj_watch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1159 {
1160 JSObject *funobj;
1161 JSFunction *fun;
1162 jsval userid, value;
1163 jsid propid;
1164 uintN attrs;
1165
1166 if (JSVAL_IS_FUNCTION(cx, argv[1])) {
1167 funobj = JSVAL_TO_OBJECT(argv[1]);
1168 } else {
1169 fun = js_ValueToFunction(cx, &argv[1], 0);
1170 if (!fun)
1171 return JS_FALSE;
1172 funobj = fun->object;
1173 }
1174 argv[1] = OBJECT_TO_JSVAL(funobj);
1175
1176 /* Compute the unique int/atom symbol id needed by js_LookupProperty. */
1177 userid = argv[0];
1178 if (!JS_ValueToId(cx, userid, &propid))
1179 return JS_FALSE;
1180
1181 if (!OBJ_CHECK_ACCESS(cx, obj, propid, JSACC_WATCH, &value, &attrs))
1182 return JS_FALSE;
1183 if (attrs & JSPROP_READONLY)
1184 return JS_TRUE;
1185 return JS_SetWatchPoint(cx, obj, userid, obj_watch_handler, funobj);
1186 }
1187
1188 static JSBool
obj_unwatch(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1189 obj_unwatch(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1190 {
1191 return JS_ClearWatchPoint(cx, obj, argv[0], NULL, NULL);
1192 }
1193
1194 #endif /* JS_HAS_OBJ_WATCHPOINT */
1195
1196 #if JS_HAS_NEW_OBJ_METHODS
1197 /*
1198 * Prototype and property query methods, to complement the 'in' and
1199 * 'instanceof' operators.
1200 */
1201
1202 /* Proposed ECMA 15.2.4.5. */
1203 static JSBool
obj_hasOwnProperty(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1204 obj_hasOwnProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1205 jsval *rval)
1206 {
1207 jsid id;
1208 JSObject *obj2;
1209 JSProperty *prop;
1210 JSScopeProperty *sprop;
1211
1212 if (!JS_ValueToId(cx, argv[0], &id))
1213 return JS_FALSE;
1214 if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
1215 return JS_FALSE;
1216 if (!prop) {
1217 *rval = JSVAL_FALSE;
1218 } else if (obj2 == obj) {
1219 *rval = JSVAL_TRUE;
1220 } else if (OBJ_IS_NATIVE(obj2)) {
1221 sprop = (JSScopeProperty *)prop;
1222 *rval = BOOLEAN_TO_JSVAL(SPROP_IS_SHARED_PERMANENT(sprop));
1223 } else {
1224 *rval = JSVAL_FALSE;
1225 }
1226 if (prop)
1227 OBJ_DROP_PROPERTY(cx, obj2, prop);
1228 return JS_TRUE;
1229 }
1230
1231 /* Proposed ECMA 15.2.4.6. */
1232 static JSBool
obj_isPrototypeOf(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1233 obj_isPrototypeOf(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1234 jsval *rval)
1235 {
1236 JSBool b;
1237
1238 if (!js_IsDelegate(cx, obj, *argv, &b))
1239 return JS_FALSE;
1240 *rval = BOOLEAN_TO_JSVAL(b);
1241 return JS_TRUE;
1242 }
1243
1244 /* Proposed ECMA 15.2.4.7. */
1245 static JSBool
obj_propertyIsEnumerable(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1246 obj_propertyIsEnumerable(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1247 jsval *rval)
1248 {
1249 jsid id;
1250 uintN attrs;
1251 JSObject *obj2;
1252 JSProperty *prop;
1253 JSBool ok;
1254
1255 if (!JS_ValueToId(cx, argv[0], &id))
1256 return JS_FALSE;
1257
1258 if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
1259 return JS_FALSE;
1260
1261 if (!prop) {
1262 *rval = JSVAL_FALSE;
1263 return JS_TRUE;
1264 }
1265
1266 /*
1267 * XXX ECMA spec error compatible: return false unless hasOwnProperty.
1268 * The ECMA spec really should be fixed so propertyIsEnumerable and the
1269 * for..in loop agree on whether prototype properties are enumerable,
1270 * obviously by fixing this method (not by breaking the for..in loop!).
1271 *
1272 * We check here for shared permanent prototype properties, which should
1273 * be treated as if they are local to obj. They are an implementation
1274 * technique used to satisfy ECMA requirements; users should not be able
1275 * to distinguish a shared permanent proto-property from a local one.
1276 */
1277 if (obj2 != obj &&
1278 !(OBJ_IS_NATIVE(obj2) &&
1279 SPROP_IS_SHARED_PERMANENT((JSScopeProperty *)prop))) {
1280 OBJ_DROP_PROPERTY(cx, obj2, prop);
1281 *rval = JSVAL_FALSE;
1282 return JS_TRUE;
1283 }
1284
1285 ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs);
1286 OBJ_DROP_PROPERTY(cx, obj2, prop);
1287 if (ok)
1288 *rval = BOOLEAN_TO_JSVAL((attrs & JSPROP_ENUMERATE) != 0);
1289 return ok;
1290 }
1291 #endif /* JS_HAS_NEW_OBJ_METHODS */
1292
1293 #if JS_HAS_GETTER_SETTER
1294 static JSBool
obj_defineGetter(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1295 obj_defineGetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1296 jsval *rval)
1297 {
1298 jsval fval, junk;
1299 jsid id;
1300 uintN attrs;
1301
1302 fval = argv[1];
1303 if (JS_TypeOfValue(cx, fval) != JSTYPE_FUNCTION) {
1304 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1305 JSMSG_BAD_GETTER_OR_SETTER,
1306 js_getter_str);
1307 return JS_FALSE;
1308 }
1309
1310 if (!JS_ValueToId(cx, argv[0], &id))
1311 return JS_FALSE;
1312 if (!js_CheckRedeclaration(cx, obj, id, JSPROP_GETTER, NULL, NULL))
1313 return JS_FALSE;
1314 /*
1315 * Getters and setters are just like watchpoints from an access
1316 * control point of view.
1317 */
1318 if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &junk, &attrs))
1319 return JS_FALSE;
1320 return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID,
1321 (JSPropertyOp) JSVAL_TO_OBJECT(fval), NULL,
1322 JSPROP_ENUMERATE | JSPROP_GETTER | JSPROP_SHARED,
1323 NULL);
1324 }
1325
1326 static JSBool
obj_defineSetter(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1327 obj_defineSetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1328 jsval *rval)
1329 {
1330 jsval fval, junk;
1331 jsid id;
1332 uintN attrs;
1333
1334 fval = argv[1];
1335 if (JS_TypeOfValue(cx, fval) != JSTYPE_FUNCTION) {
1336 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1337 JSMSG_BAD_GETTER_OR_SETTER,
1338 js_setter_str);
1339 return JS_FALSE;
1340 }
1341
1342 if (!JS_ValueToId(cx, argv[0], &id))
1343 return JS_FALSE;
1344 if (!js_CheckRedeclaration(cx, obj, id, JSPROP_SETTER, NULL, NULL))
1345 return JS_FALSE;
1346 /*
1347 * Getters and setters are just like watchpoints from an access
1348 * control point of view.
1349 */
1350 if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &junk, &attrs))
1351 return JS_FALSE;
1352 return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID,
1353 NULL, (JSPropertyOp) JSVAL_TO_OBJECT(fval),
1354 JSPROP_ENUMERATE | JSPROP_SETTER | JSPROP_SHARED,
1355 NULL);
1356 }
1357
1358 static JSBool
obj_lookupGetter(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1359 obj_lookupGetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1360 jsval *rval)
1361 {
1362 jsid id;
1363 JSObject *pobj;
1364 JSProperty *prop;
1365 JSScopeProperty *sprop;
1366
1367 if (!JS_ValueToId(cx, argv[0], &id))
1368 return JS_FALSE;
1369 if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
1370 return JS_FALSE;
1371 if (prop) {
1372 if (OBJ_IS_NATIVE(pobj)) {
1373 sprop = (JSScopeProperty *) prop;
1374 if (sprop->attrs & JSPROP_GETTER)
1375 *rval = OBJECT_TO_JSVAL(sprop->getter);
1376 }
1377 OBJ_DROP_PROPERTY(cx, pobj, prop);
1378 }
1379 return JS_TRUE;
1380 }
1381
1382 static JSBool
obj_lookupSetter(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1383 obj_lookupSetter(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
1384 jsval *rval)
1385 {
1386 jsid id;
1387 JSObject *pobj;
1388 JSProperty *prop;
1389 JSScopeProperty *sprop;
1390
1391 if (!JS_ValueToId(cx, argv[0], &id))
1392 return JS_FALSE;
1393 if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
1394 return JS_FALSE;
1395 if (prop) {
1396 if (OBJ_IS_NATIVE(pobj)) {
1397 sprop = (JSScopeProperty *) prop;
1398 if (sprop->attrs & JSPROP_SETTER)
1399 *rval = OBJECT_TO_JSVAL(sprop->setter);
1400 }
1401 OBJ_DROP_PROPERTY(cx, pobj, prop);
1402 }
1403 return JS_TRUE;
1404 }
1405 #endif /* JS_HAS_GETTER_SETTER */
1406
1407 #if JS_HAS_OBJ_WATCHPOINT
1408 const char js_watch_str[] = "watch";
1409 const char js_unwatch_str[] = "unwatch";
1410 #endif
1411 #if JS_HAS_NEW_OBJ_METHODS
1412 const char js_hasOwnProperty_str[] = "hasOwnProperty";
1413 const char js_isPrototypeOf_str[] = "isPrototypeOf";
1414 const char js_propertyIsEnumerable_str[] = "propertyIsEnumerable";
1415 #endif
1416 #if JS_HAS_GETTER_SETTER
1417 const char js_defineGetter_str[] = "__defineGetter__";
1418 const char js_defineSetter_str[] = "__defineSetter__";
1419 const char js_lookupGetter_str[] = "__lookupGetter__";
1420 const char js_lookupSetter_str[] = "__lookupSetter__";
1421 #endif
1422
1423 static JSFunctionSpec object_methods[] = {
1424 #if JS_HAS_TOSOURCE
1425 {js_toSource_str, js_obj_toSource, 0, 0, OBJ_TOSTRING_EXTRA},
1426 #endif
1427 {js_toString_str, js_obj_toString, 0, 0, OBJ_TOSTRING_EXTRA},
1428 {js_toLocaleString_str, js_obj_toString, 0, 0, OBJ_TOSTRING_EXTRA},
1429 {js_valueOf_str, obj_valueOf, 0,0,0},
1430 {js_eval_str, obj_eval, 1,0,0},
1431 #if JS_HAS_OBJ_WATCHPOINT
1432 {js_watch_str, obj_watch, 2,0,0},
1433 {js_unwatch_str, obj_unwatch, 1,0,0},
1434 #endif
1435 #if JS_HAS_NEW_OBJ_METHODS
1436 {js_hasOwnProperty_str, obj_hasOwnProperty, 1,0,0},
1437 {js_isPrototypeOf_str, obj_isPrototypeOf, 1,0,0},
1438 {js_propertyIsEnumerable_str, obj_propertyIsEnumerable, 1,0,0},
1439 #endif
1440 #if JS_HAS_GETTER_SETTER
1441 {js_defineGetter_str, obj_defineGetter, 2,0,0},
1442 {js_defineSetter_str, obj_defineSetter, 2,0,0},
1443 {js_lookupGetter_str, obj_lookupGetter, 1,0,0},
1444 {js_lookupSetter_str, obj_lookupSetter, 1,0,0},
1445 #endif
1446 {0,0,0,0,0}
1447 };
1448
1449 static JSBool
Object(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1450 Object(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1451 {
1452 if (argc == 0) {
1453 /* Trigger logic below to construct a blank object. */
1454 obj = NULL;
1455 } else {
1456 /* If argv[0] is null or undefined, obj comes back null. */
1457 if (!js_ValueToObject(cx, argv[0], &obj))
1458 return JS_FALSE;
1459 }
1460 if (!obj) {
1461 JS_ASSERT(!argc || JSVAL_IS_NULL(argv[0]) || JSVAL_IS_VOID(argv[0]));
1462 if (cx->fp->flags & JSFRAME_CONSTRUCTING)
1463 return JS_TRUE;
1464 obj = js_NewObject(cx, &js_ObjectClass, NULL, NULL);
1465 if (!obj)
1466 return JS_FALSE;
1467 }
1468 *rval = OBJECT_TO_JSVAL(obj);
1469 return JS_TRUE;
1470 }
1471
1472 /*
1473 * ObjectOps and Class for with-statement stack objects.
1474 */
1475 static JSBool
with_LookupProperty(JSContext * cx,JSObject * obj,jsid id,JSObject ** objp,JSProperty ** propp,const char * file,uintN line)1476 with_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
1477 JSProperty **propp
1478 #if defined JS_THREADSAFE && defined DEBUG
1479 , const char *file, uintN line
1480 #endif
1481 )
1482 {
1483 JSObject *proto;
1484 JSScopeProperty *sprop;
1485 JSStackFrame *fp;
1486
1487 proto = OBJ_GET_PROTO(cx, obj);
1488 if (!proto)
1489 return js_LookupProperty(cx, obj, id, objp, propp);
1490 if (!OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp))
1491 return JS_FALSE;
1492
1493 /*
1494 * Check whether id names an argument or local variable in an active
1495 * function. If so, pretend we didn't find it, so that the real arg or
1496 * var property can be found in the function's call object, later on in
1497 * the scope chain. But skip unshared arg and var properties -- those
1498 * result when a script explicitly sets a function "static" property of
1499 * the same name. See jsinterp.c:SetFunctionSlot.
1500 *
1501 * XXX blame pre-ECMA reflection of function args and vars as properties
1502 */
1503 if ((sprop = (JSScopeProperty *) *propp) &&
1504 (proto = *objp, OBJ_IS_NATIVE(proto)) &&
1505 (sprop->getter == js_GetArgument ||
1506 sprop->getter == js_GetLocalVariable) &&
1507 (sprop->attrs & JSPROP_SHARED)) {
1508 JS_ASSERT(OBJ_GET_CLASS(cx, proto) == &js_FunctionClass);
1509 for (fp = cx->fp; fp && (!fp->fun || !fp->fun->interpreted);
1510 fp = fp->down) {
1511 continue;
1512 }
1513 if (fp && fp->fun == (JSFunction *) JS_GetPrivate(cx, proto)) {
1514 OBJ_DROP_PROPERTY(cx, proto, *propp);
1515 *objp = NULL;
1516 *propp = NULL;
1517 }
1518 }
1519 return JS_TRUE;
1520 }
1521
1522 static JSBool
with_GetProperty(JSContext * cx,JSObject * obj,jsid id,jsval * vp)1523 with_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
1524 {
1525 JSObject *proto = OBJ_GET_PROTO(cx, obj);
1526 if (!proto)
1527 return js_GetProperty(cx, obj, id, vp);
1528 return OBJ_GET_PROPERTY(cx, proto, id, vp);
1529 }
1530
1531 static JSBool
with_SetProperty(JSContext * cx,JSObject * obj,jsid id,jsval * vp)1532 with_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
1533 {
1534 JSObject *proto = OBJ_GET_PROTO(cx, obj);
1535 if (!proto)
1536 return js_SetProperty(cx, obj, id, vp);
1537 return OBJ_SET_PROPERTY(cx, proto, id, vp);
1538 }
1539
1540 static JSBool
with_GetAttributes(JSContext * cx,JSObject * obj,jsid id,JSProperty * prop,uintN * attrsp)1541 with_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
1542 uintN *attrsp)
1543 {
1544 JSObject *proto = OBJ_GET_PROTO(cx, obj);
1545 if (!proto)
1546 return js_GetAttributes(cx, obj, id, prop, attrsp);
1547 return OBJ_GET_ATTRIBUTES(cx, proto, id, prop, attrsp);
1548 }
1549
1550 static JSBool
with_SetAttributes(JSContext * cx,JSObject * obj,jsid id,JSProperty * prop,uintN * attrsp)1551 with_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
1552 uintN *attrsp)
1553 {
1554 JSObject *proto = OBJ_GET_PROTO(cx, obj);
1555 if (!proto)
1556 return js_SetAttributes(cx, obj, id, prop, attrsp);
1557 return OBJ_SET_ATTRIBUTES(cx, proto, id, prop, attrsp);
1558 }
1559
1560 static JSBool
with_DeleteProperty(JSContext * cx,JSObject * obj,jsid id,jsval * rval)1561 with_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval)
1562 {
1563 JSObject *proto = OBJ_GET_PROTO(cx, obj);
1564 if (!proto)
1565 return js_DeleteProperty(cx, obj, id, rval);
1566 return OBJ_DELETE_PROPERTY(cx, proto, id, rval);
1567 }
1568
1569 static JSBool
with_DefaultValue(JSContext * cx,JSObject * obj,JSType hint,jsval * vp)1570 with_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp)
1571 {
1572 JSObject *proto = OBJ_GET_PROTO(cx, obj);
1573 if (!proto)
1574 return js_DefaultValue(cx, obj, hint, vp);
1575 return OBJ_DEFAULT_VALUE(cx, proto, hint, vp);
1576 }
1577
1578 static JSBool
with_Enumerate(JSContext * cx,JSObject * obj,JSIterateOp enum_op,jsval * statep,jsid * idp)1579 with_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
1580 jsval *statep, jsid *idp)
1581 {
1582 JSObject *proto = OBJ_GET_PROTO(cx, obj);
1583 if (!proto)
1584 return js_Enumerate(cx, obj, enum_op, statep, idp);
1585 return OBJ_ENUMERATE(cx, proto, enum_op, statep, idp);
1586 }
1587
1588 static JSBool
with_CheckAccess(JSContext * cx,JSObject * obj,jsid id,JSAccessMode mode,jsval * vp,uintN * attrsp)1589 with_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
1590 jsval *vp, uintN *attrsp)
1591 {
1592 JSObject *proto = OBJ_GET_PROTO(cx, obj);
1593 if (!proto)
1594 return js_CheckAccess(cx, obj, id, mode, vp, attrsp);
1595 return OBJ_CHECK_ACCESS(cx, proto, id, mode, vp, attrsp);
1596 }
1597
1598 static JSObject *
with_ThisObject(JSContext * cx,JSObject * obj)1599 with_ThisObject(JSContext *cx, JSObject *obj)
1600 {
1601 JSObject *proto = OBJ_GET_PROTO(cx, obj);
1602 if (!proto)
1603 return obj;
1604 return OBJ_THIS_OBJECT(cx, proto);
1605 }
1606
1607 JS_FRIEND_DATA(JSObjectOps) js_WithObjectOps = {
1608 js_NewObjectMap, js_DestroyObjectMap,
1609 with_LookupProperty, js_DefineProperty,
1610 with_GetProperty, with_SetProperty,
1611 with_GetAttributes, with_SetAttributes,
1612 with_DeleteProperty, with_DefaultValue,
1613 with_Enumerate, with_CheckAccess,
1614 with_ThisObject, NATIVE_DROP_PROPERTY,
1615 NULL, NULL,
1616 NULL, NULL,
1617 js_SetProtoOrParent, js_SetProtoOrParent,
1618 js_Mark, js_Clear,
1619 NULL, NULL
1620 };
1621
1622 static JSObjectOps *
with_getObjectOps(JSContext * cx,JSClass * clasp)1623 with_getObjectOps(JSContext *cx, JSClass *clasp)
1624 {
1625 return &js_WithObjectOps;
1626 }
1627
1628 JSClass js_WithClass = {
1629 "With",
1630 0,
1631 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
1632 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
1633 with_getObjectOps,
1634 0,0,0,0,0,0,0
1635 };
1636
1637 #if JS_HAS_OBJ_PROTO_PROP
1638 static JSBool
With(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)1639 With(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1640 {
1641 JSObject *parent, *proto;
1642 jsval v;
1643
1644 if (!JS_ReportErrorFlagsAndNumber(cx,
1645 JSREPORT_WARNING | JSREPORT_STRICT,
1646 js_GetErrorMessage, NULL,
1647 JSMSG_DEPRECATED_USAGE,
1648 js_WithClass.name)) {
1649 return JS_FALSE;
1650 }
1651
1652 if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) {
1653 obj = js_NewObject(cx, &js_WithClass, NULL, NULL);
1654 if (!obj)
1655 return JS_FALSE;
1656 *rval = OBJECT_TO_JSVAL(obj);
1657 }
1658
1659 parent = cx->fp->scopeChain;
1660 if (argc > 0) {
1661 if (!js_ValueToObject(cx, argv[0], &proto))
1662 return JS_FALSE;
1663 v = OBJECT_TO_JSVAL(proto);
1664 if (!obj_setSlot(cx, obj, INT_TO_JSVAL(JSSLOT_PROTO), &v))
1665 return JS_FALSE;
1666 if (argc > 1) {
1667 if (!js_ValueToObject(cx, argv[1], &parent))
1668 return JS_FALSE;
1669 }
1670 }
1671 v = OBJECT_TO_JSVAL(parent);
1672 return obj_setSlot(cx, obj, INT_TO_JSVAL(JSSLOT_PARENT), &v);
1673 }
1674 #endif
1675
1676 JSObject *
js_InitObjectClass(JSContext * cx,JSObject * obj)1677 js_InitObjectClass(JSContext *cx, JSObject *obj)
1678 {
1679 JSObject *proto;
1680 jsval eval;
1681
1682 #if JS_HAS_SHARP_VARS
1683 JS_ASSERT(sizeof(jsatomid) * JS_BITS_PER_BYTE >= ATOM_INDEX_LIMIT_LOG2 + 1);
1684 #endif
1685
1686 proto = JS_InitClass(cx, obj, NULL, &js_ObjectClass, Object, 1,
1687 object_props, object_methods, NULL, NULL);
1688 if (!proto)
1689 return NULL;
1690
1691 #if JS_HAS_OBJ_PROTO_PROP
1692 if (!JS_InitClass(cx, obj, NULL, &js_WithClass, With, 0,
1693 NULL, NULL, NULL, NULL)) {
1694 return NULL;
1695 }
1696 #endif
1697
1698 /* ECMA (15.1.2.1) says 'eval' is also a property of the global object. */
1699 if (!OBJ_GET_PROPERTY(cx, proto, (jsid)cx->runtime->atomState.evalAtom,
1700 &eval)) {
1701 return NULL;
1702 }
1703 if (!OBJ_DEFINE_PROPERTY(cx, obj, (jsid)cx->runtime->atomState.evalAtom,
1704 eval, NULL, NULL, 0, NULL)) {
1705 return NULL;
1706 }
1707
1708 return proto;
1709 }
1710
1711 void
js_InitObjectMap(JSObjectMap * map,jsrefcount nrefs,JSObjectOps * ops,JSClass * clasp)1712 js_InitObjectMap(JSObjectMap *map, jsrefcount nrefs, JSObjectOps *ops,
1713 JSClass *clasp)
1714 {
1715 map->nrefs = nrefs;
1716 map->ops = ops;
1717 map->nslots = JS_INITIAL_NSLOTS;
1718 map->freeslot = JSSLOT_FREE(clasp);
1719 }
1720
1721 JSObjectMap *
js_NewObjectMap(JSContext * cx,jsrefcount nrefs,JSObjectOps * ops,JSClass * clasp,JSObject * obj)1722 js_NewObjectMap(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops,
1723 JSClass *clasp, JSObject *obj)
1724 {
1725 return (JSObjectMap *) js_NewScope(cx, nrefs, ops, clasp, obj);
1726 }
1727
1728 void
js_DestroyObjectMap(JSContext * cx,JSObjectMap * map)1729 js_DestroyObjectMap(JSContext *cx, JSObjectMap *map)
1730 {
1731 js_DestroyScope(cx, (JSScope *)map);
1732 }
1733
1734 JSObjectMap *
js_HoldObjectMap(JSContext * cx,JSObjectMap * map)1735 js_HoldObjectMap(JSContext *cx, JSObjectMap *map)
1736 {
1737 JS_ASSERT(map->nrefs >= 0);
1738 JS_ATOMIC_INCREMENT(&map->nrefs);
1739 return map;
1740 }
1741
1742 JSObjectMap *
js_DropObjectMap(JSContext * cx,JSObjectMap * map,JSObject * obj)1743 js_DropObjectMap(JSContext *cx, JSObjectMap *map, JSObject *obj)
1744 {
1745 JS_ASSERT(map->nrefs > 0);
1746 JS_ATOMIC_DECREMENT(&map->nrefs);
1747 if (map->nrefs == 0) {
1748 map->ops->destroyObjectMap(cx, map);
1749 return NULL;
1750 }
1751 if (MAP_IS_NATIVE(map) && ((JSScope *)map)->object == obj)
1752 ((JSScope *)map)->object = NULL;
1753 return map;
1754 }
1755
1756 static JSBool
1757 GetClassPrototype(JSContext *cx, JSObject *scope, const char *name,
1758 JSObject **protop);
1759
1760 JSObject *
js_NewObject(JSContext * cx,JSClass * clasp,JSObject * proto,JSObject * parent)1761 js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent)
1762 {
1763 JSObject *obj, *ctor;
1764 JSObjectOps *ops;
1765 JSObjectMap *map;
1766 JSClass *protoclasp;
1767 jsval cval;
1768 uint32 nslots, i;
1769 jsval *newslots;
1770
1771 /* Allocate an object from the GC heap and zero it. */
1772 obj = (JSObject *) js_AllocGCThing(cx, GCX_OBJECT);
1773 if (!obj)
1774 return NULL;
1775
1776 /* Bootstrap the ur-object, and make it the default prototype object. */
1777 if (!proto) {
1778 if (!GetClassPrototype(cx, parent, clasp->name, &proto))
1779 goto bad;
1780 if (!proto && !GetClassPrototype(cx, parent, js_Object_str, &proto))
1781 goto bad;
1782 }
1783
1784 /* Always call the class's getObjectOps hook if it has one. */
1785 ops = clasp->getObjectOps
1786 ? clasp->getObjectOps(cx, clasp)
1787 : &js_ObjectOps;
1788
1789 /*
1790 * Share proto's map only if it has the same JSObjectOps, and only if
1791 * proto's class has the same private and reserved slots as obj's map
1792 * and class have. We assume that if prototype and object are of the
1793 * same class, they always have the same number of computed reserved
1794 * slots (returned via clasp->reserveSlots); otherwise, prototype and
1795 * object classes must have the same (null or not) reserveSlots hook.
1796 */
1797 if (proto &&
1798 (map = proto->map)->ops == ops &&
1799 ((protoclasp = OBJ_GET_CLASS(cx, proto)) == clasp ||
1800 (!((protoclasp->flags ^ clasp->flags) &
1801 (JSCLASS_HAS_PRIVATE |
1802 (JSCLASS_RESERVED_SLOTS_MASK << JSCLASS_RESERVED_SLOTS_SHIFT))) &&
1803 protoclasp->reserveSlots == clasp->reserveSlots)))
1804 {
1805 /* Default parent to the parent of the prototype's constructor. */
1806 if (!parent) {
1807 if (!OBJ_GET_PROPERTY(cx, proto,
1808 (jsid)cx->runtime->atomState.constructorAtom,
1809 &cval)) {
1810 goto bad;
1811 }
1812 if (JSVAL_IS_OBJECT(cval) && (ctor = JSVAL_TO_OBJECT(cval)) != NULL)
1813 parent = OBJ_GET_PARENT(cx, ctor);
1814 }
1815
1816 /* Share the given prototype's map. */
1817 obj->map = js_HoldObjectMap(cx, map);
1818
1819 /* Ensure that obj starts with the minimum slots for clasp. */
1820 nslots = JS_INITIAL_NSLOTS;
1821 } else {
1822 /* Leave parent alone. Allocate a new map for obj. */
1823 map = ops->newObjectMap(cx, 1, ops, clasp, obj);
1824 if (!map)
1825 goto bad;
1826 obj->map = map;
1827
1828 /* Let ops->newObjectMap set nslots so as to reserve slots. */
1829 nslots = map->nslots;
1830 }
1831
1832 /* Allocate a slots vector, with a -1'st element telling its length. */
1833 newslots = (jsval *) JS_malloc(cx, (nslots + 1) * sizeof(jsval));
1834 if (!newslots) {
1835 js_DropObjectMap(cx, obj->map, obj);
1836 obj->map = NULL;
1837 goto bad;
1838 }
1839 newslots[0] = nslots;
1840 newslots++;
1841
1842 /* Set the proto, parent, and class properties. */
1843 newslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto);
1844 newslots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(parent);
1845 newslots[JSSLOT_CLASS] = PRIVATE_TO_JSVAL(clasp);
1846
1847 /* Clear above JSSLOT_CLASS so the GC doesn't load uninitialized memory. */
1848 for (i = JSSLOT_CLASS + 1; i < nslots; i++)
1849 newslots[i] = JSVAL_VOID;
1850
1851 /* Store newslots after initializing all of 'em, just in case. */
1852 obj->slots = newslots;
1853
1854 if (cx->runtime->objectHook) {
1855 JS_KEEP_ATOMS(cx->runtime);
1856 cx->runtime->objectHook(cx, obj, JS_TRUE, cx->runtime->objectHookData);
1857 JS_UNKEEP_ATOMS(cx->runtime);
1858 }
1859
1860 return obj;
1861
1862 bad:
1863 cx->newborn[GCX_OBJECT] = NULL;
1864 return NULL;
1865 }
1866
1867 static JSBool
FindConstructor(JSContext * cx,JSObject * start,const char * name,jsval * vp)1868 FindConstructor(JSContext *cx, JSObject *start, const char *name, jsval *vp)
1869 {
1870 JSAtom *atom;
1871 JSObject *obj, *pobj;
1872 JSProperty *prop;
1873 JSScopeProperty *sprop;
1874
1875 atom = js_Atomize(cx, name, strlen(name), 0);
1876 if (!atom)
1877 return JS_FALSE;
1878
1879 if (start || (cx->fp && (start = cx->fp->scopeChain) != NULL)) {
1880 /* Find the topmost object in the scope chain. */
1881 do {
1882 obj = start;
1883 start = OBJ_GET_PARENT(cx, obj);
1884 } while (start);
1885 } else {
1886 obj = cx->globalObject;
1887 if (!obj) {
1888 *vp = JSVAL_VOID;
1889 return JS_TRUE;
1890 }
1891 }
1892
1893 JS_ASSERT(OBJ_IS_NATIVE(obj));
1894 if (!js_LookupPropertyWithFlags(cx, obj, (jsid)atom, JSRESOLVE_CLASSNAME,
1895 &pobj, &prop
1896 #if defined JS_THREADSAFE && defined DEBUG
1897 , __FILE__, __LINE__
1898 #endif
1899 )) {
1900 return JS_FALSE;
1901 }
1902 if (!prop) {
1903 *vp = JSVAL_VOID;
1904 return JS_TRUE;
1905 }
1906
1907 JS_ASSERT(OBJ_IS_NATIVE(pobj));
1908 sprop = (JSScopeProperty *) prop;
1909 JS_ASSERT(SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj)));
1910 *vp = OBJ_GET_SLOT(cx, pobj, sprop->slot);
1911 OBJ_DROP_PROPERTY(cx, pobj, prop);
1912 return JS_TRUE;
1913 }
1914
1915 JSObject *
js_ConstructObject(JSContext * cx,JSClass * clasp,JSObject * proto,JSObject * parent,uintN argc,jsval * argv)1916 js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto,
1917 JSObject *parent, uintN argc, jsval *argv)
1918 {
1919 jsval cval, rval;
1920 JSObject *obj, *ctor;
1921
1922 if (!FindConstructor(cx, parent, clasp->name, &cval))
1923 return NULL;
1924 if (JSVAL_IS_PRIMITIVE(cval)) {
1925 js_ReportIsNotFunction(cx, &cval, JSV2F_CONSTRUCT | JSV2F_SEARCH_STACK);
1926 return NULL;
1927 }
1928
1929 /*
1930 * If proto or parent are NULL, set them to Constructor.prototype and/or
1931 * Constructor.__parent__, just like JSOP_NEW does.
1932 */
1933 ctor = JSVAL_TO_OBJECT(cval);
1934 if (!parent)
1935 parent = OBJ_GET_PARENT(cx, ctor);
1936 if (!proto) {
1937 if (!OBJ_GET_PROPERTY(cx, ctor,
1938 (jsid)cx->runtime->atomState.classPrototypeAtom,
1939 &rval)) {
1940 return NULL;
1941 }
1942 if (JSVAL_IS_OBJECT(rval))
1943 proto = JSVAL_TO_OBJECT(rval);
1944 }
1945
1946 obj = js_NewObject(cx, clasp, proto, parent);
1947 if (!obj)
1948 return NULL;
1949
1950 if (!js_InternalConstruct(cx, obj, cval, argc, argv, &rval))
1951 goto bad;
1952 return JSVAL_IS_OBJECT(rval) ? JSVAL_TO_OBJECT(rval) : obj;
1953 bad:
1954 cx->newborn[GCX_OBJECT] = NULL;
1955 return NULL;
1956 }
1957
1958 void
js_FinalizeObject(JSContext * cx,JSObject * obj)1959 js_FinalizeObject(JSContext *cx, JSObject *obj)
1960 {
1961 JSObjectMap *map;
1962
1963 /* Cope with stillborn objects that have no map. */
1964 map = obj->map;
1965 if (!map)
1966 return;
1967 JS_ASSERT(obj->slots);
1968
1969 if (cx->runtime->objectHook)
1970 cx->runtime->objectHook(cx, obj, JS_FALSE, cx->runtime->objectHookData);
1971
1972 /* Remove all watchpoints with weak links to obj. */
1973 JS_ClearWatchPointsForObject(cx, obj);
1974
1975 /*
1976 * Finalize obj first, in case it needs map and slots. Optimized to use
1977 * LOCKED_OBJ_GET_CLASS instead of OBJ_GET_CLASS, so we avoid "promoting"
1978 * obj's scope from lock-free to lock-full (see jslock.c:ClaimScope) when
1979 * we're called from the GC. Only the GC should call js_FinalizeObject,
1980 * and no other threads run JS (and possibly racing to update obj->slots)
1981 * while the GC is running.
1982 */
1983 LOCKED_OBJ_GET_CLASS(obj)->finalize(cx, obj);
1984
1985 /* Drop map and free slots. */
1986 js_DropObjectMap(cx, map, obj);
1987 obj->map = NULL;
1988 JS_free(cx, obj->slots - 1);
1989 obj->slots = NULL;
1990 }
1991
1992 /* XXXbe if one adds props, deletes earlier props, adds more, the last added
1993 won't recycle the deleted props' slots. */
1994 JSBool
js_AllocSlot(JSContext * cx,JSObject * obj,uint32 * slotp)1995 js_AllocSlot(JSContext *cx, JSObject *obj, uint32 *slotp)
1996 {
1997 JSObjectMap *map;
1998 JSClass *clasp;
1999 uint32 nslots, i;
2000 jsval *newslots;
2001
2002 map = obj->map;
2003 JS_ASSERT(!MAP_IS_NATIVE(map) || ((JSScope *)map)->object == obj);
2004 clasp = LOCKED_OBJ_GET_CLASS(obj);
2005 if (map->freeslot == JSSLOT_FREE(clasp)) {
2006 /* Adjust map->freeslot to include computed reserved slots, if any. */
2007 if (clasp->reserveSlots)
2008 map->freeslot += clasp->reserveSlots(cx, obj);
2009 }
2010 nslots = map->nslots;
2011 if (map->freeslot >= nslots) {
2012 nslots = map->freeslot;
2013 JS_ASSERT(nslots >= JS_INITIAL_NSLOTS);
2014 nslots += (nslots + 1) / 2;
2015
2016 newslots = (jsval *)
2017 JS_realloc(cx, obj->slots - 1, (nslots + 1) * sizeof(jsval));
2018 if (!newslots)
2019 return JS_FALSE;
2020 for (i = 1 + newslots[0]; i <= nslots; i++)
2021 newslots[i] = JSVAL_VOID;
2022 newslots[0] = map->nslots = nslots;
2023 obj->slots = newslots + 1;
2024 }
2025
2026 #ifdef TOO_MUCH_GC
2027 obj->slots[map->freeslot] = JSVAL_VOID;
2028 #endif
2029 *slotp = map->freeslot++;
2030 return JS_TRUE;
2031 }
2032
2033 void
js_FreeSlot(JSContext * cx,JSObject * obj,uint32 slot)2034 js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot)
2035 {
2036 JSObjectMap *map;
2037 uint32 nslots;
2038 jsval *newslots;
2039
2040 OBJ_CHECK_SLOT(obj, slot);
2041 obj->slots[slot] = JSVAL_VOID;
2042 map = obj->map;
2043 JS_ASSERT(!MAP_IS_NATIVE(map) || ((JSScope *)map)->object == obj);
2044 if (map->freeslot == slot + 1)
2045 map->freeslot = slot;
2046 nslots = map->nslots;
2047 if (nslots > JS_INITIAL_NSLOTS && map->freeslot < nslots / 2) {
2048 nslots = map->freeslot;
2049 nslots += nslots / 2;
2050 if (nslots < JS_INITIAL_NSLOTS)
2051 nslots = JS_INITIAL_NSLOTS;
2052
2053 newslots = (jsval *)
2054 JS_realloc(cx, obj->slots - 1, (nslots + 1) * sizeof(jsval));
2055 if (!newslots)
2056 return;
2057 newslots[0] = map->nslots = nslots;
2058 obj->slots = newslots + 1;
2059 }
2060 }
2061
2062 #if JS_BUG_EMPTY_INDEX_ZERO
2063 #define CHECK_FOR_EMPTY_INDEX(id) \
2064 JS_BEGIN_MACRO \
2065 if (JSSTRING_LENGTH(_str) == 0) \
2066 id = JSVAL_ZERO; \
2067 JS_END_MACRO
2068 #else
2069 #define CHECK_FOR_EMPTY_INDEX(id) /* nothing */
2070 #endif
2071
2072 /* JSVAL_INT_MAX as a string */
2073 #define JSVAL_INT_MAX_STRING "1073741823"
2074
2075 #define CHECK_FOR_FUNNY_INDEX(id) \
2076 JS_BEGIN_MACRO \
2077 if (!JSVAL_IS_INT(id)) { \
2078 JSAtom *atom_ = (JSAtom *)id; \
2079 JSString *str_ = ATOM_TO_STRING(atom_); \
2080 const jschar *cp_ = str_->chars; \
2081 JSBool negative_ = (*cp_ == '-'); \
2082 if (negative_) cp_++; \
2083 if (JS7_ISDEC(*cp_) && \
2084 str_->length - negative_ <= sizeof(JSVAL_INT_MAX_STRING)-1) { \
2085 id = CheckForFunnyIndex(id, cp_, negative_); \
2086 } else { \
2087 CHECK_FOR_EMPTY_INDEX(id); \
2088 } \
2089 } \
2090 JS_END_MACRO
2091
2092 static jsid
CheckForFunnyIndex(jsid id,const jschar * cp,JSBool negative)2093 CheckForFunnyIndex(jsid id, const jschar *cp, JSBool negative)
2094 {
2095 jsuint index = JS7_UNDEC(*cp++);
2096 jsuint oldIndex = 0;
2097 jsuint c = 0;
2098
2099 if (index != 0) {
2100 while (JS7_ISDEC(*cp)) {
2101 oldIndex = index;
2102 c = JS7_UNDEC(*cp);
2103 index = 10 * index + c;
2104 cp++;
2105 }
2106 }
2107 if (*cp == 0 &&
2108 (oldIndex < (JSVAL_INT_MAX / 10) ||
2109 (oldIndex == (JSVAL_INT_MAX / 10) &&
2110 c <= (JSVAL_INT_MAX % 10)))) {
2111 if (negative)
2112 index = 0 - index;
2113 id = INT_TO_JSVAL((jsint)index);
2114 }
2115 return id;
2116 }
2117
2118 JSScopeProperty *
js_AddNativeProperty(JSContext * cx,JSObject * obj,jsid id,JSPropertyOp getter,JSPropertyOp setter,uint32 slot,uintN attrs,uintN flags,intN shortid)2119 js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id,
2120 JSPropertyOp getter, JSPropertyOp setter, uint32 slot,
2121 uintN attrs, uintN flags, intN shortid)
2122 {
2123 JSScope *scope;
2124 JSScopeProperty *sprop;
2125
2126 JS_LOCK_OBJ(cx, obj);
2127 scope = js_GetMutableScope(cx, obj);
2128 if (!scope) {
2129 sprop = NULL;
2130 } else {
2131 /*
2132 * Handle old bug that took empty string as zero index. Also convert
2133 * string indices to integers if appropriate.
2134 */
2135 CHECK_FOR_FUNNY_INDEX(id);
2136 sprop = js_AddScopeProperty(cx, scope, id, getter, setter, slot, attrs,
2137 flags, shortid);
2138 }
2139 JS_UNLOCK_OBJ(cx, obj);
2140 return sprop;
2141 }
2142
2143 JSScopeProperty *
js_ChangeNativePropertyAttrs(JSContext * cx,JSObject * obj,JSScopeProperty * sprop,uintN attrs,uintN mask,JSPropertyOp getter,JSPropertyOp setter)2144 js_ChangeNativePropertyAttrs(JSContext *cx, JSObject *obj,
2145 JSScopeProperty *sprop, uintN attrs, uintN mask,
2146 JSPropertyOp getter, JSPropertyOp setter)
2147 {
2148 JSScope *scope;
2149
2150 JS_LOCK_OBJ(cx, obj);
2151 scope = js_GetMutableScope(cx, obj);
2152 if (!scope) {
2153 sprop = NULL;
2154 } else {
2155 sprop = js_ChangeScopePropertyAttrs(cx, scope, sprop, attrs, mask,
2156 getter, setter);
2157 if (sprop) {
2158 PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, sprop->id,
2159 sprop);
2160 }
2161 }
2162 JS_UNLOCK_OBJ(cx, obj);
2163 return sprop;
2164 }
2165
2166 JSBool
js_DefineProperty(JSContext * cx,JSObject * obj,jsid id,jsval value,JSPropertyOp getter,JSPropertyOp setter,uintN attrs,JSProperty ** propp)2167 js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
2168 JSPropertyOp getter, JSPropertyOp setter, uintN attrs,
2169 JSProperty **propp)
2170 {
2171 return js_DefineNativeProperty(cx, obj, id, value, getter, setter, attrs,
2172 0, 0, propp);
2173 }
2174
2175 JSBool
js_DefineNativeProperty(JSContext * cx,JSObject * obj,jsid id,jsval value,JSPropertyOp getter,JSPropertyOp setter,uintN attrs,uintN flags,intN shortid,JSProperty ** propp)2176 js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
2177 JSPropertyOp getter, JSPropertyOp setter, uintN attrs,
2178 uintN flags, intN shortid, JSProperty **propp)
2179 {
2180 JSClass *clasp;
2181 JSScope *scope;
2182 JSProperty *prop;
2183 JSScopeProperty *sprop;
2184
2185 /*
2186 * Handle old bug that took empty string as zero index. Also convert
2187 * string indices to integers if appropriate.
2188 */
2189 CHECK_FOR_FUNNY_INDEX(id);
2190
2191 #if JS_HAS_GETTER_SETTER
2192 /*
2193 * If defining a getter or setter, we must check for its counterpart and
2194 * update the attributes and property ops. A getter or setter is really
2195 * only half of a property.
2196 */
2197 if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
2198 JSObject *pobj;
2199
2200 /*
2201 * If JS_THREADSAFE and id is found, js_LookupProperty returns with
2202 * sprop non-null and pobj locked. If pobj == obj, the property is
2203 * already in obj and obj has its own (mutable) scope. So if we are
2204 * defining a getter whose setter was already defined, or vice versa,
2205 * finish the job via js_ChangeScopePropertyAttributes, and refresh
2206 * the property cache line for (obj, id) to map sprop.
2207 */
2208 if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
2209 return JS_FALSE;
2210 sprop = (JSScopeProperty *) prop;
2211 if (sprop &&
2212 pobj == obj &&
2213 (sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
2214 sprop = js_ChangeScopePropertyAttrs(cx, OBJ_SCOPE(obj), sprop,
2215 attrs, sprop->attrs,
2216 (attrs & JSPROP_GETTER)
2217 ? getter
2218 : sprop->getter,
2219 (attrs & JSPROP_SETTER)
2220 ? setter
2221 : sprop->setter);
2222
2223 /* NB: obj == pobj, so we can share unlock code at the bottom. */
2224 if (!sprop)
2225 goto bad;
2226 goto out;
2227 }
2228
2229 if (prop) {
2230 /* NB: call OBJ_DROP_PROPERTY, as pobj might not be native. */
2231 OBJ_DROP_PROPERTY(cx, pobj, prop);
2232 prop = NULL;
2233 }
2234 }
2235 #endif /* JS_HAS_GETTER_SETTER */
2236
2237 /* Lock if object locking is required by this implementation. */
2238 JS_LOCK_OBJ(cx, obj);
2239
2240 /* Use the object's class getter and setter by default. */
2241 clasp = LOCKED_OBJ_GET_CLASS(obj);
2242 if (!getter)
2243 getter = clasp->getProperty;
2244 if (!setter)
2245 setter = clasp->setProperty;
2246
2247 /* Get obj's own scope if it has one, or create a new one for obj. */
2248 scope = js_GetMutableScope(cx, obj);
2249 if (!scope)
2250 goto bad;
2251
2252 /* Add the property to scope, or replace an existing one of the same id. */
2253 if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES)
2254 attrs |= JSPROP_SHARED;
2255 sprop = js_AddScopeProperty(cx, scope, id, getter, setter,
2256 SPROP_INVALID_SLOT, attrs, flags, shortid);
2257 if (!sprop)
2258 goto bad;
2259
2260 /* XXXbe called with lock held */
2261 if (!clasp->addProperty(cx, obj, SPROP_USERID(sprop), &value)) {
2262 (void) js_RemoveScopeProperty(cx, scope, id);
2263 goto bad;
2264 }
2265
2266 if (SPROP_HAS_VALID_SLOT(sprop, scope))
2267 LOCKED_OBJ_SET_SLOT(obj, sprop->slot, value);
2268
2269 #if JS_HAS_GETTER_SETTER
2270 out:
2271 #endif
2272 PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, id, sprop);
2273 if (propp)
2274 *propp = (JSProperty *) sprop;
2275 else
2276 JS_UNLOCK_OBJ(cx, obj);
2277 return JS_TRUE;
2278
2279 bad:
2280 JS_UNLOCK_OBJ(cx, obj);
2281 return JS_FALSE;
2282 }
2283
2284 /*
2285 * Given pc pointing after a property accessing bytecode, return true if the
2286 * access is a "object-detecting" in the sense used by web pages, e.g., when
2287 * checking whether document.all is defined.
2288 */
2289 static JSBool
Detecting(JSContext * cx,jsbytecode * pc)2290 Detecting(JSContext *cx, jsbytecode *pc)
2291 {
2292 JSScript *script;
2293 jsbytecode *endpc;
2294 JSOp op;
2295 JSAtom *atom;
2296
2297 script = cx->fp->script;
2298 for (endpc = script->code + script->length; pc < endpc; pc++) {
2299 /* General case: a branch or equality op follows the access. */
2300 op = (JSOp) *pc;
2301 if (js_CodeSpec[op].format & JOF_DETECTING)
2302 return JS_TRUE;
2303
2304 /*
2305 * Special case #1: handle (document.all == null). Don't sweat about
2306 * JS1.2's revision of the equality operators here.
2307 */
2308 if (op == JSOP_NULL) {
2309 if (++pc < endpc)
2310 return *pc == JSOP_EQ || *pc == JSOP_NE;
2311 break;
2312 }
2313
2314 /*
2315 * Special case #2: handle (document.all == undefined). Don't worry
2316 * about someone redefining undefined, which was added by Edition 3,
2317 * so was read/write for backward compatibility.
2318 */
2319 if (op == JSOP_NAME) {
2320 atom = GET_ATOM(cx, script, pc);
2321 if (atom == cx->runtime->atomState.typeAtoms[JSTYPE_VOID] &&
2322 (pc += js_CodeSpec[op].length) < endpc) {
2323 op = (JSOp) *pc;
2324 return op == JSOP_EQ || op == JSOP_NE ||
2325 op == JSOP_NEW_EQ || op == JSOP_NEW_NE;
2326 }
2327 break;
2328 }
2329
2330 /* At this point, anything but grouping means we're not detecting. */
2331 if (op != JSOP_GROUP)
2332 break;
2333 }
2334 return JS_FALSE;
2335 }
2336
2337 JSBool
js_LookupPropertyWithFlags(JSContext * cx,JSObject * obj,jsid id,uintN flags,JSObject ** objp,JSProperty ** propp,const char * file,uintN line)2338 js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags,
2339 JSObject **objp, JSProperty **propp
2340 #if defined JS_THREADSAFE && defined DEBUG
2341 , const char *file, uintN line
2342 #endif
2343 )
2344 {
2345 JSObject *start, *obj2, *proto;
2346 JSScope *scope;
2347 JSScopeProperty *sprop;
2348 JSClass *clasp;
2349 JSResolveOp resolve;
2350 JSResolvingKey key;
2351 JSResolvingEntry *entry;
2352 uint32 generation;
2353 JSNewResolveOp newresolve;
2354 jsbytecode *pc;
2355 const JSCodeSpec *cs;
2356 uint32 format;
2357 JSBool ok;
2358
2359 /*
2360 * Handle old bug that took empty string as zero index. Also convert
2361 * string indices to integers if appropriate.
2362 */
2363 CHECK_FOR_FUNNY_INDEX(id);
2364
2365 /* Search scopes starting with obj and following the prototype link. */
2366 start = obj;
2367 for (;;) {
2368 JS_LOCK_OBJ(cx, obj);
2369 SET_OBJ_INFO(obj, file, line);
2370 scope = OBJ_SCOPE(obj);
2371 if (scope->object == obj) {
2372 sprop = SCOPE_GET_PROPERTY(scope, id);
2373 } else {
2374 /* Shared prototype scope: try resolve before lookup. */
2375 sprop = NULL;
2376 }
2377
2378 /* Try obj's class resolve hook if id was not found in obj's scope. */
2379 if (!sprop) {
2380 clasp = LOCKED_OBJ_GET_CLASS(obj);
2381 resolve = clasp->resolve;
2382 if (resolve != JS_ResolveStub) {
2383 /* Avoid recursion on (obj, id) already being resolved on cx. */
2384 key.obj = obj;
2385 key.id = id;
2386
2387 /*
2388 * Once we have successfully added an entry for (obj, key) to
2389 * cx->resolvingTable, control must go through cleanup: before
2390 * returning. But note that JS_DHASH_ADD may find an existing
2391 * entry, in which case we bail to suppress runaway recursion.
2392 */
2393 if (!js_StartResolving(cx, &key, JSRESFLAG_LOOKUP, &entry)) {
2394 JS_UNLOCK_OBJ(cx, obj);
2395 return JS_FALSE;
2396 }
2397 if (!entry) {
2398 /* Already resolving id in obj -- dampen recursion. */
2399 JS_UNLOCK_OBJ(cx, obj);
2400 goto out;
2401 }
2402 generation = cx->resolvingTable->generation;
2403
2404 /* Null *propp here so we can test it at cleanup: safely. */
2405 *propp = NULL;
2406
2407 if (clasp->flags & JSCLASS_NEW_RESOLVE) {
2408 newresolve = (JSNewResolveOp)resolve;
2409 if (cx->fp && (pc = cx->fp->pc)) {
2410 cs = &js_CodeSpec[*pc];
2411 format = cs->format;
2412 if ((format & JOF_MODEMASK) != JOF_NAME)
2413 flags |= JSRESOLVE_QUALIFIED;
2414 if ((format & JOF_ASSIGNING) ||
2415 (cx->fp->flags & JSFRAME_ASSIGNING)) {
2416 flags |= JSRESOLVE_ASSIGNING;
2417 } else {
2418 pc += cs->length;
2419 if (Detecting(cx, pc))
2420 flags |= JSRESOLVE_DETECTING;
2421 }
2422 if (format & JOF_DECLARING)
2423 flags |= JSRESOLVE_DECLARING;
2424 }
2425 obj2 = (clasp->flags & JSCLASS_NEW_RESOLVE_GETS_START)
2426 ? start
2427 : NULL;
2428 JS_UNLOCK_OBJ(cx, obj);
2429
2430 /* Protect id and all atoms from a GC nested in resolve. */
2431 JS_KEEP_ATOMS(cx->runtime);
2432 ok = newresolve(cx, obj, ID_TO_VALUE(id), flags, &obj2);
2433 JS_UNKEEP_ATOMS(cx->runtime);
2434 if (!ok)
2435 goto cleanup;
2436
2437 JS_LOCK_OBJ(cx, obj);
2438 SET_OBJ_INFO(obj, file, line);
2439 if (obj2) {
2440 /* Resolved: juggle locks and lookup id again. */
2441 if (obj2 != obj) {
2442 JS_UNLOCK_OBJ(cx, obj);
2443 JS_LOCK_OBJ(cx, obj2);
2444 }
2445 scope = OBJ_SCOPE(obj2);
2446 if (!MAP_IS_NATIVE(&scope->map)) {
2447 /* Whoops, newresolve handed back a foreign obj2. */
2448 JS_ASSERT(obj2 != obj);
2449 JS_UNLOCK_OBJ(cx, obj2);
2450 ok = OBJ_LOOKUP_PROPERTY(cx, obj2, id, objp, propp);
2451 if (!ok || *propp)
2452 goto cleanup;
2453 JS_LOCK_OBJ(cx, obj2);
2454 } else {
2455 /*
2456 * Require that obj2 have its own scope now, as we
2457 * do for old-style resolve. If it doesn't, then
2458 * id was not truly resolved, and we'll find it in
2459 * the proto chain, or miss it if obj2's proto is
2460 * not on obj's proto chain. That last case is a
2461 * "too bad!" case.
2462 */
2463 if (scope->object == obj2)
2464 sprop = SCOPE_GET_PROPERTY(scope, id);
2465 }
2466 if (obj2 != obj && !sprop) {
2467 JS_UNLOCK_OBJ(cx, obj2);
2468 JS_LOCK_OBJ(cx, obj);
2469 }
2470 }
2471 } else {
2472 /*
2473 * Old resolve always requires id re-lookup if obj owns
2474 * its scope after resolve returns.
2475 */
2476 JS_UNLOCK_OBJ(cx, obj);
2477 ok = resolve(cx, obj, ID_TO_VALUE(id));
2478 if (!ok)
2479 goto cleanup;
2480 JS_LOCK_OBJ(cx, obj);
2481 SET_OBJ_INFO(obj, file, line);
2482 scope = OBJ_SCOPE(obj);
2483 JS_ASSERT(MAP_IS_NATIVE(&scope->map));
2484 if (scope->object == obj)
2485 sprop = SCOPE_GET_PROPERTY(scope, id);
2486 }
2487
2488 cleanup:
2489 js_StopResolving(cx, &key, JSRESFLAG_LOOKUP, entry, generation);
2490 if (!ok || *propp)
2491 return ok;
2492 }
2493 }
2494
2495 if (sprop) {
2496 JS_ASSERT(OBJ_SCOPE(obj) == scope);
2497 *objp = scope->object; /* XXXbe hide in jsscope.[ch] */
2498
2499 *propp = (JSProperty *) sprop;
2500 return JS_TRUE;
2501 }
2502
2503 proto = LOCKED_OBJ_GET_PROTO(obj);
2504 JS_UNLOCK_OBJ(cx, obj);
2505 if (!proto)
2506 break;
2507 if (!OBJ_IS_NATIVE(proto))
2508 return OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp);
2509 obj = proto;
2510 }
2511
2512 out:
2513 *objp = NULL;
2514 *propp = NULL;
2515 return JS_TRUE;
2516 }
2517
2518 #if defined JS_THREADSAFE && defined DEBUG
2519 JS_FRIEND_API(JSBool)
_js_LookupProperty(JSContext * cx,JSObject * obj,jsid id,JSObject ** objp,JSProperty ** propp,const char * file,uintN line)2520 _js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
2521 JSProperty **propp, const char *file, uintN line)
2522 {
2523 return js_LookupPropertyWithFlags(cx, obj, id, 0, objp, propp, file, line);
2524 }
2525 #else
2526 JS_FRIEND_API(JSBool)
js_LookupProperty(JSContext * cx,JSObject * obj,jsid id,JSObject ** objp,JSProperty ** propp)2527 js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
2528 JSProperty **propp)
2529 {
2530 return js_LookupPropertyWithFlags(cx, obj, id, 0, objp, propp);
2531 }
2532 #endif
2533
2534 JS_FRIEND_API(JSBool)
js_FindProperty(JSContext * cx,jsid id,JSObject ** objp,JSObject ** pobjp,JSProperty ** propp)2535 js_FindProperty(JSContext *cx, jsid id, JSObject **objp, JSObject **pobjp,
2536 JSProperty **propp)
2537 {
2538 JSRuntime *rt;
2539 JSObject *obj, *pobj, *lastobj;
2540 JSScopeProperty *sprop;
2541 JSProperty *prop;
2542
2543 rt = cx->runtime;
2544 obj = cx->fp->scopeChain;
2545 do {
2546 /* Try the property cache and return immediately on cache hit. */
2547 if (OBJ_IS_NATIVE(obj)) {
2548 JS_LOCK_OBJ(cx, obj);
2549 PROPERTY_CACHE_TEST(&rt->propertyCache, obj, id, sprop);
2550 if (sprop) {
2551 JS_ASSERT(OBJ_IS_NATIVE(obj));
2552 *objp = obj;
2553 *pobjp = obj;
2554 *propp = (JSProperty *) sprop;
2555 return JS_TRUE;
2556 }
2557 JS_UNLOCK_OBJ(cx, obj);
2558 }
2559
2560 /* If cache miss, take the slow path. */
2561 if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
2562 return JS_FALSE;
2563 if (prop) {
2564 if (OBJ_IS_NATIVE(pobj)) {
2565 sprop = (JSScopeProperty *) prop;
2566 PROPERTY_CACHE_FILL(&rt->propertyCache, pobj, id, sprop);
2567 }
2568 *objp = obj;
2569 *pobjp = pobj;
2570 *propp = prop;
2571 return JS_TRUE;
2572 }
2573 lastobj = obj;
2574 } while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL);
2575
2576 *objp = lastobj;
2577 *pobjp = NULL;
2578 *propp = NULL;
2579 return JS_TRUE;
2580 }
2581
2582 JSObject *
js_FindIdentifierBase(JSContext * cx,jsid id)2583 js_FindIdentifierBase(JSContext *cx, jsid id)
2584 {
2585 JSObject *obj, *pobj;
2586 JSProperty *prop;
2587
2588 /*
2589 * Look for id's property along the "with" statement chain and the
2590 * statically-linked scope chain.
2591 */
2592 if (!js_FindProperty(cx, id, &obj, &pobj, &prop))
2593 return NULL;
2594 if (prop) {
2595 OBJ_DROP_PROPERTY(cx, pobj, prop);
2596 return obj;
2597 }
2598
2599 /*
2600 * Use the top-level scope from the scope chain, which won't end in the
2601 * same scope as cx->globalObject for cross-context function calls.
2602 */
2603 JS_ASSERT(obj);
2604
2605 /*
2606 * Property not found. Give a strict warning if binding an undeclared
2607 * top-level variable.
2608 */
2609 if (JS_HAS_STRICT_OPTION(cx)) {
2610 JSString *str = JSVAL_TO_STRING(ID_TO_VALUE(id));
2611 if (!JS_ReportErrorFlagsAndNumber(cx,
2612 JSREPORT_WARNING | JSREPORT_STRICT,
2613 js_GetErrorMessage, NULL,
2614 JSMSG_UNDECLARED_VAR,
2615 JS_GetStringBytes(str))) {
2616 return NULL;
2617 }
2618 }
2619 return obj;
2620 }
2621
2622 JSBool
js_GetProperty(JSContext * cx,JSObject * obj,jsid id,jsval * vp)2623 js_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
2624 {
2625 JSObject *obj2;
2626 JSProperty *prop;
2627 JSScope *scope;
2628 JSScopeProperty *sprop;
2629 uint32 slot;
2630
2631 /*
2632 * Handle old bug that took empty string as zero index. Also convert
2633 * string indices to integers if appropriate.
2634 */
2635 CHECK_FOR_FUNNY_INDEX(id);
2636
2637 if (!js_LookupProperty(cx, obj, id, &obj2, &prop))
2638 return JS_FALSE;
2639 if (!prop) {
2640 jsval default_val;
2641
2642 #if JS_BUG_NULL_INDEX_PROPS
2643 /* Indexed properties defaulted to null in old versions. */
2644 default_val = (JSVAL_IS_INT(id) && JSVAL_TO_INT(id) >= 0)
2645 ? JSVAL_NULL
2646 : JSVAL_VOID;
2647 #else
2648 default_val = JSVAL_VOID;
2649 #endif
2650 *vp = default_val;
2651
2652 if (!OBJ_GET_CLASS(cx, obj)->getProperty(cx, obj, ID_TO_VALUE(id), vp))
2653 return JS_FALSE;
2654
2655 /*
2656 * Give a strict warning if foo.bar is evaluated by a script for an
2657 * object foo with no property named 'bar'.
2658 */
2659 if (JS_HAS_STRICT_OPTION(cx) &&
2660 *vp == default_val &&
2661 cx->fp && cx->fp->pc &&
2662 (*cx->fp->pc == JSOP_GETPROP || *cx->fp->pc == JSOP_GETELEM))
2663 {
2664 jsbytecode *pc;
2665 JSString *str;
2666
2667 /* Kludge to allow (typeof foo == "undefined") tests. */
2668 JS_ASSERT(cx->fp->script);
2669 pc = cx->fp->pc;
2670 pc += js_CodeSpec[*pc].length;
2671 if (Detecting(cx, pc))
2672 return JS_TRUE;
2673
2674 /* Ok, bad undefined property reference: whine about it. */
2675 str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK,
2676 ID_TO_VALUE(id), NULL);
2677 if (!str ||
2678 !JS_ReportErrorFlagsAndNumber(cx,
2679 JSREPORT_WARNING|JSREPORT_STRICT,
2680 js_GetErrorMessage, NULL,
2681 JSMSG_UNDEFINED_PROP,
2682 JS_GetStringBytes(str))) {
2683 return JS_FALSE;
2684 }
2685 }
2686 return JS_TRUE;
2687 }
2688
2689 if (!OBJ_IS_NATIVE(obj2)) {
2690 OBJ_DROP_PROPERTY(cx, obj2, prop);
2691 return OBJ_GET_PROPERTY(cx, obj2, id, vp);
2692 }
2693
2694 /* Unlock obj2 before calling getter, relock after to avoid deadlock. */
2695 scope = OBJ_SCOPE(obj2);
2696 sprop = (JSScopeProperty *) prop;
2697 slot = sprop->slot;
2698 if (slot != SPROP_INVALID_SLOT) {
2699 JS_ASSERT(slot < obj2->map->freeslot);
2700 *vp = LOCKED_OBJ_GET_SLOT(obj2, slot);
2701
2702 /* If sprop has a stub getter, we're done. */
2703 if (!sprop->getter)
2704 goto out;
2705 } else {
2706 *vp = JSVAL_VOID;
2707 }
2708
2709 JS_UNLOCK_SCOPE(cx, scope);
2710 if (!SPROP_GET(cx, sprop, obj, obj2, vp))
2711 return JS_FALSE;
2712 JS_LOCK_SCOPE(cx, scope);
2713
2714 if (SPROP_HAS_VALID_SLOT(sprop, scope)) {
2715 LOCKED_OBJ_SET_SLOT(obj2, slot, *vp);
2716 PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj2, id, sprop);
2717 }
2718
2719 out:
2720 JS_UNLOCK_SCOPE(cx, scope);
2721 return JS_TRUE;
2722 }
2723
2724 JSBool
js_SetProperty(JSContext * cx,JSObject * obj,jsid id,jsval * vp)2725 js_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
2726 {
2727 JSObject *pobj;
2728 JSProperty *prop;
2729 JSScopeProperty *sprop;
2730 JSScope *scope;
2731 uintN attrs, flags;
2732 intN shortid;
2733 JSClass *clasp;
2734 JSPropertyOp getter, setter;
2735 jsval pval;
2736 uint32 slot;
2737
2738 /*
2739 * Handle old bug that took empty string as zero index. Also convert
2740 * string indices to integers if appropriate.
2741 */
2742 CHECK_FOR_FUNNY_INDEX(id);
2743
2744 if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
2745 return JS_FALSE;
2746
2747 if (prop && !OBJ_IS_NATIVE(pobj)) {
2748 OBJ_DROP_PROPERTY(cx, pobj, prop);
2749 prop = NULL;
2750 }
2751 sprop = (JSScopeProperty *) prop;
2752
2753 /*
2754 * Now either sprop is null, meaning id was not found in obj or one of its
2755 * prototypes; or sprop is non-null, meaning id was found in pobj's scope.
2756 * If JS_THREADSAFE and sprop is non-null, then scope is locked, and sprop
2757 * is held: we must OBJ_DROP_PROPERTY or JS_UNLOCK_SCOPE before we return
2758 * (the two are equivalent for native objects, but we use JS_UNLOCK_SCOPE
2759 * because it is cheaper).
2760 */
2761 attrs = JSPROP_ENUMERATE;
2762 flags = 0;
2763 shortid = 0;
2764 clasp = OBJ_GET_CLASS(cx, obj);
2765 getter = clasp->getProperty;
2766 setter = clasp->setProperty;
2767
2768 if (sprop) {
2769 /*
2770 * Set scope for use below. It was locked by js_LookupProperty, and
2771 * we know pobj owns it (i.e., scope->object == pobj). Therefore we
2772 * optimize JS_UNLOCK_OBJ(cx, pobj) into JS_UNLOCK_SCOPE(cx, scope).
2773 */
2774 scope = OBJ_SCOPE(pobj);
2775
2776 attrs = sprop->attrs;
2777 if ((attrs & JSPROP_READONLY) ||
2778 (SCOPE_IS_SEALED(scope) && pobj == obj)) {
2779 JS_UNLOCK_SCOPE(cx, scope);
2780 if ((attrs & JSPROP_READONLY) && JSVERSION_IS_ECMA(cx->version))
2781 return JS_TRUE;
2782 goto read_only_error;
2783 }
2784
2785 if (pobj != obj) {
2786 /*
2787 * We found id in a prototype object: prepare to share or shadow.
2788 * NB: Thanks to the immutable, garbage-collected property tree
2789 * maintained by jsscope.c in cx->runtime, we needn't worry about
2790 * sprop going away behind our back after we've unlocked scope.
2791 */
2792 JS_UNLOCK_SCOPE(cx, scope);
2793
2794 /* Don't clone a shared prototype property. */
2795 if (attrs & JSPROP_SHARED)
2796 return SPROP_SET(cx, sprop, obj, pobj, vp);
2797
2798 /* Restore attrs to the ECMA default for new properties. */
2799 attrs = JSPROP_ENUMERATE;
2800
2801 /*
2802 * Preserve the shortid, getter, and setter when shadowing any
2803 * property that has a shortid. An old API convention requires
2804 * that the property's getter and setter functions receive the
2805 * shortid, not id, when they are called on the shadow we are
2806 * about to create in obj's scope.
2807 */
2808 if (sprop->flags & SPROP_HAS_SHORTID) {
2809 flags = SPROP_HAS_SHORTID;
2810 shortid = sprop->shortid;
2811 getter = sprop->getter;
2812 setter = sprop->setter;
2813 }
2814
2815 /*
2816 * Forget we found the proto-property now that we've copied any
2817 * needed member values.
2818 */
2819 sprop = NULL;
2820 }
2821 #ifdef __GNUC__ /* suppress bogus gcc warnings */
2822 } else {
2823 scope = NULL;
2824 #endif
2825 }
2826
2827 if (!sprop) {
2828 if (SCOPE_IS_SEALED(OBJ_SCOPE(obj)) && OBJ_SCOPE(obj)->object == obj)
2829 goto read_only_error;
2830
2831 /* Find or make a property descriptor with the right heritage. */
2832 JS_LOCK_OBJ(cx, obj);
2833 scope = js_GetMutableScope(cx, obj);
2834 if (!scope) {
2835 JS_UNLOCK_OBJ(cx, obj);
2836 return JS_FALSE;
2837 }
2838 if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES)
2839 attrs |= JSPROP_SHARED;
2840 sprop = js_AddScopeProperty(cx, scope, id, getter, setter,
2841 SPROP_INVALID_SLOT, attrs, flags, shortid);
2842 if (!sprop) {
2843 JS_UNLOCK_SCOPE(cx, scope);
2844 return JS_FALSE;
2845 }
2846
2847 /* XXXbe called with obj locked */
2848 if (!clasp->addProperty(cx, obj, SPROP_USERID(sprop), vp)) {
2849 (void) js_RemoveScopeProperty(cx, scope, id);
2850 JS_UNLOCK_SCOPE(cx, scope);
2851 return JS_FALSE;
2852 }
2853
2854 /* Initialize new property value (passed to setter) to undefined. */
2855 if (SPROP_HAS_VALID_SLOT(sprop, scope))
2856 LOCKED_OBJ_SET_SLOT(obj, sprop->slot, JSVAL_VOID);
2857
2858 PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, id, sprop);
2859 }
2860
2861 /* Get the current property value from its slot. */
2862 slot = sprop->slot;
2863 if (slot != SPROP_INVALID_SLOT) {
2864 JS_ASSERT(slot < obj->map->freeslot);
2865 pval = LOCKED_OBJ_GET_SLOT(obj, slot);
2866
2867 /* If sprop has a stub setter, keep scope locked and just store *vp. */
2868 if (!sprop->setter)
2869 goto set_slot;
2870 }
2871
2872 /* Avoid deadlock by unlocking obj's scope while calling sprop's setter. */
2873 JS_UNLOCK_SCOPE(cx, scope);
2874
2875 /* Let the setter modify vp before copying from it to obj->slots[slot]. */
2876 if (!SPROP_SET(cx, sprop, obj, obj, vp))
2877 return JS_FALSE;
2878
2879 /* Relock obj's scope until we are done with sprop. */
2880 JS_LOCK_SCOPE(cx, scope);
2881
2882 /*
2883 * Check whether sprop is still around (was not deleted), and whether it
2884 * has a slot (it may never have had one, or we may have lost a race with
2885 * someone who cleared scope).
2886 */
2887 if (SPROP_HAS_VALID_SLOT(sprop, scope)) {
2888 set_slot:
2889 GC_POKE(cx, pval);
2890 LOCKED_OBJ_SET_SLOT(obj, slot, *vp);
2891 }
2892 JS_UNLOCK_SCOPE(cx, scope);
2893 return JS_TRUE;
2894
2895 read_only_error: {
2896 JSString *str = js_DecompileValueGenerator(cx,
2897 JSDVG_IGNORE_STACK,
2898 ID_TO_VALUE(id),
2899 NULL);
2900 if (str) {
2901 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2902 JSMSG_READ_ONLY,
2903 JS_GetStringBytes(str));
2904 }
2905 return JS_FALSE;
2906 }
2907 }
2908
2909 JSBool
js_GetAttributes(JSContext * cx,JSObject * obj,jsid id,JSProperty * prop,uintN * attrsp)2910 js_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
2911 uintN *attrsp)
2912 {
2913 JSBool noprop, ok;
2914 JSScopeProperty *sprop;
2915
2916 noprop = !prop;
2917 if (noprop) {
2918 if (!js_LookupProperty(cx, obj, id, &obj, &prop))
2919 return JS_FALSE;
2920 if (!prop) {
2921 *attrsp = 0;
2922 return JS_TRUE;
2923 }
2924 if (!OBJ_IS_NATIVE(obj)) {
2925 ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, attrsp);
2926 OBJ_DROP_PROPERTY(cx, obj, prop);
2927 return ok;
2928 }
2929 }
2930 sprop = (JSScopeProperty *)prop;
2931 *attrsp = sprop->attrs;
2932 if (noprop)
2933 OBJ_DROP_PROPERTY(cx, obj, prop);
2934 return JS_TRUE;
2935 }
2936
2937 JSBool
js_SetAttributes(JSContext * cx,JSObject * obj,jsid id,JSProperty * prop,uintN * attrsp)2938 js_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
2939 uintN *attrsp)
2940 {
2941 JSBool noprop, ok;
2942 JSScopeProperty *sprop;
2943
2944 noprop = !prop;
2945 if (noprop) {
2946 if (!js_LookupProperty(cx, obj, id, &obj, &prop))
2947 return JS_FALSE;
2948 if (!prop)
2949 return JS_TRUE;
2950 if (!OBJ_IS_NATIVE(obj)) {
2951 ok = OBJ_SET_ATTRIBUTES(cx, obj, id, prop, attrsp);
2952 OBJ_DROP_PROPERTY(cx, obj, prop);
2953 return ok;
2954 }
2955 }
2956 sprop = (JSScopeProperty *)prop;
2957 sprop = js_ChangeNativePropertyAttrs(cx, obj, sprop,
2958 *attrsp &
2959 ~(JSPROP_GETTER | JSPROP_SETTER), 0,
2960 sprop->getter, sprop->setter);
2961 if (noprop)
2962 OBJ_DROP_PROPERTY(cx, obj, prop);
2963 return (sprop != NULL);
2964 }
2965
2966 JSBool
js_DeleteProperty(JSContext * cx,JSObject * obj,jsid id,jsval * rval)2967 js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval)
2968 {
2969 #if JS_HAS_PROP_DELETE
2970
2971 JSObject *proto;
2972 JSProperty *prop;
2973 JSScopeProperty *sprop;
2974 JSString *str;
2975 JSScope *scope;
2976 JSBool ok;
2977
2978 *rval = JSVERSION_IS_ECMA(cx->version) ? JSVAL_TRUE : JSVAL_VOID;
2979
2980 /*
2981 * Handle old bug that took empty string as zero index. Also convert
2982 * string indices to integers if appropriate.
2983 */
2984 CHECK_FOR_FUNNY_INDEX(id);
2985
2986 if (!js_LookupProperty(cx, obj, id, &proto, &prop))
2987 return JS_FALSE;
2988 if (!prop || proto != obj) {
2989 /*
2990 * If the property was found in a native prototype, check whether it's
2991 * shared and permanent. Such a property stands for direct properties
2992 * in all delegating objects, matching ECMA semantics without bloating
2993 * each delegating object.
2994 */
2995 if (prop) {
2996 if (OBJ_IS_NATIVE(proto)) {
2997 sprop = (JSScopeProperty *)prop;
2998 if (SPROP_IS_SHARED_PERMANENT(sprop))
2999 *rval = JSVAL_FALSE;
3000 }
3001 OBJ_DROP_PROPERTY(cx, proto, prop);
3002 if (*rval == JSVAL_FALSE)
3003 return JS_TRUE;
3004 }
3005
3006 /*
3007 * If no property, or the property comes unshared or impermanent from
3008 * a prototype, call the class's delProperty hook, passing rval as the
3009 * result parameter.
3010 */
3011 return OBJ_GET_CLASS(cx, obj)->delProperty(cx, obj, ID_TO_VALUE(id),
3012 rval);
3013 }
3014
3015 sprop = (JSScopeProperty *)prop;
3016 if (sprop->attrs & JSPROP_PERMANENT) {
3017 OBJ_DROP_PROPERTY(cx, obj, prop);
3018 if (JSVERSION_IS_ECMA(cx->version)) {
3019 *rval = JSVAL_FALSE;
3020 return JS_TRUE;
3021 }
3022 str = js_DecompileValueGenerator(cx, JSDVG_IGNORE_STACK,
3023 ID_TO_VALUE(id), NULL);
3024 if (str) {
3025 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3026 JSMSG_PERMANENT, JS_GetStringBytes(str));
3027 }
3028 return JS_FALSE;
3029 }
3030
3031 /* XXXbe called with obj locked */
3032 if (!LOCKED_OBJ_GET_CLASS(obj)->delProperty(cx, obj, SPROP_USERID(sprop),
3033 rval)) {
3034 OBJ_DROP_PROPERTY(cx, obj, prop);
3035 return JS_FALSE;
3036 }
3037
3038 scope = OBJ_SCOPE(obj);
3039 if (SPROP_HAS_VALID_SLOT(sprop, scope))
3040 GC_POKE(cx, LOCKED_OBJ_GET_SLOT(obj, sprop->slot));
3041
3042 PROPERTY_CACHE_FILL(&cx->runtime->propertyCache, obj, id, NULL);
3043 ok = js_RemoveScopeProperty(cx, scope, id);
3044 OBJ_DROP_PROPERTY(cx, obj, prop);
3045 return ok;
3046
3047 #else /* !JS_HAS_PROP_DELETE */
3048
3049 jsval null = JSVAL_NULL;
3050
3051 *rval = JSVAL_VOID;
3052 return js_SetProperty(cx, obj, id, &null);
3053
3054 #endif /* !JS_HAS_PROP_DELETE */
3055 }
3056
3057 JSBool
js_DefaultValue(JSContext * cx,JSObject * obj,JSType hint,jsval * vp)3058 js_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp)
3059 {
3060 jsval v;
3061 JSString *str;
3062
3063 v = OBJECT_TO_JSVAL(obj);
3064 switch (hint) {
3065 case JSTYPE_STRING:
3066 /*
3067 * Propagate the exception if js_TryMethod finds an appropriate
3068 * method, and calling that method returned failure.
3069 */
3070 if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0, NULL,
3071 &v))
3072 return JS_FALSE;
3073
3074 if (!JSVAL_IS_PRIMITIVE(v)) {
3075 if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, hint, &v))
3076 return JS_FALSE;
3077
3078 /*
3079 * JS1.2 never failed (except for malloc failure) to convert an
3080 * object to a string. ECMA requires an error if both toString
3081 * and valueOf fail to produce a primitive value.
3082 */
3083 if (!JSVAL_IS_PRIMITIVE(v) && cx->version == JSVERSION_1_2) {
3084 char *bytes = JS_smprintf("[object %s]",
3085 OBJ_GET_CLASS(cx, obj)->name);
3086 if (!bytes)
3087 return JS_FALSE;
3088 str = JS_NewString(cx, bytes, strlen(bytes));
3089 if (!str) {
3090 free(bytes);
3091 return JS_FALSE;
3092 }
3093 v = STRING_TO_JSVAL(str);
3094 goto out;
3095 }
3096 }
3097 break;
3098
3099 default:
3100 if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, hint, &v))
3101 return JS_FALSE;
3102 if (!JSVAL_IS_PRIMITIVE(v)) {
3103 JSType type = JS_TypeOfValue(cx, v);
3104 if (type == hint ||
3105 (type == JSTYPE_FUNCTION && hint == JSTYPE_OBJECT)) {
3106 goto out;
3107 }
3108 /* Don't convert to string (source object literal) for JS1.2. */
3109 if (cx->version == JSVERSION_1_2 && hint == JSTYPE_BOOLEAN)
3110 goto out;
3111 if (!js_TryMethod(cx, obj, cx->runtime->atomState.toStringAtom, 0,
3112 NULL, &v))
3113 return JS_FALSE;
3114 }
3115 break;
3116 }
3117 if (!JSVAL_IS_PRIMITIVE(v)) {
3118 /* Avoid recursive death through js_DecompileValueGenerator. */
3119 if (hint == JSTYPE_STRING) {
3120 str = JS_InternString(cx, OBJ_GET_CLASS(cx, obj)->name);
3121 if (!str)
3122 return JS_FALSE;
3123 } else {
3124 str = NULL;
3125 }
3126 *vp = OBJECT_TO_JSVAL(obj);
3127 str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, str);
3128 if (str) {
3129 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3130 JSMSG_CANT_CONVERT_TO,
3131 JS_GetStringBytes(str),
3132 (hint == JSTYPE_VOID)
3133 ? "primitive type"
3134 : js_type_str[hint]);
3135 }
3136 return JS_FALSE;
3137 }
3138 out:
3139 *vp = v;
3140 return JS_TRUE;
3141 }
3142
3143 JSIdArray *
js_NewIdArray(JSContext * cx,jsint length)3144 js_NewIdArray(JSContext *cx, jsint length)
3145 {
3146 JSIdArray *ida;
3147
3148 ida = (JSIdArray *)
3149 JS_malloc(cx, sizeof(JSIdArray) + (length - 1) * sizeof(jsval));
3150 if (ida)
3151 ida->length = length;
3152 return ida;
3153 }
3154
3155 JSIdArray *
js_GrowIdArray(JSContext * cx,JSIdArray * ida,jsint length)3156 js_GrowIdArray(JSContext *cx, JSIdArray *ida, jsint length)
3157 {
3158 ida = (JSIdArray *)
3159 JS_realloc(cx, ida, sizeof(JSIdArray) + (length - 1) * sizeof(jsval));
3160 if (ida)
3161 ida->length = length;
3162 return ida;
3163 }
3164
3165 /* Private type used to iterate over all properties of a native JS object */
3166 typedef struct JSNativeIteratorState {
3167 jsint next_index; /* index into jsid array */
3168 JSIdArray *ida; /* All property ids in enumeration */
3169 } JSNativeIteratorState;
3170
3171 /*
3172 * This function is used to enumerate the properties of native JSObjects
3173 * and those host objects that do not define a JSNewEnumerateOp-style iterator
3174 * function.
3175 */
3176 JSBool
js_Enumerate(JSContext * cx,JSObject * obj,JSIterateOp enum_op,jsval * statep,jsid * idp)3177 js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
3178 jsval *statep, jsid *idp)
3179 {
3180 JSObject *proto_obj;
3181 JSClass *clasp;
3182 JSEnumerateOp enumerate;
3183 JSScopeProperty *sprop, *lastProp;
3184 jsint i, length;
3185 JSScope *scope;
3186 JSIdArray *ida;
3187 JSNativeIteratorState *state;
3188
3189 clasp = OBJ_GET_CLASS(cx, obj);
3190 enumerate = clasp->enumerate;
3191 if (clasp->flags & JSCLASS_NEW_ENUMERATE)
3192 return ((JSNewEnumerateOp) enumerate)(cx, obj, enum_op, statep, idp);
3193
3194 switch (enum_op) {
3195
3196 case JSENUMERATE_INIT:
3197 if (!enumerate(cx, obj))
3198 goto init_error;
3199 length = 0;
3200
3201 /*
3202 * The set of all property ids is pre-computed when the iterator
3203 * is initialized so as to avoid problems with properties being
3204 * deleted during the iteration.
3205 */
3206 JS_LOCK_OBJ(cx, obj);
3207 scope = OBJ_SCOPE(obj);
3208
3209 /*
3210 * If this object shares a scope with its prototype, don't enumerate
3211 * its properties. Otherwise they will be enumerated a second time
3212 * when the prototype object is enumerated.
3213 */
3214 proto_obj = OBJ_GET_PROTO(cx, obj);
3215 if (proto_obj && scope == OBJ_SCOPE(proto_obj)) {
3216 ida = js_NewIdArray(cx, 0);
3217 if (!ida) {
3218 JS_UNLOCK_OBJ(cx, obj);
3219 goto init_error;
3220 }
3221 } else {
3222 /* Object has a private scope; Enumerate all props in scope. */
3223 for (sprop = lastProp = SCOPE_LAST_PROP(scope); sprop;
3224 sprop = sprop->parent) {
3225 if ((
3226 #ifdef DUMP_CALL_TABLE
3227 (cx->options & JSOPTION_LOGCALL_TOSOURCE) ||
3228 #endif
3229 (sprop->attrs & JSPROP_ENUMERATE)) &&
3230 !(sprop->flags & SPROP_IS_ALIAS) &&
3231 (!SCOPE_HAD_MIDDLE_DELETE(scope) ||
3232 SCOPE_HAS_PROPERTY(scope, sprop))) {
3233 length++;
3234 }
3235 }
3236 ida = js_NewIdArray(cx, length);
3237 if (!ida) {
3238 JS_UNLOCK_OBJ(cx, obj);
3239 goto init_error;
3240 }
3241 i = length;
3242 for (sprop = lastProp; sprop; sprop = sprop->parent) {
3243 if ((
3244 #ifdef DUMP_CALL_TABLE
3245 (cx->options & JSOPTION_LOGCALL_TOSOURCE) ||
3246 #endif
3247 (sprop->attrs & JSPROP_ENUMERATE)) &&
3248 !(sprop->flags & SPROP_IS_ALIAS) &&
3249 (!SCOPE_HAD_MIDDLE_DELETE(scope) ||
3250 SCOPE_HAS_PROPERTY(scope, sprop))) {
3251 JS_ASSERT(i > 0);
3252 ida->vector[--i] = sprop->id;
3253 }
3254 }
3255 }
3256 JS_UNLOCK_OBJ(cx, obj);
3257
3258 state = (JSNativeIteratorState *)
3259 JS_malloc(cx, sizeof(JSNativeIteratorState));
3260 if (!state) {
3261 JS_DestroyIdArray(cx, ida);
3262 goto init_error;
3263 }
3264 state->ida = ida;
3265 state->next_index = 0;
3266 *statep = PRIVATE_TO_JSVAL(state);
3267 if (idp)
3268 *idp = INT_TO_JSVAL(length);
3269 return JS_TRUE;
3270
3271 case JSENUMERATE_NEXT:
3272 state = (JSNativeIteratorState *) JSVAL_TO_PRIVATE(*statep);
3273 ida = state->ida;
3274 length = ida->length;
3275 if (state->next_index != length) {
3276 *idp = ida->vector[state->next_index++];
3277 return JS_TRUE;
3278 }
3279
3280 /* Fall through ... */
3281
3282 case JSENUMERATE_DESTROY:
3283 state = (JSNativeIteratorState *) JSVAL_TO_PRIVATE(*statep);
3284 JS_DestroyIdArray(cx, state->ida);
3285 JS_free(cx, state);
3286 *statep = JSVAL_NULL;
3287 return JS_TRUE;
3288
3289 default:
3290 JS_ASSERT(0);
3291 return JS_FALSE;
3292 }
3293
3294 init_error:
3295 *statep = JSVAL_NULL;
3296 return JS_FALSE;
3297 }
3298
3299 JSBool
js_CheckAccess(JSContext * cx,JSObject * obj,jsid id,JSAccessMode mode,jsval * vp,uintN * attrsp)3300 js_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
3301 jsval *vp, uintN *attrsp)
3302 {
3303 JSObject *pobj;
3304 JSProperty *prop;
3305 JSScopeProperty *sprop;
3306 JSClass *clasp;
3307 JSBool ok;
3308
3309 if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
3310 return JS_FALSE;
3311 if (!prop) {
3312 *vp = JSVAL_VOID;
3313 *attrsp = 0;
3314 clasp = OBJ_GET_CLASS(cx, obj);
3315 return !clasp->checkAccess ||
3316 clasp->checkAccess(cx, obj, ID_TO_VALUE(id), mode, vp);
3317 }
3318 if (!OBJ_IS_NATIVE(pobj)) {
3319 OBJ_DROP_PROPERTY(cx, pobj, prop);
3320 return OBJ_CHECK_ACCESS(cx, pobj, id, mode, vp, attrsp);
3321 }
3322 sprop = (JSScopeProperty *)prop;
3323 *vp = (SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj)))
3324 ? LOCKED_OBJ_GET_SLOT(pobj, sprop->slot)
3325 : JSVAL_VOID;
3326 *attrsp = sprop->attrs;
3327 clasp = LOCKED_OBJ_GET_CLASS(obj);
3328 if (clasp->checkAccess) {
3329 JS_UNLOCK_OBJ(cx, pobj);
3330 ok = clasp->checkAccess(cx, obj, ID_TO_VALUE(id), mode, vp);
3331 JS_LOCK_OBJ(cx, pobj);
3332 } else {
3333 ok = JS_TRUE;
3334 }
3335 OBJ_DROP_PROPERTY(cx, pobj, prop);
3336 return ok;
3337 }
3338
3339 #ifdef JS_THREADSAFE
3340 void
js_DropProperty(JSContext * cx,JSObject * obj,JSProperty * prop)3341 js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop)
3342 {
3343 JS_UNLOCK_OBJ(cx, obj);
3344 }
3345 #endif
3346
3347 static void
ReportIsNotFunction(JSContext * cx,jsval * vp,uintN flags)3348 ReportIsNotFunction(JSContext *cx, jsval *vp, uintN flags)
3349 {
3350 /*
3351 * The decompiler may need to access the args of the function in
3352 * progress rather than the one we had hoped to call.
3353 * So we switch the cx->fp to the frame below us. We stick the
3354 * current frame in the dormantFrameChain to protect it from gc.
3355 */
3356
3357 JSStackFrame *fp = cx->fp;
3358 if (fp->down) {
3359 JS_ASSERT(!fp->dormantNext);
3360 fp->dormantNext = cx->dormantFrameChain;
3361 cx->dormantFrameChain = fp;
3362 cx->fp = fp->down;
3363 }
3364
3365 js_ReportIsNotFunction(cx, vp, flags);
3366
3367 if (fp->down) {
3368 JS_ASSERT(cx->dormantFrameChain == fp);
3369 cx->dormantFrameChain = fp->dormantNext;
3370 fp->dormantNext = NULL;
3371 cx->fp = fp;
3372 }
3373 }
3374
3375 #ifdef NARCISSUS
3376 static JSBool
GetCurrentExecutionContext(JSContext * cx,JSObject * obj,jsval * rval)3377 GetCurrentExecutionContext(JSContext *cx, JSObject *obj, jsval *rval)
3378 {
3379 JSObject *tmp;
3380 jsval xcval;
3381
3382 while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL)
3383 obj = tmp;
3384 if (!OBJ_GET_PROPERTY(cx, obj,
3385 (jsid)cx->runtime->atomState.ExecutionContextAtom,
3386 &xcval)) {
3387 return JS_FALSE;
3388 }
3389 if (JSVAL_IS_PRIMITIVE(xcval)) {
3390 JS_ReportError(cx, "invalid ExecutionContext in global object");
3391 return JS_FALSE;
3392 }
3393 if (!OBJ_GET_PROPERTY(cx, JSVAL_TO_OBJECT(xcval),
3394 (jsid)cx->runtime->atomState.currentAtom,
3395 rval)) {
3396 return JS_FALSE;
3397 }
3398 return JS_TRUE;
3399 }
3400 #endif
3401
3402 JSBool
js_Call(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)3403 js_Call(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
3404 {
3405 JSClass *clasp;
3406
3407 clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(argv[-2]));
3408 if (!clasp->call) {
3409 #ifdef NARCISSUS
3410 JSObject *callee, *args;
3411 jsval fval, nargv[3];
3412 JSBool ok;
3413
3414 callee = JSVAL_TO_OBJECT(argv[-2]);
3415 if (!OBJ_GET_PROPERTY(cx, callee,
3416 (jsid)cx->runtime->atomState.callAtom,
3417 &fval)) {
3418 return JS_FALSE;
3419 }
3420 if (JSVAL_IS_FUNCTION(cx, fval)) {
3421 if (!GetCurrentExecutionContext(cx, obj, &nargv[2]))
3422 return JS_FALSE;
3423 args = js_GetArgsObject(cx, cx->fp);
3424 if (!args)
3425 return JS_FALSE;
3426 nargv[0] = OBJECT_TO_JSVAL(obj);
3427 nargv[1] = OBJECT_TO_JSVAL(args);
3428 return js_InternalCall(cx, callee, fval, 3, nargv, rval);
3429 }
3430 if (JSVAL_IS_OBJECT(fval) && JSVAL_TO_OBJECT(fval) != callee) {
3431 argv[-2] = fval;
3432 ok = js_Call(cx, obj, argc, argv, rval);
3433 argv[-2] = OBJECT_TO_JSVAL(callee);
3434 return ok;
3435 }
3436 #endif
3437 ReportIsNotFunction(cx, &argv[-2], 0);
3438 return JS_FALSE;
3439 }
3440 return clasp->call(cx, obj, argc, argv, rval);
3441 }
3442
3443 JSBool
js_Construct(JSContext * cx,JSObject * obj,uintN argc,jsval * argv,jsval * rval)3444 js_Construct(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
3445 jsval *rval)
3446 {
3447 JSClass *clasp;
3448
3449 clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(argv[-2]));
3450 if (!clasp->construct) {
3451 #ifdef NARCISSUS
3452 JSObject *callee, *args;
3453 jsval cval, nargv[2];
3454 JSBool ok;
3455
3456 callee = JSVAL_TO_OBJECT(argv[-2]);
3457 if (!OBJ_GET_PROPERTY(cx, callee,
3458 (jsid)cx->runtime->atomState.constructAtom,
3459 &cval)) {
3460 return JS_FALSE;
3461 }
3462 if (JSVAL_IS_FUNCTION(cx, cval)) {
3463 if (!GetCurrentExecutionContext(cx, obj, &nargv[1]))
3464 return JS_FALSE;
3465 args = js_GetArgsObject(cx, cx->fp);
3466 if (!args)
3467 return JS_FALSE;
3468 nargv[0] = OBJECT_TO_JSVAL(args);
3469 return js_InternalCall(cx, callee, cval, 2, nargv, rval);
3470 }
3471 if (JSVAL_IS_OBJECT(cval) && JSVAL_TO_OBJECT(cval) != callee) {
3472 argv[-2] = cval;
3473 ok = js_Call(cx, obj, argc, argv, rval);
3474 argv[-2] = OBJECT_TO_JSVAL(callee);
3475 return ok;
3476 }
3477 #endif
3478 ReportIsNotFunction(cx, &argv[-2], JSV2F_CONSTRUCT);
3479 return JS_FALSE;
3480 }
3481 return clasp->construct(cx, obj, argc, argv, rval);
3482 }
3483
3484 JSBool
js_HasInstance(JSContext * cx,JSObject * obj,jsval v,JSBool * bp)3485 js_HasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
3486 {
3487 JSClass *clasp;
3488
3489 clasp = OBJ_GET_CLASS(cx, obj);
3490 if (clasp->hasInstance)
3491 return clasp->hasInstance(cx, obj, v, bp);
3492 #ifdef NARCISSUS
3493 {
3494 jsval fval, rval;
3495
3496 if (!OBJ_GET_PROPERTY(cx, obj,
3497 (jsid)cx->runtime->atomState.hasInstanceAtom,
3498 &fval)) {
3499 return JS_FALSE;
3500 }
3501 if (JSVAL_IS_FUNCTION(cx, fval)) {
3502 return js_InternalCall(cx, obj, fval, 1, &v, &rval) &&
3503 js_ValueToBoolean(cx, rval, bp);
3504 }
3505 }
3506 #endif
3507 *bp = JS_FALSE;
3508 return JS_TRUE;
3509 }
3510
3511 JSBool
js_IsDelegate(JSContext * cx,JSObject * obj,jsval v,JSBool * bp)3512 js_IsDelegate(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
3513 {
3514 JSObject *obj2;
3515
3516 *bp = JS_FALSE;
3517 if (JSVAL_IS_PRIMITIVE(v))
3518 return JS_TRUE;
3519 obj2 = JSVAL_TO_OBJECT(v);
3520 while ((obj2 = OBJ_GET_PROTO(cx, obj2)) != NULL) {
3521 if (obj2 == obj) {
3522 *bp = JS_TRUE;
3523 break;
3524 }
3525 }
3526 return JS_TRUE;
3527 }
3528
3529 JSBool
js_GetClassPrototype(JSContext * cx,const char * name,JSObject ** protop)3530 js_GetClassPrototype(JSContext *cx, const char *name, JSObject **protop)
3531 {
3532 return GetClassPrototype(cx, NULL, name, protop);
3533 }
3534
3535 static JSBool
GetClassPrototype(JSContext * cx,JSObject * scope,const char * name,JSObject ** protop)3536 GetClassPrototype(JSContext *cx, JSObject *scope, const char *name,
3537 JSObject **protop)
3538 {
3539 jsval v;
3540 JSObject *ctor;
3541
3542 if (!FindConstructor(cx, scope, name, &v))
3543 return JS_FALSE;
3544 if (JSVAL_IS_FUNCTION(cx, v)) {
3545 ctor = JSVAL_TO_OBJECT(v);
3546 if (!OBJ_GET_PROPERTY(cx, ctor,
3547 (jsid)cx->runtime->atomState.classPrototypeAtom,
3548 &v)) {
3549 return JS_FALSE;
3550 }
3551 }
3552 *protop = JSVAL_IS_OBJECT(v) ? JSVAL_TO_OBJECT(v) : NULL;
3553 return JS_TRUE;
3554 }
3555
3556 JSBool
js_SetClassPrototype(JSContext * cx,JSObject * ctor,JSObject * proto,uintN attrs)3557 js_SetClassPrototype(JSContext *cx, JSObject *ctor, JSObject *proto,
3558 uintN attrs)
3559 {
3560 /*
3561 * Use the given attributes for the prototype property of the constructor,
3562 * as user-defined constructors have a DontDelete prototype (which may be
3563 * reset), while native or "system" constructors have DontEnum | ReadOnly |
3564 * DontDelete.
3565 */
3566 if (!OBJ_DEFINE_PROPERTY(cx, ctor,
3567 (jsid)cx->runtime->atomState.classPrototypeAtom,
3568 OBJECT_TO_JSVAL(proto), NULL, NULL,
3569 attrs, NULL)) {
3570 return JS_FALSE;
3571 }
3572
3573 /*
3574 * ECMA says that Object.prototype.constructor, or f.prototype.constructor
3575 * for a user-defined function f, is DontEnum.
3576 */
3577 return OBJ_DEFINE_PROPERTY(cx, proto,
3578 (jsid)cx->runtime->atomState.constructorAtom,
3579 OBJECT_TO_JSVAL(ctor), NULL, NULL,
3580 0, NULL);
3581 }
3582
3583 JSBool
js_ValueToObject(JSContext * cx,jsval v,JSObject ** objp)3584 js_ValueToObject(JSContext *cx, jsval v, JSObject **objp)
3585 {
3586 JSObject *obj;
3587
3588 if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v)) {
3589 obj = NULL;
3590 } else if (JSVAL_IS_OBJECT(v)) {
3591 obj = JSVAL_TO_OBJECT(v);
3592 if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_OBJECT, &v))
3593 return JS_FALSE;
3594 if (JSVAL_IS_OBJECT(v))
3595 obj = JSVAL_TO_OBJECT(v);
3596 } else {
3597 if (JSVAL_IS_STRING(v)) {
3598 obj = js_StringToObject(cx, JSVAL_TO_STRING(v));
3599 } else if (JSVAL_IS_INT(v)) {
3600 obj = js_NumberToObject(cx, (jsdouble)JSVAL_TO_INT(v));
3601 } else if (JSVAL_IS_DOUBLE(v)) {
3602 obj = js_NumberToObject(cx, *JSVAL_TO_DOUBLE(v));
3603 } else {
3604 JS_ASSERT(JSVAL_IS_BOOLEAN(v));
3605 obj = js_BooleanToObject(cx, JSVAL_TO_BOOLEAN(v));
3606 }
3607 if (!obj)
3608 return JS_FALSE;
3609 }
3610 *objp = obj;
3611 return JS_TRUE;
3612 }
3613
3614 JSObject *
js_ValueToNonNullObject(JSContext * cx,jsval v)3615 js_ValueToNonNullObject(JSContext *cx, jsval v)
3616 {
3617 JSObject *obj;
3618 JSString *str;
3619
3620 if (!js_ValueToObject(cx, v, &obj))
3621 return NULL;
3622 if (!obj) {
3623 str = js_DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NULL);
3624 if (str) {
3625 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3626 JSMSG_NO_PROPERTIES, JS_GetStringBytes(str));
3627 }
3628 }
3629 return obj;
3630 }
3631
3632 JSBool
js_TryValueOf(JSContext * cx,JSObject * obj,JSType type,jsval * rval)3633 js_TryValueOf(JSContext *cx, JSObject *obj, JSType type, jsval *rval)
3634 {
3635 #if JS_HAS_VALUEOF_HINT
3636 jsval argv[1];
3637
3638 argv[0] = ATOM_KEY(cx->runtime->atomState.typeAtoms[type]);
3639 return js_TryMethod(cx, obj, cx->runtime->atomState.valueOfAtom, 1, argv,
3640 rval);
3641 #else
3642 return js_TryMethod(cx, obj, cx->runtime->atomState.valueOfAtom, 0, NULL,
3643 rval);
3644 #endif
3645 }
3646
3647 JSBool
js_TryMethod(JSContext * cx,JSObject * obj,JSAtom * atom,uintN argc,jsval * argv,jsval * rval)3648 js_TryMethod(JSContext *cx, JSObject *obj, JSAtom *atom,
3649 uintN argc, jsval *argv, jsval *rval)
3650 {
3651 JSErrorReporter older;
3652 jsval fval;
3653 JSBool ok;
3654
3655 /*
3656 * Report failure only if an appropriate method was found, and calling it
3657 * returned failure. We propagate failure in this case to make exceptions
3658 * behave properly.
3659 */
3660 older = JS_SetErrorReporter(cx, NULL);
3661 if (!OBJ_GET_PROPERTY(cx, obj, (jsid)atom, &fval)) {
3662 JS_ClearPendingException(cx);
3663 ok = JS_TRUE;
3664 } else if (!JSVAL_IS_PRIMITIVE(fval)) {
3665 ok = js_InternalCall(cx, obj, fval, argc, argv, rval);
3666 if (!ok)
3667 JS_ClearPendingException(cx);
3668 } else {
3669 ok = JS_TRUE;
3670 }
3671 JS_SetErrorReporter(cx, older);
3672 return ok;
3673 }
3674
3675 #if JS_HAS_XDR
3676
3677 #include "jsxdrapi.h"
3678
3679 JSBool
js_XDRObject(JSXDRState * xdr,JSObject ** objp)3680 js_XDRObject(JSXDRState *xdr, JSObject **objp)
3681 {
3682 JSContext *cx;
3683 JSClass *clasp;
3684 const char *className;
3685 uint32 classId, classDef;
3686 JSBool ok;
3687 JSObject *proto;
3688
3689 cx = xdr->cx;
3690 if (xdr->mode == JSXDR_ENCODE) {
3691 clasp = OBJ_GET_CLASS(cx, *objp);
3692 className = clasp->name;
3693 classId = JS_XDRFindClassIdByName(xdr, className);
3694 classDef = !classId;
3695 if (classDef && !JS_XDRRegisterClass(xdr, clasp, &classId))
3696 return JS_FALSE;
3697 } else {
3698 classDef = 0;
3699 className = NULL;
3700 clasp = NULL; /* quell GCC overwarning */
3701 }
3702
3703 /* XDR a flag word followed (if true) by the class name. */
3704 if (!JS_XDRUint32(xdr, &classDef))
3705 return JS_FALSE;
3706 if (classDef && !JS_XDRCString(xdr, (char **) &className))
3707 return JS_FALSE;
3708
3709 /* From here on, return through out: to free className if it was set. */
3710 ok = JS_XDRUint32(xdr, &classId);
3711 if (!ok)
3712 goto out;
3713
3714 if (xdr->mode != JSXDR_ENCODE) {
3715 if (classDef) {
3716 ok = js_GetClassPrototype(cx, className, &proto);
3717 if (!ok)
3718 goto out;
3719 clasp = OBJ_GET_CLASS(cx, proto);
3720 ok = JS_XDRRegisterClass(xdr, clasp, &classId);
3721 if (!ok)
3722 goto out;
3723 } else {
3724 clasp = JS_XDRFindClassById(xdr, classId);
3725 if (!clasp) {
3726 char numBuf[12];
3727 JS_snprintf(numBuf, sizeof numBuf, "%ld", (long)classId);
3728 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3729 JSMSG_CANT_FIND_CLASS, numBuf);
3730 ok = JS_FALSE;
3731 goto out;
3732 }
3733 }
3734 }
3735
3736 if (!clasp->xdrObject) {
3737 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3738 JSMSG_CANT_XDR_CLASS, clasp->name);
3739 ok = JS_FALSE;
3740 } else {
3741 ok = clasp->xdrObject(xdr, objp);
3742 }
3743 out:
3744 if (xdr->mode != JSXDR_ENCODE && className)
3745 JS_free(cx, (void *)className);
3746 return ok;
3747 }
3748
3749 #endif /* JS_HAS_XDR */
3750
3751 #ifdef DEBUG_brendan
3752
3753 #include <stdio.h>
3754 #include <math.h>
3755
3756 uint32 js_entry_count_max;
3757 uint32 js_entry_count_sum;
3758 double js_entry_count_sqsum;
3759 uint32 js_entry_count_hist[11];
3760
3761 static void
MeterEntryCount(uintN count)3762 MeterEntryCount(uintN count)
3763 {
3764 if (count) {
3765 js_entry_count_sum += count;
3766 js_entry_count_sqsum += (double)count * count;
3767 if (count > js_entry_count_max)
3768 js_entry_count_max = count;
3769 }
3770 js_entry_count_hist[JS_MIN(count, 10)]++;
3771 }
3772
3773 void
js_DumpScopeMeters(JSRuntime * rt)3774 js_DumpScopeMeters(JSRuntime *rt)
3775 {
3776 static FILE *logfp;
3777 if (!logfp)
3778 logfp = fopen("/tmp/scope.stats", "a");
3779
3780 {
3781 double mean = 0., var = 0., sigma = 0.;
3782 double nscopes = rt->liveScopes;
3783 double nentrys = js_entry_count_sum;
3784 if (nscopes > 0 && nentrys >= 0) {
3785 mean = nentrys / nscopes;
3786 var = nscopes * js_entry_count_sqsum - nentrys * nentrys;
3787 if (var < 0.0 || nscopes <= 1)
3788 var = 0.0;
3789 else
3790 var /= nscopes * (nscopes - 1);
3791
3792 /* Windows says sqrt(0.0) is "-1.#J" (?!) so we must test. */
3793 sigma = (var != 0.) ? sqrt(var) : 0.;
3794 }
3795
3796 fprintf(logfp,
3797 "scopes %g entries %g mean %g sigma %g max %u",
3798 nscopes, nentrys, mean, sigma, js_entry_count_max);
3799 }
3800
3801 fprintf(logfp, " histogram %u %u %u %u %u %u %u %u %u %u %u\n",
3802 js_entry_count_hist[0], js_entry_count_hist[1],
3803 js_entry_count_hist[2], js_entry_count_hist[3],
3804 js_entry_count_hist[4], js_entry_count_hist[5],
3805 js_entry_count_hist[6], js_entry_count_hist[7],
3806 js_entry_count_hist[8], js_entry_count_hist[9],
3807 js_entry_count_hist[10]);
3808 js_entry_count_sum = js_entry_count_max = 0;
3809 js_entry_count_sqsum = 0;
3810 memset(js_entry_count_hist, 0, sizeof js_entry_count_hist);
3811 fflush(logfp);
3812 }
3813
3814 #endif /* DEBUG_brendan */
3815
3816 uint32
js_Mark(JSContext * cx,JSObject * obj,void * arg)3817 js_Mark(JSContext *cx, JSObject *obj, void *arg)
3818 {
3819 JSScope *scope;
3820 JSScopeProperty *sprop;
3821 JSClass *clasp;
3822
3823 JS_ASSERT(OBJ_IS_NATIVE(obj));
3824 scope = OBJ_SCOPE(obj);
3825 #ifdef DEBUG_brendan
3826 if (scope->object == obj)
3827 MeterEntryCount(scope->entryCount);
3828 #endif
3829
3830 JS_ASSERT(!SCOPE_LAST_PROP(scope) ||
3831 SCOPE_HAS_PROPERTY(scope, SCOPE_LAST_PROP(scope)));
3832
3833 for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
3834 if (SCOPE_HAD_MIDDLE_DELETE(scope) && !SCOPE_HAS_PROPERTY(scope, sprop))
3835 continue;
3836 MARK_SCOPE_PROPERTY(sprop);
3837 if (!JSVAL_IS_INT(sprop->id))
3838 GC_MARK_ATOM(cx, (JSAtom *)sprop->id, arg);
3839
3840 #if JS_HAS_GETTER_SETTER
3841 if (sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
3842 #ifdef GC_MARK_DEBUG
3843 char buf[64];
3844 JSAtom *atom = (JSAtom *)sprop->id;
3845 const char *id = (atom && ATOM_IS_STRING(atom))
3846 ? JS_GetStringBytes(ATOM_TO_STRING(atom))
3847 : "unknown";
3848 #endif
3849
3850 if (sprop->attrs & JSPROP_GETTER) {
3851 #ifdef GC_MARK_DEBUG
3852 JS_snprintf(buf, sizeof buf, "%s %s",
3853 id, js_getter_str);
3854 #endif
3855 GC_MARK(cx,
3856 JSVAL_TO_GCTHING((jsval) sprop->getter),
3857 buf,
3858 arg);
3859 }
3860 if (sprop->attrs & JSPROP_SETTER) {
3861 #ifdef GC_MARK_DEBUG
3862 JS_snprintf(buf, sizeof buf, "%s %s",
3863 id, js_setter_str);
3864 #endif
3865 GC_MARK(cx,
3866 JSVAL_TO_GCTHING((jsval) sprop->setter),
3867 buf,
3868 arg);
3869 }
3870 }
3871 #endif /* JS_HAS_GETTER_SETTER */
3872 }
3873
3874 /* No one runs while the GC is running, so we can use LOCKED_... here. */
3875 clasp = LOCKED_OBJ_GET_CLASS(obj);
3876 if (clasp->mark)
3877 (void) clasp->mark(cx, obj, arg);
3878
3879 if (scope->object != obj) {
3880 /*
3881 * An unmutated object that shares a prototype's scope. We can't tell
3882 * how many slots are allocated and in use at obj->slots by looking at
3883 * scope, so we get obj->slots' length from its -1'st element.
3884 */
3885 return (uint32) obj->slots[-1];
3886 }
3887 return JS_MIN(scope->map.freeslot, scope->map.nslots);
3888 }
3889
3890 void
js_Clear(JSContext * cx,JSObject * obj)3891 js_Clear(JSContext *cx, JSObject *obj)
3892 {
3893 JSScope *scope;
3894 JSRuntime *rt;
3895 JSScopeProperty *sprop;
3896 uint32 i, n;
3897
3898 /*
3899 * Clear our scope and the property cache of all obj's properties only if
3900 * obj owns the scope (i.e., not if obj is unmutated and therefore sharing
3901 * its prototype's scope). NB: we do not clear any reserved slots lying
3902 * below JSSLOT_FREE(clasp).
3903 */
3904 JS_LOCK_OBJ(cx, obj);
3905 scope = OBJ_SCOPE(obj);
3906 if (scope->object == obj) {
3907 /* Clear the property cache before we clear the scope. */
3908 rt = cx->runtime;
3909 for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
3910 if (!SCOPE_HAD_MIDDLE_DELETE(scope) ||
3911 SCOPE_HAS_PROPERTY(scope, sprop)) {
3912 PROPERTY_CACHE_FILL(&rt->propertyCache, obj, sprop->id, NULL);
3913 }
3914 }
3915
3916 /* Now that we're done using scope->lastProp/table, clear scope. */
3917 js_ClearScope(cx, scope);
3918
3919 /* Clear slot values and reset freeslot so we're consistent. */
3920 i = scope->map.nslots;
3921 n = JSSLOT_FREE(LOCKED_OBJ_GET_CLASS(obj));
3922 while (--i >= n)
3923 obj->slots[i] = JSVAL_VOID;
3924 scope->map.freeslot = n;
3925 }
3926 JS_UNLOCK_OBJ(cx, obj);
3927 }
3928
3929 jsval
js_GetRequiredSlot(JSContext * cx,JSObject * obj,uint32 slot)3930 js_GetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot)
3931 {
3932 jsval v;
3933
3934 JS_LOCK_OBJ(cx, obj);
3935 v = (slot < (uint32) obj->slots[-1]) ? obj->slots[slot] : JSVAL_VOID;
3936 JS_UNLOCK_OBJ(cx, obj);
3937 return v;
3938 }
3939
3940 JSBool
js_SetRequiredSlot(JSContext * cx,JSObject * obj,uint32 slot,jsval v)3941 js_SetRequiredSlot(JSContext *cx, JSObject *obj, uint32 slot, jsval v)
3942 {
3943 JSScope *scope;
3944 uint32 nslots, i;
3945 JSClass *clasp;
3946 jsval *newslots;
3947
3948 JS_LOCK_OBJ(cx, obj);
3949 scope = OBJ_SCOPE(obj);
3950 nslots = (uint32) obj->slots[-1];
3951 if (slot >= nslots) {
3952 /*
3953 * At this point, obj may or may not own scope. If some path calls
3954 * js_GetMutableScope but does not add a slot-owning property, then
3955 * scope->object == obj but nslots will be nominal. If obj shares a
3956 * prototype's scope, then we cannot update scope->map here, but we
3957 * must update obj->slots[-1] when we grow obj->slots.
3958 *
3959 * See js_Mark, before the last return, where we make a special case
3960 * for unmutated (scope->object != obj) objects.
3961 */
3962 JS_ASSERT(nslots == JS_INITIAL_NSLOTS);
3963 clasp = LOCKED_OBJ_GET_CLASS(obj);
3964 nslots = JSSLOT_FREE(clasp);
3965 if (clasp->reserveSlots)
3966 nslots += clasp->reserveSlots(cx, obj);
3967 JS_ASSERT(slot < nslots);
3968
3969 newslots = (jsval *)
3970 JS_realloc(cx, obj->slots - 1, (nslots + 1) * sizeof(jsval));
3971 if (!newslots) {
3972 JS_UNLOCK_SCOPE(cx, scope);
3973 return JS_FALSE;
3974 }
3975 for (i = 1 + newslots[0]; i <= nslots; i++)
3976 newslots[i] = JSVAL_VOID;
3977 if (scope->object == obj)
3978 scope->map.nslots = nslots;
3979 newslots[0] = nslots;
3980 obj->slots = newslots + 1;
3981 }
3982
3983 /* Whether or not we grew nslots, we may need to advance freeslot. */
3984 if (scope->object == obj && slot >= scope->map.freeslot)
3985 scope->map.freeslot = slot + 1;
3986
3987 obj->slots[slot] = v;
3988 JS_UNLOCK_SCOPE(cx, scope);
3989 return JS_TRUE;
3990 }
3991
3992 #ifdef DEBUG
3993
3994 /* Routines to print out values during debugging. */
3995
printChar(jschar * cp)3996 void printChar(jschar *cp) {
3997 fprintf(stderr, "jschar* (0x%p) \"", (void *)cp);
3998 while (*cp)
3999 fputc(*cp++, stderr);
4000 fputc('"', stderr);
4001 fputc('\n', stderr);
4002 }
4003
printString(JSString * str)4004 void printString(JSString *str) {
4005 size_t i, n;
4006 jschar *s;
4007 fprintf(stderr, "string (0x%p) \"", (void *)str);
4008 s = JSSTRING_CHARS(str);
4009 for (i=0, n=JSSTRING_LENGTH(str); i < n; i++)
4010 fputc(s[i], stderr);
4011 fputc('"', stderr);
4012 fputc('\n', stderr);
4013 }
4014
4015 void printVal(JSContext *cx, jsval val);
4016
printObj(JSContext * cx,JSObject * jsobj)4017 void printObj(JSContext *cx, JSObject *jsobj) {
4018 jsuint i;
4019 jsval val;
4020 JSClass *clasp;
4021
4022 fprintf(stderr, "object 0x%p\n", (void *)jsobj);
4023 clasp = OBJ_GET_CLASS(cx, jsobj);
4024 fprintf(stderr, "class 0x%p %s\n", (void *)clasp, clasp->name);
4025 for (i=0; i < jsobj->map->nslots; i++) {
4026 fprintf(stderr, "slot %3d ", i);
4027 val = jsobj->slots[i];
4028 if (JSVAL_IS_OBJECT(val))
4029 fprintf(stderr, "object 0x%p\n", (void *)JSVAL_TO_OBJECT(val));
4030 else
4031 printVal(cx, val);
4032 }
4033 }
4034
printVal(JSContext * cx,jsval val)4035 void printVal(JSContext *cx, jsval val) {
4036 fprintf(stderr, "val %d (0x%p) = ", (int)val, (void *)val);
4037 if (JSVAL_IS_NULL(val)) {
4038 fprintf(stderr, "null\n");
4039 } else if (JSVAL_IS_VOID(val)) {
4040 fprintf(stderr, "undefined\n");
4041 } else if (JSVAL_IS_OBJECT(val)) {
4042 printObj(cx, JSVAL_TO_OBJECT(val));
4043 } else if (JSVAL_IS_INT(val)) {
4044 fprintf(stderr, "(int) %d\n", JSVAL_TO_INT(val));
4045 } else if (JSVAL_IS_STRING(val)) {
4046 printString(JSVAL_TO_STRING(val));
4047 } else if (JSVAL_IS_DOUBLE(val)) {
4048 fprintf(stderr, "(double) %g\n", *JSVAL_TO_DOUBLE(val));
4049 } else {
4050 JS_ASSERT(JSVAL_IS_BOOLEAN(val));
4051 fprintf(stderr, "(boolean) %s\n",
4052 JSVAL_TO_BOOLEAN(val) ? "true" : "false");
4053 }
4054 fflush(stderr);
4055 }
4056
printId(JSContext * cx,jsid id)4057 void printId(JSContext *cx, jsid id) {
4058 fprintf(stderr, "id %d (0x%p) is ", (int)id, (void *)id);
4059 printVal(cx, ID_TO_VALUE(id));
4060 }
4061
printAtom(JSAtom * atom)4062 void printAtom(JSAtom *atom) {
4063 printString(ATOM_TO_STRING(atom));
4064 }
4065
4066 #endif
4067