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

Contents of /trunk/js/jsobj.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 585 - (show annotations)
Sun Sep 12 15:13:23 2010 UTC (9 years, 1 month ago) by siliconforks
File size: 196889 byte(s)
Update to SpiderMonkey from Firefox 3.6.9.

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