/[jscoverage]/trunk/js/jsobj.cpp
ViewVC logotype

Contents of /trunk/js/jsobj.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 332 - (show annotations)
Thu Oct 23 19:03:33 2008 UTC (11 years, 9 months ago) by siliconforks
File size: 173220 byte(s)
Add SpiderMonkey from Firefox 3.1b1.

The following directories and files were removed:
correct/, correct.js
liveconnect/
nanojit/
t/
v8/
vprof/
xpconnect/
all JavaScript files (Y.js, call.js, if.js, math-partial-sums.js, md5.js, perfect.js, trace-test.js, trace.js)


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 "jsbit.h"
50 #include "jsutil.h" /* Added by JSIFY */
51 #include "jshash.h" /* Added by JSIFY */
52 #include "jsdhash.h"
53 #include "jsprf.h"
54 #include "jsapi.h"
55 #include "jsarray.h"
56 #include "jsatom.h"
57 #include "jsbool.h"
58 #include "jscntxt.h"
59 #include "jsversion.h"
60 #include "jsemit.h"
61 #include "jsfun.h"
62 #include "jsgc.h"
63 #include "jsinterp.h"
64 #include "jslock.h"
65 #include "jsnum.h"
66 #include "jsobj.h"
67 #include "jsopcode.h"
68 #include "jsparse.h"
69 #include "jsscope.h"
70 #include "jsscript.h"
71 #include "jsstr.h"
72 #include "jsdbgapi.h" /* whether or not JS_HAS_OBJ_WATCHPOINT */
73 #include "jsstaticcheck.h"
74
75 #if JS_HAS_GENERATORS
76 #include "jsiter.h"
77 #endif
78
79 #if JS_HAS_XML_SUPPORT
80 #include "jsxml.h"
81 #endif
82
83 #if JS_HAS_XDR
84 #include "jsxdrapi.h"
85 #endif
86
87 #ifdef INCLUDE_MOZILLA_DTRACE
88 #include "jsdtracef.h"
89 #endif
90
91 #include "jsautooplen.h"
92
93 #ifdef JS_THREADSAFE
94 #define NATIVE_DROP_PROPERTY js_DropProperty
95
96 extern void
97 js_DropProperty(JSContext *cx, JSObject *obj, JSProperty *prop);
98 #else
99 #define NATIVE_DROP_PROPERTY NULL
100 #endif
101
102 JS_FRIEND_DATA(JSObjectOps) js_ObjectOps = {
103 js_NewObjectMap, js_DestroyObjectMap,
104 js_LookupProperty, js_DefineProperty,
105 js_GetProperty, js_SetProperty,
106 js_GetAttributes, js_SetAttributes,
107 js_DeleteProperty, js_DefaultValue,
108 js_Enumerate, js_CheckAccess,
109 NULL, NATIVE_DROP_PROPERTY,
110 js_Call, js_Construct,
111 NULL, js_HasInstance,
112 js_SetProtoOrParent, js_SetProtoOrParent,
113 js_TraceObject, js_Clear,
114 js_GetRequiredSlot, js_SetRequiredSlot
115 };
116
117 JSClass js_ObjectClass = {
118 js_Object_str,
119 JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
120 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
121 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
122 JSCLASS_NO_OPTIONAL_MEMBERS
123 };
124
125 #if JS_HAS_OBJ_PROTO_PROP
126
127 static JSBool
128 obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
129
130 static JSBool
131 obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
132
133 static JSBool
134 obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp);
135
136 static JSPropertySpec object_props[] = {
137 /* These two must come first; see object_props[slot].name usage below. */
138 {js_proto_str, JSSLOT_PROTO, JSPROP_PERMANENT|JSPROP_SHARED,
139 obj_getSlot, obj_setSlot},
140 {js_parent_str,JSSLOT_PARENT,JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED,
141 obj_getSlot, obj_setSlot},
142 {js_count_str, 0, JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED,
143 obj_getCount, NULL},
144 {0,0,0,0,0}
145 };
146
147 /* NB: JSSLOT_PROTO and JSSLOT_PARENT are already indexes into object_props. */
148 #define JSSLOT_COUNT 2
149
150 static JSBool
151 ReportStrictSlot(JSContext *cx, uint32 slot)
152 {
153 if (slot == JSSLOT_PROTO)
154 return JS_TRUE;
155 return JS_ReportErrorFlagsAndNumber(cx,
156 JSREPORT_WARNING | JSREPORT_STRICT,
157 js_GetErrorMessage, NULL,
158 JSMSG_DEPRECATED_USAGE,
159 object_props[slot].name);
160 }
161
162 static JSBool
163 obj_getSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
164 {
165 uint32 slot;
166 jsid propid;
167 JSAccessMode mode;
168 uintN attrs;
169 JSObject *pobj;
170 JSClass *clasp;
171 JSExtendedClass *xclasp;
172
173 slot = (uint32) JSVAL_TO_INT(id);
174 if (id == INT_TO_JSVAL(JSSLOT_PROTO)) {
175 propid = ATOM_TO_JSID(cx->runtime->atomState.protoAtom);
176 mode = JSACC_PROTO;
177 } else {
178 propid = ATOM_TO_JSID(cx->runtime->atomState.parentAtom);
179 mode = JSACC_PARENT;
180 }
181
182 /* Let OBJ_CHECK_ACCESS get the slot's value, based on the access mode. */
183 if (!OBJ_CHECK_ACCESS(cx, obj, propid, mode, vp, &attrs))
184 return JS_FALSE;
185
186 pobj = JSVAL_TO_OBJECT(*vp);
187 if (pobj) {
188 clasp = OBJ_GET_CLASS(cx, pobj);
189 if (clasp == &js_CallClass || clasp == &js_BlockClass) {
190 /* Censor activations and lexical scopes per ECMA-262. */
191 *vp = JSVAL_NULL;
192 } else if (clasp->flags & JSCLASS_IS_EXTENDED) {
193 xclasp = (JSExtendedClass *) clasp;
194 if (xclasp->outerObject) {
195 pobj = xclasp->outerObject(cx, pobj);
196 if (!pobj)
197 return JS_FALSE;
198 *vp = OBJECT_TO_JSVAL(pobj);
199 }
200 }
201 }
202 return JS_TRUE;
203 }
204
205 static JSBool
206 obj_setSlot(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
207 {
208 JSObject *pobj;
209 uint32 slot;
210 jsid propid;
211 uintN attrs;
212
213 if (!JSVAL_IS_OBJECT(*vp))
214 return JS_TRUE;
215 pobj = JSVAL_TO_OBJECT(*vp);
216
217 if (pobj) {
218 /*
219 * Innerize pobj here to avoid sticking unwanted properties on the
220 * outer object. This ensures that any with statements only grant
221 * access to the inner object.
222 */
223 OBJ_TO_INNER_OBJECT(cx, pobj);
224 if (!pobj)
225 return JS_FALSE;
226 }
227 slot = (uint32) JSVAL_TO_INT(id);
228 if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, slot))
229 return JS_FALSE;
230
231 /* __parent__ is readonly and permanent, only __proto__ may be set. */
232 propid = ATOM_TO_JSID(cx->runtime->atomState.protoAtom);
233 if (!OBJ_CHECK_ACCESS(cx, obj, propid,
234 (JSAccessMode)(JSACC_PROTO|JSACC_WRITE), vp,
235 &attrs)) {
236 return JS_FALSE;
237 }
238
239 return js_SetProtoOrParent(cx, obj, slot, pobj);
240 }
241
242 static JSBool
243 obj_getCount(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
244 {
245 jsval iter_state;
246 jsid num_properties;
247 JSBool ok;
248
249 if (JS_HAS_STRICT_OPTION(cx) && !ReportStrictSlot(cx, JSSLOT_COUNT))
250 return JS_FALSE;
251
252 /* Get the number of properties to enumerate. */
253 iter_state = JSVAL_NULL;
254 ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_INIT, &iter_state, &num_properties);
255 if (!ok)
256 goto out;
257
258 if (!JSVAL_IS_INT(num_properties)) {
259 JS_ASSERT(0);
260 *vp = JSVAL_ZERO;
261 goto out;
262 }
263 *vp = num_properties;
264
265 out:
266 if (iter_state != JSVAL_NULL)
267 ok = OBJ_ENUMERATE(cx, obj, JSENUMERATE_DESTROY, &iter_state, 0);
268 return ok;
269 }
270
271 #else /* !JS_HAS_OBJ_PROTO_PROP */
272
273 #define object_props NULL
274
275 #endif /* !JS_HAS_OBJ_PROTO_PROP */
276
277 JSBool
278 js_SetProtoOrParent(JSContext *cx, JSObject *obj, uint32 slot, JSObject *pobj)
279 {
280 JSSetSlotRequest ssr;
281 JSRuntime *rt;
282
283 /* Optimize the null case to avoid the unnecessary overhead of js_GC. */
284 if (!pobj) {
285 JS_LOCK_OBJ(cx, obj);
286 if (slot == JSSLOT_PROTO && !js_GetMutableScope(cx, obj)) {
287 JS_UNLOCK_OBJ(cx, obj);
288 return JS_FALSE;
289 }
290 LOCKED_OBJ_SET_SLOT(obj, slot, JSVAL_NULL);
291 JS_UNLOCK_OBJ(cx, obj);
292 return JS_TRUE;
293 }
294
295 ssr.obj = obj;
296 ssr.pobj = pobj;
297 ssr.slot = (uint16) slot;
298 ssr.errnum = (uint16) JSMSG_NOT_AN_ERROR;
299
300 rt = cx->runtime;
301 JS_LOCK_GC(rt);
302 ssr.next = rt->setSlotRequests;
303 rt->setSlotRequests = &ssr;
304 for (;;) {
305 js_GC(cx, GC_SET_SLOT_REQUEST);
306 JS_UNLOCK_GC(rt);
307 if (!rt->setSlotRequests)
308 break;
309 JS_LOCK_GC(rt);
310 }
311
312 if (ssr.errnum != JSMSG_NOT_AN_ERROR) {
313 if (ssr.errnum == JSMSG_OUT_OF_MEMORY) {
314 JS_ReportOutOfMemory(cx);
315 } else {
316 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, ssr.errnum,
317 #if JS_HAS_OBJ_PROTO_PROP
318 object_props[slot].name
319 #else
320 (slot == JSSLOT_PROTO) ? js_proto_str
321 : js_parent_str
322 #endif
323 );
324 }
325 return JS_FALSE;
326 }
327 return JS_TRUE;
328 }
329
330 static JSHashNumber
331 js_hash_object(const void *key)
332 {
333 return (JSHashNumber)JS_PTR_TO_UINT32(key) >> JSVAL_TAGBITS;
334 }
335
336 static JSHashEntry *
337 MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap)
338 {
339 JSSharpObjectMap *map;
340 JSHashTable *table;
341 JSHashNumber hash;
342 JSHashEntry **hep, *he;
343 jsatomid sharpid;
344 JSIdArray *ida;
345 JSBool ok;
346 jsint i, length;
347 jsid id;
348 #if JS_HAS_GETTER_SETTER
349 JSObject *obj2;
350 JSProperty *prop;
351 uintN attrs;
352 #endif
353 jsval val;
354
355 JS_CHECK_RECURSION(cx, return NULL);
356
357 map = &cx->sharpObjectMap;
358 table = map->table;
359 hash = js_hash_object(obj);
360 hep = JS_HashTableRawLookup(table, hash, obj);
361 he = *hep;
362 if (!he) {
363 sharpid = 0;
364 he = JS_HashTableRawAdd(table, hep, hash, obj,
365 JS_UINT32_TO_PTR(sharpid));
366 if (!he) {
367 JS_ReportOutOfMemory(cx);
368 return NULL;
369 }
370
371 /*
372 * Increment map->depth to protect js_EnterSharpObject from reentering
373 * itself badly. Without this fix, if we reenter the basis case where
374 * map->depth == 0, when unwinding the inner call we will destroy the
375 * newly-created hash table and crash.
376 */
377 ++map->depth;
378 ida = JS_Enumerate(cx, obj);
379 --map->depth;
380 if (!ida)
381 return NULL;
382
383 ok = JS_TRUE;
384 for (i = 0, length = ida->length; i < length; i++) {
385 id = ida->vector[i];
386 #if JS_HAS_GETTER_SETTER
387 ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
388 if (!ok)
389 break;
390 if (!prop)
391 continue;
392 ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs);
393 if (ok) {
394 if (OBJ_IS_NATIVE(obj2) &&
395 (attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
396 val = JSVAL_NULL;
397 if (attrs & JSPROP_GETTER)
398 val = (jsval) ((JSScopeProperty*)prop)->getter;
399 if (attrs & JSPROP_SETTER) {
400 if (val != JSVAL_NULL) {
401 /* Mark the getter, then set val to setter. */
402 ok = (MarkSharpObjects(cx, JSVAL_TO_OBJECT(val),
403 NULL)
404 != NULL);
405 }
406 val = (jsval) ((JSScopeProperty*)prop)->setter;
407 }
408 } else {
409 ok = OBJ_GET_PROPERTY(cx, obj, id, &val);
410 }
411 }
412 OBJ_DROP_PROPERTY(cx, obj2, prop);
413 #else
414 ok = OBJ_GET_PROPERTY(cx, obj, id, &val);
415 #endif
416 if (!ok)
417 break;
418 if (!JSVAL_IS_PRIMITIVE(val) &&
419 !MarkSharpObjects(cx, JSVAL_TO_OBJECT(val), NULL)) {
420 ok = JS_FALSE;
421 break;
422 }
423 }
424 if (!ok || !idap)
425 JS_DestroyIdArray(cx, ida);
426 if (!ok)
427 return NULL;
428 } else {
429 sharpid = JS_PTR_TO_UINT32(he->value);
430 if (sharpid == 0) {
431 sharpid = ++map->sharpgen << SHARP_ID_SHIFT;
432 he->value = JS_UINT32_TO_PTR(sharpid);
433 }
434 ida = NULL;
435 }
436 if (idap)
437 *idap = ida;
438 return he;
439 }
440
441 JSHashEntry *
442 js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap,
443 jschar **sp)
444 {
445 JSSharpObjectMap *map;
446 JSHashTable *table;
447 JSIdArray *ida;
448 JSHashNumber hash;
449 JSHashEntry *he, **hep;
450 jsatomid sharpid;
451 char buf[20];
452 size_t len;
453
454 if (!JS_CHECK_OPERATION_LIMIT(cx, JSOW_ENTER_SHARP))
455 return NULL;
456
457 /* Set to null in case we return an early error. */
458 *sp = NULL;
459 map = &cx->sharpObjectMap;
460 table = map->table;
461 if (!table) {
462 table = JS_NewHashTable(8, js_hash_object, JS_CompareValues,
463 JS_CompareValues, NULL, NULL);
464 if (!table) {
465 JS_ReportOutOfMemory(cx);
466 return NULL;
467 }
468 map->table = table;
469 JS_KEEP_ATOMS(cx->runtime);
470 }
471
472 /* From this point the control must flow either through out: or bad:. */
473 ida = NULL;
474 if (map->depth == 0) {
475 he = MarkSharpObjects(cx, obj, &ida);
476 if (!he)
477 goto bad;
478 JS_ASSERT((JS_PTR_TO_UINT32(he->value) & SHARP_BIT) == 0);
479 if (!idap) {
480 JS_DestroyIdArray(cx, ida);
481 ida = NULL;
482 }
483 } else {
484 hash = js_hash_object(obj);
485 hep = JS_HashTableRawLookup(table, hash, obj);
486 he = *hep;
487
488 /*
489 * It's possible that the value of a property has changed from the
490 * first time the object's properties are traversed (when the property
491 * ids are entered into the hash table) to the second (when they are
492 * converted to strings), i.e., the OBJ_GET_PROPERTY() call is not
493 * idempotent.
494 */
495 if (!he) {
496 he = JS_HashTableRawAdd(table, hep, hash, obj, NULL);
497 if (!he) {
498 JS_ReportOutOfMemory(cx);
499 goto bad;
500 }
501 sharpid = 0;
502 goto out;
503 }
504 }
505
506 sharpid = JS_PTR_TO_UINT32(he->value);
507 if (sharpid != 0) {
508 len = JS_snprintf(buf, sizeof buf, "#%u%c",
509 sharpid >> SHARP_ID_SHIFT,
510 (sharpid & SHARP_BIT) ? '#' : '=');
511 *sp = js_InflateString(cx, buf, &len);
512 if (!*sp) {
513 if (ida)
514 JS_DestroyIdArray(cx, ida);
515 goto bad;
516 }
517 }
518
519 out:
520 JS_ASSERT(he);
521 if ((sharpid & SHARP_BIT) == 0) {
522 if (idap && !ida) {
523 ida = JS_Enumerate(cx, obj);
524 if (!ida) {
525 if (*sp) {
526 JS_free(cx, *sp);
527 *sp = NULL;
528 }
529 goto bad;
530 }
531 }
532 map->depth++;
533 }
534
535 if (idap)
536 *idap = ida;
537 return he;
538
539 bad:
540 /* Clean up the sharpObjectMap table on outermost error. */
541 if (map->depth == 0) {
542 JS_UNKEEP_ATOMS(cx->runtime);
543 map->sharpgen = 0;
544 JS_HashTableDestroy(map->table);
545 map->table = NULL;
546 }
547 return NULL;
548 }
549
550 void
551 js_LeaveSharpObject(JSContext *cx, JSIdArray **idap)
552 {
553 JSSharpObjectMap *map;
554 JSIdArray *ida;
555
556 map = &cx->sharpObjectMap;
557 JS_ASSERT(map->depth > 0);
558 if (--map->depth == 0) {
559 JS_UNKEEP_ATOMS(cx->runtime);
560 map->sharpgen = 0;
561 JS_HashTableDestroy(map->table);
562 map->table = NULL;
563 }
564 if (idap) {
565 ida = *idap;
566 if (ida) {
567 JS_DestroyIdArray(cx, ida);
568 *idap = NULL;
569 }
570 }
571 }
572
573 static intN
574 gc_sharp_table_entry_marker(JSHashEntry *he, intN i, void *arg)
575 {
576 JS_CALL_OBJECT_TRACER((JSTracer *)arg, (JSObject *)he->key,
577 "sharp table entry");
578 return JS_DHASH_NEXT;
579 }
580
581 void
582 js_TraceSharpMap(JSTracer *trc, JSSharpObjectMap *map)
583 {
584 JS_ASSERT(map->depth > 0);
585 JS_ASSERT(map->table);
586
587 /*
588 * During recursive calls to MarkSharpObjects a non-native object or
589 * object with a custom getProperty method can potentially return an
590 * unrooted value or even cut from the object graph an argument of one of
591 * MarkSharpObjects recursive invocations. So we must protect map->table
592 * entries against GC.
593 *
594 * We can not simply use JSTempValueRooter to mark the obj argument of
595 * MarkSharpObjects during recursion as we have to protect *all* entries
596 * in JSSharpObjectMap including those that contains otherwise unreachable
597 * objects just allocated through custom getProperty. Otherwise newer
598 * allocations can re-use the address of an object stored in the hashtable
599 * confusing js_EnterSharpObject. So to address the problem we simply
600 * mark all objects from map->table.
601 *
602 * An alternative "proper" solution is to use JSTempValueRooter in
603 * MarkSharpObjects with code to remove during finalization entries
604 * with otherwise unreachable objects. But this is way too complex
605 * to justify spending efforts.
606 */
607 JS_HashTableEnumerateEntries(map->table, gc_sharp_table_entry_marker, trc);
608 }
609
610 #if JS_HAS_TOSOURCE
611 static JSBool
612 obj_toSource(JSContext *cx, uintN argc, jsval *vp)
613 {
614 JSBool ok, outermost;
615 JSObject *obj;
616 JSHashEntry *he;
617 JSIdArray *ida;
618 jschar *chars, *ochars, *vsharp;
619 const jschar *idstrchars, *vchars;
620 size_t nchars, idstrlength, gsoplength, vlength, vsharplength, curlen;
621 const char *comma;
622 jsint i, j, length, valcnt;
623 jsid id;
624 #if JS_HAS_GETTER_SETTER
625 JSObject *obj2;
626 JSProperty *prop;
627 uintN attrs;
628 #endif
629 jsval *val;
630 jsval localroot[4] = {JSVAL_NULL, JSVAL_NULL, JSVAL_NULL, JSVAL_NULL};
631 JSTempValueRooter tvr;
632 JSString *gsopold[2];
633 JSString *gsop[2];
634 JSString *idstr, *valstr, *str;
635
636 JS_CHECK_RECURSION(cx, return JS_FALSE);
637
638 MUST_FLOW_THROUGH("out");
639 JS_PUSH_TEMP_ROOT(cx, 4, localroot, &tvr);
640
641 /* If outermost, we need parentheses to be an expression, not a block. */
642 outermost = (cx->sharpObjectMap.depth == 0);
643 obj = JS_THIS_OBJECT(cx, vp);
644 if (!obj || !(he = js_EnterSharpObject(cx, obj, &ida, &chars))) {
645 ok = JS_FALSE;
646 goto out;
647 }
648 if (IS_SHARP(he)) {
649 /*
650 * We didn't enter -- obj is already "sharp", meaning we've visited it
651 * already in our depth first search, and therefore chars contains a
652 * string of the form "#n#".
653 */
654 JS_ASSERT(!ida);
655 #if JS_HAS_SHARP_VARS
656 nchars = js_strlen(chars);
657 #else
658 chars[0] = '{';
659 chars[1] = '}';
660 chars[2] = 0;
661 nchars = 2;
662 #endif
663 goto make_string;
664 }
665 JS_ASSERT(ida);
666 ok = JS_TRUE;
667
668 if (!chars) {
669 /* If outermost, allocate 4 + 1 for "({})" and the terminator. */
670 chars = (jschar *) malloc(((outermost ? 4 : 2) + 1) * sizeof(jschar));
671 nchars = 0;
672 if (!chars)
673 goto error;
674 if (outermost)
675 chars[nchars++] = '(';
676 } else {
677 /* js_EnterSharpObject returned a string of the form "#n=" in chars. */
678 MAKE_SHARP(he);
679 nchars = js_strlen(chars);
680 chars = (jschar *)
681 realloc((ochars = chars), (nchars + 2 + 1) * sizeof(jschar));
682 if (!chars) {
683 free(ochars);
684 goto error;
685 }
686 if (outermost) {
687 /*
688 * No need for parentheses around the whole shebang, because #n=
689 * unambiguously begins an object initializer, and never a block
690 * statement.
691 */
692 outermost = JS_FALSE;
693 }
694 }
695
696 chars[nchars++] = '{';
697
698 comma = NULL;
699
700 /*
701 * We have four local roots for cooked and raw value GC safety. Hoist the
702 * "localroot + 2" out of the loop using the val local, which refers to
703 * the raw (unconverted, "uncooked") values.
704 */
705 val = localroot + 2;
706
707 for (i = 0, length = ida->length; i < length; i++) {
708 JSBool idIsLexicalIdentifier, needOldStyleGetterSetter;
709
710 /* Get strings for id and value and GC-root them via vp. */
711 id = ida->vector[i];
712
713 #if JS_HAS_GETTER_SETTER
714 ok = OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop);
715 if (!ok)
716 goto error;
717 #endif
718
719 /*
720 * Convert id to a jsval and then to a string. Decide early whether we
721 * prefer get/set or old getter/setter syntax.
722 */
723 idstr = js_ValueToString(cx, ID_TO_VALUE(id));
724 if (!idstr) {
725 ok = JS_FALSE;
726 OBJ_DROP_PROPERTY(cx, obj2, prop);
727 goto error;
728 }
729 *vp = STRING_TO_JSVAL(idstr); /* local root */
730 idIsLexicalIdentifier = js_IsIdentifier(idstr);
731 needOldStyleGetterSetter =
732 !idIsLexicalIdentifier ||
733 js_CheckKeyword(JSSTRING_CHARS(idstr),
734 JSSTRING_LENGTH(idstr)) != TOK_EOF;
735
736 #if JS_HAS_GETTER_SETTER
737
738 valcnt = 0;
739 if (prop) {
740 ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs);
741 if (!ok) {
742 OBJ_DROP_PROPERTY(cx, obj2, prop);
743 goto error;
744 }
745 if (OBJ_IS_NATIVE(obj2) &&
746 (attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
747 if (attrs & JSPROP_GETTER) {
748 val[valcnt] = (jsval) ((JSScopeProperty *)prop)->getter;
749 gsopold[valcnt] =
750 ATOM_TO_STRING(cx->runtime->atomState.getterAtom);
751 gsop[valcnt] =
752 ATOM_TO_STRING(cx->runtime->atomState.getAtom);
753
754 valcnt++;
755 }
756 if (attrs & JSPROP_SETTER) {
757 val[valcnt] = (jsval) ((JSScopeProperty *)prop)->setter;
758 gsopold[valcnt] =
759 ATOM_TO_STRING(cx->runtime->atomState.setterAtom);
760 gsop[valcnt] =
761 ATOM_TO_STRING(cx->runtime->atomState.setAtom);
762
763 valcnt++;
764 }
765 } else {
766 valcnt = 1;
767 gsop[0] = NULL;
768 gsopold[0] = NULL;
769 ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]);
770 }
771 OBJ_DROP_PROPERTY(cx, obj2, prop);
772 }
773
774 #else /* !JS_HAS_GETTER_SETTER */
775
776 /*
777 * We simplify the source code at the price of minor dead code bloat in
778 * the ECMA version (for testing only, see jsversion.h). The null
779 * default values in gsop[j] suffice to disable non-ECMA getter and
780 * setter code.
781 */
782 valcnt = 1;
783 gsop[0] = NULL;
784 gsopold[0] = NULL;
785 ok = OBJ_GET_PROPERTY(cx, obj, id, &val[0]);
786
787 #endif /* !JS_HAS_GETTER_SETTER */
788
789 if (!ok)
790 goto error;
791
792 /*
793 * If id is a string that's not an identifier, then it needs to be
794 * quoted. Also, negative integer ids must be quoted.
795 */
796 if (JSID_IS_ATOM(id)
797 ? !idIsLexicalIdentifier
798 : (!JSID_IS_INT(id) || JSID_TO_INT(id) < 0)) {
799 idstr = js_QuoteString(cx, idstr, (jschar)'\'');
800 if (!idstr) {
801 ok = JS_FALSE;
802 goto error;
803 }
804 *vp = STRING_TO_JSVAL(idstr); /* local root */
805 }
806 JSSTRING_CHARS_AND_LENGTH(idstr, idstrchars, idstrlength);
807
808 for (j = 0; j < valcnt; j++) {
809 /* Convert val[j] to its canonical source form. */
810 valstr = js_ValueToSource(cx, val[j]);
811 if (!valstr) {
812 ok = JS_FALSE;
813 goto error;
814 }
815 localroot[j] = STRING_TO_JSVAL(valstr); /* local root */
816 JSSTRING_CHARS_AND_LENGTH(valstr, vchars, vlength);
817
818 if (vchars[0] == '#')
819 needOldStyleGetterSetter = JS_TRUE;
820
821 if (needOldStyleGetterSetter)
822 gsop[j] = gsopold[j];
823
824 /* If val[j] is a non-sharp object, consider sharpening it. */
825 vsharp = NULL;
826 vsharplength = 0;
827 #if JS_HAS_SHARP_VARS
828 if (!JSVAL_IS_PRIMITIVE(val[j]) && vchars[0] != '#') {
829 he = js_EnterSharpObject(cx, JSVAL_TO_OBJECT(val[j]), NULL,
830 &vsharp);
831 if (!he) {
832 ok = JS_FALSE;
833 goto error;
834 }
835 if (IS_SHARP(he)) {
836 vchars = vsharp;
837 vlength = js_strlen(vchars);
838 needOldStyleGetterSetter = JS_TRUE;
839 gsop[j] = gsopold[j];
840 } else {
841 if (vsharp) {
842 vsharplength = js_strlen(vsharp);
843 MAKE_SHARP(he);
844 needOldStyleGetterSetter = JS_TRUE;
845 gsop[j] = gsopold[j];
846 }
847 js_LeaveSharpObject(cx, NULL);
848 }
849 }
850 #endif
851
852 #ifndef OLD_GETTER_SETTER
853 /*
854 * Remove '(function ' from the beginning of valstr and ')' from the
855 * end so that we can put "get" in front of the function definition.
856 */
857 if (gsop[j] && VALUE_IS_FUNCTION(cx, val[j]) &&
858 !needOldStyleGetterSetter) {
859 JSFunction *fun = JS_ValueToFunction(cx, val[j]);
860 const jschar *start = vchars;
861 const jschar *end = vchars + vlength;
862
863 uint8 parenChomp = 0;
864 if (vchars[0] == '(') {
865 vchars++;
866 parenChomp = 1;
867 }
868
869 /*
870 * Try to jump "getter" or "setter" keywords, if we suspect
871 * they might appear here. This code can be confused by people
872 * defining Function.prototype.toString, so let's be cautious.
873 */
874 if (JSFUN_GETTER_TEST(fun->flags) ||
875 JSFUN_SETTER_TEST(fun->flags)) { /* skip "getter/setter" */
876 const jschar *tmp = js_strchr_limit(vchars, ' ', end);
877 if (tmp)
878 vchars = tmp + 1;
879 }
880
881 /* Try to jump "function" keyword. */
882 if (vchars)
883 vchars = js_strchr_limit(vchars, ' ', end);
884
885 if (vchars) {
886 if (*vchars == ' ')
887 vchars++;
888 vlength = end - vchars - parenChomp;
889 } else {
890 gsop[j] = NULL;
891 vchars = start;
892 }
893 }
894 #else
895 needOldStyleGetterSetter = JS_TRUE;
896 gsop[j] = gsopold[j];
897 #endif
898
899 #define SAFE_ADD(n) \
900 JS_BEGIN_MACRO \
901 size_t n_ = (n); \
902 curlen += n_; \
903 if (curlen < n_) \
904 goto overflow; \
905 JS_END_MACRO
906
907 curlen = nchars;
908 if (comma)
909 SAFE_ADD(2);
910 SAFE_ADD(idstrlength + 1);
911 if (gsop[j])
912 SAFE_ADD(JSSTRING_LENGTH(gsop[j]) + 1);
913 SAFE_ADD(vsharplength);
914 SAFE_ADD(vlength);
915 /* Account for the trailing null. */
916 SAFE_ADD((outermost ? 2 : 1) + 1);
917 #undef SAFE_ADD
918
919 if (curlen > (size_t)-1 / sizeof(jschar))
920 goto overflow;
921
922 /* Allocate 1 + 1 at end for closing brace and terminating 0. */
923 chars = (jschar *)
924 realloc((ochars = chars), curlen * sizeof(jschar));
925 if (!chars) {
926 /* Save code space on error: let JS_free ignore null vsharp. */
927 JS_free(cx, vsharp);
928 free(ochars);
929 goto error;
930 }
931
932 if (comma) {
933 chars[nchars++] = comma[0];
934 chars[nchars++] = comma[1];
935 }
936 comma = ", ";
937
938 if (needOldStyleGetterSetter) {
939 js_strncpy(&chars[nchars], idstrchars, idstrlength);
940 nchars += idstrlength;
941 if (gsop[j]) {
942 chars[nchars++] = ' ';
943 gsoplength = JSSTRING_LENGTH(gsop[j]);
944 js_strncpy(&chars[nchars], JSSTRING_CHARS(gsop[j]),
945 gsoplength);
946 nchars += gsoplength;
947 }
948 chars[nchars++] = ':';
949 } else { /* New style "decompilation" */
950 if (gsop[j]) {
951 gsoplength = JSSTRING_LENGTH(gsop[j]);
952 js_strncpy(&chars[nchars], JSSTRING_CHARS(gsop[j]),
953 gsoplength);
954 nchars += gsoplength;
955 chars[nchars++] = ' ';
956 }
957 js_strncpy(&chars[nchars], idstrchars, idstrlength);
958 nchars += idstrlength;
959 /* Extraneous space after id here will be extracted later */
960 chars[nchars++] = gsop[j] ? ' ' : ':';
961 }
962
963 if (vsharplength) {
964 js_strncpy(&chars[nchars], vsharp, vsharplength);
965 nchars += vsharplength;
966 }
967 js_strncpy(&chars[nchars], vchars, vlength);
968 nchars += vlength;
969
970 if (vsharp)
971 JS_free(cx, vsharp);
972 }
973 }
974
975 chars[nchars++] = '}';
976 if (outermost)
977 chars[nchars++] = ')';
978 chars[nchars] = 0;
979
980 error:
981 js_LeaveSharpObject(cx, &ida);
982
983 if (!ok) {
984 if (chars)
985 free(chars);
986 goto out;
987 }
988
989 if (!chars) {
990 JS_ReportOutOfMemory(cx);
991 ok = JS_FALSE;
992 goto out;
993 }
994 make_string:
995 str = js_NewString(cx, chars, nchars);
996 if (!str) {
997 free(chars);
998 ok = JS_FALSE;
999 goto out;
1000 }
1001 *vp = STRING_TO_JSVAL(str);
1002 ok = JS_TRUE;
1003 out:
1004 JS_POP_TEMP_ROOT(cx, &tvr);
1005 return ok;
1006
1007 overflow:
1008 JS_free(cx, vsharp);
1009 free(chars);
1010 chars = NULL;
1011 goto error;
1012 }
1013 #endif /* JS_HAS_TOSOURCE */
1014
1015 static JSBool
1016 obj_toString(JSContext *cx, uintN argc, jsval *vp)
1017 {
1018 JSObject *obj;
1019 jschar *chars;
1020 size_t nchars;
1021 const char *clazz, *prefix;
1022 JSString *str;
1023
1024 obj = JS_THIS_OBJECT(cx, vp);
1025 if (!obj)
1026 return JS_FALSE;
1027 obj = js_GetWrappedObject(cx, obj);
1028 clazz = OBJ_GET_CLASS(cx, obj)->name;
1029 nchars = 9 + strlen(clazz); /* 9 for "[object ]" */
1030 chars = (jschar *) JS_malloc(cx, (nchars + 1) * sizeof(jschar));
1031 if (!chars)
1032 return JS_FALSE;
1033
1034 prefix = "[object ";
1035 nchars = 0;
1036 while ((chars[nchars] = (jschar)*prefix) != 0)
1037 nchars++, prefix++;
1038 while ((chars[nchars] = (jschar)*clazz) != 0)
1039 nchars++, clazz++;
1040 chars[nchars++] = ']';
1041 chars[nchars] = 0;
1042
1043 str = js_NewString(cx, chars, nchars);
1044 if (!str) {
1045 JS_free(cx, chars);
1046 return JS_FALSE;
1047 }
1048 *vp = STRING_TO_JSVAL(str);
1049 return JS_TRUE;
1050 }
1051
1052 static JSBool
1053 obj_toLocaleString(JSContext *cx, uintN argc, jsval *vp)
1054 {
1055 jsval thisv;
1056 JSString *str;
1057
1058 thisv = JS_THIS(cx, vp);
1059 if (JSVAL_IS_NULL(thisv))
1060 return JS_FALSE;
1061
1062 str = js_ValueToString(cx, thisv);
1063 if (!str)
1064 return JS_FALSE;
1065
1066 *vp = STRING_TO_JSVAL(str);
1067 return JS_TRUE;
1068 }
1069
1070 static JSBool
1071 obj_valueOf(JSContext *cx, uintN argc, jsval *vp)
1072 {
1073 *vp = JS_THIS(cx, vp);
1074 return !JSVAL_IS_NULL(*vp);
1075 }
1076
1077 /*
1078 * Check whether principals subsumes scopeobj's principals, and return true
1079 * if so (or if scopeobj has no principals, for backward compatibility with
1080 * the JS API, which does not require principals), and false otherwise.
1081 */
1082 JSBool
1083 js_CheckPrincipalsAccess(JSContext *cx, JSObject *scopeobj,
1084 JSPrincipals *principals, JSAtom *caller)
1085 {
1086 JSSecurityCallbacks *callbacks;
1087 JSPrincipals *scopePrincipals;
1088 const char *callerstr;
1089
1090 callbacks = JS_GetSecurityCallbacks(cx);
1091 if (callbacks && callbacks->findObjectPrincipals) {
1092 scopePrincipals = callbacks->findObjectPrincipals(cx, scopeobj);
1093 if (!principals || !scopePrincipals ||
1094 !principals->subsume(principals, scopePrincipals)) {
1095 callerstr = js_AtomToPrintableString(cx, caller);
1096 if (!callerstr)
1097 return JS_FALSE;
1098 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1099 JSMSG_BAD_INDIRECT_CALL, callerstr);
1100 return JS_FALSE;
1101 }
1102 }
1103 return JS_TRUE;
1104 }
1105
1106 JSObject *
1107 js_CheckScopeChainValidity(JSContext *cx, JSObject *scopeobj, const char *caller)
1108 {
1109 JSClass *clasp;
1110 JSExtendedClass *xclasp;
1111 JSObject *inner;
1112
1113 if (!scopeobj)
1114 goto bad;
1115
1116 OBJ_TO_INNER_OBJECT(cx, scopeobj);
1117 if (!scopeobj)
1118 return NULL;
1119
1120 inner = scopeobj;
1121
1122 /* XXX This is an awful gross hack. */
1123 while (scopeobj) {
1124 clasp = OBJ_GET_CLASS(cx, scopeobj);
1125 if (clasp->flags & JSCLASS_IS_EXTENDED) {
1126 xclasp = (JSExtendedClass*)clasp;
1127 if (xclasp->innerObject &&
1128 xclasp->innerObject(cx, scopeobj) != scopeobj) {
1129 goto bad;
1130 }
1131 }
1132
1133 scopeobj = OBJ_GET_PARENT(cx, scopeobj);
1134 }
1135
1136 return inner;
1137
1138 bad:
1139 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1140 JSMSG_BAD_INDIRECT_CALL, caller);
1141 return NULL;
1142 }
1143
1144 const char *
1145 js_ComputeFilename(JSContext *cx, JSStackFrame *caller,
1146 JSPrincipals *principals, uintN *linenop)
1147 {
1148 uint32 flags;
1149 #ifdef DEBUG
1150 JSSecurityCallbacks *callbacks = JS_GetSecurityCallbacks(cx);
1151 #endif
1152
1153 JS_ASSERT(principals || !(callbacks && callbacks->findObjectPrincipals));
1154 flags = JS_GetScriptFilenameFlags(caller->script);
1155 if ((flags & JSFILENAME_PROTECTED) &&
1156 principals &&
1157 strcmp(principals->codebase, "[System Principal]")) {
1158 *linenop = 0;
1159 return principals->codebase;
1160 }
1161
1162 if (caller->regs && *caller->regs->pc == JSOP_EVAL) {
1163 JS_ASSERT(caller->regs->pc[JSOP_EVAL_LENGTH] == JSOP_RESUME);
1164 JS_ASSERT(caller->regs->pc[JSOP_EVAL_LENGTH + JSOP_RESUME_LENGTH] == JSOP_LINENO);
1165 *linenop = GET_UINT16(caller->regs->pc + JSOP_EVAL_LENGTH + JSOP_RESUME_LENGTH);
1166 } else {
1167 *linenop = js_PCToLineNumber(cx, caller->script,
1168 caller->regs ? caller->regs->pc : NULL);
1169 }
1170 return caller->script->filename;
1171 }
1172
1173 JSBool
1174 js_obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1175 {
1176 JSStackFrame *fp, *caller;
1177 JSBool indirectCall;
1178 JSObject *scopeobj;
1179 JSString *str;
1180 const char *file;
1181 uintN line;
1182 JSPrincipals *principals;
1183 uint32 tcflags;
1184 JSScript *script;
1185 JSBool ok;
1186 #if JS_HAS_EVAL_THIS_SCOPE
1187 JSObject *callerScopeChain = NULL, *callerVarObj = NULL;
1188 JSObject *setCallerScopeChain = NULL;
1189 JSBool setCallerVarObj = JS_FALSE;
1190 #endif
1191
1192 fp = cx->fp;
1193 caller = JS_GetScriptedCaller(cx, fp);
1194 indirectCall = (caller && caller->regs && *caller->regs->pc != JSOP_EVAL);
1195
1196 /*
1197 * Ban all indirect uses of eval (global.foo = eval; global.foo(...)) and
1198 * calls that attempt to use a non-global object as the "with" object in
1199 * the former indirect case.
1200 */
1201 scopeobj = OBJ_GET_PARENT(cx, obj);
1202 if (scopeobj) {
1203 scopeobj = js_GetWrappedObject(cx, obj);
1204 scopeobj = OBJ_GET_PARENT(cx, scopeobj);
1205 }
1206 if (indirectCall || scopeobj) {
1207 uintN flags = scopeobj
1208 ? JSREPORT_ERROR
1209 : JSREPORT_STRICT | JSREPORT_WARNING;
1210 if (!JS_ReportErrorFlagsAndNumber(cx, flags, js_GetErrorMessage, NULL,
1211 JSMSG_BAD_INDIRECT_CALL,
1212 js_eval_str)) {
1213 return JS_FALSE;
1214 }
1215 }
1216
1217 if (!JSVAL_IS_STRING(argv[0])) {
1218 *rval = argv[0];
1219 return JS_TRUE;
1220 }
1221
1222 /*
1223 * If the caller is a lightweight function and doesn't have a variables
1224 * object, then we need to provide one for the compiler to stick any
1225 * declared (var) variables into.
1226 */
1227 if (caller && !caller->varobj && !js_GetCallObject(cx, caller, NULL))
1228 return JS_FALSE;
1229
1230 /* eval no longer takes an optional trailing argument. */
1231 if (argc >= 2 &&
1232 !JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING | JSREPORT_STRICT,
1233 js_GetErrorMessage, NULL,
1234 JSMSG_EVAL_ARITY)) {
1235 return JS_FALSE;
1236 }
1237
1238 /* From here on, control must exit through label out with ok set. */
1239 MUST_FLOW_THROUGH("out");
1240 if (!scopeobj) {
1241 #if JS_HAS_EVAL_THIS_SCOPE
1242 /* If obj.eval(str), emulate 'with (obj) eval(str)' in the caller. */
1243 if (indirectCall) {
1244 callerScopeChain = js_GetScopeChain(cx, caller);
1245 if (!callerScopeChain) {
1246 ok = JS_FALSE;
1247 goto out;
1248 }
1249 OBJ_TO_INNER_OBJECT(cx, obj);
1250 if (!obj) {
1251 ok = JS_FALSE;
1252 goto out;
1253 }
1254 if (obj != callerScopeChain) {
1255 ok = js_CheckPrincipalsAccess(cx, obj,
1256 caller->script->principals,
1257 cx->runtime->atomState.evalAtom);
1258 if (!ok)
1259 goto out;
1260
1261 scopeobj = js_NewWithObject(cx, obj, callerScopeChain, -1);
1262 if (!scopeobj) {
1263 ok = JS_FALSE;
1264 goto out;
1265 }
1266
1267 /* Set fp->scopeChain too, for the compiler. */
1268 caller->scopeChain = fp->scopeChain = scopeobj;
1269
1270 /* Remember scopeobj so we can null its private when done. */
1271 setCallerScopeChain = scopeobj;
1272 }
1273
1274 callerVarObj = caller->varobj;
1275 if (obj != callerVarObj) {
1276 /* Set fp->varobj too, for the compiler. */
1277 caller->varobj = fp->varobj = obj;
1278 setCallerVarObj = JS_TRUE;
1279 }
1280 }
1281 #endif
1282
1283 /*
1284 * Compile using caller's current scope object.
1285 *
1286 * NB: This means that native callers (who reach this point through
1287 * the C API) must use the two parameter form.
1288 */
1289 if (caller) {
1290 scopeobj = js_GetScopeChain(cx, caller);
1291 if (!scopeobj) {
1292 ok = JS_FALSE;
1293 goto out;
1294 }
1295 }
1296 }
1297
1298 /* Ensure we compile this eval with the right object in the scope chain. */
1299 scopeobj = js_CheckScopeChainValidity(cx, scopeobj, js_eval_str);
1300 if (!scopeobj) {
1301 ok = JS_FALSE;
1302 goto out;
1303 }
1304
1305 str = JSVAL_TO_STRING(argv[0]);
1306 if (caller) {
1307 principals = JS_EvalFramePrincipals(cx, fp, caller);
1308 file = js_ComputeFilename(cx, caller, principals, &line);
1309 } else {
1310 file = NULL;
1311 line = 0;
1312 principals = NULL;
1313 }
1314
1315 tcflags = TCF_COMPILE_N_GO;
1316 if (caller)
1317 tcflags |= TCF_PUT_STATIC_DEPTH(caller->script->staticDepth + 1);
1318 script = js_CompileScript(cx, scopeobj, caller, principals, tcflags,
1319 JSSTRING_CHARS(str), JSSTRING_LENGTH(str),
1320 NULL, file, line);
1321 if (!script) {
1322 ok = JS_FALSE;
1323 goto out;
1324 }
1325
1326 if (argc < 2) {
1327 /* Execute using caller's new scope object (might be a Call object). */
1328 if (caller)
1329 scopeobj = caller->scopeChain;
1330 }
1331
1332 /*
1333 * Belt-and-braces: check that the lesser of eval's principals and the
1334 * caller's principals has access to scopeobj.
1335 */
1336 ok = js_CheckPrincipalsAccess(cx, scopeobj, principals,
1337 cx->runtime->atomState.evalAtom);
1338 if (ok)
1339 ok = js_Execute(cx, scopeobj, script, caller, JSFRAME_EVAL, rval);
1340
1341 script->u.nextToGC = JS_SCRIPTS_TO_GC(cx);
1342 JS_SCRIPTS_TO_GC(cx) = script;
1343 #ifdef CHECK_SCRIPT_OWNER
1344 script->owner = NULL;
1345 #endif
1346
1347 out:
1348 #if JS_HAS_EVAL_THIS_SCOPE
1349 /* Restore OBJ_GET_PARENT(scopeobj) not callerScopeChain in case of Call. */
1350 if (setCallerScopeChain) {
1351 caller->scopeChain = callerScopeChain;
1352 JS_ASSERT(OBJ_GET_CLASS(cx, setCallerScopeChain) == &js_WithClass);
1353 JS_SetPrivate(cx, setCallerScopeChain, NULL);
1354 }
1355 if (setCallerVarObj)
1356 caller->varobj = callerVarObj;
1357 #endif
1358 return ok;
1359 }
1360
1361 #if JS_HAS_OBJ_WATCHPOINT
1362
1363 static JSBool
1364 obj_watch_handler(JSContext *cx, JSObject *obj, jsval id, jsval old, jsval *nvp,
1365 void *closure)
1366 {
1367 JSObject *callable;
1368 JSSecurityCallbacks *callbacks;
1369 JSStackFrame *caller;
1370 JSPrincipals *subject, *watcher;
1371 JSResolvingKey key;
1372 JSResolvingEntry *entry;
1373 uint32 generation;
1374 jsval argv[3];
1375 JSBool ok;
1376
1377 callable = (JSObject *) closure;
1378
1379 callbacks = JS_GetSecurityCallbacks(cx);
1380 if (callbacks && callbacks->findObjectPrincipals) {
1381 /* Skip over any obj_watch_* frames between us and the real subject. */
1382 caller = JS_GetScriptedCaller(cx, cx->fp);
1383 if (caller) {
1384 /*
1385 * Only call the watch handler if the watcher is allowed to watch
1386 * the currently executing script.
1387 */
1388 watcher = callbacks->findObjectPrincipals(cx, callable);
1389 subject = JS_StackFramePrincipals(cx, caller);
1390
1391 if (watcher && subject && !watcher->subsume(watcher, subject)) {
1392 /* Silently don't call the watch handler. */
1393 return JS_TRUE;
1394 }
1395 }
1396 }
1397
1398 /* Avoid recursion on (obj, id) already being watched on cx. */
1399 key.obj = obj;
1400 key.id = id;
1401 if (!js_StartResolving(cx, &key, JSRESFLAG_WATCH, &entry))
1402 return JS_FALSE;
1403 if (!entry)
1404 return JS_TRUE;
1405 generation = cx->resolvingTable->generation;
1406
1407 argv[0] = id;
1408 argv[1] = old;
1409 argv[2] = *nvp;
1410 ok = js_InternalCall(cx, obj, OBJECT_TO_JSVAL(callable), 3, argv, nvp);
1411 js_StopResolving(cx, &key, JSRESFLAG_WATCH, entry, generation);
1412 return ok;
1413 }
1414
1415 static JSBool
1416 obj_watch(JSContext *cx, uintN argc, jsval *vp)
1417 {
1418 JSObject *callable;
1419 jsval userid, value;
1420 jsid propid;
1421 JSObject *obj;
1422 uintN attrs;
1423
1424 if (argc <= 1) {
1425 js_ReportMissingArg(cx, vp, 1);
1426 return JS_FALSE;
1427 }
1428
1429 callable = js_ValueToCallableObject(cx, &vp[3], 0);
1430 if (!callable)
1431 return JS_FALSE;
1432
1433 /* Compute the unique int/atom symbol id needed by js_LookupProperty. */
1434 userid = vp[2];
1435 if (!JS_ValueToId(cx, userid, &propid))
1436 return JS_FALSE;
1437
1438 obj = JS_THIS_OBJECT(cx, vp);
1439 if (!obj || !OBJ_CHECK_ACCESS(cx, obj, propid, JSACC_WATCH, &value, &attrs))
1440 return JS_FALSE;
1441 if (attrs & JSPROP_READONLY)
1442 return JS_TRUE;
1443 *vp = JSVAL_VOID;
1444
1445 if (OBJ_IS_DENSE_ARRAY(cx, obj) && !js_MakeArraySlow(cx, obj))
1446 return JS_FALSE;
1447 return JS_SetWatchPoint(cx, obj, userid, obj_watch_handler, callable);
1448 }
1449
1450 static JSBool
1451 obj_unwatch(JSContext *cx, uintN argc, jsval *vp)
1452 {
1453 JSObject *obj;
1454
1455 obj = JS_THIS_OBJECT(cx, vp);
1456 if (!obj)
1457 return JS_FALSE;
1458 *vp = JSVAL_VOID;
1459 return JS_ClearWatchPoint(cx, obj, argc != 0 ? vp[2] : JSVAL_VOID,
1460 NULL, NULL);
1461 }
1462
1463 #endif /* JS_HAS_OBJ_WATCHPOINT */
1464
1465 /*
1466 * Prototype and property query methods, to complement the 'in' and
1467 * 'instanceof' operators.
1468 */
1469
1470 /* Proposed ECMA 15.2.4.5. */
1471 JSBool
1472 js_obj_hasOwnProperty(JSContext *cx, uintN argc, jsval *vp)
1473 {
1474 JSObject *obj;
1475
1476 obj = JS_THIS_OBJECT(cx, vp);
1477 return obj &&
1478 js_HasOwnPropertyHelper(cx, obj->map->ops->lookupProperty, argc, vp);
1479 }
1480
1481 JSBool
1482 js_HasOwnPropertyHelper(JSContext *cx, JSLookupPropOp lookup, uintN argc,
1483 jsval *vp)
1484 {
1485 jsid id;
1486 JSObject *obj;
1487
1488 if (!JS_ValueToId(cx, argc != 0 ? vp[2] : JSVAL_VOID, &id))
1489 return JS_FALSE;
1490 obj = JS_THIS_OBJECT(cx, vp);
1491 return obj && js_HasOwnProperty(cx, lookup, obj, id, vp);
1492 }
1493
1494 JSBool
1495 js_HasOwnProperty(JSContext *cx, JSLookupPropOp lookup, JSObject *obj, jsid id,
1496 jsval *vp)
1497 {
1498 JSObject *obj2;
1499 JSProperty *prop;
1500 JSScopeProperty *sprop;
1501
1502 if (!lookup(cx, obj, id, &obj2, &prop))
1503 return JS_FALSE;
1504 if (!prop) {
1505 *vp = JSVAL_FALSE;
1506 } else if (obj2 == obj) {
1507 *vp = JSVAL_TRUE;
1508 } else {
1509 JSClass *clasp;
1510 JSExtendedClass *xclasp;
1511 JSObject *outer;
1512
1513 clasp = OBJ_GET_CLASS(cx, obj2);
1514 if (!(clasp->flags & JSCLASS_IS_EXTENDED) ||
1515 !(xclasp = (JSExtendedClass *) clasp)->outerObject) {
1516 outer = NULL;
1517 } else {
1518 outer = xclasp->outerObject(cx, obj2);
1519 if (!outer)
1520 return JS_FALSE;
1521 }
1522 if (outer == obj) {
1523 *vp = JSVAL_TRUE;
1524 } else if (OBJ_IS_NATIVE(obj2) && OBJ_GET_CLASS(cx, obj) == clasp) {
1525 /*
1526 * The combination of JSPROP_SHARED and JSPROP_PERMANENT in a
1527 * delegated property makes that property appear to be direct in
1528 * all delegating instances of the same native class. This hack
1529 * avoids bloating every function instance with its own 'length'
1530 * (AKA 'arity') property. But it must not extend across class
1531 * boundaries, to avoid making hasOwnProperty lie (bug 320854).
1532 *
1533 * It's not really a hack, of course: a permanent property can't
1534 * be deleted, and JSPROP_SHARED means "don't allocate a slot in
1535 * any instance, prototype or delegating". Without a slot, and
1536 * without the ability to remove and recreate (with differences)
1537 * the property, there is no way to tell whether it is directly
1538 * owned, or indirectly delegated.
1539 */
1540 sprop = (JSScopeProperty *)prop;
1541 *vp = BOOLEAN_TO_JSVAL(SPROP_IS_SHARED_PERMANENT(sprop));
1542 } else {
1543 *vp = JSVAL_FALSE;
1544 }
1545 }
1546 if (prop)
1547 OBJ_DROP_PROPERTY(cx, obj2, prop);
1548 return JS_TRUE;
1549 }
1550
1551 /* Proposed ECMA 15.2.4.6. */
1552 static JSBool
1553 obj_isPrototypeOf(JSContext *cx, uintN argc, jsval *vp)
1554 {
1555 JSBool b;
1556
1557 if (!js_IsDelegate(cx, JS_THIS_OBJECT(cx, vp),
1558 argc != 0 ? vp[2] : JSVAL_VOID, &b)) {
1559 return JS_FALSE;
1560 }
1561 *vp = BOOLEAN_TO_JSVAL(b);
1562 return JS_TRUE;
1563 }
1564
1565 /* Proposed ECMA 15.2.4.7. */
1566 JSBool
1567 js_obj_propertyIsEnumerable(JSContext *cx, uintN argc, jsval *vp)
1568 {
1569 jsid id;
1570 JSObject *obj;
1571
1572 if (!JS_ValueToId(cx, argc != 0 ? vp[2] : JSVAL_VOID, &id))
1573 return JS_FALSE;
1574
1575 obj = JS_THIS_OBJECT(cx, vp);
1576 return obj && js_PropertyIsEnumerable(cx, obj, id, vp);
1577 }
1578
1579 JSBool
1580 js_PropertyIsEnumerable(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
1581 {
1582 JSObject *pobj;
1583 uintN attrs;
1584 JSProperty *prop;
1585 JSBool ok;
1586
1587 if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
1588 return JS_FALSE;
1589
1590 if (!prop) {
1591 *vp = JSVAL_FALSE;
1592 return JS_TRUE;
1593 }
1594
1595 /*
1596 * XXX ECMA spec error compatible: return false unless hasOwnProperty.
1597 * The ECMA spec really should be fixed so propertyIsEnumerable and the
1598 * for..in loop agree on whether prototype properties are enumerable,
1599 * obviously by fixing this method (not by breaking the for..in loop!).
1600 *
1601 * We check here for shared permanent prototype properties, which should
1602 * be treated as if they are local to obj. They are an implementation
1603 * technique used to satisfy ECMA requirements; users should not be able
1604 * to distinguish a shared permanent proto-property from a local one.
1605 */
1606 if (pobj != obj &&
1607 !(OBJ_IS_NATIVE(pobj) &&
1608 SPROP_IS_SHARED_PERMANENT((JSScopeProperty *)prop))) {
1609 OBJ_DROP_PROPERTY(cx, pobj, prop);
1610 *vp = JSVAL_FALSE;
1611 return JS_TRUE;
1612 }
1613
1614 ok = OBJ_GET_ATTRIBUTES(cx, pobj, id, prop, &attrs);
1615 OBJ_DROP_PROPERTY(cx, pobj, prop);
1616 if (ok)
1617 *vp = BOOLEAN_TO_JSVAL((attrs & JSPROP_ENUMERATE) != 0);
1618 return ok;
1619 }
1620
1621 #if JS_HAS_GETTER_SETTER
1622 static JSBool
1623 obj_defineGetter(JSContext *cx, uintN argc, jsval *vp)
1624 {
1625 jsval fval, junk;
1626 jsid id;
1627 JSObject *obj;
1628 uintN attrs;
1629
1630 if (argc <= 1 || JS_TypeOfValue(cx, vp[3]) != JSTYPE_FUNCTION) {
1631 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1632 JSMSG_BAD_GETTER_OR_SETTER,
1633 js_getter_str);
1634 return JS_FALSE;
1635 }
1636 fval = vp[3];
1637
1638 if (!JS_ValueToId(cx, vp[2], &id))
1639 return JS_FALSE;
1640 obj = JS_THIS_OBJECT(cx, vp);
1641 if (!obj || !js_CheckRedeclaration(cx, obj, id, JSPROP_GETTER, NULL, NULL))
1642 return JS_FALSE;
1643 /*
1644 * Getters and setters are just like watchpoints from an access
1645 * control point of view.
1646 */
1647 if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &junk, &attrs))
1648 return JS_FALSE;
1649 *vp = JSVAL_VOID;
1650 return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID,
1651 (JSPropertyOp) JSVAL_TO_OBJECT(fval),
1652 JS_PropertyStub,
1653 JSPROP_ENUMERATE | JSPROP_GETTER | JSPROP_SHARED,
1654 NULL);
1655 }
1656
1657 static JSBool
1658 obj_defineSetter(JSContext *cx, uintN argc, jsval *vp)
1659 {
1660 jsval fval, junk;
1661 jsid id;
1662 JSObject *obj;
1663 uintN attrs;
1664
1665 if (argc <= 1 || JS_TypeOfValue(cx, vp[3]) != JSTYPE_FUNCTION) {
1666 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1667 JSMSG_BAD_GETTER_OR_SETTER,
1668 js_setter_str);
1669 return JS_FALSE;
1670 }
1671 fval = vp[3];
1672
1673 if (!JS_ValueToId(cx, vp[2], &id))
1674 return JS_FALSE;
1675 obj = JS_THIS_OBJECT(cx, vp);
1676 if (!obj || !js_CheckRedeclaration(cx, obj, id, JSPROP_SETTER, NULL, NULL))
1677 return JS_FALSE;
1678 /*
1679 * Getters and setters are just like watchpoints from an access
1680 * control point of view.
1681 */
1682 if (!OBJ_CHECK_ACCESS(cx, obj, id, JSACC_WATCH, &junk, &attrs))
1683 return JS_FALSE;
1684 *vp = JSVAL_VOID;
1685 return OBJ_DEFINE_PROPERTY(cx, obj, id, JSVAL_VOID,
1686 JS_PropertyStub,
1687 (JSPropertyOp) JSVAL_TO_OBJECT(fval),
1688 JSPROP_ENUMERATE | JSPROP_SETTER | JSPROP_SHARED,
1689 NULL);
1690 }
1691
1692 static JSBool
1693 obj_lookupGetter(JSContext *cx, uintN argc, jsval *vp)
1694 {
1695 jsid id;
1696 JSObject *obj, *pobj;
1697 JSProperty *prop;
1698 JSScopeProperty *sprop;
1699
1700 if (!JS_ValueToId(cx, argc != 0 ? vp[2] : JSVAL_VOID, &id))
1701 return JS_FALSE;
1702 obj = JS_THIS_OBJECT(cx, vp);
1703 if (!obj || !OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
1704 return JS_FALSE;
1705 *vp = JSVAL_VOID;
1706 if (prop) {
1707 if (OBJ_IS_NATIVE(pobj)) {
1708 sprop = (JSScopeProperty *) prop;
1709 if (sprop->attrs & JSPROP_GETTER)
1710 *vp = OBJECT_TO_JSVAL(sprop->getter);
1711 }
1712 OBJ_DROP_PROPERTY(cx, pobj, prop);
1713 }
1714 return JS_TRUE;
1715 }
1716
1717 static JSBool
1718 obj_lookupSetter(JSContext *cx, uintN argc, jsval *vp)
1719 {
1720 jsid id;
1721 JSObject *obj, *pobj;
1722 JSProperty *prop;
1723 JSScopeProperty *sprop;
1724
1725 if (!JS_ValueToId(cx, argc != 0 ? vp[2] : JSVAL_VOID, &id))
1726 return JS_FALSE;
1727 obj = JS_THIS_OBJECT(cx, vp);
1728 if (!obj || !OBJ_LOOKUP_PROPERTY(cx, obj, id, &pobj, &prop))
1729 return JS_FALSE;
1730 *vp = JSVAL_VOID;
1731 if (prop) {
1732 if (OBJ_IS_NATIVE(pobj)) {
1733 sprop = (JSScopeProperty *) prop;
1734 if (sprop->attrs & JSPROP_SETTER)
1735 *vp = OBJECT_TO_JSVAL(sprop->setter);
1736 }
1737 OBJ_DROP_PROPERTY(cx, pobj, prop);
1738 }
1739 return JS_TRUE;
1740 }
1741 #endif /* JS_HAS_GETTER_SETTER */
1742
1743 JSBool
1744 obj_getPrototypeOf(JSContext *cx, uintN argc, jsval *vp)
1745 {
1746 JSObject *obj;
1747 uintN attrs;
1748
1749 if (argc == 0) {
1750 js_ReportMissingArg(cx, vp, 0);
1751 return JS_FALSE;
1752 }
1753
1754 obj = js_ValueToNonNullObject(cx, vp[2]);
1755 if (!obj)
1756 return JS_FALSE;
1757 vp[2] = OBJECT_TO_JSVAL(obj);
1758
1759 return OBJ_CHECK_ACCESS(cx, obj,
1760 ATOM_TO_JSID(cx->runtime->atomState.protoAtom),
1761 JSACC_PROTO, vp, &attrs);
1762 }
1763
1764 #if JS_HAS_OBJ_WATCHPOINT
1765 const char js_watch_str[] = "watch";
1766 const char js_unwatch_str[] = "unwatch";
1767 #endif
1768 const char js_hasOwnProperty_str[] = "hasOwnProperty";
1769 const char js_isPrototypeOf_str[] = "isPrototypeOf";
1770 const char js_propertyIsEnumerable_str[] = "propertyIsEnumerable";
1771 #if JS_HAS_GETTER_SETTER
1772 const char js_defineGetter_str[] = "__defineGetter__";
1773 const char js_defineSetter_str[] = "__defineSetter__";
1774 const char js_lookupGetter_str[] = "__lookupGetter__";
1775 const char js_lookupSetter_str[] = "__lookupSetter__";
1776 #endif
1777
1778 static JSFunctionSpec object_methods[] = {
1779 #if JS_HAS_TOSOURCE
1780 JS_FN(js_toSource_str, obj_toSource, 0,0),
1781 #endif
1782 JS_FN(js_toString_str, obj_toString, 0,0),
1783 JS_FN(js_toLocaleString_str, obj_toLocaleString, 0,0),
1784 JS_FN(js_valueOf_str, obj_valueOf, 0,0),
1785 #if JS_HAS_OBJ_WATCHPOINT
1786 JS_FN(js_watch_str, obj_watch, 2,0),
1787 JS_FN(js_unwatch_str, obj_unwatch, 1,0),
1788 #endif
1789 JS_FN(js_hasOwnProperty_str, js_obj_hasOwnProperty, 1,0),
1790 JS_FN(js_isPrototypeOf_str, obj_isPrototypeOf, 1,0),
1791 JS_FN(js_propertyIsEnumerable_str, js_obj_propertyIsEnumerable, 1,0),
1792 #if JS_HAS_GETTER_SETTER
1793 JS_FN(js_defineGetter_str, obj_defineGetter, 2,0),
1794 JS_FN(js_defineSetter_str, obj_defineSetter, 2,0),
1795 JS_FN(js_lookupGetter_str, obj_lookupGetter, 1,0),
1796 JS_FN(js_lookupSetter_str, obj_lookupSetter, 1,0),
1797 #endif
1798 JS_FS_END
1799 };
1800
1801 static JSFunctionSpec object_static_methods[] = {
1802 JS_FN("getPrototypeOf", obj_getPrototypeOf, 1,0),
1803 JS_FS_END
1804 };
1805
1806 JSBool
1807 js_Object(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
1808 {
1809 if (argc == 0) {
1810 /* Trigger logic below to construct a blank object. */
1811 obj = NULL;
1812 } else {
1813 /* If argv[0] is null or undefined, obj comes back null. */
1814 if (!js_ValueToObject(cx, argv[0], &obj))
1815 return JS_FALSE;
1816 }
1817 if (!obj) {
1818 JS_ASSERT(!argc || JSVAL_IS_NULL(argv[0]) || JSVAL_IS_VOID(argv[0]));
1819 if (cx->fp->flags & JSFRAME_CONSTRUCTING)
1820 return JS_TRUE;
1821 obj = js_NewObject(cx, &js_ObjectClass, NULL, NULL, 0);
1822 if (!obj)
1823 return JS_FALSE;
1824 }
1825 *rval = OBJECT_TO_JSVAL(obj);
1826 return JS_TRUE;
1827 }
1828
1829 /*
1830 * ObjectOps and Class for with-statement stack objects.
1831 */
1832 static JSBool
1833 with_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
1834 JSProperty **propp)
1835 {
1836 JSObject *proto = OBJ_GET_PROTO(cx, obj);
1837 if (!proto)
1838 return js_LookupProperty(cx, obj, id, objp, propp);
1839 return OBJ_LOOKUP_PROPERTY(cx, proto, id, objp, propp);
1840 }
1841
1842 static JSBool
1843 with_GetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
1844 {
1845 JSObject *proto = OBJ_GET_PROTO(cx, obj);
1846 if (!proto)
1847 return js_GetProperty(cx, obj, id, vp);
1848 return OBJ_GET_PROPERTY(cx, proto, id, vp);
1849 }
1850
1851 static JSBool
1852 with_SetProperty(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
1853 {
1854 JSObject *proto = OBJ_GET_PROTO(cx, obj);
1855 if (!proto)
1856 return js_SetProperty(cx, obj, id, vp);
1857 return OBJ_SET_PROPERTY(cx, proto, id, vp);
1858 }
1859
1860 static JSBool
1861 with_GetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
1862 uintN *attrsp)
1863 {
1864 JSObject *proto = OBJ_GET_PROTO(cx, obj);
1865 if (!proto)
1866 return js_GetAttributes(cx, obj, id, prop, attrsp);
1867 return OBJ_GET_ATTRIBUTES(cx, proto, id, prop, attrsp);
1868 }
1869
1870 static JSBool
1871 with_SetAttributes(JSContext *cx, JSObject *obj, jsid id, JSProperty *prop,
1872 uintN *attrsp)
1873 {
1874 JSObject *proto = OBJ_GET_PROTO(cx, obj);
1875 if (!proto)
1876 return js_SetAttributes(cx, obj, id, prop, attrsp);
1877 return OBJ_SET_ATTRIBUTES(cx, proto, id, prop, attrsp);
1878 }
1879
1880 static JSBool
1881 with_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval)
1882 {
1883 JSObject *proto = OBJ_GET_PROTO(cx, obj);
1884 if (!proto)
1885 return js_DeleteProperty(cx, obj, id, rval);
1886 return OBJ_DELETE_PROPERTY(cx, proto, id, rval);
1887 }
1888
1889 static JSBool
1890 with_DefaultValue(JSContext *cx, JSObject *obj, JSType hint, jsval *vp)
1891 {
1892 JSObject *proto = OBJ_GET_PROTO(cx, obj);
1893 if (!proto)
1894 return js_DefaultValue(cx, obj, hint, vp);
1895 return OBJ_DEFAULT_VALUE(cx, proto, hint, vp);
1896 }
1897
1898 static JSBool
1899 with_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
1900 jsval *statep, jsid *idp)
1901 {
1902 JSObject *proto = OBJ_GET_PROTO(cx, obj);
1903 if (!proto)
1904 return js_Enumerate(cx, obj, enum_op, statep, idp);
1905 return OBJ_ENUMERATE(cx, proto, enum_op, statep, idp);
1906 }
1907
1908 static JSBool
1909 with_CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
1910 jsval *vp, uintN *attrsp)
1911 {
1912 JSObject *proto = OBJ_GET_PROTO(cx, obj);
1913 if (!proto)
1914 return js_CheckAccess(cx, obj, id, mode, vp, attrsp);
1915 return OBJ_CHECK_ACCESS(cx, proto, id, mode, vp, attrsp);
1916 }
1917
1918 static JSObject *
1919 with_ThisObject(JSContext *cx, JSObject *obj)
1920 {
1921 JSObject *proto = OBJ_GET_PROTO(cx, obj);
1922 if (!proto)
1923 return obj;
1924 return OBJ_THIS_OBJECT(cx, proto);
1925 }
1926
1927 JS_FRIEND_DATA(JSObjectOps) js_WithObjectOps = {
1928 js_NewObjectMap, js_DestroyObjectMap,
1929 with_LookupProperty, js_DefineProperty,
1930 with_GetProperty, with_SetProperty,
1931 with_GetAttributes, with_SetAttributes,
1932 with_DeleteProperty, with_DefaultValue,
1933 with_Enumerate, with_CheckAccess,
1934 with_ThisObject, NATIVE_DROP_PROPERTY,
1935 NULL, NULL,
1936 NULL, NULL,
1937 js_SetProtoOrParent, js_SetProtoOrParent,
1938 js_TraceObject, js_Clear,
1939 NULL, NULL
1940 };
1941
1942 static JSObjectOps *
1943 with_getObjectOps(JSContext *cx, JSClass *clasp)
1944 {
1945 return &js_WithObjectOps;
1946 }
1947
1948 JSClass js_WithClass = {
1949 "With",
1950 JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_IS_ANONYMOUS,
1951 JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
1952 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
1953 with_getObjectOps,
1954 0,0,0,0,0,0,0
1955 };
1956
1957 JSObject *
1958 js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth)
1959 {
1960 JSObject *obj;
1961
1962 obj = js_NewObject(cx, &js_WithClass, proto, parent, 0);
1963 if (!obj)
1964 return NULL;
1965 STOBJ_SET_SLOT(obj, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(cx->fp));
1966 OBJ_SET_BLOCK_DEPTH(cx, obj, depth);
1967 return obj;
1968 }
1969
1970 JSObject *
1971 js_NewBlockObject(JSContext *cx)
1972 {
1973 JSObject *obj;
1974 JSBool ok;
1975
1976 /*
1977 * Null obj's proto slot so that Object.prototype.* does not pollute block
1978 * scopes. Make sure obj has its own scope too, since clearing proto does
1979 * not affect OBJ_SCOPE(obj).
1980 */
1981 obj = js_NewObject(cx, &js_BlockClass, NULL, NULL, 0);
1982 if (!obj)
1983 return NULL;
1984 JS_LOCK_OBJ(cx, obj);
1985 ok = js_GetMutableScope(cx, obj) != NULL;
1986 JS_UNLOCK_OBJ(cx, obj);
1987 if (!ok)
1988 return NULL;
1989 OBJ_CLEAR_PROTO(cx, obj);
1990 return obj;
1991 }
1992
1993 JSObject *
1994 js_CloneBlockObject(JSContext *cx, JSObject *proto, JSObject *parent,
1995 JSStackFrame *fp)
1996 {
1997 JSObject *clone;
1998
1999 JS_ASSERT(STOBJ_GET_CLASS(proto) == &js_BlockClass);
2000 JS_ASSERT(!OBJ_IS_CLONED_BLOCK(proto));
2001 clone = js_NewObject(cx, &js_BlockClass, proto, parent, 0);
2002 if (!clone)
2003 return NULL;
2004 STOBJ_SET_SLOT(clone, JSSLOT_PRIVATE, PRIVATE_TO_JSVAL(fp));
2005 STOBJ_SET_SLOT(clone, JSSLOT_BLOCK_DEPTH,
2006 OBJ_GET_SLOT(cx, proto, JSSLOT_BLOCK_DEPTH));
2007 JS_ASSERT(OBJ_IS_CLONED_BLOCK(clone));
2008 return clone;
2009 }
2010
2011 JSBool
2012 js_PutBlockObject(JSContext *cx, JSBool normalUnwind)
2013 {
2014 JSStackFrame *fp;
2015 JSObject *obj;
2016 uintN depth, count;
2017
2018 /* Blocks have one fixed slot available for the first local.*/
2019 JS_STATIC_ASSERT(JS_INITIAL_NSLOTS == JSSLOT_BLOCK_DEPTH + 2);
2020
2021 fp = cx->fp;
2022 obj = fp->scopeChain;
2023 JS_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_BlockClass);
2024 JS_ASSERT(OBJ_GET_PRIVATE(cx, obj) == cx->fp);
2025 JS_ASSERT(OBJ_IS_CLONED_BLOCK(obj));
2026
2027 /*
2028 * Block objects should never be exposed to scripts. Thus the clone should
2029 * not own the property map and rather always share it with the prototype
2030 * object. This allows to skip updating OBJ_SCOPE(obj)->map.freeslot after
2031 * we copy the stack slots into reserved slots.
2032 */
2033 JS_ASSERT(OBJ_SCOPE(obj)->object != obj);
2034
2035 /* Block objects should not have reserved slots before they are put. */
2036 JS_ASSERT(STOBJ_NSLOTS(obj) == JS_INITIAL_NSLOTS);
2037
2038 /* The block and its locals must be on the current stack for GC safety. */
2039 depth = OBJ_BLOCK_DEPTH(cx, obj);
2040 count = OBJ_BLOCK_COUNT(cx, obj);
2041 JS_ASSERT(depth <= (size_t) (fp->regs->sp - StackBase(fp)));
2042 JS_ASSERT(count <= (size_t) (fp->regs->sp - StackBase(fp) - depth));
2043
2044 /* See comments in CheckDestructuring from jsparse.c. */
2045 JS_ASSERT(count >= 1);
2046
2047 depth += fp->script->nfixed;
2048 obj->fslots[JSSLOT_BLOCK_DEPTH + 1] = fp->slots[depth];
2049 if (normalUnwind && count > 1) {
2050 --count;
2051 JS_LOCK_OBJ(cx, obj);
2052 if (!js_ReallocSlots(cx, obj, JS_INITIAL_NSLOTS + count, JS_TRUE))
2053 normalUnwind = JS_FALSE;
2054 else
2055 memcpy(obj->dslots, fp->slots + depth + 1, count * sizeof(jsval));
2056 JS_UNLOCK_OBJ(cx, obj);
2057 }
2058
2059 /* We must clear the private slot even with errors. */
2060 JS_SetPrivate(cx, obj, NULL);
2061 fp->scopeChain = OBJ_GET_PARENT(cx, obj);
2062 return normalUnwind;
2063 }
2064
2065 static JSBool
2066 block_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
2067 {
2068 uintN index;
2069 JSStackFrame *fp;
2070
2071 JS_ASSERT(JS_InstanceOf(cx, obj, &js_BlockClass, NULL));
2072 JS_ASSERT(OBJ_IS_CLONED_BLOCK(obj));
2073 if (!JSVAL_IS_INT(id))
2074 return JS_TRUE;
2075
2076 index = (uint16) JSVAL_TO_INT(id);
2077 fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
2078 if (fp) {
2079 index += fp->script->nfixed + OBJ_BLOCK_DEPTH(cx, obj);
2080 JS_ASSERT(index < fp->script->nslots);
2081 *vp = fp->slots[index];
2082 return JS_TRUE;
2083 }
2084
2085 /* Reserve slots start with the first slot after the private. */
2086 index += JSSLOT_BLOCK_DEPTH - JSSLOT_PRIVATE;
2087 return JS_GetReservedSlot(cx, obj, index, vp);
2088 }
2089
2090 static JSBool
2091 block_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
2092 {
2093 uintN index;
2094 JSStackFrame *fp;
2095
2096 JS_ASSERT(JS_InstanceOf(cx, obj, &js_BlockClass, NULL));
2097 if (!JSVAL_IS_INT(id))
2098 return JS_TRUE;
2099
2100 index = (uint16) JSVAL_TO_INT(id);
2101 fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
2102 if (fp) {
2103 index += fp->script->nfixed + OBJ_BLOCK_DEPTH(cx, obj);
2104 JS_ASSERT(index < fp->script->nslots);
2105 fp->slots[index] = *vp;
2106 return JS_TRUE;
2107 }
2108
2109 /* Reserve slots start with the first slot after the private. */
2110 index += JSSLOT_BLOCK_DEPTH - JSSLOT_PRIVATE;
2111 return JS_SetReservedSlot(cx, obj, index, *vp);
2112 }
2113
2114 #if JS_HAS_XDR
2115
2116 #define NO_PARENT_INDEX ((uint32)-1)
2117
2118 uint32
2119 FindObjectIndex(JSObjectArray *array, JSObject *obj)
2120 {
2121 size_t i;
2122
2123 if (array) {
2124 i = array->length;
2125 do {
2126
2127 if (array->vector[--i] == obj)
2128 return i;
2129 } while (i != 0);
2130 }
2131
2132 return NO_PARENT_INDEX;
2133 }
2134
2135 static JSBool
2136 block_xdrObject(JSXDRState *xdr, JSObject **objp)
2137 {
2138 JSContext *cx;
2139 uint32 parentId;
2140 JSObject *obj, *parent;
2141 uint16 depth, count, i;
2142 uint32 tmp;
2143 JSTempValueRooter tvr;
2144 JSScopeProperty *sprop;
2145 jsid propid;
2146 JSAtom *atom;
2147 int16 shortid;
2148 JSBool ok;
2149
2150 cx = xdr->cx;
2151 #ifdef __GNUC__
2152 obj = NULL; /* quell GCC overwarning */
2153 #endif
2154
2155 if (xdr->mode == JSXDR_ENCODE) {
2156 obj = *objp;
2157 parent = OBJ_GET_PARENT(cx, obj);
2158 parentId = (xdr->script->objectsOffset == 0)
2159 ? NO_PARENT_INDEX
2160 : FindObjectIndex(JS_SCRIPT_OBJECTS(xdr->script), parent);
2161 depth = (uint16)OBJ_BLOCK_DEPTH(cx, obj);
2162 count = (uint16)OBJ_BLOCK_COUNT(cx, obj);
2163 tmp = (uint32)(depth << 16) | count;
2164 }
2165 #ifdef __GNUC__ /* suppress bogus gcc warnings */
2166 else count = 0;
2167 #endif
2168
2169 /* First, XDR the parent atomid. */
2170 if (!JS_XDRUint32(xdr, &parentId))
2171 return JS_FALSE;
2172
2173 if (xdr->mode == JSXDR_DECODE) {
2174 obj = js_NewBlockObject(cx);
2175 if (!obj)
2176 return JS_FALSE;
2177 *objp = obj;
2178
2179 /*
2180 * If there's a parent id, then get the parent out of our script's
2181 * object array. We know that we XDR block object in outer-to-inner
2182 * order, which means that getting the parent now will work.
2183 */
2184 if (parentId == NO_PARENT_INDEX)
2185 parent = NULL;
2186 else
2187 JS_GET_SCRIPT_OBJECT(xdr->script, parentId, parent);
2188 STOBJ_SET_PARENT(obj, parent);
2189 }
2190
2191 JS_PUSH_SINGLE_TEMP_ROOT(cx, OBJECT_TO_JSVAL(obj), &tvr);
2192
2193 if (!JS_XDRUint32(xdr, &tmp)) {
2194 JS_POP_TEMP_ROOT(cx, &tvr);
2195 return JS_FALSE;
2196 }
2197
2198 if (xdr->mode == JSXDR_DECODE) {
2199 depth = (uint16)(tmp >> 16);
2200 count = (uint16)tmp;
2201 STOBJ_SET_SLOT(obj, JSSLOT_BLOCK_DEPTH, INT_TO_JSVAL(depth));
2202 }
2203
2204 /*
2205 * XDR the block object's properties. We know that there are 'count'
2206 * properties to XDR, stored as id/shortid pairs. We do not XDR any
2207 * non-native properties, only those that the compiler created.
2208 */
2209 sprop = NULL;
2210 ok = JS_TRUE;
2211 for (i = 0; i < count; i++) {
2212 if (xdr->mode == JSXDR_ENCODE) {
2213 /* Find a property to XDR. */
2214 do {
2215 /* If sprop is NULL, this is the first property. */
2216 sprop = sprop ? sprop->parent : OBJ_SCOPE(obj)->lastProp;
2217 } while (!(sprop->flags & SPROP_HAS_SHORTID));
2218
2219 JS_ASSERT(sprop->getter == js_BlockClass.getProperty);
2220 propid = sprop->id;
2221 JS_ASSERT(JSID_IS_ATOM(propid));
2222 atom = JSID_TO_ATOM(propid);
2223 shortid = sprop->shortid;
2224 JS_ASSERT(shortid >= 0);
2225 }
2226
2227 /* XDR the real id, then the shortid. */
2228 if (!js_XDRStringAtom(xdr, &atom) ||
2229 !JS_XDRUint16(xdr, (uint16 *)&shortid)) {
2230 ok = JS_FALSE;
2231 break;
2232 }
2233
2234 if (xdr->mode == JSXDR_DECODE) {
2235 if (!js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom),
2236 JSVAL_VOID, NULL, NULL,
2237 JSPROP_ENUMERATE | JSPROP_PERMANENT,
2238 SPROP_HAS_SHORTID, shortid, NULL)) {
2239 ok = JS_FALSE;
2240 break;
2241 }
2242 }
2243 }
2244
2245 JS_POP_TEMP_ROOT(cx, &tvr);
2246 return ok;
2247 }
2248
2249 #else
2250 # define block_xdrObject NULL
2251 #endif
2252
2253 static uint32
2254 block_reserveSlots(JSContext *cx, JSObject *obj)
2255 {
2256 return OBJ_IS_CLONED_BLOCK(obj) ? OBJ_BLOCK_COUNT(cx, obj) : 0;
2257 }
2258
2259 JSClass js_BlockClass = {
2260 "Block",
2261 JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) |
2262 JSCLASS_IS_ANONYMOUS | JSCLASS_HAS_CACHED_PROTO(JSProto_Block),
2263 JS_PropertyStub, JS_PropertyStub, block_getProperty, block_setProperty,
2264 JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
2265 NULL, NULL, NULL, NULL, block_xdrObject, NULL, NULL, block_reserveSlots
2266 };
2267
2268 JSObject*
2269 js_InitBlockClass(JSContext *cx, JSObject* obj)
2270 {
2271 JSObject *proto;
2272
2273 proto = JS_InitClass(cx, obj, NULL, &js_BlockClass, NULL, 0, NULL,
2274 NULL, NULL, NULL);
2275 if (!proto)
2276 return NULL;
2277
2278 OBJ_CLEAR_PROTO(cx, proto);
2279 return proto;
2280 }
2281
2282 JSObject *
2283 js_InitEval(JSContext *cx, JSObject *obj)
2284 {
2285 /* ECMA (15.1.2.1) says 'eval' is a property of the global object. */
2286 if (!js_DefineFunction(cx, obj, cx->runtime->atomState.evalAtom,
2287 js_obj_eval, 1, 0)) {
2288 return NULL;
2289 }
2290
2291 return obj;
2292 }
2293
2294 JSObject *
2295 js_InitObjectClass(JSContext *cx, JSObject *obj)
2296 {
2297 return JS_InitClass(cx, obj, NULL, &js_ObjectClass, js_Object, 1,
2298 object_props, object_methods, NULL,
2299 object_static_methods);
2300 }
2301
2302 void
2303 js_InitObjectMap(JSObjectMap *map, jsrefcount nrefs, JSObjectOps *ops,
2304 JSClass *clasp)
2305 {
2306 map->nrefs = nrefs;
2307 map->ops = ops;
2308 map->freeslot = JSSLOT_FREE(clasp);
2309 }
2310
2311 JSObjectMap *
2312 js_NewObjectMap(JSContext *cx, jsrefcount nrefs, JSObjectOps *ops,
2313 JSClass *clasp, JSObject *obj)
2314 {
2315 return (JSObjectMap *) js_NewScope(cx, nrefs, ops, clasp, obj);
2316 }
2317
2318 void
2319 js_DestroyObjectMap(JSContext *cx, JSObjectMap *map)
2320 {
2321 js_DestroyScope(cx, (JSScope *)map);
2322 }
2323
2324 JSObjectMap *
2325 js_HoldObjectMap(JSContext *cx, JSObjectMap *map)
2326 {
2327 JS_ASSERT(map->nrefs >= 0);
2328 JS_ATOMIC_INCREMENT(&map->nrefs);
2329 return map;
2330 }
2331
2332 JSObjectMap *
2333 js_DropObjectMap(JSContext *cx, JSObjectMap *map, JSObject *obj)
2334 {
2335 JS_ASSERT(map->nrefs > 0);
2336 JS_ATOMIC_DECREMENT(&map->nrefs);
2337 if (map->nrefs == 0) {
2338 map->ops->destroyObjectMap(cx, map);
2339 return NULL;
2340 }
2341 if (MAP_IS_NATIVE(map) && ((JSScope *)map)->object == obj)
2342 ((JSScope *)map)->object = NULL;
2343 return map;
2344 }
2345
2346 static void
2347 FreeSlots(JSContext *cx, JSObject *obj)
2348 {
2349 if (obj->dslots) {
2350 JS_ASSERT((uint32)obj->dslots[-1] > JS_INITIAL_NSLOTS);
2351 JS_free(cx, obj->dslots - 1);
2352 obj->dslots = NULL;
2353 }
2354 }
2355
2356 #define SLOTS_TO_DYNAMIC_WORDS(nslots) \
2357 (JS_ASSERT((nslots) > JS_INITIAL_NSLOTS), (nslots) + 1 - JS_INITIAL_NSLOTS)
2358
2359 #define DYNAMIC_WORDS_TO_SLOTS(words) \
2360 (JS_ASSERT((words) > 1), (words) - 1 + JS_INITIAL_NSLOTS)
2361
2362 JSBool
2363 js_ReallocSlots(JSContext *cx, JSObject *obj, uint32 nslots,
2364 JSBool exactAllocation)
2365 {
2366 jsval *old, *slots;
2367 uint32 oslots, nwords, owords, log, i;
2368
2369 /*
2370 * Minimal number of dynamic slots to allocate.
2371 */
2372 #define MIN_DYNAMIC_WORDS 4
2373
2374 /*
2375 * The limit to switch to linear allocation strategy from the power of 2
2376 * growth no to waste too much memory.
2377 */
2378 #define LINEAR_GROWTH_STEP JS_BIT(16)
2379
2380 old = obj->dslots;
2381 if (nslots <= JS_INITIAL_NSLOTS) {
2382 if (old &&
2383 (exactAllocation ||
2384 SLOTS_TO_DYNAMIC_WORDS((uint32)old[-1]) != MIN_DYNAMIC_WORDS ||
2385 nslots <= (JS_INITIAL_NSLOTS +
2386 JSSLOT_FREE(STOBJ_GET_CLASS(obj))) / 2)) {
2387 /*
2388 * We do not want to free dynamic slots when allocation is a hint,
2389 * we reached minimal allocation and almost all fixed slots are
2390 * used. It avoids allocating dynamic slots again when properties
2391 * are added to the object.
2392 *
2393 * If there were no private or reserved slots, the condition to
2394 * free the slots would be
2395 *
2396 * nslots <= JS_INITIAL_NSLOTS / 2
2397 *
2398 * but to account for never removed slots before JSSLOT_FREE(class)
2399 * we need to subtract it from the slot counts which gives
2400 *
2401 * nslots - JSSLOT_FREE <= (JS_INITIAL_NSLOTS - JSSLOT_FREE) / 2
2402 *
2403 * or
2404 *
2405 * nslots <= (JS_INITIAL_NSLOTS + JSSLOT_FREE) / 2
2406 */
2407 FreeSlots(cx, obj);
2408 }
2409 return JS_TRUE;
2410 }
2411
2412 oslots = (old) ? (uint32)*--old : JS_INITIAL_NSLOTS;
2413 nwords = SLOTS_TO_DYNAMIC_WORDS(nslots);
2414
2415 if (nslots > oslots) {
2416 if (!exactAllocation) {
2417 /*
2418 * Round up nslots so the number of bytes in dslots array is power
2419 * of 2 to ensure exponential grouth.
2420 */
2421 if (nwords <= MIN_DYNAMIC_WORDS) {
2422 nwords = MIN_DYNAMIC_WORDS;
2423 } else if (nwords < LINEAR_GROWTH_STEP) {
2424 JS_CEILING_LOG2(log, nwords);
2425 nwords = JS_BIT(log);
2426 } else {
2427 nwords = JS_ROUNDUP(nwords, LINEAR_GROWTH_STEP);
2428 }
2429 }
2430 slots = (jsval *)JS_realloc(cx, old, nwords * sizeof(jsval));
2431 if (!slots)
2432 return JS_FALSE;
2433 } else {
2434 JS_ASSERT(nslots < oslots);
2435 if (!exactAllocation) {
2436 owords = DYNAMIC_WORDS_TO_SLOTS(oslots);
2437 if (owords <= MIN_DYNAMIC_WORDS)
2438 return JS_TRUE;
2439 if (owords < LINEAR_GROWTH_STEP * 2) {
2440 /*
2441 * Shrink only if 1/4 of slots are left and we need to grow
2442 * the array at least twice to reach the current capacity. It
2443 * prevents frequent capacity growth/shrinking when slots are
2444 * often removed and added.
2445 */
2446 if (nwords > owords / 4)
2447 return JS_TRUE;
2448 JS_CEILING_LOG2(log, nwords);
2449 nwords = JS_BIT(log);
2450 if (nwords < MIN_DYNAMIC_WORDS)
2451 nwords = MIN_DYNAMIC_WORDS;
2452 } else {
2453 /*
2454 * Shrink only if we free at least 2 linear allocation
2455 * segments, to prevent growth/shrinking resonance.
2456 */
2457 if (nwords > owords - LINEAR_GROWTH_STEP * 2)
2458 return JS_TRUE;
2459 nwords = JS_ROUNDUP(nwords, LINEAR_GROWTH_STEP);
2460 }
2461 }
2462
2463 /* We avoid JS_realloc not to report a failed shrink attempt. */
2464 slots = (jsval *)realloc(old, nwords * sizeof(jsval));
2465 if (!slots)
2466 slots = old;
2467 }
2468
2469 nslots = DYNAMIC_WORDS_TO_SLOTS(nwords);
2470 *slots++ = (jsval)nslots;
2471 obj->dslots = slots;
2472
2473 /* If we're extending an allocation, initialize free slots. */
2474 for (i = oslots; i < nslots; i++)
2475 slots[i - JS_INITIAL_NSLOTS] = JSVAL_VOID;
2476
2477 return JS_TRUE;
2478
2479 #undef LINEAR_GROWTH_STEP
2480 #undef MIN_DYNAMIC_WORDS
2481 }
2482
2483 extern JSBool
2484 js_GetClassId(JSContext *cx, JSClass *clasp, jsid *idp)
2485 {
2486 JSProtoKey key;
2487 JSAtom *atom;
2488
2489 key = JSCLASS_CACHED_PROTO_KEY(clasp);
2490 if (key != JSProto_Null) {
2491 *idp = INT_TO_JSID(key);
2492 } else if (clasp->flags & JSCLASS_IS_ANONYMOUS) {
2493 *idp = INT_TO_JSID(JSProto_Object);
2494 } else {
2495 atom = js_Atomize(cx, clasp->name, strlen(clasp->name), 0);
2496 if (!atom)
2497 return JS_FALSE;
2498 *idp = ATOM_TO_JSID(atom);
2499 }
2500 return JS_TRUE;
2501 }
2502
2503 JSObject *
2504 js_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent,
2505 uintN objectSize)
2506 {
2507 jsid id;
2508
2509 /* Bootstrap the ur-object, and make it the default prototype object. */
2510 if (!proto) {
2511 if (!js_GetClassId(cx, clasp, &id))
2512 return NULL;
2513 if (!js_GetClassPrototype(cx, parent, id, &proto))
2514 return NULL;
2515 if (!proto &&
2516 !js_GetClassPrototype(cx, parent, INT_TO_JSID(JSProto_Object),
2517 &proto)) {
2518 return NULL;
2519 }
2520 }
2521
2522 return js_NewObjectWithGivenProto(cx, clasp, proto, parent, objectSize);
2523 }
2524
2525 JSObject *
2526 js_NewObjectWithGivenProto(JSContext *cx, JSClass *clasp, JSObject *proto,
2527 JSObject *parent, uintN objectSize)
2528 {
2529 JSObject *obj;
2530 JSObjectOps *ops;
2531 JSObjectMap *map;
2532 JSClass *protoclasp;
2533 uint32 nslots, i;
2534 JSTempValueRooter tvr;
2535
2536 #ifdef INCLUDE_MOZILLA_DTRACE
2537 if (JAVASCRIPT_OBJECT_CREATE_START_ENABLED())
2538 jsdtrace_object_create_start(cx->fp, clasp);
2539 #endif
2540
2541 /* Currently only functions can have non-standard allocation size. */
2542 if (clasp == &js_FunctionClass) {
2543 if (objectSize == 0)
2544 objectSize = sizeof(JSFunction);
2545 else
2546 JS_ASSERT(objectSize == sizeof(JSObject));
2547 } else {
2548 JS_ASSERT(objectSize == 0);
2549 objectSize = sizeof(JSObject);
2550 }
2551
2552 /*
2553 * Allocate an object from the GC heap and initialize all its fields before
2554 * doing any operation that can potentially trigger GC.
2555 */
2556 obj = (JSObject *) js_NewGCThing(cx, GCX_OBJECT, objectSize);
2557 if (!obj)
2558 goto earlybad;
2559
2560 obj->map = NULL;
2561 obj->dslots = NULL;
2562
2563 /*
2564 * Set the class slot with the initial value of the system and delegate
2565 * flags set to false.
2566 */
2567 JS_ASSERT(((jsuword) clasp & 3) == 0);
2568 obj->classword = jsuword(clasp);
2569 JS_ASSERT(!STOBJ_IS_DELEGATE(obj));
2570 JS_ASSERT(!STOBJ_IS_SYSTEM(obj));
2571
2572 /* Set the proto and parent properties. */
2573 STOBJ_SET_PROTO(obj, proto);
2574 STOBJ_SET_PARENT(obj, parent);
2575
2576 /* Initialize the remaining fixed slots. */
2577 for (i = JSSLOT_PRIVATE; i != JS_INITIAL_NSLOTS; ++i)
2578 obj->fslots[i] = JSVAL_VOID;
2579
2580 #ifdef DEBUG
2581 memset((uint8 *) obj + sizeof(JSObject), JS_FREE_PATTERN,
2582 objectSize - sizeof(JSObject));
2583 #endif
2584
2585 /*
2586 * Root obj to prevent it from being collected out from under this call to
2587 * js_NewObject. There's a possibilty of GC under the objectHook call-out
2588 * further below.
2589 */
2590 JS_PUSH_TEMP_ROOT_OBJECT(cx, obj, &tvr);
2591
2592 /* Always call the class's getObjectOps hook if it has one. */
2593 ops = clasp->getObjectOps
2594 ? clasp->getObjectOps(cx, clasp)
2595 : &js_ObjectOps;
2596
2597 /*
2598 * Default parent to the parent of the prototype, which was set from
2599 * the parent of the prototype's constructor.
2600 */
2601 if (proto && !parent)
2602 STOBJ_SET_PARENT(obj, OBJ_GET_PARENT(cx, proto));
2603
2604 /*
2605 * Share proto's map only if it has the same JSObjectOps, and only if
2606 * proto's class has the same private and reserved slots as obj's map
2607 * and class have. We assume that if prototype and object are of the
2608 * same class, they always have the same number of computed reserved
2609 * slots (returned via clasp->reserveSlots); otherwise, prototype and
2610 * object classes must have the same (null or not) reserveSlots hook.
2611 */
2612 if (proto &&
2613 (map = proto->map)->ops == ops &&
2614 ((protoclasp = OBJ_GET_CLASS(cx, proto)) == clasp ||
2615 (!((protoclasp->flags ^ clasp->flags) &
2616 (JSCLASS_HAS_PRIVATE |
2617 (JSCLASS_RESERVED_SLOTS_MASK << JSCLASS_RESERVED_SLOTS_SHIFT))) &&
2618 protoclasp->reserveSlots == clasp->reserveSlots)))
2619 {
2620 /* Share the given prototype's map. */
2621 obj->map = js_HoldObjectMap(cx, map);
2622 } else {
2623 map = ops->newObjectMap(cx, 1, ops, clasp, obj);
2624 if (!map)
2625 goto bad;
2626 obj->map = map;
2627
2628 /* Let ops->newObjectMap set freeslot so as to reserve slots. */
2629 nslots = map->freeslot;
2630 JS_ASSERT(nslots >= JSSLOT_PRIVATE);
2631 if (nslots > JS_INITIAL_NSLOTS &&
2632 !js_ReallocSlots(cx, obj, nslots, JS_TRUE)) {
2633 js_DropObjectMap(cx, map, obj);
2634 obj->map = NULL;
2635 goto bad;
2636 }
2637 }
2638
2639 if (cx->debugHooks->objectHook) {
2640 JS_KEEP_ATOMS(cx->runtime);
2641 cx->debugHooks->objectHook(cx, obj, JS_TRUE,
2642 cx->debugHooks->objectHookData);
2643 JS_UNKEEP_ATOMS(cx->runtime);
2644 }
2645
2646 out:
2647 JS_POP_TEMP_ROOT(cx, &tvr);
2648 cx->weakRoots.newborn[GCX_OBJECT] = obj;
2649 #ifdef INCLUDE_MOZILLA_DTRACE
2650 if (JAVASCRIPT_OBJECT_CREATE_ENABLED())
2651 jsdtrace_object_create(cx, clasp, obj);
2652 if (JAVASCRIPT_OBJECT_CREATE_DONE_ENABLED())
2653 jsdtrace_object_create_done(cx->fp, clasp);
2654 #endif
2655 return obj;
2656
2657 bad:
2658 obj = NULL;
2659 goto out;
2660
2661 earlybad:
2662 #ifdef INCLUDE_MOZILLA_DTRACE
2663 if (JAVASCRIPT_OBJECT_CREATE_ENABLED())
2664 jsdtrace_object_create(cx, clasp, NULL);
2665 if (JAVASCRIPT_OBJECT_CREATE_DONE_ENABLED())
2666 jsdtrace_object_create_done(cx->fp, clasp);
2667 #endif
2668 return NULL;
2669 }
2670
2671 JS_BEGIN_EXTERN_C
2672
2673 static JSObject *
2674 js_InitNullClass(JSContext *cx, JSObject *obj)
2675 {
2676 JS_ASSERT(0);
2677 return NULL;
2678 }
2679
2680 #define JS_PROTO(name,code,init) extern JSObject *init(JSContext *, JSObject *);
2681 #include "jsproto.tbl"
2682 #undef JS_PROTO
2683
2684 static JSObjectOp lazy_prototype_init[JSProto_LIMIT] = {
2685 #define JS_PROTO(name,code,init) init,
2686 #include "jsproto.tbl"
2687 #undef JS_PROTO
2688 };
2689
2690 JS_END_EXTERN_C
2691
2692 JSBool
2693 js_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key,
2694 JSObject **objp)
2695 {
2696 JSBool ok;
2697 JSObject *tmp, *cobj;
2698 JSResolvingKey rkey;
2699 JSResolvingEntry *rentry;
2700 uint32 generation;
2701 JSObjectOp init;
2702 jsval v;
2703
2704 while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL)
2705 obj = tmp;
2706 if (!(OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_IS_GLOBAL)) {
2707 *objp = NULL;
2708 return JS_TRUE;
2709 }
2710
2711 ok = JS_GetReservedSlot(cx, obj, key, &v);
2712 if (!ok)
2713 return JS_FALSE;
2714 if (!JSVAL_IS_PRIMITIVE(v)) {
2715 *objp = JSVAL_TO_OBJECT(v);
2716 return JS_TRUE;
2717 }
2718
2719 rkey.obj = obj;
2720 rkey.id = ATOM_TO_JSID(cx->runtime->atomState.classAtoms[key]);
2721 if (!js_StartResolving(cx, &rkey, JSRESFLAG_LOOKUP, &rentry))
2722 return JS_FALSE;
2723 if (!rentry) {
2724 /* Already caching key in obj -- suppress recursion. */
2725 *objp = NULL;
2726 return JS_TRUE;
2727 }
2728 generation = cx->resolvingTable->generation;
2729
2730 cobj = NULL;
2731 init = lazy_prototype_init[key];
2732 if (init) {
2733 if (!init(cx, obj)) {
2734 ok = JS_FALSE;
2735 } else {
2736 ok = JS_GetReservedSlot(cx, obj, key, &v);
2737 if (ok && !JSVAL_IS_PRIMITIVE(v))
2738 cobj = JSVAL_TO_OBJECT(v);
2739 }
2740 }
2741
2742 js_StopResolving(cx, &rkey, JSRESFLAG_LOOKUP, rentry, generation);
2743 *objp = cobj;
2744 return ok;
2745 }
2746
2747 JSBool
2748 js_SetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, JSObject *cobj)
2749 {
2750 JS_ASSERT(!OBJ_GET_PARENT(cx, obj));
2751 if (!(OBJ_GET_CLASS(cx, obj)->flags & JSCLASS_IS_GLOBAL))
2752 return JS_TRUE;
2753
2754 return JS_SetReservedSlot(cx, obj, key, OBJECT_TO_JSVAL(cobj));
2755 }
2756
2757 JSBool
2758 js_FindClassObject(JSContext *cx, JSObject *start, jsid id, jsval *vp)
2759 {
2760 JSObject *obj, *cobj, *pobj;
2761 JSProtoKey key;
2762 JSProperty *prop;
2763 jsval v;
2764 JSScopeProperty *sprop;
2765
2766 if (start || (cx->fp && (start = cx->fp->scopeChain) != NULL)) {
2767 /* Find the topmost object in the scope chain. */
2768 do {
2769 obj = start;
2770 start = OBJ_GET_PARENT(cx, obj);
2771 } while (start);
2772 } else {
2773 obj = cx->globalObject;
2774 if (!obj) {
2775 *vp = JSVAL_VOID;
2776 return JS_TRUE;
2777 }
2778 }
2779
2780 OBJ_TO_INNER_OBJECT(cx, obj);
2781 if (!obj)
2782 return JS_FALSE;
2783
2784 if (JSID_IS_INT(id)) {
2785 key = (JSProtoKey) JSID_TO_INT(id);
2786 JS_ASSERT(key != JSProto_Null);
2787 if (!js_GetClassObject(cx, obj, key, &cobj))
2788 return JS_FALSE;
2789 if (cobj) {
2790 *vp = OBJECT_TO_JSVAL(cobj);
2791 return JS_TRUE;
2792 }
2793 id = ATOM_TO_JSID(cx->runtime->atomState.classAtoms[key]);
2794 }
2795
2796 JS_ASSERT(OBJ_IS_NATIVE(obj));
2797 if (js_LookupPropertyWithFlags(cx, obj, id, JSRESOLVE_CLASSNAME,
2798 &pobj, &prop) < 0) {
2799 return JS_FALSE;
2800 }
2801 v = JSVAL_VOID;
2802 if (prop) {
2803 if (OBJ_IS_NATIVE(pobj)) {
2804 sprop = (JSScopeProperty *) prop;
2805 if (SPROP_HAS_VALID_SLOT(sprop, OBJ_SCOPE(pobj))) {
2806 v = LOCKED_OBJ_GET_SLOT(pobj, sprop->slot);
2807 if (JSVAL_IS_PRIMITIVE(v))
2808 v = JSVAL_VOID;
2809 }
2810 }
2811 OBJ_DROP_PROPERTY(cx, pobj, prop);
2812 }
2813 *vp = v;
2814 return JS_TRUE;
2815 }
2816
2817 JSObject *
2818 js_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto,
2819 JSObject *parent, uintN argc, jsval *argv)
2820 {
2821 jsid id;
2822 jsval cval, rval;
2823 JSTempValueRooter argtvr, tvr;
2824 JSObject *obj, *ctor;
2825
2826 JS_PUSH_TEMP_ROOT(cx, argc, argv, &argtvr);
2827
2828 if (!js_GetClassId(cx, clasp, &id) ||
2829 !js_FindClassObject(cx, parent, id, &cval)) {
2830 JS_POP_TEMP_ROOT(cx, &argtvr);
2831 return NULL;
2832 }
2833
2834 if (JSVAL_IS_PRIMITIVE(cval)) {
2835 js_ReportIsNotFunction(cx, &cval, JSV2F_CONSTRUCT | JSV2F_SEARCH_STACK);
2836 JS_POP_TEMP_ROOT(cx, &argtvr);
2837 return NULL;
2838 }
2839
2840 /*
2841 * Protect cval in case a crazy getter for .prototype uproots it. After
2842 * this point, all control flow must exit through label out with obj set.
2843 */
2844 JS_PUSH_SINGLE_TEMP_ROOT(cx, cval, &tvr);
2845 MUST_FLOW_THROUGH("out");
2846
2847 /*
2848 * If proto or parent are NULL, set them to Constructor.prototype and/or
2849 * Constructor.__parent__, just like JSOP_NEW does.
2850 */
2851 ctor = JSVAL_TO_OBJECT(cval);
2852 if (!parent)
2853 parent = OBJ_GET_PARENT(cx, ctor);
2854 if (!proto) {
2855 if (!OBJ_GET_PROPERTY(cx, ctor,
2856 ATOM_TO_JSID(cx->runtime->atomState
2857 .classPrototypeAtom),
2858 &rval)) {
2859 obj = NULL;
2860 goto out;
2861 }
2862 if (JSVAL_IS_OBJECT(rval))
2863 proto = JSVAL_TO_OBJECT(rval);
2864 }
2865
2866 obj = js_NewObject(cx, clasp, proto, parent, 0);
2867 if (!obj)
2868 goto out;
2869
2870 if (!js_InternalConstruct(cx, obj, cval, argc, argv, &rval))
2871 goto bad;
2872
2873 if (JSVAL_IS_PRIMITIVE(rval))
2874 goto out;
2875 obj = JSVAL_TO_OBJECT(rval);
2876
2877 /*
2878 * If the instance's class differs from what was requested, throw a type
2879 * error. If the given class has both the JSCLASS_HAS_PRIVATE and the
2880 * JSCLASS_CONSTRUCT_PROTOTYPE flags, and the instance does not have its
2881 * private data set at this point, then the constructor was replaced and
2882 * we should throw a type error.
2883 */
2884 if (OBJ_GET_CLASS(cx, obj) != clasp ||
2885 (!(~clasp->flags & (JSCLASS_HAS_PRIVATE |
2886 JSCLASS_CONSTRUCT_PROTOTYPE)) &&
2887 !JS_GetPrivate(cx, obj))) {
2888 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2889 JSMSG_WRONG_CONSTRUCTOR, clasp->name);
2890 goto bad;
2891 }
2892
2893 out:
2894 JS_POP_TEMP_ROOT(cx, &tvr);
2895 JS_POP_TEMP_ROOT(cx, &argtvr);
2896 return obj;
2897
2898 bad:
2899 cx->weakRoots.newborn[GCX_OBJECT] = NULL;
2900 obj = NULL;
2901 goto out;
2902 }
2903
2904 void
2905 js_FinalizeObject(JSContext *cx, JSObject *obj)
2906 {
2907 JSObjectMap *map;
2908
2909 /* Cope with stillborn objects that have no map. */
2910 map = obj->map;
2911 if (!map)
2912 return;
2913
2914 if (cx->debugHooks->objectHook) {
2915 cx->debugHooks->objectHook(cx, obj, JS_FALSE,
2916 cx->debugHooks->objectHookData);
2917 }
2918
2919 /* Finalize obj first, in case it needs map and slots. */
2920 STOBJ_GET_CLASS(obj)->finalize(cx, obj);
2921
2922 #ifdef INCLUDE_MOZILLA_DTRACE
2923 if (JAVASCRIPT_OBJECT_FINALIZE_ENABLED())
2924 jsdtrace_object_finalize(obj);
2925 #endif
2926
2927 /* Drop map and free slots. */
2928 js_DropObjectMap(cx, map, obj);
2929 FreeSlots(cx, obj);
2930 }
2931
2932 /* XXXbe if one adds props, deletes earlier props, adds more, the last added
2933 won't recycle the deleted props' slots. */
2934 JSBool
2935 js_AllocSlot(JSContext *cx, JSObject *obj, uint32 *slotp)
2936 {
2937 JSObjectMap *map;
2938 JSClass *clasp;
2939
2940 map = obj->map;
2941 JS_ASSERT(!MAP_IS_NATIVE(map) || ((JSScope *)map)->object == obj);
2942 clasp = LOCKED_OBJ_GET_CLASS(obj);
2943 if (map->freeslot == JSSLOT_FREE(clasp) && clasp->reserveSlots) {
2944 /* Adjust map->freeslot to include computed reserved slots, if any. */
2945 map->freeslot += clasp->reserveSlots(cx, obj);
2946 }
2947
2948 if (map->freeslot >= STOBJ_NSLOTS(obj) &&
2949 !js_ReallocSlots(cx, obj, map->freeslot + 1, JS_FALSE)) {
2950 return JS_FALSE;
2951 }
2952
2953 /* js_ReallocSlots or js_FreeSlot should set the free slots to void. */
2954 JS_ASSERT(JSVAL_IS_VOID(STOBJ_GET_SLOT(obj, map->freeslot)));
2955 *slotp = map->freeslot++;
2956 return JS_TRUE;
2957 }
2958
2959 void
2960 js_FreeSlot(JSContext *cx, JSObject *obj, uint32 slot)
2961 {
2962 JSObjectMap *map;
2963
2964 map = obj->map;
2965 JS_ASSERT(!MAP_IS_NATIVE(map) || ((JSScope *)map)->object == obj);
2966 LOCKED_OBJ_SET_SLOT(obj, slot, JSVAL_VOID);
2967 if (map->freeslot == slot + 1) {
2968 map->freeslot = slot;
2969
2970 /* When shrinking, js_ReallocSlots always returns true. */
2971 js_ReallocSlots(cx, obj, slot, JS_FALSE);
2972 }
2973 }
2974
2975 jsid
2976 js_CheckForStringIndex(jsid id, const jschar *cp, const jschar *end,
2977 JSBool negative)
2978 {
2979 jsuint index = JS7_UNDEC(*cp++);
2980 jsuint oldIndex = 0;
2981 jsuint c = 0;
2982
2983 if (index != 0) {
2984 while (JS7_ISDEC(*cp)) {
2985 oldIndex = index;
2986 c = JS7_UNDEC(*cp);
2987 index = 10 * index + c;
2988 cp++;
2989 }
2990 }
2991 if (cp == end &&
2992 (oldIndex < (JSVAL_INT_MAX / 10) ||
2993 (oldIndex == (JSVAL_INT_MAX / 10) &&
2994 c <= (JSVAL_INT_MAX % 10)))) {
2995 if (negative)
2996 index = 0 - index;
2997 id = INT_TO_JSID((jsint)index);
2998 }
2999 return id;
3000 }
3001
3002 static JSBool
3003 PurgeProtoChain(JSContext *cx, JSObject *obj, jsid id)
3004 {
3005 JSScope *scope;
3006 JSScopeProperty *sprop;
3007
3008 while (obj) {
3009 if (!OBJ_IS_NATIVE(obj)) {
3010 obj = OBJ_GET_PROTO(cx, obj);
3011 continue;
3012 }
3013 JS_LOCK_OBJ(cx, obj);
3014 scope = OBJ_SCOPE(obj);
3015 sprop = SCOPE_GET_PROPERTY(scope, id);
3016 if (sprop) {
3017 PCMETER(JS_PROPERTY_CACHE(cx).pcpurges++);
3018 SCOPE_MAKE_UNIQUE_SHAPE(cx, scope);
3019 JS_UNLOCK_SCOPE(cx, scope);
3020 return JS_TRUE;
3021 }
3022 obj = LOCKED_OBJ_GET_PROTO(scope->object);
3023 JS_UNLOCK_SCOPE(cx, scope);
3024 }
3025 return JS_FALSE;
3026 }
3027
3028 static void
3029 PurgeScopeChain(JSContext *cx, JSObject *obj, jsid id)
3030 {
3031 if (!OBJ_IS_DELEGATE(cx, obj))
3032 return;
3033
3034 PurgeProtoChain(cx, OBJ_GET_PROTO(cx, obj), id);
3035 while ((obj = OBJ_GET_PARENT(cx, obj)) != NULL) {
3036 if (PurgeProtoChain(cx, obj, id))
3037 return;
3038 }
3039 }
3040
3041 JSScopeProperty *
3042 js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id,
3043 JSPropertyOp getter, JSPropertyOp setter, uint32 slot,
3044 uintN attrs, uintN flags, intN shortid)
3045 {
3046 JSScope *scope;
3047 JSScopeProperty *sprop;
3048
3049 /*
3050 * Purge the property cache of now-shadowed id in obj's scope chain. Do
3051 * this optimistically (assuming no failure below) before locking obj, so
3052 * we can lock the shadowed scope.
3053 */
3054 PurgeScopeChain(cx, obj, id);
3055
3056 JS_LOCK_OBJ(cx, obj);
3057 scope = js_GetMutableScope(cx, obj);
3058 if (!scope) {
3059 sprop = NULL;
3060 } else {
3061 /* Convert string indices to integers if appropriate. */
3062 CHECK_FOR_STRING_INDEX(id);
3063 sprop = js_AddScopeProperty(cx, scope, id, getter, setter, slot, attrs,
3064 flags, shortid);
3065 }
3066 JS_UNLOCK_OBJ(cx, obj);
3067 return sprop;
3068 }
3069
3070 JSScopeProperty *
3071 js_ChangeNativePropertyAttrs(JSContext *cx, JSObject *obj,
3072 JSScopeProperty *sprop, uintN attrs, uintN mask,
3073 JSPropertyOp getter, JSPropertyOp setter)
3074 {
3075 JSScope *scope;
3076
3077 JS_LOCK_OBJ(cx, obj);
3078 scope = js_GetMutableScope(cx, obj);
3079 if (!scope) {
3080 sprop = NULL;
3081 } else {
3082 sprop = js_ChangeScopePropertyAttrs(cx, scope, sprop, attrs, mask,
3083 getter, setter);
3084 }
3085 JS_UNLOCK_OBJ(cx, obj);
3086 return sprop;
3087 }
3088
3089 JSBool
3090 js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
3091 JSPropertyOp getter, JSPropertyOp setter, uintN attrs,
3092 JSProperty **propp)
3093 {
3094 return js_DefineNativeProperty(cx, obj, id, value, getter, setter, attrs,
3095 0, 0, propp);
3096 }
3097
3098 /*
3099 * Backward compatibility requires allowing addProperty hooks to mutate the
3100 * nominal initial value of a slot-full property, while GC safety wants that
3101 * value to be stored before the call-out through the hook. Optimize to do
3102 * both while saving cycles for classes that stub their addProperty hook.
3103 */
3104 #define ADD_PROPERTY_HELPER(cx,clasp,obj,scope,sprop,vp,cleanup) \
3105 JS_BEGIN_MACRO \
3106 if ((clasp)->addProperty != JS_PropertyStub) { \
3107 jsval nominal_ = *(vp); \
3108 if (!(clasp)->addProperty(cx, obj, SPROP_USERID(sprop), vp)) { \
3109 cleanup; \
3110 } \
3111 if (*(vp) != nominal_) { \
3112 if (SPROP_HAS_VALID_SLOT(sprop, scope)) \
3113 LOCKED_OBJ_WRITE_BARRIER(cx, obj, (sprop)->slot, *(vp)); \
3114 } \
3115 } \
3116 JS_END_MACRO
3117
3118 JSBool
3119 js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
3120 JSPropertyOp getter, JSPropertyOp setter, uintN attrs,
3121 uintN flags, intN shortid, JSProperty **propp)
3122 {
3123 JSClass *clasp;
3124 JSScope *scope;
3125 JSScopeProperty *sprop;
3126
3127 /* Convert string indices to integers if appropriate. */
3128 CHECK_FOR_STRING_INDEX(id);
3129
3130 #if JS_HAS_GETTER_SETTER
3131 /*
3132 * If defining a getter or setter, we must check for its counterpart and
3133 * update the attributes and property ops. A getter or setter is really
3134 * only half of a property.
3135 */
3136 sprop = NULL;
3137 if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
3138 JSObject *pobj;
3139 JSProperty *prop;
3140
3141 /*
3142 * If JS_THREADSAFE and id is found, js_LookupProperty returns with
3143 * sprop non-null and pobj locked. If pobj == obj, the property is
3144 * already in obj and obj has its own (mutable) scope. So if we are
3145 * defining a getter whose setter was already defined, or vice versa,
3146 * finish the job via js_ChangeScopePropertyAttributes, and refresh
3147 * the property cache line for (obj, id) to map sprop.
3148 */
3149 if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
3150 return JS_FALSE;
3151 sprop = (JSScopeProperty *) prop;
3152 if (sprop &&
3153 pobj == obj &&
3154 (sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
3155 sprop = js_ChangeScopePropertyAttrs(cx, OBJ_SCOPE(obj), sprop,
3156 attrs, sprop->attrs,
3157 (attrs & JSPROP_GETTER)
3158 ? getter
3159 : sprop->getter,
3160 (attrs & JSPROP_SETTER)
3161 ? setter
3162 : sprop->setter);
3163
3164 /* NB: obj == pobj, so we can share unlock code at the bottom. */
3165 if (!sprop)
3166 goto bad;
3167 } else if (prop) {
3168 /* NB: call OBJ_DROP_PROPERTY, as pobj might not be native. */
3169 OBJ_DROP_PROPERTY(cx, pobj, prop);
3170 prop = NULL;
3171 sprop = NULL;
3172 }
3173 }
3174 #endif /* JS_HAS_GETTER_SETTER */
3175
3176 /*
3177 * Purge the property cache of now-shadowed id in obj's scope chain.
3178 * Do this early, before locking obj to avoid nesting locks.
3179 */
3180 PurgeScopeChain(cx, obj, id);
3181
3182 /* Lock if object locking is required by this implementation. */
3183 JS_LOCK_OBJ(cx, obj);
3184
3185 /* Use the object's class getter and setter by default. */
3186 clasp = LOCKED_OBJ_GET_CLASS(obj);
3187 if (!getter)
3188 getter = clasp->getProperty;
3189 if (!setter)
3190 setter = clasp->setProperty;
3191
3192 /* Get obj's own scope if it has one, or create a new one for obj. */
3193 scope = js_GetMutableScope(cx, obj);
3194 if (!scope)
3195 goto bad;
3196
3197 if (!sprop) {
3198 /* Add a new property, or replace an existing one of the same id. */
3199 if (clasp->flags & JSCLASS_SHARE_ALL_PROPERTIES)
3200 attrs |= JSPROP_SHARED;
3201 sprop = js_AddScopeProperty(cx, scope, id, getter, setter,
3202 SPROP_INVALID_SLOT, attrs, flags,
3203 shortid);
3204 if (!sprop)
3205 goto bad;
3206 }
3207
3208 /* Store value before calling addProperty, in case the latter GC's. */
3209 if (SPROP_HAS_VALID_SLOT(sprop, scope))
3210 LOCKED_OBJ_WRITE_BARRIER(cx, obj, sprop->slot, value);
3211
3212 /* XXXbe called with lock held */
3213 ADD_PROPERTY_HELPER(cx, clasp, obj, scope, sprop, &value,
3214 js_RemoveScopeProperty(cx, scope, id);
3215 goto bad);
3216
3217 if (propp)
3218 *propp = (JSProperty *) sprop;
3219 else
3220 JS_UNLOCK_OBJ(cx, obj);
3221 return JS_TRUE;
3222
3223 bad:
3224 JS_UNLOCK_OBJ(cx, obj);
3225 return JS_FALSE;
3226 }
3227
3228 /*
3229 * Given pc pointing after a property accessing bytecode, return true if the
3230 * access is "object-detecting" in the sense used by web scripts, e.g., when
3231 * checking whether document.all is defined.
3232 */
3233 static JSBool
3234 Detecting(JSContext *cx, jsbytecode *pc)
3235 {
3236 JSScript *script;
3237 jsbytecode *endpc;
3238 JSOp op;
3239 JSAtom *atom;
3240
3241 if (!cx->fp)
3242 return JS_FALSE;
3243 script = cx->fp->script;
3244 for (endpc = script->code + script->length;
3245 pc < endpc;
3246 pc += js_CodeSpec[op].length) {
3247 /* General case: a branch or equality op follows the access. */
3248 op = (JSOp) *pc;
3249 if (js_CodeSpec[op].format & JOF_DETECTING)
3250 return JS_TRUE;
3251
3252 switch (op) {
3253 case JSOP_NULL:
3254 /*
3255 * Special case #1: handle (document.all == null). Don't sweat
3256 * about JS1.2's revision of the equality operators here.
3257 */
3258 if (++pc < endpc)
3259 return *pc == JSOP_EQ || *pc == JSOP_NE;
3260 return JS_FALSE;
3261
3262 case JSOP_NAME:
3263 /*
3264 * Special case #2: handle (document.all == undefined). Don't
3265 * worry about someone redefining undefined, which was added by
3266 * Edition 3, so is read/write for backward compatibility.
3267 */
3268 GET_ATOM_FROM_BYTECODE(script, pc, 0, atom);
3269 if (atom == cx->runtime->atomState.typeAtoms[JSTYPE_VOID] &&
3270 (pc += js_CodeSpec[op].length) < endpc) {
3271 op = (JSOp) *pc;
3272 return op == JSOP_EQ || op == JSOP_NE ||
3273 op == JSOP_STRICTEQ || op == JSOP_STRICTNE;
3274 }
3275 return JS_FALSE;
3276
3277 case JSOP_GROUP:
3278 break;
3279
3280 default:
3281 /*
3282 * At this point, anything but an extended atom index prefix means
3283 * we're not detecting.
3284 */
3285 if (!(js_CodeSpec[op].format & JOF_INDEXBASE))
3286 return JS_FALSE;
3287 break;
3288 }
3289 }
3290 return JS_FALSE;
3291 }
3292
3293 JS_FRIEND_API(JSBool)
3294 js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
3295 JSProperty **propp)
3296 {
3297 return js_LookupPropertyWithFlags(cx, obj, id, cx->resolveFlags,
3298 objp, propp) >= 0;
3299 }
3300
3301 #define SCOPE_DEPTH_ACCUM(bs,val) \
3302 JS_SCOPE_DEPTH_METERING(JS_BASIC_STATS_ACCUM(bs, val))
3303
3304 int
3305 js_LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, uintN flags,
3306 JSObject **objp, JSProperty **propp)
3307 {
3308 JSObject *start, *obj2, *proto;
3309 int protoIndex;
3310 JSScope *scope;
3311 JSScopeProperty *sprop;
3312 JSClass *clasp;
3313 JSResolveOp resolve;
3314 JSResolvingKey key;
3315 JSResolvingEntry *entry;
3316 uint32 generation;
3317 JSNewResolveOp newresolve;
3318 jsbytecode *pc;
3319 const JSCodeSpec *cs;
3320 uint32 format;
3321 JSBool ok;
3322
3323 /* Convert string indices to integers if appropriate. */
3324 CHECK_FOR_STRING_INDEX(id);
3325 JS_COUNT_OPERATION(cx, JSOW_LOOKUP_PROPERTY);
3326
3327 /* Search scopes starting with obj and following the prototype link. */
3328