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