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 :
47 : #include "mozilla/Util.h"
48 :
49 : #include "jstypes.h"
50 : #include "jsutil.h"
51 : #include "jshash.h"
52 : #include "jsdhash.h"
53 : #include "jsprf.h"
54 : #include "jsapi.h"
55 : #include "jsarray.h"
56 : #include "jsatom.h"
57 : #include "jsbool.h"
58 : #include "jscntxt.h"
59 : #include "jsversion.h"
60 : #include "jsfun.h"
61 : #include "jsgc.h"
62 : #include "jsgcmark.h"
63 : #include "jsinterp.h"
64 : #include "jsiter.h"
65 : #include "jslock.h"
66 : #include "jsnum.h"
67 : #include "jsobj.h"
68 : #include "jsonparser.h"
69 : #include "jsopcode.h"
70 : #include "jsprobes.h"
71 : #include "jsproxy.h"
72 : #include "jsscope.h"
73 : #include "jsscript.h"
74 : #include "jsstr.h"
75 : #include "jsdbgapi.h"
76 : #include "json.h"
77 : #include "jswatchpoint.h"
78 : #include "jswrapper.h"
79 :
80 : #include "builtin/MapObject.h"
81 : #include "frontend/BytecodeCompiler.h"
82 : #include "frontend/BytecodeEmitter.h"
83 : #include "frontend/Parser.h"
84 : #include "js/MemoryMetrics.h"
85 :
86 : #include "jsarrayinlines.h"
87 : #include "jsatominlines.h"
88 : #include "jsinterpinlines.h"
89 : #include "jsobjinlines.h"
90 : #include "jsscopeinlines.h"
91 : #include "jsscriptinlines.h"
92 :
93 : #include "vm/MethodGuard-inl.h"
94 : #include "vm/StringBuffer-inl.h"
95 :
96 : #if JS_HAS_XML_SUPPORT
97 : #include "jsxml.h"
98 : #endif
99 :
100 : #if JS_HAS_XDR
101 : #include "jsxdrapi.h"
102 : #endif
103 :
104 : #include "jsautooplen.h"
105 :
106 : using namespace mozilla;
107 : using namespace js;
108 : using namespace js::gc;
109 : using namespace js::types;
110 :
111 : JS_STATIC_ASSERT(int32_t((JSObject::NELEMENTS_LIMIT - 1) * sizeof(Value)) == int64_t((JSObject::NELEMENTS_LIMIT - 1) * sizeof(Value)));
112 :
113 : Class js::ObjectClass = {
114 : js_Object_str,
115 : JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
116 : JS_PropertyStub, /* addProperty */
117 : JS_PropertyStub, /* delProperty */
118 : JS_PropertyStub, /* getProperty */
119 : JS_StrictPropertyStub, /* setProperty */
120 : JS_EnumerateStub,
121 : JS_ResolveStub,
122 : JS_ConvertStub
123 : };
124 :
125 : JS_FRIEND_API(JSObject *)
126 6924829 : JS_ObjectToInnerObject(JSContext *cx, JSObject *obj)
127 : {
128 6924829 : if (!obj) {
129 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INACTIVE);
130 0 : return NULL;
131 : }
132 6924829 : OBJ_TO_INNER_OBJECT(cx, obj);
133 6924829 : return obj;
134 : }
135 :
136 : JS_FRIEND_API(JSObject *)
137 3947421 : JS_ObjectToOuterObject(JSContext *cx, JSObject *obj)
138 : {
139 3947421 : OBJ_TO_OUTER_OBJECT(cx, obj);
140 3947421 : return obj;
141 : }
142 :
143 : #if JS_HAS_OBJ_PROTO_PROP
144 :
145 : static JSBool
146 : obj_getProto(JSContext *cx, JSObject *obj, jsid id, Value *vp);
147 :
148 : static JSBool
149 : obj_setProto(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp);
150 :
151 : JSPropertySpec object_props[] = {
152 : {js_proto_str, 0, JSPROP_PERMANENT|JSPROP_SHARED, obj_getProto, obj_setProto},
153 : {0,0,0,0,0}
154 : };
155 :
156 : static JSBool
157 1455 : obj_getProto(JSContext *cx, JSObject *obj, jsid id, Value *vp)
158 : {
159 : /* Let CheckAccess get the slot's value, based on the access mode. */
160 : unsigned attrs;
161 1455 : id = ATOM_TO_JSID(cx->runtime->atomState.protoAtom);
162 1455 : return CheckAccess(cx, obj, id, JSACC_PROTO, vp, &attrs);
163 : }
164 :
165 : size_t sSetProtoCalled = 0;
166 :
167 : static JSBool
168 15025 : obj_setProto(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value *vp)
169 : {
170 15025 : if (!cx->runningWithTrustedPrincipals())
171 855 : ++sSetProtoCalled;
172 :
173 : /* ECMAScript 5 8.6.2 forbids changing [[Prototype]] if not [[Extensible]]. */
174 15025 : if (!obj->isExtensible()) {
175 0 : obj->reportNotExtensible(cx);
176 0 : return false;
177 : }
178 :
179 15025 : if (!vp->isObjectOrNull())
180 144 : return true;
181 :
182 14881 : JSObject *pobj = vp->toObjectOrNull();
183 : unsigned attrs;
184 14881 : id = ATOM_TO_JSID(cx->runtime->atomState.protoAtom);
185 14881 : if (!CheckAccess(cx, obj, id, JSAccessMode(JSACC_PROTO|JSACC_WRITE), vp, &attrs))
186 0 : return false;
187 :
188 14881 : return SetProto(cx, obj, pobj, true);
189 : }
190 :
191 : #else /* !JS_HAS_OBJ_PROTO_PROP */
192 :
193 : #define object_props NULL
194 :
195 : #endif /* !JS_HAS_OBJ_PROTO_PROP */
196 :
197 : static bool
198 2408 : MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap, JSSharpInfo *value)
199 : {
200 2408 : JS_CHECK_RECURSION(cx, return NULL);
201 :
202 : JSIdArray *ida;
203 :
204 2408 : JSSharpObjectMap *map = &cx->sharpObjectMap;
205 2408 : JS_ASSERT(map->depth >= 1);
206 2408 : JSSharpInfo sharpid;
207 2408 : JSSharpTable::Ptr p = map->table.lookup(obj);
208 2408 : if (!p) {
209 2407 : if (!map->table.put(obj, sharpid))
210 0 : return false;
211 :
212 2407 : ida = JS_Enumerate(cx, obj);
213 2407 : if (!ida)
214 0 : return false;
215 :
216 2407 : bool ok = true;
217 5242 : for (int i = 0, length = ida->length; i < length; i++) {
218 2835 : jsid id = ida->vector[i];
219 : JSObject *obj2;
220 : JSProperty *prop;
221 2835 : ok = obj->lookupGeneric(cx, id, &obj2, &prop);
222 2835 : if (!ok)
223 0 : break;
224 2835 : if (!prop)
225 0 : continue;
226 : bool hasGetter, hasSetter;
227 5670 : AutoValueRooter v(cx);
228 5670 : AutoValueRooter setter(cx);
229 2835 : if (obj2->isNative()) {
230 1741 : const Shape *shape = (Shape *) prop;
231 1741 : hasGetter = shape->hasGetterValue();
232 1741 : hasSetter = shape->hasSetterValue();
233 1741 : if (hasGetter)
234 2 : v.set(shape->getterValue());
235 1741 : if (hasSetter)
236 1 : setter.set(shape->setterValue());
237 : } else {
238 1094 : hasGetter = hasSetter = false;
239 : }
240 2835 : if (hasSetter) {
241 : /* Mark the getter, then set val to setter. */
242 1 : if (hasGetter && v.value().isObject()) {
243 1 : ok = MarkSharpObjects(cx, &v.value().toObject(), NULL, NULL);
244 1 : if (!ok)
245 : break;
246 : }
247 1 : v.set(setter.value());
248 2834 : } else if (!hasGetter) {
249 2833 : ok = obj->getGeneric(cx, id, v.addr());
250 2833 : if (!ok)
251 : break;
252 : }
253 2835 : if (v.value().isObject() && !MarkSharpObjects(cx, &v.value().toObject(), NULL, NULL)) {
254 0 : ok = false;
255 : break;
256 : }
257 : }
258 2407 : if (!ok || !idap)
259 1588 : JS_DestroyIdArray(cx, ida);
260 2407 : if (!ok)
261 0 : return false;
262 : } else {
263 1 : if (!p->value.hasGen && !p->value.isSharp) {
264 1 : p->value.hasGen = true;
265 : }
266 1 : sharpid = p->value;
267 1 : ida = NULL;
268 : }
269 2408 : if (idap)
270 819 : *idap = ida;
271 2408 : if (value)
272 819 : *value = sharpid;
273 2408 : return true;
274 : }
275 :
276 : bool
277 871 : js_EnterSharpObject(JSContext *cx, JSObject *obj, JSIdArray **idap, bool *alreadySeen, bool *isSharp)
278 : {
279 871 : if (!JS_CHECK_OPERATION_LIMIT(cx))
280 0 : return false;
281 :
282 871 : *alreadySeen = false;
283 :
284 871 : JSSharpObjectMap *map = &cx->sharpObjectMap;
285 :
286 871 : JS_ASSERT_IF(map->depth == 0, map->table.count() == 0);
287 871 : JS_ASSERT_IF(map->table.count() == 0, map->depth == 0);
288 :
289 871 : JSSharpTable::Ptr p;
290 871 : JSSharpInfo sharpid;
291 871 : JSIdArray *ida = NULL;
292 :
293 : /* From this point the control must flow either through out: or bad:. */
294 871 : if (map->depth == 0) {
295 819 : JS_KEEP_ATOMS(cx->runtime);
296 :
297 : /*
298 : * Although MarkSharpObjects tries to avoid invoking getters,
299 : * it ends up doing so anyway under some circumstances; for
300 : * example, if a wrapped object has getters, the wrapper will
301 : * prevent MarkSharpObjects from recognizing them as such.
302 : * This could lead to js_LeaveSharpObject being called while
303 : * MarkSharpObjects is still working.
304 : *
305 : * Increment map->depth while we call MarkSharpObjects, to
306 : * ensure that such a call doesn't free the hash table we're
307 : * still using.
308 : */
309 819 : map->depth = 1;
310 819 : bool success = MarkSharpObjects(cx, obj, &ida, &sharpid);
311 819 : JS_ASSERT(map->depth == 1);
312 819 : map->depth = 0;
313 819 : if (!success)
314 0 : goto bad;
315 819 : JS_ASSERT(!sharpid.isSharp);
316 819 : if (!idap) {
317 261 : JS_DestroyIdArray(cx, ida);
318 261 : ida = NULL;
319 : }
320 : } else {
321 : /*
322 : * It's possible that the value of a property has changed from the
323 : * first time the object's properties are traversed (when the property
324 : * ids are entered into the hash table) to the second (when they are
325 : * converted to strings), i.e., the JSObject::getProperty() call is not
326 : * idempotent.
327 : */
328 52 : p = map->table.lookup(obj);
329 52 : if (!p) {
330 9 : if (!map->table.put(obj, sharpid))
331 0 : goto bad;
332 9 : goto out;
333 : }
334 43 : sharpid = p->value;
335 : }
336 :
337 862 : if (sharpid.isSharp || sharpid.hasGen)
338 0 : *alreadySeen = true;
339 :
340 : out:
341 871 : if (!sharpid.isSharp) {
342 871 : if (idap && !ida) {
343 40 : ida = JS_Enumerate(cx, obj);
344 40 : if (!ida)
345 0 : goto bad;
346 : }
347 871 : map->depth++;
348 : }
349 :
350 871 : if (idap)
351 598 : *idap = ida;
352 871 : *isSharp = sharpid.isSharp;
353 871 : return true;
354 :
355 : bad:
356 : /* Clean up the sharpObjectMap table on outermost error. */
357 0 : if (map->depth == 0) {
358 0 : JS_UNKEEP_ATOMS(cx->runtime);
359 0 : map->sharpgen = 0;
360 0 : map->table.clear();
361 : }
362 0 : return false;
363 : }
364 :
365 : void
366 871 : js_LeaveSharpObject(JSContext *cx, JSIdArray **idap)
367 : {
368 871 : JSSharpObjectMap *map = &cx->sharpObjectMap;
369 871 : JS_ASSERT(map->depth > 0);
370 871 : if (--map->depth == 0) {
371 819 : JS_UNKEEP_ATOMS(cx->runtime);
372 819 : map->sharpgen = 0;
373 819 : map->table.clear();
374 : }
375 871 : if (idap) {
376 598 : if (JSIdArray *ida = *idap) {
377 598 : JS_DestroyIdArray(cx, ida);
378 598 : *idap = NULL;
379 : }
380 : }
381 871 : }
382 :
383 : void
384 3 : js_TraceSharpMap(JSTracer *trc, JSSharpObjectMap *map)
385 : {
386 3 : JS_ASSERT(map->depth > 0);
387 :
388 : /*
389 : * During recursive calls to MarkSharpObjects a non-native object or
390 : * object with a custom getProperty method can potentially return an
391 : * unrooted value or even cut from the object graph an argument of one of
392 : * MarkSharpObjects recursive invocations. So we must protect map->table
393 : * entries against GC.
394 : *
395 : * We can not simply use JSTempValueRooter to mark the obj argument of
396 : * MarkSharpObjects during recursion as we have to protect *all* entries
397 : * in JSSharpObjectMap including those that contains otherwise unreachable
398 : * objects just allocated through custom getProperty. Otherwise newer
399 : * allocations can re-use the address of an object stored in the hashtable
400 : * confusing js_EnterSharpObject. So to address the problem we simply
401 : * mark all objects from map->table.
402 : *
403 : * An alternative "proper" solution is to use JSTempValueRooter in
404 : * MarkSharpObjects with code to remove during finalization entries
405 : * with otherwise unreachable objects. But this is way too complex
406 : * to justify spending efforts.
407 : */
408 12 : for (JSSharpTable::Range r = map->table.all(); !r.empty(); r.popFront()) {
409 9 : JSObject *tmp = r.front().key;
410 9 : MarkObjectRoot(trc, &tmp, "sharp table entry");
411 9 : JS_ASSERT(tmp == r.front().key);
412 : }
413 3 : }
414 :
415 : #if JS_HAS_TOSOURCE
416 : static JSBool
417 598 : obj_toSource(JSContext *cx, unsigned argc, Value *vp)
418 : {
419 598 : bool comma = false;
420 : const jschar *vchars;
421 : size_t vlength;
422 : Value *val;
423 : JSString *gsop[2];
424 :
425 598 : JS_CHECK_RECURSION(cx, return JS_FALSE);
426 :
427 : Value localroot[4];
428 598 : PodArrayZero(localroot);
429 1196 : AutoArrayRooter tvr(cx, ArrayLength(localroot), localroot);
430 :
431 : /* If outermost, we need parentheses to be an expression, not a block. */
432 598 : bool outermost = (cx->sharpObjectMap.depth == 0);
433 :
434 598 : JSObject *obj = ToObject(cx, &vp[1]);
435 598 : if (!obj)
436 0 : return false;
437 :
438 : JSIdArray *ida;
439 598 : bool alreadySeen = false;
440 598 : bool isSharp = false;
441 598 : if (!js_EnterSharpObject(cx, obj, &ida, &alreadySeen, &isSharp))
442 0 : return false;
443 :
444 598 : if (!ida) {
445 : /*
446 : * We've already seen obj, so don't serialize it again (particularly as
447 : * we might recur in the process): just serialize an empty object.
448 : */
449 0 : JS_ASSERT(alreadySeen);
450 0 : JSString *str = js_NewStringCopyZ(cx, "{}");
451 0 : if (!str)
452 0 : return false;
453 0 : vp->setString(str);
454 0 : return true;
455 : }
456 :
457 598 : JS_ASSERT(!isSharp);
458 598 : if (alreadySeen) {
459 0 : JSSharpTable::Ptr p = cx->sharpObjectMap.table.lookup(obj);
460 0 : JS_ASSERT(p);
461 0 : JS_ASSERT(!p->value.isSharp);
462 0 : p->value.isSharp = true;
463 : }
464 :
465 : /* Automatically call js_LeaveSharpObject when we leave this frame. */
466 : class AutoLeaveSharpObject {
467 : JSContext *cx;
468 : JSIdArray *ida;
469 : public:
470 598 : AutoLeaveSharpObject(JSContext *cx, JSIdArray *ida) : cx(cx), ida(ida) {}
471 598 : ~AutoLeaveSharpObject() {
472 598 : js_LeaveSharpObject(cx, &ida);
473 598 : }
474 1196 : } autoLeaveSharpObject(cx, ida);
475 :
476 1196 : StringBuffer buf(cx);
477 598 : if (outermost && !buf.append('('))
478 0 : return false;
479 598 : if (!buf.append('{'))
480 0 : return false;
481 :
482 : /*
483 : * We have four local roots for cooked and raw value GC safety. Hoist the
484 : * "localroot + 2" out of the loop using the val local, which refers to
485 : * the raw (unconverted, "uncooked") values.
486 : */
487 598 : val = localroot + 2;
488 :
489 2292 : for (int i = 0; i < ida->length; i++) {
490 : /* Get strings for id and value and GC-root them via vp. */
491 1712 : jsid id = ida->vector[i];
492 : JSLinearString *idstr;
493 :
494 : JSObject *obj2;
495 : JSProperty *prop;
496 1712 : if (!obj->lookupGeneric(cx, id, &obj2, &prop))
497 0 : return false;
498 :
499 : /*
500 : * Convert id to a value and then to a string. Decide early whether we
501 : * prefer get/set or old getter/setter syntax.
502 : */
503 1712 : JSString *s = ToString(cx, IdToValue(id));
504 1712 : if (!s || !(idstr = s->ensureLinear(cx)))
505 0 : return false;
506 :
507 1712 : int valcnt = 0;
508 1712 : if (prop) {
509 1712 : bool doGet = true;
510 1712 : if (obj2->isNative()) {
511 1712 : const Shape *shape = (Shape *) prop;
512 1712 : unsigned attrs = shape->attributes();
513 1712 : if (attrs & JSPROP_GETTER) {
514 0 : doGet = false;
515 0 : val[valcnt] = shape->getterValue();
516 0 : gsop[valcnt] = cx->runtime->atomState.getAtom;
517 0 : valcnt++;
518 : }
519 1712 : if (attrs & JSPROP_SETTER) {
520 0 : doGet = false;
521 0 : val[valcnt] = shape->setterValue();
522 0 : gsop[valcnt] = cx->runtime->atomState.setAtom;
523 0 : valcnt++;
524 : }
525 : }
526 1712 : if (doGet) {
527 1712 : valcnt = 1;
528 1712 : gsop[0] = NULL;
529 1712 : if (!obj->getGeneric(cx, id, &val[0]))
530 0 : return false;
531 : }
532 : }
533 :
534 : /*
535 : * If id is a string that's not an identifier, or if it's a negative
536 : * integer, then it must be quoted.
537 : */
538 3434 : if (JSID_IS_ATOM(id)
539 1702 : ? !IsIdentifier(idstr)
540 20 : : (!JSID_IS_INT(id) || JSID_TO_INT(id) < 0)) {
541 0 : s = js_QuoteString(cx, idstr, jschar('\''));
542 0 : if (!s || !(idstr = s->ensureLinear(cx)))
543 0 : return false;
544 : }
545 :
546 3406 : for (int j = 0; j < valcnt; j++) {
547 : /*
548 : * Censor an accessor descriptor getter or setter part if it's
549 : * undefined.
550 : */
551 1712 : if (gsop[j] && val[j].isUndefined())
552 0 : continue;
553 :
554 : /* Convert val[j] to its canonical source form. */
555 1712 : JSString *valstr = js_ValueToSource(cx, val[j]);
556 1712 : if (!valstr)
557 18 : return false;
558 1694 : localroot[j].setString(valstr); /* local root */
559 1694 : vchars = valstr->getChars(cx);
560 1694 : if (!vchars)
561 0 : return false;
562 1694 : vlength = valstr->length();
563 :
564 : /*
565 : * Remove '(function ' from the beginning of valstr and ')' from the
566 : * end so that we can put "get" in front of the function definition.
567 : */
568 1694 : if (gsop[j] && IsFunctionObject(val[j])) {
569 0 : const jschar *start = vchars;
570 0 : const jschar *end = vchars + vlength;
571 :
572 0 : uint8_t parenChomp = 0;
573 0 : if (vchars[0] == '(') {
574 0 : vchars++;
575 0 : parenChomp = 1;
576 : }
577 :
578 : /* Try to jump "function" keyword. */
579 0 : if (vchars)
580 0 : vchars = js_strchr_limit(vchars, ' ', end);
581 :
582 : /*
583 : * Jump over the function's name: it can't be encoded as part
584 : * of an ECMA getter or setter.
585 : */
586 0 : if (vchars)
587 0 : vchars = js_strchr_limit(vchars, '(', end);
588 :
589 0 : if (vchars) {
590 0 : if (*vchars == ' ')
591 0 : vchars++;
592 0 : vlength = end - vchars - parenChomp;
593 : } else {
594 0 : gsop[j] = NULL;
595 0 : vchars = start;
596 : }
597 : }
598 :
599 1694 : if (comma && !buf.append(", "))
600 0 : return false;
601 1694 : comma = true;
602 :
603 1694 : if (gsop[j])
604 0 : if (!buf.append(gsop[j]) || !buf.append(' '))
605 0 : return false;
606 :
607 1694 : if (!buf.append(idstr))
608 0 : return false;
609 1694 : if (!buf.append(gsop[j] ? ' ' : ':'))
610 0 : return false;
611 :
612 1694 : if (!buf.append(vchars, vlength))
613 0 : return false;
614 : }
615 : }
616 :
617 580 : if (!buf.append('}'))
618 0 : return false;
619 580 : if (outermost && !buf.append(')'))
620 0 : return false;
621 :
622 580 : JSString *str = buf.finishString();
623 580 : if (!str)
624 0 : return false;
625 580 : vp->setString(str);
626 580 : return true;
627 : }
628 : #endif /* JS_HAS_TOSOURCE */
629 :
630 : namespace js {
631 :
632 : JSString *
633 49459 : obj_toStringHelper(JSContext *cx, JSObject *obj)
634 : {
635 49459 : if (obj->isProxy())
636 1948 : return Proxy::obj_toString(cx, obj);
637 :
638 95022 : StringBuffer sb(cx);
639 47511 : const char *className = obj->getClass()->name;
640 95022 : if (!sb.append("[object ") || !sb.appendInflated(className, strlen(className)) ||
641 47511 : !sb.append("]"))
642 : {
643 0 : return NULL;
644 : }
645 47511 : return sb.finishString();
646 : }
647 :
648 : JSObject *
649 4110 : NonNullObject(JSContext *cx, const Value &v)
650 : {
651 4110 : if (v.isPrimitive()) {
652 162 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_NONNULL_OBJECT);
653 162 : return NULL;
654 : }
655 3948 : return &v.toObject();
656 : }
657 :
658 : const char *
659 2276 : InformalValueTypeName(const Value &v)
660 : {
661 2276 : if (v.isObject())
662 2114 : return v.toObject().getClass()->name;
663 162 : if (v.isString())
664 0 : return "string";
665 162 : if (v.isNumber())
666 0 : return "number";
667 162 : if (v.isBoolean())
668 0 : return "boolean";
669 162 : if (v.isNull())
670 72 : return "null";
671 90 : if (v.isUndefined())
672 90 : return "undefined";
673 0 : return "value";
674 : }
675 :
676 : } /* namespace js */
677 :
678 : /* ES5 15.2.4.2. Note steps 1 and 2 are errata. */
679 : static JSBool
680 47515 : obj_toString(JSContext *cx, unsigned argc, Value *vp)
681 : {
682 47515 : Value &thisv = vp[1];
683 :
684 : /* Step 1. */
685 47515 : if (thisv.isUndefined()) {
686 0 : vp->setString(cx->runtime->atomState.objectUndefinedAtom);
687 0 : return true;
688 : }
689 :
690 : /* Step 2. */
691 47515 : if (thisv.isNull()) {
692 0 : vp->setString(cx->runtime->atomState.objectNullAtom);
693 0 : return true;
694 : }
695 :
696 : /* Step 3. */
697 47515 : JSObject *obj = ToObject(cx, &thisv);
698 47515 : if (!obj)
699 0 : return false;
700 :
701 : /* Steps 4-5. */
702 47515 : JSString *str = js::obj_toStringHelper(cx, obj);
703 47515 : if (!str)
704 0 : return false;
705 47515 : vp->setString(str);
706 47515 : return true;
707 : }
708 :
709 : /* ES5 15.2.4.3. */
710 : static JSBool
711 9 : obj_toLocaleString(JSContext *cx, unsigned argc, Value *vp)
712 : {
713 9 : JS_CHECK_RECURSION(cx, return false);
714 :
715 : /* Step 1. */
716 9 : JSObject *obj = ToObject(cx, &vp[1]);
717 9 : if (!obj)
718 0 : return false;
719 :
720 : /* Steps 2-4. */
721 9 : return obj->callMethod(cx, ATOM_TO_JSID(cx->runtime->atomState.toStringAtom), 0, NULL, vp);
722 : }
723 :
724 : static JSBool
725 370621 : obj_valueOf(JSContext *cx, unsigned argc, Value *vp)
726 : {
727 370621 : JSObject *obj = ToObject(cx, &vp[1]);
728 370621 : if (!obj)
729 0 : return false;
730 370621 : vp->setObject(*obj);
731 370621 : return true;
732 : }
733 :
734 : /* We should be able to assert this for *any* fp->scopeChain(). */
735 : static void
736 97904 : AssertInnerizedScopeChain(JSContext *cx, JSObject &scopeobj)
737 : {
738 : #ifdef DEBUG
739 298215 : for (JSObject *o = &scopeobj; o; o = o->enclosingScope()) {
740 200311 : if (JSObjectOp op = o->getClass()->ext.innerObject)
741 0 : JS_ASSERT(op(cx, o) == o);
742 : }
743 : #endif
744 97904 : }
745 :
746 : #ifndef EVAL_CACHE_CHAIN_LIMIT
747 : # define EVAL_CACHE_CHAIN_LIMIT 4
748 : #endif
749 :
750 : static inline JSScript **
751 93099 : EvalCacheHash(JSContext *cx, JSLinearString *str)
752 : {
753 93099 : const jschar *s = str->chars();
754 93099 : size_t n = str->length();
755 :
756 93099 : if (n > 100)
757 5859 : n = 100;
758 : uint32_t h;
759 1778187 : for (h = 0; n; s++, n--)
760 1685088 : h = JS_ROTATE_LEFT32(h, 4) ^ *s;
761 :
762 93099 : h *= JS_GOLDEN_RATIO;
763 93099 : h >>= 32 - JS_EVAL_CACHE_SHIFT;
764 93099 : return &cx->compartment->evalCache[h];
765 : }
766 :
767 : static JS_ALWAYS_INLINE JSScript *
768 78265 : EvalCacheLookup(JSContext *cx, JSLinearString *str, StackFrame *caller, unsigned staticLevel,
769 : JSPrincipals *principals, JSObject &scopeobj, JSScript **bucket)
770 : {
771 : /*
772 : * Cache local eval scripts indexed by source qualified by scope.
773 : *
774 : * An eval cache entry should never be considered a hit unless its
775 : * strictness matches that of the new eval code. The existing code takes
776 : * care of this, because hits are qualified by the function from which
777 : * eval was called, whose strictness doesn't change. (We don't cache evals
778 : * in eval code, so the calling function corresponds to the calling script,
779 : * and its strictness never varies.) Scripts produced by calls to eval from
780 : * global code aren't cached.
781 : *
782 : * FIXME bug 620141: Qualify hits by calling script rather than function.
783 : * Then we wouldn't need the unintuitive !isEvalFrame() hack in EvalKernel
784 : * to avoid caching nested evals in functions (thus potentially mismatching
785 : * on strict mode), and we could cache evals in global code if desired.
786 : */
787 78265 : unsigned count = 0;
788 78265 : JSScript **scriptp = bucket;
789 :
790 78265 : JSVersion version = cx->findVersion();
791 : JSScript *script;
792 78265 : JSSubsumePrincipalsOp subsume = cx->runtime->securityCallbacks->subsumePrincipals;
793 205614 : while ((script = *scriptp) != NULL) {
794 354469 : if (script->savedCallerFun &&
795 : script->staticLevel == staticLevel &&
796 117751 : script->getVersion() == version &&
797 117751 : !script->hasSingletons &&
798 : (!subsume || script->principals == principals ||
799 0 : (subsume(principals, script->principals) &&
800 0 : subsume(script->principals, principals)))) {
801 : /*
802 : * Get the prior (cache-filling) eval's saved caller function.
803 : * See frontend::CompileScript.
804 : */
805 99017 : JSFunction *fun = script->getCallerFunction();
806 :
807 99017 : if (fun == caller->fun()) {
808 : /*
809 : * Get the source string passed for safekeeping in the atom map
810 : * by the prior eval to frontend::CompileScript.
811 : */
812 97045 : JSAtom *src = script->atoms[0];
813 :
814 97045 : if (src == str || EqualStrings(src, str)) {
815 : /*
816 : * Source matches. Make sure there are no inner objects
817 : * which might use the wrong parent and/or call scope by
818 : * reusing the previous eval's script. Skip the script's
819 : * first object, which entrains the eval's scope.
820 : */
821 69227 : JS_ASSERT(script->objects()->length >= 1);
822 128013 : if (script->objects()->length == 1 &&
823 58786 : !JSScript::isValidOffset(script->regexpsOffset)) {
824 58786 : JS_ASSERT(staticLevel == script->staticLevel);
825 58786 : *scriptp = script->evalHashLink();
826 58786 : script->evalHashLink() = NULL;
827 58786 : return script;
828 : }
829 : }
830 : }
831 : }
832 :
833 60181 : if (++count == EVAL_CACHE_CHAIN_LIMIT)
834 11097 : return NULL;
835 49084 : scriptp = &script->evalHashLink();
836 : }
837 8382 : return NULL;
838 : }
839 :
840 : /*
841 : * There are two things we want to do with each script executed in EvalKernel:
842 : * 1. notify jsdbgapi about script creation/destruction
843 : * 2. add the script to the eval cache when EvalKernel is finished
844 : *
845 : * NB: Although the eval cache keeps a script alive wrt to the JS engine, from
846 : * a jsdbgapi user's perspective, we want each eval() to create and destroy a
847 : * script. This hides implementation details and means we don't have to deal
848 : * with calls to JS_GetScriptObject for scripts in the eval cache (currently,
849 : * script->object aliases script->evalHashLink()).
850 : */
851 : class EvalScriptGuard
852 : {
853 : JSContext *cx_;
854 : JSLinearString *str_;
855 : JSScript **bucket_;
856 : JSScript *script_;
857 :
858 : public:
859 93099 : EvalScriptGuard(JSContext *cx, JSLinearString *str)
860 : : cx_(cx),
861 : str_(str),
862 93099 : script_(NULL) {
863 93099 : bucket_ = EvalCacheHash(cx, str);
864 93099 : }
865 :
866 93099 : ~EvalScriptGuard() {
867 93099 : if (script_) {
868 92874 : js_CallDestroyScriptHook(cx_, script_);
869 92874 : script_->isActiveEval = false;
870 92874 : script_->isCachedEval = true;
871 92874 : script_->evalHashLink() = *bucket_;
872 92874 : *bucket_ = script_;
873 : }
874 93099 : }
875 :
876 78265 : void lookupInEvalCache(StackFrame *caller, unsigned staticLevel,
877 : JSPrincipals *principals, JSObject &scopeobj) {
878 78265 : if (JSScript *found = EvalCacheLookup(cx_, str_, caller, staticLevel,
879 78265 : principals, scopeobj, bucket_)) {
880 58786 : js_CallNewScriptHook(cx_, found, NULL);
881 58786 : script_ = found;
882 58786 : script_->isCachedEval = false;
883 58786 : script_->isActiveEval = true;
884 : }
885 78265 : }
886 :
887 34088 : void setNewScript(JSScript *script) {
888 : /* NewScriptFromEmitter has already called js_CallNewScriptHook. */
889 34088 : JS_ASSERT(!script_ && script);
890 34088 : script_ = script;
891 34088 : script_->isActiveEval = true;
892 34088 : }
893 :
894 93099 : bool foundScript() {
895 93099 : return !!script_;
896 : }
897 :
898 92874 : JSScript *script() const {
899 92874 : JS_ASSERT(script_);
900 92874 : return script_;
901 : }
902 : };
903 :
904 : /* Define subset of ExecuteType so that casting performs the injection. */
905 : enum EvalType { DIRECT_EVAL = EXECUTE_DIRECT_EVAL, INDIRECT_EVAL = EXECUTE_INDIRECT_EVAL };
906 :
907 : /*
908 : * Common code implementing direct and indirect eval.
909 : *
910 : * Evaluate call.argv[2], if it is a string, in the context of the given calling
911 : * frame, with the provided scope chain, with the semantics of either a direct
912 : * or indirect eval (see ES5 10.4.2). If this is an indirect eval, scopeobj
913 : * must be a global object.
914 : *
915 : * On success, store the completion value in call.rval and return true.
916 : */
917 : static bool
918 97904 : EvalKernel(JSContext *cx, const CallArgs &args, EvalType evalType, StackFrame *caller,
919 : JSObject &scopeobj)
920 : {
921 97904 : JS_ASSERT((evalType == INDIRECT_EVAL) == (caller == NULL));
922 97904 : AssertInnerizedScopeChain(cx, scopeobj);
923 :
924 97904 : if (!scopeobj.global().isRuntimeCodeGenEnabled(cx)) {
925 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CSP_BLOCKED_EVAL);
926 0 : return false;
927 : }
928 :
929 : /* ES5 15.1.2.1 step 1. */
930 97904 : if (args.length() < 1) {
931 2555 : args.rval().setUndefined();
932 2555 : return true;
933 : }
934 95349 : if (!args[0].isString()) {
935 225 : args.rval() = args[0];
936 225 : return true;
937 : }
938 95124 : JSString *str = args[0].toString();
939 :
940 : /* ES5 15.1.2.1 steps 2-8. */
941 :
942 : /*
943 : * Per ES5, indirect eval runs in the global scope. (eval is specified this
944 : * way so that the compiler can make assumptions about what bindings may or
945 : * may not exist in the current frame if it doesn't see 'eval'.)
946 : */
947 : unsigned staticLevel;
948 : Value thisv;
949 95124 : if (evalType == DIRECT_EVAL) {
950 81286 : staticLevel = caller->script()->staticLevel + 1;
951 :
952 : /*
953 : * Direct calls to eval are supposed to see the caller's |this|. If we
954 : * haven't wrapped that yet, do so now, before we make a copy of it for
955 : * the eval code to use.
956 : */
957 81286 : if (!ComputeThis(cx, caller))
958 0 : return false;
959 81286 : thisv = caller->thisValue();
960 :
961 : #ifdef DEBUG
962 81286 : jsbytecode *callerPC = caller->pcQuadratic(cx);
963 81286 : JS_ASSERT(callerPC && JSOp(*callerPC) == JSOP_EVAL);
964 : #endif
965 : } else {
966 13838 : JS_ASSERT(args.callee().global() == scopeobj);
967 13838 : staticLevel = 0;
968 :
969 : /* Use the global as 'this', modulo outerization. */
970 13838 : JSObject *thisobj = scopeobj.thisObject(cx);
971 13838 : if (!thisobj)
972 0 : return false;
973 13838 : thisv = ObjectValue(*thisobj);
974 : }
975 :
976 95124 : JSLinearString *linearStr = str->ensureLinear(cx);
977 95124 : if (!linearStr)
978 0 : return false;
979 95124 : const jschar *chars = linearStr->chars();
980 95124 : size_t length = linearStr->length();
981 :
982 : /*
983 : * If the eval string starts with '(' or '[' and ends with ')' or ']', it may be JSON.
984 : * Try the JSON parser first because it's much faster. If the eval string
985 : * isn't JSON, JSON parsing will probably fail quickly, so little time
986 : * will be lost.
987 : *
988 : * Don't use the JSON parser if the caller is strict mode code, because in
989 : * strict mode object literals must not have repeated properties, and the
990 : * JSON parser cheerfully (and correctly) accepts them. If you're parsing
991 : * JSON with eval and using strict mode, you deserve to be slow.
992 : */
993 297787 : if (length > 2 &&
994 97663 : ((chars[0] == '[' && chars[length - 1] == ']') ||
995 100164 : (chars[0] == '(' && chars[length - 1] == ')')) &&
996 4836 : (!caller || !caller->script()->strictModeCode))
997 : {
998 : /*
999 : * Remarkably, JavaScript syntax is not a superset of JSON syntax:
1000 : * strings in JavaScript cannot contain the Unicode line and paragraph
1001 : * terminator characters U+2028 and U+2029, but strings in JSON can.
1002 : * Rather than force the JSON parser to handle this quirk when used by
1003 : * eval, we simply don't use the JSON parser when either character
1004 : * appears in the provided string. See bug 657367.
1005 : */
1006 1683305 : for (const jschar *cp = &chars[1], *end = &chars[length - 2]; ; cp++) {
1007 1683305 : if (*cp == 0x2028 || *cp == 0x2029)
1008 0 : break;
1009 :
1010 1683305 : if (cp == end) {
1011 4988 : bool isArray = (chars[0] == '[');
1012 : JSONParser parser(cx, isArray ? chars : chars + 1, isArray ? length : length - 2,
1013 4988 : JSONParser::StrictJSON, JSONParser::NoError);
1014 : Value tmp;
1015 4988 : if (!parser.parse(&tmp))
1016 0 : return false;
1017 4988 : if (tmp.isUndefined())
1018 2963 : break;
1019 2025 : args.rval() = tmp;
1020 2025 : return true;
1021 : }
1022 : }
1023 : }
1024 :
1025 186198 : EvalScriptGuard esg(cx, linearStr);
1026 :
1027 93099 : JSPrincipals *principals = PrincipalsForCompiledCode(args, cx);
1028 :
1029 93099 : if (evalType == DIRECT_EVAL && caller->isNonEvalFunctionFrame())
1030 78265 : esg.lookupInEvalCache(caller, staticLevel, principals, scopeobj);
1031 :
1032 93099 : if (!esg.foundScript()) {
1033 : unsigned lineno;
1034 : const char *filename;
1035 : JSPrincipals *originPrincipals;
1036 : CurrentScriptFileLineOrigin(cx, &filename, &lineno, &originPrincipals,
1037 : evalType == DIRECT_EVAL ? CALLED_FROM_JSOP_EVAL
1038 34313 : : NOT_CALLED_FROM_JSOP_EVAL);
1039 34313 : uint32_t tcflags = TCF_COMPILE_N_GO | TCF_COMPILE_FOR_EVAL;
1040 : JSScript *compiled = frontend::CompileScript(cx, &scopeobj, caller,
1041 : principals, originPrincipals,
1042 : tcflags, chars, length, filename,
1043 : lineno, cx->findVersion(), linearStr,
1044 34313 : staticLevel);
1045 34313 : if (!compiled)
1046 225 : return false;
1047 :
1048 34088 : esg.setNewScript(compiled);
1049 : }
1050 :
1051 : return ExecuteKernel(cx, esg.script(), scopeobj, thisv, ExecuteType(evalType),
1052 92874 : NULL /* evalInFrame */, &args.rval());
1053 : }
1054 :
1055 : /*
1056 : * We once supported a second argument to eval to use as the scope chain
1057 : * when evaluating the code string. Warn when such uses are seen so that
1058 : * authors will know that support for eval(s, o) has been removed.
1059 : */
1060 : static inline bool
1061 97904 : WarnOnTooManyArgs(JSContext *cx, const CallArgs &args)
1062 : {
1063 97904 : if (args.length() > 1) {
1064 99 : if (JSScript *script = cx->stack.currentScript()) {
1065 90 : if (!script->warnedAboutTwoArgumentEval) {
1066 : static const char TWO_ARGUMENT_WARNING[] =
1067 : "Support for eval(code, scopeObject) has been removed. "
1068 : "Use |with (scopeObject) eval(code);| instead.";
1069 63 : if (!JS_ReportWarning(cx, TWO_ARGUMENT_WARNING))
1070 0 : return false;
1071 63 : script->warnedAboutTwoArgumentEval = true;
1072 : }
1073 : } else {
1074 : /*
1075 : * In the case of an indirect call without a caller frame, avoid a
1076 : * potential warning-flood by doing nothing.
1077 : */
1078 : }
1079 : }
1080 :
1081 97904 : return true;
1082 : }
1083 :
1084 : namespace js {
1085 :
1086 : /*
1087 : * ES5 15.1.2.1.
1088 : *
1089 : * NB: This method handles only indirect eval.
1090 : */
1091 : JSBool
1092 13838 : eval(JSContext *cx, unsigned argc, Value *vp)
1093 : {
1094 13838 : CallArgs args = CallArgsFromVp(argc, vp);
1095 13838 : return WarnOnTooManyArgs(cx, args) &&
1096 13838 : EvalKernel(cx, args, INDIRECT_EVAL, NULL, args.callee().global());
1097 : }
1098 :
1099 : bool
1100 84066 : DirectEval(JSContext *cx, const CallArgs &args)
1101 : {
1102 : /* Direct eval can assume it was called from an interpreted frame. */
1103 84066 : StackFrame *caller = cx->fp();
1104 84066 : JS_ASSERT(caller->isScriptFrame());
1105 84066 : JS_ASSERT(IsBuiltinEvalForScope(&caller->scopeChain(), args.calleev()));
1106 84066 : JS_ASSERT(JSOp(*cx->regs().pc) == JSOP_EVAL);
1107 :
1108 168132 : AutoFunctionCallProbe callProbe(cx, args.callee().toFunction(), caller->script());
1109 :
1110 84066 : JSObject *scopeChain = GetScopeChain(cx, caller);
1111 84066 : if (!scopeChain)
1112 0 : return false;
1113 :
1114 84066 : if (!WarnOnTooManyArgs(cx, args))
1115 0 : return false;
1116 :
1117 84066 : return EvalKernel(cx, args, DIRECT_EVAL, caller, *scopeChain);
1118 : }
1119 :
1120 : bool
1121 168159 : IsBuiltinEvalForScope(JSObject *scopeChain, const Value &v)
1122 : {
1123 168159 : return scopeChain->global().getOriginalEval() == v;
1124 : }
1125 :
1126 : bool
1127 103553 : IsAnyBuiltinEval(JSFunction *fun)
1128 : {
1129 103553 : return fun->maybeNative() == eval;
1130 : }
1131 :
1132 : JSPrincipals *
1133 103513 : PrincipalsForCompiledCode(const CallReceiver &call, JSContext *cx)
1134 : {
1135 113927 : JS_ASSERT(IsAnyBuiltinEval(call.callee().toFunction()) ||
1136 113927 : IsBuiltinFunctionConstructor(call.callee().toFunction()));
1137 :
1138 : /*
1139 : * To compute the principals of the compiled eval/Function code, we simply
1140 : * use the callee's principals. To see why the caller's principals are
1141 : * ignored, consider first that, in the capability-model we assume, the
1142 : * high-privileged eval/Function should never have escaped to the
1143 : * low-privileged caller. (For the Mozilla embedding, this is brute-enforced
1144 : * by explicit filtering by wrappers.) Thus, the caller's privileges should
1145 : * subsume the callee's.
1146 : *
1147 : * In the converse situation, where the callee has lower privileges than the
1148 : * caller, we might initially guess that the caller would want to retain
1149 : * their higher privileges in the generated code. However, since the
1150 : * compiled code will be run with the callee's scope chain, this would make
1151 : * fp->script()->compartment() != fp->compartment().
1152 : */
1153 :
1154 103513 : return call.callee().principals(cx);
1155 : }
1156 :
1157 : } /* namespace js */
1158 :
1159 : #if JS_HAS_OBJ_WATCHPOINT
1160 :
1161 : static JSBool
1162 3783 : obj_watch_handler(JSContext *cx, JSObject *obj, jsid id, jsval old,
1163 : jsval *nvp, void *closure)
1164 : {
1165 3783 : JSObject *callable = (JSObject *) closure;
1166 3783 : if (JSSubsumePrincipalsOp subsume = cx->runtime->securityCallbacks->subsumePrincipals) {
1167 0 : if (JSPrincipals *watcher = callable->principals(cx)) {
1168 0 : if (JSObject *scopeChain = cx->stack.currentScriptedScopeChain()) {
1169 0 : if (JSPrincipals *subject = scopeChain->principals(cx)) {
1170 0 : if (!subsume(watcher, subject)) {
1171 : /* Silently don't call the watch handler. */
1172 0 : return true;
1173 : }
1174 : }
1175 : }
1176 : }
1177 : }
1178 :
1179 : /* Avoid recursion on (obj, id) already being watched on cx. */
1180 7566 : AutoResolving resolving(cx, obj, id, AutoResolving::WATCH);
1181 3783 : if (resolving.alreadyStarted())
1182 0 : return true;
1183 :
1184 3783 : Value argv[] = { IdToValue(id), old, *nvp };
1185 3783 : return Invoke(cx, ObjectValue(*obj), ObjectOrNullValue(callable), ArrayLength(argv), argv, nvp);
1186 : }
1187 :
1188 : static JSBool
1189 3639 : obj_watch(JSContext *cx, unsigned argc, Value *vp)
1190 : {
1191 3639 : if (argc <= 1) {
1192 0 : js_ReportMissingArg(cx, *vp, 1);
1193 0 : return false;
1194 : }
1195 :
1196 3639 : JSObject *callable = js_ValueToCallableObject(cx, &vp[3], 0);
1197 3639 : if (!callable)
1198 9 : return false;
1199 :
1200 : jsid propid;
1201 3630 : if (!ValueToId(cx, vp[2], &propid))
1202 0 : return false;
1203 :
1204 3630 : JSObject *obj = ToObject(cx, &vp[1]);
1205 3630 : if (!obj)
1206 0 : return false;
1207 :
1208 : Value tmp;
1209 : unsigned attrs;
1210 3630 : if (!CheckAccess(cx, obj, propid, JSACC_WATCH, &tmp, &attrs))
1211 0 : return false;
1212 :
1213 3630 : vp->setUndefined();
1214 :
1215 3630 : if (obj->isDenseArray() && !obj->makeDenseArraySlow(cx))
1216 0 : return false;
1217 3630 : return JS_SetWatchPoint(cx, obj, propid, obj_watch_handler, callable);
1218 : }
1219 :
1220 : static JSBool
1221 945 : obj_unwatch(JSContext *cx, unsigned argc, Value *vp)
1222 : {
1223 945 : JSObject *obj = ToObject(cx, &vp[1]);
1224 945 : if (!obj)
1225 0 : return false;
1226 945 : vp->setUndefined();
1227 : jsid id;
1228 945 : if (argc != 0) {
1229 9 : if (!ValueToId(cx, vp[2], &id))
1230 0 : return false;
1231 : } else {
1232 936 : id = JSID_VOID;
1233 : }
1234 945 : return JS_ClearWatchPoint(cx, obj, id, NULL, NULL);
1235 : }
1236 :
1237 : #endif /* JS_HAS_OBJ_WATCHPOINT */
1238 :
1239 : /*
1240 : * Prototype and property query methods, to complement the 'in' and
1241 : * 'instanceof' operators.
1242 : */
1243 :
1244 : /* Proposed ECMA 15.2.4.5. */
1245 : static JSBool
1246 92508 : obj_hasOwnProperty(JSContext *cx, unsigned argc, Value *vp)
1247 : {
1248 92508 : JSObject *obj = ToObject(cx, &vp[1]);
1249 92508 : if (!obj)
1250 0 : return false;
1251 92508 : return js_HasOwnPropertyHelper(cx, obj->getOps()->lookupGeneric, argc, vp);
1252 : }
1253 :
1254 : JSBool
1255 92508 : js_HasOwnPropertyHelper(JSContext *cx, LookupGenericOp lookup, unsigned argc,
1256 : Value *vp)
1257 : {
1258 : jsid id;
1259 92508 : if (!ValueToId(cx, argc != 0 ? vp[2] : UndefinedValue(), &id))
1260 0 : return JS_FALSE;
1261 :
1262 92508 : JSObject *obj = ToObject(cx, &vp[1]);
1263 92508 : if (!obj)
1264 0 : return false;
1265 : JSObject *obj2;
1266 : JSProperty *prop;
1267 92508 : if (obj->isProxy()) {
1268 : bool has;
1269 0 : if (!Proxy::hasOwn(cx, obj, id, &has))
1270 0 : return false;
1271 0 : vp->setBoolean(has);
1272 0 : return true;
1273 : }
1274 92508 : if (!js_HasOwnProperty(cx, lookup, obj, id, &obj2, &prop))
1275 0 : return JS_FALSE;
1276 92508 : vp->setBoolean(!!prop);
1277 92508 : return JS_TRUE;
1278 : }
1279 :
1280 : JSBool
1281 699910 : js_HasOwnProperty(JSContext *cx, LookupGenericOp lookup, JSObject *obj, jsid id,
1282 : JSObject **objp, JSProperty **propp)
1283 : {
1284 1399820 : JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_DETECTING);
1285 699910 : if (!(lookup ? lookup : js_LookupProperty)(cx, obj, id, objp, propp))
1286 0 : return false;
1287 699910 : if (!*propp)
1288 577735 : return true;
1289 :
1290 122175 : if (*objp == obj)
1291 98738 : return true;
1292 :
1293 23437 : JSObject *outer = NULL;
1294 23437 : if (JSObjectOp op = (*objp)->getClass()->ext.outerObject) {
1295 0 : outer = op(cx, *objp);
1296 0 : if (!outer)
1297 0 : return false;
1298 : }
1299 :
1300 23437 : if (outer != *objp)
1301 23437 : *propp = NULL;
1302 23437 : return true;
1303 : }
1304 :
1305 : /* ES5 15.2.4.6. */
1306 : static JSBool
1307 0 : obj_isPrototypeOf(JSContext *cx, unsigned argc, Value *vp)
1308 : {
1309 : /* Step 1. */
1310 0 : if (argc < 1 || !vp[2].isObject()) {
1311 0 : vp->setBoolean(false);
1312 0 : return true;
1313 : }
1314 :
1315 : /* Step 2. */
1316 0 : JSObject *obj = ToObject(cx, &vp[1]);
1317 0 : if (!obj)
1318 0 : return false;
1319 :
1320 : /* Step 3. */
1321 0 : vp->setBoolean(js_IsDelegate(cx, obj, vp[2]));
1322 0 : return true;
1323 : }
1324 :
1325 : /* ES5 15.2.4.7. */
1326 : static JSBool
1327 21 : obj_propertyIsEnumerable(JSContext *cx, unsigned argc, Value *vp)
1328 : {
1329 : /* Step 1. */
1330 : jsid id;
1331 21 : if (!ValueToId(cx, argc != 0 ? vp[2] : UndefinedValue(), &id))
1332 0 : return false;
1333 :
1334 : /* Step 2. */
1335 21 : JSObject *obj = ToObject(cx, &vp[1]);
1336 21 : if (!obj)
1337 9 : return false;
1338 :
1339 : /* Steps 3-5. */
1340 12 : return js_PropertyIsEnumerable(cx, obj, id, vp);
1341 : }
1342 :
1343 : JSBool
1344 12 : js_PropertyIsEnumerable(JSContext *cx, JSObject *obj, jsid id, Value *vp)
1345 : {
1346 : JSObject *pobj;
1347 : JSProperty *prop;
1348 12 : if (!obj->lookupGeneric(cx, id, &pobj, &prop))
1349 0 : return false;
1350 :
1351 12 : if (!prop) {
1352 0 : vp->setBoolean(false);
1353 0 : return true;
1354 : }
1355 :
1356 : /*
1357 : * ECMA spec botch: return false unless hasOwnProperty. Leaving "own" out
1358 : * of propertyIsEnumerable's name was a mistake.
1359 : */
1360 12 : if (pobj != obj) {
1361 0 : vp->setBoolean(false);
1362 0 : return true;
1363 : }
1364 :
1365 : unsigned attrs;
1366 12 : if (!pobj->getGenericAttributes(cx, id, &attrs))
1367 0 : return false;
1368 :
1369 12 : vp->setBoolean((attrs & JSPROP_ENUMERATE) != 0);
1370 12 : return true;
1371 : }
1372 :
1373 : #if OLD_GETTER_SETTER_METHODS
1374 :
1375 : const char js_defineGetter_str[] = "__defineGetter__";
1376 : const char js_defineSetter_str[] = "__defineSetter__";
1377 : const char js_lookupGetter_str[] = "__lookupGetter__";
1378 : const char js_lookupSetter_str[] = "__lookupSetter__";
1379 :
1380 : enum DefineType { Getter, Setter };
1381 :
1382 : template<DefineType Type>
1383 : static bool
1384 297480 : DefineAccessor(JSContext *cx, unsigned argc, Value *vp)
1385 : {
1386 297480 : CallArgs args = CallArgsFromVp(argc, vp);
1387 297480 : if (!BoxNonStrictThis(cx, args))
1388 0 : return false;
1389 :
1390 297480 : if (args.length() < 2 || !js_IsCallable(args[1])) {
1391 9 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1392 : JSMSG_BAD_GETTER_OR_SETTER,
1393 : Type == Getter ? js_getter_str : js_setter_str);
1394 9 : return false;
1395 : }
1396 :
1397 : jsid id;
1398 297471 : if (!ValueToId(cx, args[0], &id))
1399 0 : return false;
1400 :
1401 297471 : JSObject *descObj = NewBuiltinClassInstance(cx, &ObjectClass);
1402 297471 : if (!descObj)
1403 0 : return false;
1404 :
1405 297471 : JSAtomState &state = cx->runtime->atomState;
1406 : /* enumerable: true */
1407 297471 : if (!descObj->defineProperty(cx, state.enumerableAtom, BooleanValue(true)))
1408 0 : return false;
1409 :
1410 : /* configurable: true */
1411 297471 : if (!descObj->defineProperty(cx, state.configurableAtom, BooleanValue(true)))
1412 0 : return false;
1413 :
1414 : /* enumerable: true */
1415 297471 : PropertyName *acc = (Type == Getter) ? state.getAtom : state.setAtom;
1416 297471 : if (!descObj->defineProperty(cx, acc, args[1]))
1417 0 : return false;
1418 :
1419 : JSBool dummy;
1420 297471 : if (!js_DefineOwnProperty(cx, &args.thisv().toObject(), id, ObjectValue(*descObj), &dummy))
1421 0 : return false;
1422 297471 : args.rval().setUndefined();
1423 297471 : return true;
1424 : }
1425 :
1426 : JS_FRIEND_API(JSBool)
1427 280948 : js::obj_defineGetter(JSContext *cx, unsigned argc, Value *vp)
1428 : {
1429 280948 : return DefineAccessor<Getter>(cx, argc, vp);
1430 : }
1431 :
1432 : JS_FRIEND_API(JSBool)
1433 16532 : js::obj_defineSetter(JSContext *cx, unsigned argc, Value *vp)
1434 : {
1435 16532 : return DefineAccessor<Setter>(cx, argc, vp);
1436 : }
1437 :
1438 : static JSBool
1439 26980 : obj_lookupGetter(JSContext *cx, unsigned argc, Value *vp)
1440 : {
1441 : jsid id;
1442 26980 : if (!ValueToId(cx, argc != 0 ? vp[2] : UndefinedValue(), &id))
1443 0 : return JS_FALSE;
1444 26980 : JSObject *obj = ToObject(cx, &vp[1]);
1445 26980 : if (!obj)
1446 0 : return JS_FALSE;
1447 : JSObject *pobj;
1448 : JSProperty *prop;
1449 26980 : if (!obj->lookupGeneric(cx, id, &pobj, &prop))
1450 0 : return JS_FALSE;
1451 26980 : vp->setUndefined();
1452 26980 : if (prop) {
1453 24572 : if (pobj->isNative()) {
1454 24572 : Shape *shape = (Shape *) prop;
1455 24572 : if (shape->hasGetterValue())
1456 12870 : *vp = shape->getterValue();
1457 : }
1458 : }
1459 26980 : return JS_TRUE;
1460 : }
1461 :
1462 : static JSBool
1463 19195 : obj_lookupSetter(JSContext *cx, unsigned argc, Value *vp)
1464 : {
1465 : jsid id;
1466 19195 : if (!ValueToId(cx, argc != 0 ? vp[2] : UndefinedValue(), &id))
1467 0 : return JS_FALSE;
1468 19195 : JSObject *obj = ToObject(cx, &vp[1]);
1469 19195 : if (!obj)
1470 0 : return JS_FALSE;
1471 : JSObject *pobj;
1472 : JSProperty *prop;
1473 19195 : if (!obj->lookupGeneric(cx, id, &pobj, &prop))
1474 0 : return JS_FALSE;
1475 19195 : vp->setUndefined();
1476 19195 : if (prop) {
1477 19195 : if (pobj->isNative()) {
1478 19195 : Shape *shape = (Shape *) prop;
1479 19195 : if (shape->hasSetterValue())
1480 4972 : *vp = shape->setterValue();
1481 : }
1482 : }
1483 19195 : return JS_TRUE;
1484 : }
1485 : #endif /* OLD_GETTER_SETTER_METHODS */
1486 :
1487 : JSBool
1488 706 : obj_getPrototypeOf(JSContext *cx, unsigned argc, Value *vp)
1489 : {
1490 706 : if (argc == 0) {
1491 0 : js_ReportMissingArg(cx, *vp, 0);
1492 0 : return JS_FALSE;
1493 : }
1494 :
1495 706 : if (vp[2].isPrimitive()) {
1496 9 : char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, vp[2], NULL);
1497 9 : if (!bytes)
1498 0 : return JS_FALSE;
1499 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
1500 9 : JSMSG_UNEXPECTED_TYPE, bytes, "not an object");
1501 9 : JS_free(cx, bytes);
1502 9 : return JS_FALSE;
1503 : }
1504 :
1505 697 : JSObject *obj = &vp[2].toObject();
1506 : unsigned attrs;
1507 697 : return CheckAccess(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.protoAtom),
1508 697 : JSACC_PROTO, vp, &attrs);
1509 : }
1510 :
1511 : namespace js {
1512 :
1513 : bool
1514 14430 : NewPropertyDescriptorObject(JSContext *cx, const PropertyDescriptor *desc, Value *vp)
1515 : {
1516 14430 : if (!desc->obj) {
1517 440 : vp->setUndefined();
1518 440 : return true;
1519 : }
1520 :
1521 : /* We have our own property, so start creating the descriptor. */
1522 13990 : PropDesc d;
1523 13990 : d.initFromPropertyDescriptor(*desc);
1524 13990 : if (!d.makeObject(cx))
1525 0 : return false;
1526 13990 : *vp = d.pd;
1527 13990 : return true;
1528 : }
1529 :
1530 : void
1531 13990 : PropDesc::initFromPropertyDescriptor(const PropertyDescriptor &desc)
1532 : {
1533 13990 : pd.setUndefined();
1534 13990 : attrs = uint8_t(desc.attrs);
1535 13990 : JS_ASSERT_IF(attrs & JSPROP_READONLY, !(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
1536 13990 : if (desc.attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
1537 2344 : hasGet = true;
1538 : get = ((desc.attrs & JSPROP_GETTER) && desc.getter)
1539 2335 : ? CastAsObjectJsval(desc.getter)
1540 4679 : : UndefinedValue();
1541 2344 : hasSet = true;
1542 : set = ((desc.attrs & JSPROP_SETTER) && desc.setter)
1543 63 : ? CastAsObjectJsval(desc.setter)
1544 2407 : : UndefinedValue();
1545 2344 : hasValue = false;
1546 2344 : value.setUndefined();
1547 2344 : hasWritable = false;
1548 : } else {
1549 11646 : hasGet = false;
1550 11646 : get.setUndefined();
1551 11646 : hasSet = false;
1552 11646 : set.setUndefined();
1553 11646 : hasValue = true;
1554 11646 : value = desc.value;
1555 11646 : hasWritable = true;
1556 : }
1557 13990 : hasEnumerable = true;
1558 13990 : hasConfigurable = true;
1559 13990 : }
1560 :
1561 : bool
1562 13999 : PropDesc::makeObject(JSContext *cx)
1563 : {
1564 13999 : JSObject *obj = NewBuiltinClassInstance(cx, &ObjectClass);
1565 13999 : if (!obj)
1566 0 : return false;
1567 :
1568 13999 : const JSAtomState &atomState = cx->runtime->atomState;
1569 69995 : if ((hasConfigurable &&
1570 : !obj->defineProperty(cx, atomState.configurableAtom,
1571 13999 : BooleanValue((attrs & JSPROP_PERMANENT) == 0))) ||
1572 : (hasEnumerable &&
1573 : !obj->defineProperty(cx, atomState.enumerableAtom,
1574 13999 : BooleanValue((attrs & JSPROP_ENUMERATE) != 0))) ||
1575 : (hasGet &&
1576 2344 : !obj->defineProperty(cx, atomState.getAtom, get)) ||
1577 : (hasSet &&
1578 2344 : !obj->defineProperty(cx, atomState.setAtom, set)) ||
1579 : (hasValue &&
1580 11655 : !obj->defineProperty(cx, atomState.valueAtom, value)) ||
1581 : (hasWritable &&
1582 : !obj->defineProperty(cx, atomState.writableAtom,
1583 11655 : BooleanValue((attrs & JSPROP_READONLY) == 0))))
1584 : {
1585 0 : return false;
1586 : }
1587 :
1588 13999 : pd.setObject(*obj);
1589 13999 : return true;
1590 : }
1591 :
1592 : bool
1593 12667 : GetOwnPropertyDescriptor(JSContext *cx, JSObject *obj, jsid id, PropertyDescriptor *desc)
1594 : {
1595 12667 : if (obj->isProxy())
1596 45 : return Proxy::getOwnPropertyDescriptor(cx, obj, id, false, desc);
1597 :
1598 : JSObject *pobj;
1599 : JSProperty *prop;
1600 12622 : if (!js_HasOwnProperty(cx, obj->getOps()->lookupGeneric, obj, id, &pobj, &prop))
1601 0 : return false;
1602 12622 : if (!prop) {
1603 440 : desc->obj = NULL;
1604 440 : return true;
1605 : }
1606 :
1607 12182 : bool doGet = true;
1608 12182 : if (pobj->isNative()) {
1609 12047 : Shape *shape = (Shape *) prop;
1610 12047 : desc->attrs = shape->attributes();
1611 12047 : if (desc->attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
1612 574 : doGet = false;
1613 574 : if (desc->attrs & JSPROP_GETTER)
1614 574 : desc->getter = CastAsPropertyOp(shape->getterObject());
1615 574 : if (desc->attrs & JSPROP_SETTER)
1616 63 : desc->setter = CastAsStrictPropertyOp(shape->setterObject());
1617 : }
1618 : } else {
1619 135 : if (!pobj->getGenericAttributes(cx, id, &desc->attrs))
1620 0 : return false;
1621 : }
1622 :
1623 12182 : if (doGet && !obj->getGeneric(cx, id, &desc->value))
1624 0 : return false;
1625 :
1626 12182 : desc->obj = obj;
1627 12182 : return true;
1628 : }
1629 :
1630 : bool
1631 11825 : GetOwnPropertyDescriptor(JSContext *cx, JSObject *obj, jsid id, Value *vp)
1632 : {
1633 23650 : AutoPropertyDescriptorRooter desc(cx);
1634 11825 : return GetOwnPropertyDescriptor(cx, obj, id, &desc) &&
1635 11825 : NewPropertyDescriptorObject(cx, &desc, vp);
1636 : }
1637 :
1638 : }
1639 :
1640 : static bool
1641 315007 : GetFirstArgumentAsObject(JSContext *cx, unsigned argc, Value *vp, const char *method, JSObject **objp)
1642 : {
1643 315007 : if (argc == 0) {
1644 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
1645 0 : method, "0", "s");
1646 0 : return false;
1647 : }
1648 :
1649 315007 : const Value &v = vp[2];
1650 315007 : if (!v.isObject()) {
1651 36 : char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NULL);
1652 36 : if (!bytes)
1653 0 : return false;
1654 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_UNEXPECTED_TYPE,
1655 36 : bytes, "not an object");
1656 36 : JS_free(cx, bytes);
1657 36 : return false;
1658 : }
1659 :
1660 314971 : *objp = &v.toObject();
1661 314971 : return true;
1662 : }
1663 :
1664 : static JSBool
1665 11825 : obj_getOwnPropertyDescriptor(JSContext *cx, unsigned argc, Value *vp)
1666 : {
1667 : JSObject *obj;
1668 11825 : if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.getOwnPropertyDescriptor", &obj))
1669 0 : return JS_FALSE;
1670 23650 : AutoIdRooter nameidr(cx);
1671 11825 : if (!ValueToId(cx, argc >= 2 ? vp[3] : UndefinedValue(), nameidr.addr()))
1672 0 : return JS_FALSE;
1673 11825 : return GetOwnPropertyDescriptor(cx, obj, nameidr.id(), vp);
1674 : }
1675 :
1676 : static JSBool
1677 1500 : obj_keys(JSContext *cx, unsigned argc, Value *vp)
1678 : {
1679 : JSObject *obj;
1680 1500 : if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.keys", &obj))
1681 0 : return false;
1682 :
1683 3000 : AutoIdVector props(cx);
1684 1500 : if (!GetPropertyNames(cx, obj, JSITER_OWNONLY, &props))
1685 0 : return false;
1686 :
1687 3000 : AutoValueVector vals(cx);
1688 1500 : if (!vals.reserve(props.length()))
1689 0 : return false;
1690 17305 : for (size_t i = 0, len = props.length(); i < len; i++) {
1691 15805 : jsid id = props[i];
1692 15805 : if (JSID_IS_STRING(id)) {
1693 15337 : vals.infallibleAppend(StringValue(JSID_TO_STRING(id)));
1694 468 : } else if (JSID_IS_INT(id)) {
1695 468 : JSString *str = js_IntToString(cx, JSID_TO_INT(id));
1696 468 : if (!str)
1697 0 : return false;
1698 468 : vals.infallibleAppend(StringValue(str));
1699 : } else {
1700 0 : JS_ASSERT(JSID_IS_OBJECT(id));
1701 : }
1702 : }
1703 :
1704 1500 : JS_ASSERT(props.length() <= UINT32_MAX);
1705 1500 : JSObject *aobj = NewDenseCopiedArray(cx, uint32_t(vals.length()), vals.begin());
1706 1500 : if (!aobj)
1707 0 : return false;
1708 1500 : vp->setObject(*aobj);
1709 :
1710 1500 : return true;
1711 : }
1712 :
1713 : static bool
1714 3586014 : HasProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp, bool *foundp)
1715 : {
1716 3586014 : if (!obj->hasProperty(cx, id, foundp, JSRESOLVE_QUALIFIED | JSRESOLVE_DETECTING))
1717 0 : return false;
1718 3586014 : if (!*foundp) {
1719 2087335 : vp->setUndefined();
1720 2087335 : return true;
1721 : }
1722 :
1723 : /*
1724 : * We must go through the method read barrier in case id is 'get' or 'set'.
1725 : * There is no obvious way to defer cloning a joined function object whose
1726 : * identity will be used by DefinePropertyOnObject, e.g., or reflected via
1727 : * js::GetOwnPropertyDescriptor, as the getter or setter callable object.
1728 : */
1729 1498679 : return !!obj->getGeneric(cx, id, vp);
1730 : }
1731 :
1732 611659 : PropDesc::PropDesc()
1733 : : pd(UndefinedValue()),
1734 : value(UndefinedValue()),
1735 : get(UndefinedValue()),
1736 : set(UndefinedValue()),
1737 : attrs(0),
1738 : hasGet(false),
1739 : hasSet(false),
1740 : hasValue(false),
1741 : hasWritable(false),
1742 : hasEnumerable(false),
1743 611659 : hasConfigurable(false)
1744 : {
1745 611659 : }
1746 :
1747 : bool
1748 597669 : PropDesc::initialize(JSContext *cx, const Value &origval, bool checkAccessors)
1749 : {
1750 597669 : Value v = origval;
1751 :
1752 : /* 8.10.5 step 1 */
1753 597669 : if (v.isPrimitive()) {
1754 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_NONNULL_OBJECT);
1755 0 : return false;
1756 : }
1757 597669 : JSObject *desc = &v.toObject();
1758 :
1759 : /* Make a copy of the descriptor. We might need it later. */
1760 597669 : pd = v;
1761 :
1762 : /* Start with the proper defaults. */
1763 597669 : attrs = JSPROP_PERMANENT | JSPROP_READONLY;
1764 :
1765 : bool found;
1766 :
1767 : /* 8.10.5 step 3 */
1768 : #ifdef __GNUC__ /* quell GCC overwarning */
1769 597669 : found = false;
1770 : #endif
1771 597669 : if (!HasProperty(cx, desc, ATOM_TO_JSID(cx->runtime->atomState.enumerableAtom), &v, &found))
1772 0 : return false;
1773 597669 : if (found) {
1774 596139 : hasEnumerable = JS_TRUE;
1775 596139 : if (js_ValueToBoolean(v))
1776 592634 : attrs |= JSPROP_ENUMERATE;
1777 : }
1778 :
1779 : /* 8.10.5 step 4 */
1780 597669 : if (!HasProperty(cx, desc, ATOM_TO_JSID(cx->runtime->atomState.configurableAtom), &v, &found))
1781 0 : return false;
1782 597669 : if (found) {
1783 301200 : hasConfigurable = JS_TRUE;
1784 301200 : if (js_ValueToBoolean(v))
1785 297680 : attrs &= ~JSPROP_PERMANENT;
1786 : }
1787 :
1788 : /* 8.10.5 step 5 */
1789 597669 : if (!HasProperty(cx, desc, ATOM_TO_JSID(cx->runtime->atomState.valueAtom), &v, &found))
1790 0 : return false;
1791 597669 : if (found) {
1792 858 : hasValue = true;
1793 858 : value = v;
1794 : }
1795 :
1796 : /* 8.10.6 step 6 */
1797 597669 : if (!HasProperty(cx, desc, ATOM_TO_JSID(cx->runtime->atomState.writableAtom), &v, &found))
1798 0 : return false;
1799 597669 : if (found) {
1800 247 : hasWritable = JS_TRUE;
1801 247 : if (js_ValueToBoolean(v))
1802 142 : attrs &= ~JSPROP_READONLY;
1803 : }
1804 :
1805 : /* 8.10.7 step 7 */
1806 597669 : if (!HasProperty(cx, desc, ATOM_TO_JSID(cx->runtime->atomState.getAtom), &v, &found))
1807 0 : return false;
1808 597669 : if (found) {
1809 579801 : hasGet = true;
1810 579801 : get = v;
1811 579801 : attrs |= JSPROP_GETTER | JSPROP_SHARED;
1812 579801 : attrs &= ~JSPROP_READONLY;
1813 579801 : if (checkAccessors && !checkGetter(cx))
1814 0 : return false;
1815 : }
1816 :
1817 : /* 8.10.7 step 8 */
1818 597669 : if (!HasProperty(cx, desc, ATOM_TO_JSID(cx->runtime->atomState.setAtom), &v, &found))
1819 0 : return false;
1820 597669 : if (found) {
1821 20434 : hasSet = true;
1822 20434 : set = v;
1823 20434 : attrs |= JSPROP_SETTER | JSPROP_SHARED;
1824 20434 : attrs &= ~JSPROP_READONLY;
1825 20434 : if (checkAccessors && !checkSetter(cx))
1826 0 : return false;
1827 : }
1828 :
1829 : /* 8.10.7 step 9 */
1830 597669 : if ((hasGet || hasSet) && (hasValue || hasWritable)) {
1831 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INVALID_DESCRIPTOR);
1832 0 : return false;
1833 : }
1834 :
1835 597669 : JS_ASSERT_IF(attrs & JSPROP_READONLY, !(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
1836 :
1837 597669 : return true;
1838 : }
1839 :
1840 : static JSBool
1841 27 : Reject(JSContext *cx, unsigned errorNumber, bool throwError, jsid id, bool *rval)
1842 : {
1843 27 : if (throwError) {
1844 : jsid idstr;
1845 27 : if (!js_ValueToStringId(cx, IdToValue(id), &idstr))
1846 0 : return JS_FALSE;
1847 54 : JSAutoByteString bytes(cx, JSID_TO_STRING(idstr));
1848 27 : if (!bytes)
1849 0 : return JS_FALSE;
1850 27 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, errorNumber, bytes.ptr());
1851 27 : return JS_FALSE;
1852 : }
1853 :
1854 0 : *rval = false;
1855 0 : return JS_TRUE;
1856 : }
1857 :
1858 : static JSBool
1859 0 : Reject(JSContext *cx, JSObject *obj, unsigned errorNumber, bool throwError, bool *rval)
1860 : {
1861 0 : if (throwError) {
1862 0 : if (js_ErrorFormatString[errorNumber].argCount == 1) {
1863 : js_ReportValueErrorFlags(cx, JSREPORT_ERROR, errorNumber,
1864 : JSDVG_IGNORE_STACK, ObjectValue(*obj),
1865 0 : NULL, NULL, NULL);
1866 : } else {
1867 0 : JS_ASSERT(js_ErrorFormatString[errorNumber].argCount == 0);
1868 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, errorNumber);
1869 : }
1870 0 : return JS_FALSE;
1871 : }
1872 :
1873 0 : *rval = false;
1874 0 : return JS_TRUE;
1875 : }
1876 :
1877 : static JSBool
1878 593996 : DefinePropertyOnObject(JSContext *cx, JSObject *obj, const jsid &id, const PropDesc &desc,
1879 : bool throwError, bool *rval)
1880 : {
1881 : /* 8.12.9 step 1. */
1882 : JSProperty *current;
1883 : JSObject *obj2;
1884 593996 : JS_ASSERT(!obj->getOps()->lookupGeneric);
1885 593996 : if (!js_HasOwnProperty(cx, NULL, obj, id, &obj2, ¤t))
1886 0 : return JS_FALSE;
1887 :
1888 593996 : JS_ASSERT(!obj->getOps()->defineProperty);
1889 :
1890 : /* 8.12.9 steps 2-4. */
1891 593996 : if (!current) {
1892 576982 : if (!obj->isExtensible())
1893 0 : return Reject(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE, throwError, rval);
1894 :
1895 576982 : *rval = true;
1896 :
1897 576982 : if (desc.isGenericDescriptor() || desc.isDataDescriptor()) {
1898 537 : JS_ASSERT(!obj->getOps()->defineProperty);
1899 : return js_DefineProperty(cx, obj, id, &desc.value,
1900 537 : JS_PropertyStub, JS_StrictPropertyStub, desc.attrs);
1901 : }
1902 :
1903 576445 : JS_ASSERT(desc.isAccessorDescriptor());
1904 :
1905 : /*
1906 : * Getters and setters are just like watchpoints from an access
1907 : * control point of view.
1908 : */
1909 : Value dummy;
1910 : unsigned dummyAttrs;
1911 576445 : if (!CheckAccess(cx, obj, id, JSACC_WATCH, &dummy, &dummyAttrs))
1912 0 : return JS_FALSE;
1913 :
1914 576445 : Value tmp = UndefinedValue();
1915 : return js_DefineProperty(cx, obj, id, &tmp,
1916 576445 : desc.getter(), desc.setter(), desc.attrs);
1917 : }
1918 :
1919 : /* 8.12.9 steps 5-6 (note 5 is merely a special case of 6). */
1920 17014 : Value v = UndefinedValue();
1921 :
1922 17014 : JS_ASSERT(obj == obj2);
1923 :
1924 17014 : const Shape *shape = reinterpret_cast<Shape *>(current);
1925 : do {
1926 17014 : if (desc.isAccessorDescriptor()) {
1927 16627 : if (!shape->isAccessorDescriptor())
1928 348 : break;
1929 :
1930 16279 : if (desc.hasGet) {
1931 : bool same;
1932 35 : if (!SameValue(cx, desc.getterValue(), shape->getterOrUndefined(), &same))
1933 0 : return false;
1934 35 : if (!same)
1935 29 : break;
1936 : }
1937 :
1938 16250 : if (desc.hasSet) {
1939 : bool same;
1940 16244 : if (!SameValue(cx, desc.setterValue(), shape->setterOrUndefined(), &same))
1941 0 : return false;
1942 16244 : if (!same)
1943 16244 : break;
1944 : }
1945 : } else {
1946 : /*
1947 : * Determine the current value of the property once, if the current
1948 : * value might actually need to be used or preserved later. NB: we
1949 : * guard on whether the current property is a data descriptor to
1950 : * avoid calling a getter; we won't need the value if it's not a
1951 : * data descriptor.
1952 : */
1953 387 : if (shape->isDataDescriptor()) {
1954 : /*
1955 : * We must rule out a non-configurable js::PropertyOp-guarded
1956 : * property becoming a writable unguarded data property, since
1957 : * such a property can have its value changed to one the getter
1958 : * and setter preclude.
1959 : *
1960 : * A desc lacking writable but with value is a data descriptor
1961 : * and we must reject it as if it had writable: true if current
1962 : * is writable.
1963 : */
1964 459 : if (!shape->configurable() &&
1965 90 : (!shape->hasDefaultGetter() || !shape->hasDefaultSetter()) &&
1966 0 : desc.isDataDescriptor() &&
1967 0 : (desc.hasWritable ? desc.writable() : shape->writable()))
1968 : {
1969 0 : return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
1970 : }
1971 :
1972 369 : if (!js_NativeGet(cx, obj, obj2, shape, JSGET_NO_METHOD_BARRIER, &v))
1973 0 : return JS_FALSE;
1974 : }
1975 :
1976 387 : if (desc.isDataDescriptor()) {
1977 360 : if (!shape->isDataDescriptor())
1978 9 : break;
1979 :
1980 : bool same;
1981 351 : if (desc.hasValue) {
1982 324 : if (!SameValue(cx, desc.value, v, &same))
1983 0 : return false;
1984 324 : if (!same) {
1985 : /*
1986 : * Insist that a non-configurable js::PropertyOp data
1987 : * property is frozen at exactly the last-got value.
1988 : *
1989 : * Duplicate the first part of the big conjunction that
1990 : * we tested above, rather than add a local bool flag.
1991 : * Likewise, don't try to keep shape->writable() in a
1992 : * flag we veto from true to false for non-configurable
1993 : * PropertyOp-based data properties and test before the
1994 : * SameValue check later on in order to re-use that "if
1995 : * (!SameValue) Reject" logic.
1996 : *
1997 : * This function is large and complex enough that it
1998 : * seems best to repeat a small bit of code and return
1999 : * Reject(...) ASAP, instead of being clever.
2000 : */
2001 378 : if (!shape->configurable() &&
2002 54 : (!shape->hasDefaultGetter() || !shape->hasDefaultSetter()))
2003 : {
2004 0 : return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
2005 : }
2006 324 : break;
2007 : }
2008 : }
2009 27 : if (desc.hasWritable && desc.writable() != shape->writable())
2010 27 : break;
2011 : } else {
2012 : /* The only fields in desc will be handled below. */
2013 27 : JS_ASSERT(desc.isGenericDescriptor());
2014 : }
2015 : }
2016 :
2017 33 : if (desc.hasConfigurable && desc.configurable() != shape->configurable())
2018 18 : break;
2019 15 : if (desc.hasEnumerable && desc.enumerable() != shape->enumerable())
2020 9 : break;
2021 :
2022 : /* The conditions imposed by step 5 or step 6 apply. */
2023 6 : *rval = true;
2024 6 : return JS_TRUE;
2025 : } while (0);
2026 :
2027 : /* 8.12.9 step 7. */
2028 17008 : if (!shape->configurable()) {
2029 : /*
2030 : * Since [[Configurable]] defaults to false, we don't need to check
2031 : * whether it was specified. We can't do likewise for [[Enumerable]]
2032 : * because its putative value is used in a comparison -- a comparison
2033 : * whose result must always be false per spec if the [[Enumerable]]
2034 : * field is not present. Perfectly pellucid logic, eh?
2035 : */
2036 45 : JS_ASSERT_IF(!desc.hasConfigurable, !desc.configurable());
2037 45 : if (desc.configurable() ||
2038 0 : (desc.hasEnumerable && desc.enumerable() != shape->enumerable())) {
2039 0 : return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
2040 : }
2041 : }
2042 :
2043 17008 : bool callDelProperty = false;
2044 :
2045 17008 : if (desc.isGenericDescriptor()) {
2046 : /* 8.12.9 step 8, no validation required */
2047 16981 : } else if (desc.isDataDescriptor() != shape->isDataDescriptor()) {
2048 : /* 8.12.9 step 9. */
2049 357 : if (!shape->configurable())
2050 0 : return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
2051 16624 : } else if (desc.isDataDescriptor()) {
2052 : /* 8.12.9 step 10. */
2053 351 : JS_ASSERT(shape->isDataDescriptor());
2054 351 : if (!shape->configurable() && !shape->writable()) {
2055 27 : if (desc.hasWritable && desc.writable())
2056 0 : return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
2057 27 : if (desc.hasValue) {
2058 : bool same;
2059 27 : if (!SameValue(cx, desc.value, v, &same))
2060 0 : return false;
2061 27 : if (!same)
2062 27 : return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
2063 : }
2064 : }
2065 :
2066 324 : callDelProperty = !shape->hasDefaultGetter() || !shape->hasDefaultSetter();
2067 : } else {
2068 : /* 8.12.9 step 11. */
2069 16273 : JS_ASSERT(desc.isAccessorDescriptor() && shape->isAccessorDescriptor());
2070 16273 : if (!shape->configurable()) {
2071 0 : if (desc.hasSet) {
2072 : bool same;
2073 0 : if (!SameValue(cx, desc.setterValue(), shape->setterOrUndefined(), &same))
2074 0 : return false;
2075 0 : if (!same)
2076 0 : return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
2077 : }
2078 :
2079 0 : if (desc.hasGet) {
2080 : bool same;
2081 0 : if (!SameValue(cx, desc.getterValue(), shape->getterOrUndefined(), &same))
2082 0 : return false;
2083 0 : if (!same)
2084 0 : return Reject(cx, JSMSG_CANT_REDEFINE_PROP, throwError, id, rval);
2085 : }
2086 : }
2087 : }
2088 :
2089 : /* 8.12.9 step 12. */
2090 : unsigned attrs;
2091 : PropertyOp getter;
2092 : StrictPropertyOp setter;
2093 16981 : if (desc.isGenericDescriptor()) {
2094 27 : unsigned changed = 0;
2095 27 : if (desc.hasConfigurable)
2096 18 : changed |= JSPROP_PERMANENT;
2097 27 : if (desc.hasEnumerable)
2098 9 : changed |= JSPROP_ENUMERATE;
2099 :
2100 27 : attrs = (shape->attributes() & ~changed) | (desc.attrs & changed);
2101 27 : if (shape->isMethod()) {
2102 0 : JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
2103 0 : getter = JS_PropertyStub;
2104 0 : setter = JS_StrictPropertyStub;
2105 : } else {
2106 27 : getter = shape->getter();
2107 27 : setter = shape->setter();
2108 : }
2109 16954 : } else if (desc.isDataDescriptor()) {
2110 333 : unsigned unchanged = 0;
2111 333 : if (!desc.hasConfigurable)
2112 333 : unchanged |= JSPROP_PERMANENT;
2113 333 : if (!desc.hasEnumerable)
2114 315 : unchanged |= JSPROP_ENUMERATE;
2115 : /* Watch out for accessor -> data transformations here. */
2116 333 : if (!desc.hasWritable && shape->isDataDescriptor())
2117 279 : unchanged |= JSPROP_READONLY;
2118 :
2119 333 : if (desc.hasValue)
2120 306 : v = desc.value;
2121 333 : attrs = (desc.attrs & ~unchanged) | (shape->attributes() & unchanged);
2122 333 : getter = JS_PropertyStub;
2123 333 : setter = JS_StrictPropertyStub;
2124 : } else {
2125 16621 : JS_ASSERT(desc.isAccessorDescriptor());
2126 :
2127 : /*
2128 : * Getters and setters are just like watchpoints from an access
2129 : * control point of view.
2130 : */
2131 : Value dummy;
2132 16621 : if (!CheckAccess(cx, obj2, id, JSACC_WATCH, &dummy, &attrs))
2133 0 : return JS_FALSE;
2134 :
2135 16621 : JS_ASSERT_IF(shape->isMethod(), !(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
2136 :
2137 : /* 8.12.9 step 12. */
2138 16621 : unsigned changed = 0;
2139 16621 : if (desc.hasConfigurable)
2140 16594 : changed |= JSPROP_PERMANENT;
2141 16621 : if (desc.hasEnumerable)
2142 16585 : changed |= JSPROP_ENUMERATE;
2143 16621 : if (desc.hasGet)
2144 332 : changed |= JSPROP_GETTER | JSPROP_SHARED | JSPROP_READONLY;
2145 16621 : if (desc.hasSet)
2146 16289 : changed |= JSPROP_SETTER | JSPROP_SHARED | JSPROP_READONLY;
2147 :
2148 16621 : attrs = (desc.attrs & changed) | (shape->attributes() & ~changed);
2149 16621 : if (desc.hasGet) {
2150 332 : getter = desc.getter();
2151 : } else {
2152 32767 : getter = (shape->isMethod() || (shape->hasDefaultGetter() && !shape->hasGetterValue()))
2153 : ? JS_PropertyStub
2154 32767 : : shape->getter();
2155 : }
2156 16621 : if (desc.hasSet) {
2157 16289 : setter = desc.setter();
2158 : } else {
2159 663 : setter = (shape->hasDefaultSetter() && !shape->hasSetterValue())
2160 : ? JS_StrictPropertyStub
2161 663 : : shape->setter();
2162 : }
2163 : }
2164 :
2165 16981 : *rval = true;
2166 :
2167 : /*
2168 : * Since "data" properties implemented using native C functions may rely on
2169 : * side effects during setting, we must make them aware that they have been
2170 : * "assigned"; deleting the property before redefining it does the trick.
2171 : * See bug 539766, where we ran into problems when we redefined
2172 : * arguments.length without making the property aware that its value had
2173 : * been changed (which would have happened if we had deleted it before
2174 : * redefining it or we had invoked its setter to change its value).
2175 : */
2176 16981 : if (callDelProperty) {
2177 18 : Value dummy = UndefinedValue();
2178 18 : if (!CallJSPropertyOp(cx, obj2->getClass()->delProperty, obj2, id, &dummy))
2179 0 : return false;
2180 : }
2181 :
2182 16981 : return js_DefineProperty(cx, obj, id, &v, getter, setter, attrs);
2183 : }
2184 :
2185 : static JSBool
2186 241 : DefinePropertyOnArray(JSContext *cx, JSObject *obj, const jsid &id, const PropDesc &desc,
2187 : bool throwError, bool *rval)
2188 : {
2189 : /*
2190 : * We probably should optimize dense array property definitions where
2191 : * the descriptor describes a traditional array property (enumerable,
2192 : * configurable, writable, numeric index or length without altering its
2193 : * attributes). Such definitions are probably unlikely, so we don't bother
2194 : * for now.
2195 : */
2196 241 : if (obj->isDenseArray() && !obj->makeDenseArraySlow(cx))
2197 0 : return JS_FALSE;
2198 :
2199 241 : uint32_t oldLen = obj->getArrayLength();
2200 :
2201 241 : if (JSID_IS_ATOM(id, cx->runtime->atomState.lengthAtom)) {
2202 : /*
2203 : * Our optimization of storage of the length property of arrays makes
2204 : * it very difficult to properly implement defining the property. For
2205 : * now simply throw an exception (NB: not merely Reject) on any attempt
2206 : * to define the "length" property, rather than attempting to implement
2207 : * some difficult-for-authors-to-grasp subset of that functionality.
2208 : */
2209 9 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_DEFINE_ARRAY_LENGTH);
2210 9 : return JS_FALSE;
2211 : }
2212 :
2213 : uint32_t index;
2214 232 : if (js_IdIsIndex(id, &index)) {
2215 : /*
2216 : // Disabled until we support defining "length":
2217 : if (index >= oldLen && lengthPropertyNotWritable())
2218 : return ThrowTypeError(cx, JSMSG_CANT_APPEND_TO_ARRAY);
2219 : */
2220 232 : if (!DefinePropertyOnObject(cx, obj, id, desc, false, rval))
2221 0 : return JS_FALSE;
2222 232 : if (!*rval)
2223 0 : return Reject(cx, obj, JSMSG_CANT_DEFINE_ARRAY_INDEX, throwError, rval);
2224 :
2225 232 : if (index >= oldLen) {
2226 89 : JS_ASSERT(index != UINT32_MAX);
2227 89 : obj->setArrayLength(cx, index + 1);
2228 : }
2229 :
2230 232 : *rval = true;
2231 232 : return JS_TRUE;
2232 : }
2233 :
2234 0 : return DefinePropertyOnObject(cx, obj, id, desc, throwError, rval);
2235 : }
2236 :
2237 : namespace js {
2238 :
2239 : bool
2240 595809 : DefineProperty(JSContext *cx, JSObject *obj, const jsid &id, const PropDesc &desc, bool throwError,
2241 : bool *rval)
2242 : {
2243 595809 : if (obj->isArray())
2244 241 : return DefinePropertyOnArray(cx, obj, id, desc, throwError, rval);
2245 :
2246 595568 : if (obj->getOps()->lookupGeneric) {
2247 1804 : if (obj->isProxy())
2248 1804 : return Proxy::defineProperty(cx, obj, id, desc.pd);
2249 0 : return Reject(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE, throwError, rval);
2250 : }
2251 :
2252 593764 : return DefinePropertyOnObject(cx, obj, id, desc, throwError, rval);
2253 : }
2254 :
2255 : } /* namespace js */
2256 :
2257 : JSBool
2258 595249 : js_DefineOwnProperty(JSContext *cx, JSObject *obj, jsid id, const Value &descriptor, JSBool *bp)
2259 : {
2260 1190498 : AutoPropDescArrayRooter descs(cx);
2261 595249 : PropDesc *desc = descs.append();
2262 595249 : if (!desc || !desc->initialize(cx, descriptor))
2263 0 : return false;
2264 :
2265 : bool rval;
2266 595249 : if (!DefineProperty(cx, obj, id, *desc, true, &rval))
2267 1768 : return false;
2268 593481 : *bp = !!rval;
2269 593481 : return true;
2270 : }
2271 :
2272 : /* ES5 15.2.3.6: Object.defineProperty(O, P, Attributes) */
2273 : static JSBool
2274 297805 : obj_defineProperty(JSContext *cx, unsigned argc, Value *vp)
2275 : {
2276 : JSObject *obj;
2277 297805 : if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.defineProperty", &obj))
2278 27 : return false;
2279 :
2280 : jsid id;
2281 297778 : if (!ValueToId(cx, argc >= 2 ? vp[3] : UndefinedValue(), &id))
2282 0 : return JS_FALSE;
2283 :
2284 297778 : const Value descval = argc >= 3 ? vp[4] : UndefinedValue();
2285 :
2286 : JSBool junk;
2287 297778 : if (!js_DefineOwnProperty(cx, obj, id, descval, &junk))
2288 1768 : return false;
2289 :
2290 296010 : vp->setObject(*obj);
2291 296010 : return true;
2292 : }
2293 :
2294 : namespace js {
2295 :
2296 : bool
2297 304 : ReadPropertyDescriptors(JSContext *cx, JSObject *props, bool checkAccessors,
2298 : AutoIdVector *ids, AutoPropDescArrayRooter *descs)
2299 : {
2300 304 : if (!GetPropertyNames(cx, props, JSITER_OWNONLY, ids))
2301 0 : return false;
2302 :
2303 738 : for (size_t i = 0, len = ids->length(); i < len; i++) {
2304 434 : jsid id = (*ids)[i];
2305 434 : PropDesc* desc = descs->append();
2306 : Value v;
2307 434 : if (!desc || !props->getGeneric(cx, id, &v) || !desc->initialize(cx, v, checkAccessors))
2308 0 : return false;
2309 : }
2310 304 : return true;
2311 : }
2312 :
2313 : } /* namespace js */
2314 :
2315 : static bool
2316 214 : DefineProperties(JSContext *cx, JSObject *obj, JSObject *props)
2317 : {
2318 428 : AutoIdVector ids(cx);
2319 428 : AutoPropDescArrayRooter descs(cx);
2320 214 : if (!ReadPropertyDescriptors(cx, props, true, &ids, &descs))
2321 0 : return false;
2322 :
2323 : bool dummy;
2324 522 : for (size_t i = 0, len = ids.length(); i < len; i++) {
2325 317 : if (!DefineProperty(cx, obj, ids[i], descs[i], true, &dummy))
2326 9 : return false;
2327 : }
2328 :
2329 205 : return true;
2330 : }
2331 :
2332 : extern JSBool
2333 36 : js_PopulateObject(JSContext *cx, JSObject *newborn, JSObject *props)
2334 : {
2335 36 : return DefineProperties(cx, newborn, props);
2336 : }
2337 :
2338 : /* ES5 15.2.3.7: Object.defineProperties(O, Properties) */
2339 : static JSBool
2340 81 : obj_defineProperties(JSContext *cx, unsigned argc, Value *vp)
2341 : {
2342 : /* Steps 1 and 7. */
2343 : JSObject *obj;
2344 81 : if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.defineProperties", &obj))
2345 0 : return false;
2346 81 : vp->setObject(*obj);
2347 :
2348 : /* Step 2. */
2349 81 : if (argc < 2) {
2350 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
2351 0 : "Object.defineProperties", "0", "s");
2352 0 : return false;
2353 : }
2354 81 : JSObject *props = ToObject(cx, &vp[3]);
2355 81 : if (!props)
2356 0 : return false;
2357 :
2358 : /* Steps 3-6. */
2359 81 : return DefineProperties(cx, obj, props);
2360 : }
2361 :
2362 : /* ES5 15.2.3.5: Object.create(O [, Properties]) */
2363 : static JSBool
2364 442 : obj_create(JSContext *cx, unsigned argc, Value *vp)
2365 : {
2366 442 : if (argc == 0) {
2367 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_MORE_ARGS_NEEDED,
2368 0 : "Object.create", "0", "s");
2369 0 : return false;
2370 : }
2371 :
2372 442 : CallArgs args = CallArgsFromVp(argc, vp);
2373 442 : const Value &v = args[0];
2374 442 : if (!v.isObjectOrNull()) {
2375 27 : char *bytes = DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, v, NULL);
2376 27 : if (!bytes)
2377 0 : return false;
2378 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_UNEXPECTED_TYPE,
2379 27 : bytes, "not an object or null");
2380 27 : JS_free(cx, bytes);
2381 27 : return false;
2382 : }
2383 :
2384 415 : JSObject *proto = v.toObjectOrNull();
2385 415 : if (proto && proto->isXML()) {
2386 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_XML_PROTO_FORBIDDEN);
2387 0 : return false;
2388 : }
2389 :
2390 : /*
2391 : * Use the callee's global as the parent of the new object to avoid dynamic
2392 : * scoping (i.e., using the caller's global).
2393 : */
2394 415 : JSObject *obj = NewObjectWithGivenProto(cx, &ObjectClass, proto, &args.callee().global());
2395 415 : if (!obj)
2396 0 : return false;
2397 :
2398 : /* Don't track types or array-ness for objects created here. */
2399 415 : MarkTypeObjectUnknownProperties(cx, obj->type());
2400 :
2401 : /* 15.2.3.5 step 4. */
2402 415 : if (args.hasDefined(1)) {
2403 97 : if (args[1].isPrimitive()) {
2404 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NOT_NONNULL_OBJECT);
2405 0 : return false;
2406 : }
2407 :
2408 97 : if (!DefineProperties(cx, obj, &args[1].toObject()))
2409 0 : return false;
2410 : }
2411 :
2412 : /* 5. Return obj. */
2413 415 : args.rval().setObject(*obj);
2414 415 : return true;
2415 : }
2416 :
2417 : static JSBool
2418 828 : obj_getOwnPropertyNames(JSContext *cx, unsigned argc, Value *vp)
2419 : {
2420 : JSObject *obj;
2421 828 : if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.getOwnPropertyNames", &obj))
2422 0 : return false;
2423 :
2424 1656 : AutoIdVector keys(cx);
2425 828 : if (!GetPropertyNames(cx, obj, JSITER_OWNONLY | JSITER_HIDDEN, &keys))
2426 0 : return false;
2427 :
2428 1656 : AutoValueVector vals(cx);
2429 828 : if (!vals.resize(keys.length()))
2430 0 : return false;
2431 :
2432 14562 : for (size_t i = 0, len = keys.length(); i < len; i++) {
2433 13734 : jsid id = keys[i];
2434 13734 : if (JSID_IS_INT(id)) {
2435 261 : JSString *str = js_IntToString(cx, JSID_TO_INT(id));
2436 261 : if (!str)
2437 0 : return false;
2438 261 : vals[i].setString(str);
2439 13473 : } else if (JSID_IS_ATOM(id)) {
2440 13473 : vals[i].setString(JSID_TO_STRING(id));
2441 : } else {
2442 0 : vals[i].setObject(*JSID_TO_OBJECT(id));
2443 : }
2444 : }
2445 :
2446 828 : JSObject *aobj = NewDenseCopiedArray(cx, vals.length(), vals.begin());
2447 828 : if (!aobj)
2448 0 : return false;
2449 :
2450 828 : vp->setObject(*aobj);
2451 828 : return true;
2452 : }
2453 :
2454 : static JSBool
2455 846 : obj_isExtensible(JSContext *cx, unsigned argc, Value *vp)
2456 : {
2457 : JSObject *obj;
2458 846 : if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.isExtensible", &obj))
2459 0 : return false;
2460 :
2461 846 : vp->setBoolean(obj->isExtensible());
2462 846 : return true;
2463 : }
2464 :
2465 : static JSBool
2466 81 : obj_preventExtensions(JSContext *cx, unsigned argc, Value *vp)
2467 : {
2468 : JSObject *obj;
2469 81 : if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.preventExtensions", &obj))
2470 0 : return false;
2471 :
2472 81 : vp->setObject(*obj);
2473 81 : if (!obj->isExtensible())
2474 0 : return true;
2475 :
2476 162 : AutoIdVector props(cx);
2477 81 : return obj->preventExtensions(cx, &props);
2478 : }
2479 :
2480 : /* static */ inline unsigned
2481 3722414 : JSObject::getSealedOrFrozenAttributes(unsigned attrs, ImmutabilityType it)
2482 : {
2483 : /* Make all attributes permanent; if freezing, make data attributes read-only. */
2484 3722414 : if (it == FREEZE && !(attrs & (JSPROP_GETTER | JSPROP_SETTER)))
2485 3714660 : return JSPROP_PERMANENT | JSPROP_READONLY;
2486 7754 : return JSPROP_PERMANENT;
2487 : }
2488 :
2489 : bool
2490 1223582 : JSObject::sealOrFreeze(JSContext *cx, ImmutabilityType it)
2491 : {
2492 1223582 : assertSameCompartment(cx, this);
2493 1223582 : JS_ASSERT(it == SEAL || it == FREEZE);
2494 :
2495 2447164 : RootedVarObject self(cx, this);
2496 :
2497 2447164 : AutoIdVector props(cx);
2498 1223582 : if (isExtensible()) {
2499 1223150 : if (!preventExtensions(cx, &props))
2500 0 : return false;
2501 : } else {
2502 432 : if (!GetPropertyNames(cx, this, JSITER_HIDDEN | JSITER_OWNONLY, &props))
2503 0 : return false;
2504 : }
2505 :
2506 : /* preventExtensions must slowify dense arrays, so we can assign to holes without checks. */
2507 1223582 : JS_ASSERT(!self->isDenseArray());
2508 :
2509 1223582 : if (isNative() && !inDictionaryMode()) {
2510 : /*
2511 : * Seal/freeze non-dictionary objects by constructing a new shape
2512 : * hierarchy mirroring the original one, which can be shared if many
2513 : * objects with the same structure are sealed/frozen. If we use the
2514 : * generic path below then any non-empty object will be converted to
2515 : * dictionary mode.
2516 : */
2517 1210354 : Shape *last = EmptyShape::getInitialShape(cx, self->getClass(),
2518 1210354 : self->getProto(),
2519 : self->getParent(),
2520 1210354 : self->getAllocKind(),
2521 4841416 : self->lastProperty()->getObjectFlags());
2522 1210354 : if (!last)
2523 0 : return false;
2524 :
2525 : /* Get an in order list of the shapes in this object. */
2526 2420708 : AutoShapeVector shapes(cx);
2527 4852926 : for (Shape::Range r = self->lastProperty()->all(); !r.empty(); r.popFront()) {
2528 3642572 : if (!shapes.append(&r.front()))
2529 0 : return false;
2530 : }
2531 1210354 : Reverse(shapes.begin(), shapes.end());
2532 :
2533 4852926 : for (size_t i = 0; i < shapes.length(); i++) {
2534 3642572 : StackShape child(shapes[i]);
2535 3642572 : child.attrs |= getSealedOrFrozenAttributes(child.attrs, it);
2536 :
2537 3642572 : if (!JSID_IS_EMPTY(child.propid))
2538 3642572 : MarkTypePropertyConfigured(cx, self, child.propid);
2539 :
2540 3642572 : last = JS_PROPERTY_TREE(cx).getChild(cx, last, self->numFixedSlots(), child);
2541 3642572 : if (!last)
2542 0 : return NULL;
2543 : }
2544 :
2545 1210354 : JS_ASSERT(self->lastProperty()->slotSpan() == last->slotSpan());
2546 1210354 : JS_ALWAYS_TRUE(setLastProperty(cx, last));
2547 : } else {
2548 93070 : for (size_t i = 0; i < props.length(); i++) {
2549 79842 : jsid id = props[i];
2550 :
2551 : unsigned attrs;
2552 79842 : if (!self->getGenericAttributes(cx, id, &attrs))
2553 0 : return false;
2554 :
2555 79842 : unsigned new_attrs = getSealedOrFrozenAttributes(attrs, it);
2556 :
2557 : /* If we already have the attributes we need, skip the setAttributes call. */
2558 79842 : if ((attrs | new_attrs) == attrs)
2559 39217 : continue;
2560 :
2561 40625 : attrs |= new_attrs;
2562 40625 : if (!self->setGenericAttributes(cx, id, &attrs))
2563 0 : return false;
2564 : }
2565 : }
2566 :
2567 1223582 : return true;
2568 : }
2569 :
2570 : bool
2571 1139 : JSObject::isSealedOrFrozen(JSContext *cx, ImmutabilityType it, bool *resultp)
2572 : {
2573 1139 : if (isExtensible()) {
2574 252 : *resultp = false;
2575 252 : return true;
2576 : }
2577 :
2578 1774 : AutoIdVector props(cx);
2579 887 : if (!GetPropertyNames(cx, this, JSITER_HIDDEN | JSITER_OWNONLY, &props))
2580 0 : return false;
2581 :
2582 11694 : for (size_t i = 0, len = props.length(); i < len; i++) {
2583 11041 : jsid id = props[i];
2584 :
2585 : unsigned attrs;
2586 11041 : if (!getGenericAttributes(cx, id, &attrs))
2587 0 : return false;
2588 :
2589 : /*
2590 : * If the property is configurable, this object is neither sealed nor
2591 : * frozen. If the property is a writable data property, this object is
2592 : * not frozen.
2593 : */
2594 15269 : if (!(attrs & JSPROP_PERMANENT) ||
2595 4228 : (it == FREEZE && !(attrs & (JSPROP_READONLY | JSPROP_GETTER | JSPROP_SETTER))))
2596 : {
2597 234 : *resultp = false;
2598 234 : return true;
2599 : }
2600 : }
2601 :
2602 : /* All properties checked out. This object is sealed/frozen. */
2603 653 : *resultp = true;
2604 653 : return true;
2605 : }
2606 :
2607 : static JSBool
2608 1145 : obj_freeze(JSContext *cx, unsigned argc, Value *vp)
2609 : {
2610 : JSObject *obj;
2611 1145 : if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.freeze", &obj))
2612 0 : return false;
2613 :
2614 1145 : vp->setObject(*obj);
2615 :
2616 1145 : return obj->freeze(cx);
2617 : }
2618 :
2619 : static JSBool
2620 329 : obj_isFrozen(JSContext *cx, unsigned argc, Value *vp)
2621 : {
2622 : JSObject *obj;
2623 329 : if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.preventExtensions", &obj))
2624 9 : return false;
2625 :
2626 : bool frozen;
2627 320 : if (!obj->isFrozen(cx, &frozen))
2628 0 : return false;
2629 320 : vp->setBoolean(frozen);
2630 320 : return true;
2631 : }
2632 :
2633 : static JSBool
2634 252 : obj_seal(JSContext *cx, unsigned argc, Value *vp)
2635 : {
2636 : JSObject *obj;
2637 252 : if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.seal", &obj))
2638 0 : return false;
2639 :
2640 252 : vp->setObject(*obj);
2641 :
2642 252 : return obj->seal(cx);
2643 : }
2644 :
2645 : static JSBool
2646 315 : obj_isSealed(JSContext *cx, unsigned argc, Value *vp)
2647 : {
2648 : JSObject *obj;
2649 315 : if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.isSealed", &obj))
2650 0 : return false;
2651 :
2652 : bool sealed;
2653 315 : if (!obj->isSealed(cx, &sealed))
2654 0 : return false;
2655 315 : vp->setBoolean(sealed);
2656 315 : return true;
2657 : }
2658 :
2659 : #if JS_HAS_OBJ_WATCHPOINT
2660 : const char js_watch_str[] = "watch";
2661 : const char js_unwatch_str[] = "unwatch";
2662 : #endif
2663 : const char js_hasOwnProperty_str[] = "hasOwnProperty";
2664 : const char js_isPrototypeOf_str[] = "isPrototypeOf";
2665 : const char js_propertyIsEnumerable_str[] = "propertyIsEnumerable";
2666 :
2667 : JSFunctionSpec object_methods[] = {
2668 : #if JS_HAS_TOSOURCE
2669 : JS_FN(js_toSource_str, obj_toSource, 0,0),
2670 : #endif
2671 : JS_FN(js_toString_str, obj_toString, 0,0),
2672 : JS_FN(js_toLocaleString_str, obj_toLocaleString, 0,0),
2673 : JS_FN(js_valueOf_str, obj_valueOf, 0,0),
2674 : #if JS_HAS_OBJ_WATCHPOINT
2675 : JS_FN(js_watch_str, obj_watch, 2,0),
2676 : JS_FN(js_unwatch_str, obj_unwatch, 1,0),
2677 : #endif
2678 : JS_FN(js_hasOwnProperty_str, obj_hasOwnProperty, 1,0),
2679 : JS_FN(js_isPrototypeOf_str, obj_isPrototypeOf, 1,0),
2680 : JS_FN(js_propertyIsEnumerable_str, obj_propertyIsEnumerable, 1,0),
2681 : #if OLD_GETTER_SETTER_METHODS
2682 : JS_FN(js_defineGetter_str, js::obj_defineGetter, 2,0),
2683 : JS_FN(js_defineSetter_str, js::obj_defineSetter, 2,0),
2684 : JS_FN(js_lookupGetter_str, obj_lookupGetter, 1,0),
2685 : JS_FN(js_lookupSetter_str, obj_lookupSetter, 1,0),
2686 : #endif
2687 : JS_FS_END
2688 : };
2689 :
2690 : JSFunctionSpec object_static_methods[] = {
2691 : JS_FN("getPrototypeOf", obj_getPrototypeOf, 1,0),
2692 : JS_FN("getOwnPropertyDescriptor", obj_getOwnPropertyDescriptor,2,0),
2693 : JS_FN("keys", obj_keys, 1,0),
2694 : JS_FN("defineProperty", obj_defineProperty, 3,0),
2695 : JS_FN("defineProperties", obj_defineProperties, 2,0),
2696 : JS_FN("create", obj_create, 2,0),
2697 : JS_FN("getOwnPropertyNames", obj_getOwnPropertyNames, 1,0),
2698 : JS_FN("isExtensible", obj_isExtensible, 1,0),
2699 : JS_FN("preventExtensions", obj_preventExtensions, 1,0),
2700 : JS_FN("freeze", obj_freeze, 1,0),
2701 : JS_FN("isFrozen", obj_isFrozen, 1,0),
2702 : JS_FN("seal", obj_seal, 1,0),
2703 : JS_FN("isSealed", obj_isSealed, 1,0),
2704 : JS_FS_END
2705 : };
2706 :
2707 : JSBool
2708 4006 : js_Object(JSContext *cx, unsigned argc, Value *vp)
2709 : {
2710 : JSObject *obj;
2711 4006 : if (argc == 0) {
2712 : /* Trigger logic below to construct a blank object. */
2713 3637 : obj = NULL;
2714 : } else {
2715 : /* If argv[0] is null or undefined, obj comes back null. */
2716 369 : if (!js_ValueToObjectOrNull(cx, vp[2], &obj))
2717 0 : return JS_FALSE;
2718 : }
2719 4006 : if (!obj) {
2720 : /* Make an object whether this was called with 'new' or not. */
2721 3637 : JS_ASSERT(!argc || vp[2].isNull() || vp[2].isUndefined());
2722 3637 : gc::AllocKind kind = NewObjectGCKind(cx, &ObjectClass);
2723 3637 : obj = NewBuiltinClassInstance(cx, &ObjectClass, kind);
2724 3637 : if (!obj)
2725 0 : return JS_FALSE;
2726 3637 : TypeObject *type = GetTypeCallerInitObject(cx, JSProto_Object);
2727 3637 : if (!type)
2728 0 : return JS_FALSE;
2729 3637 : obj->setType(type);
2730 : }
2731 4006 : vp->setObject(*obj);
2732 4006 : return JS_TRUE;
2733 : }
2734 :
2735 : static inline JSObject *
2736 8844619 : NewObject(JSContext *cx, Class *clasp, types::TypeObject *type, JSObject *parent,
2737 : gc::AllocKind kind)
2738 : {
2739 8844619 : JS_ASSERT(clasp != &ArrayClass);
2740 0 : JS_ASSERT_IF(clasp == &FunctionClass,
2741 8844619 : kind == JSFunction::FinalizeKind || kind == JSFunction::ExtendedFinalizeKind);
2742 :
2743 17689238 : RootTypeObject typeRoot(cx, &type);
2744 :
2745 17689238 : RootedVarShape shape(cx);
2746 8844619 : shape = EmptyShape::getInitialShape(cx, clasp, type->proto, parent, kind);
2747 8844619 : if (!shape)
2748 0 : return NULL;
2749 :
2750 : HeapSlot *slots;
2751 8844619 : if (!PreallocateObjectDynamicSlots(cx, shape, &slots))
2752 0 : return NULL;
2753 :
2754 8844619 : JSObject *obj = JSObject::create(cx, kind, shape, typeRoot, slots);
2755 8844619 : if (!obj)
2756 0 : return NULL;
2757 :
2758 : /*
2759 : * This will cancel an already-running incremental GC from doing any more
2760 : * slices, and it will prevent any future incremental GCs.
2761 : */
2762 8844619 : if (clasp->trace && !(clasp->flags & JSCLASS_IMPLEMENTS_BARRIERS))
2763 0 : cx->runtime->gcIncrementalEnabled = false;
2764 :
2765 8844619 : Probes::createObject(cx, obj);
2766 8844619 : return obj;
2767 : }
2768 :
2769 : JSObject *
2770 4290181 : js::NewObjectWithGivenProto(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent,
2771 : gc::AllocKind kind)
2772 : {
2773 4290181 : if (CanBeFinalizedInBackground(kind, clasp))
2774 843816 : kind = GetBackgroundAllocKind(kind);
2775 :
2776 4290181 : NewObjectCache &cache = cx->compartment->newObjectCache;
2777 :
2778 4290181 : NewObjectCache::EntryIndex entry = -1;
2779 4290181 : if (proto && (!parent || parent == proto->getParent()) && !proto->isGlobal()) {
2780 4028512 : if (cache.lookupProto(clasp, proto, kind, &entry))
2781 3269421 : return cache.newObjectFromHit(cx, entry);
2782 : }
2783 :
2784 2041520 : RootObject protoRoot(cx, &proto);
2785 2041520 : RootObject parentRoot(cx, &parent);
2786 :
2787 1020760 : types::TypeObject *type = proto ? proto->getNewType(cx) : cx->compartment->getEmptyType(cx);
2788 1020760 : if (!type)
2789 0 : return NULL;
2790 :
2791 : /*
2792 : * Default parent to the parent of the prototype, which was set from
2793 : * the parent of the prototype's constructor.
2794 : */
2795 1020760 : if (!parent && proto)
2796 28208 : parent = proto->getParent();
2797 :
2798 1020760 : JSObject *obj = NewObject(cx, clasp, type, parent, kind);
2799 1020760 : if (!obj)
2800 0 : return NULL;
2801 :
2802 1020760 : if (entry != -1 && !obj->hasDynamicSlots())
2803 759091 : cache.fillProto(entry, clasp, proto, kind, obj);
2804 :
2805 1020760 : return obj;
2806 : }
2807 :
2808 : JSObject *
2809 31051111 : js::NewObjectWithClassProto(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent,
2810 : gc::AllocKind kind)
2811 : {
2812 31051111 : if (proto)
2813 3238437 : return NewObjectWithGivenProto(cx, clasp, proto, parent, kind);
2814 :
2815 27812674 : if (CanBeFinalizedInBackground(kind, clasp))
2816 6440513 : kind = GetBackgroundAllocKind(kind);
2817 :
2818 27812674 : if (!parent)
2819 13957198 : parent = GetCurrentGlobal(cx);
2820 :
2821 : /*
2822 : * Use the object cache, except for classes without a cached proto key.
2823 : * On these objects, FindProto will do a dynamic property lookup to get
2824 : * global[className].prototype, where changes to either the className or
2825 : * prototype property would render the cached lookup incorrect. For classes
2826 : * with a proto key, the prototype created during class initialization is
2827 : * stored in an immutable slot on the global (except for ClearScope, which
2828 : * will flush the new object cache).
2829 : */
2830 27812674 : JSProtoKey protoKey = GetClassProtoKey(clasp);
2831 :
2832 27812674 : NewObjectCache &cache = cx->compartment->newObjectCache;
2833 :
2834 27812674 : NewObjectCache::EntryIndex entry = -1;
2835 27812674 : if (parent->isGlobal() && protoKey != JSProto_Null) {
2836 20417209 : if (cache.lookupGlobal(clasp, &parent->asGlobal(), kind, &entry))
2837 20060930 : return cache.newObjectFromHit(cx, entry);
2838 : }
2839 :
2840 15503488 : RootObject parentRoot(cx, &parent);
2841 :
2842 7751744 : if (!FindProto(cx, clasp, parentRoot, &proto))
2843 0 : return NULL;
2844 :
2845 7751744 : types::TypeObject *type = proto->getNewType(cx);
2846 7751744 : if (!type)
2847 0 : return NULL;
2848 :
2849 7751744 : JSObject *obj = NewObject(cx, clasp, type, parent, kind);
2850 7751744 : if (!obj)
2851 0 : return NULL;
2852 :
2853 7751744 : if (entry != -1 && !obj->hasDynamicSlots())
2854 356279 : cache.fillGlobal(entry, clasp, &parent->asGlobal(), kind, obj);
2855 :
2856 7751744 : return obj;
2857 : }
2858 :
2859 : JSObject *
2860 2658510 : js::NewObjectWithType(JSContext *cx, types::TypeObject *type, JSObject *parent, gc::AllocKind kind)
2861 : {
2862 2658510 : JS_ASSERT(type->proto->hasNewType(type));
2863 2658510 : JS_ASSERT(parent);
2864 :
2865 2658510 : if (CanBeFinalizedInBackground(kind, &ObjectClass))
2866 2658510 : kind = GetBackgroundAllocKind(kind);
2867 :
2868 2658510 : NewObjectCache &cache = cx->compartment->newObjectCache;
2869 :
2870 2658510 : NewObjectCache::EntryIndex entry = -1;
2871 2658510 : if (parent == type->proto->getParent()) {
2872 2658063 : if (cache.lookupType(&ObjectClass, type, kind, &entry))
2873 2586395 : return cache.newObjectFromHit(cx, entry);
2874 : }
2875 :
2876 72115 : JSObject *obj = NewObject(cx, &ObjectClass, type, parent, kind);
2877 72115 : if (!obj)
2878 0 : return NULL;
2879 :
2880 72115 : if (entry != -1 && !obj->hasDynamicSlots())
2881 71668 : cache.fillType(entry, &ObjectClass, type, kind, obj);
2882 :
2883 72115 : return obj;
2884 : }
2885 :
2886 : JSObject *
2887 597 : js::NewReshapedObject(JSContext *cx, TypeObject *type, JSObject *parent,
2888 : gc::AllocKind kind, const Shape *shape)
2889 : {
2890 597 : JSObject *res = NewObjectWithType(cx, type, parent, kind);
2891 597 : if (!res)
2892 0 : return NULL;
2893 :
2894 597 : if (shape->isEmptyShape())
2895 0 : return res;
2896 :
2897 : /* Get all the ids in the object, in order. */
2898 1194 : js::AutoIdVector ids(cx);
2899 1628 : for (unsigned i = 0; i <= shape->slot(); i++) {
2900 1031 : if (!ids.append(JSID_VOID))
2901 0 : return NULL;
2902 : }
2903 597 : const js::Shape *nshape = shape;
2904 2225 : while (!nshape->isEmptyShape()) {
2905 1031 : ids[nshape->slot()] = nshape->propid();
2906 1031 : nshape = nshape->previous();
2907 : }
2908 :
2909 : /* Construct the new shape. */
2910 1628 : for (unsigned i = 0; i < ids.length(); i++) {
2911 2062 : if (!DefineNativeProperty(cx, res, ids[i], js::UndefinedValue(), NULL, NULL,
2912 2062 : JSPROP_ENUMERATE, 0, 0, DNP_SKIP_TYPE)) {
2913 0 : return NULL;
2914 : }
2915 : }
2916 597 : JS_ASSERT(!res->inDictionaryMode());
2917 :
2918 597 : return res;
2919 : }
2920 :
2921 : JSObject*
2922 37 : js_CreateThis(JSContext *cx, JSObject *callee)
2923 : {
2924 37 : Class *clasp = callee->getClass();
2925 :
2926 37 : Class *newclasp = &ObjectClass;
2927 37 : if (clasp == &FunctionClass) {
2928 18 : JSFunction *fun = callee->toFunction();
2929 18 : if (fun->isNative() && fun->u.n.clasp)
2930 18 : newclasp = fun->u.n.clasp;
2931 : }
2932 :
2933 : Value protov;
2934 37 : if (!callee->getProperty(cx, cx->runtime->atomState.classPrototypeAtom, &protov))
2935 0 : return NULL;
2936 :
2937 37 : JSObject *proto = protov.isObjectOrNull() ? protov.toObjectOrNull() : NULL;
2938 37 : JSObject *parent = callee->getParent();
2939 37 : gc::AllocKind kind = NewObjectGCKind(cx, newclasp);
2940 37 : return NewObjectWithClassProto(cx, newclasp, proto, parent, kind);
2941 : }
2942 :
2943 : static inline JSObject *
2944 2657913 : CreateThisForFunctionWithType(JSContext *cx, types::TypeObject *type, JSObject *parent)
2945 : {
2946 2657913 : if (type->newScript) {
2947 : /*
2948 : * Make an object with the type's associated finalize kind and shape,
2949 : * which reflects any properties that will definitely be added to the
2950 : * object before it is read from.
2951 : */
2952 349841 : gc::AllocKind kind = type->newScript->allocKind;
2953 349841 : JSObject *res = NewObjectWithType(cx, type, parent, kind);
2954 349841 : if (res)
2955 349841 : JS_ALWAYS_TRUE(res->setLastProperty(cx, (Shape *) type->newScript->shape.get()));
2956 349841 : return res;
2957 : }
2958 :
2959 2308072 : gc::AllocKind kind = NewObjectGCKind(cx, &ObjectClass);
2960 2308072 : return NewObjectWithType(cx, type, parent, kind);
2961 : }
2962 :
2963 : JSObject *
2964 2658003 : js_CreateThisForFunctionWithProto(JSContext *cx, JSObject *callee, JSObject *proto)
2965 : {
2966 : JSObject *res;
2967 :
2968 2658003 : if (proto) {
2969 2657913 : types::TypeObject *type = proto->getNewType(cx, callee->toFunction());
2970 2657913 : if (!type)
2971 0 : return NULL;
2972 2657913 : res = CreateThisForFunctionWithType(cx, type, callee->getParent());
2973 : } else {
2974 90 : gc::AllocKind kind = NewObjectGCKind(cx, &ObjectClass);
2975 90 : res = NewObjectWithClassProto(cx, &ObjectClass, proto, callee->getParent(), kind);
2976 : }
2977 :
2978 2658003 : if (res && cx->typeInferenceEnabled())
2979 780622 : TypeScript::SetThis(cx, callee->toFunction()->script(), types::Type::ObjectType(res));
2980 :
2981 2658003 : return res;
2982 : }
2983 :
2984 : JSObject *
2985 996775 : js_CreateThisForFunction(JSContext *cx, JSObject *callee, bool newType)
2986 : {
2987 : Value protov;
2988 996775 : if (!callee->getProperty(cx, cx->runtime->atomState.classPrototypeAtom, &protov))
2989 0 : return NULL;
2990 : JSObject *proto;
2991 996775 : if (protov.isObject())
2992 996725 : proto = &protov.toObject();
2993 : else
2994 50 : proto = NULL;
2995 996775 : JSObject *obj = js_CreateThisForFunctionWithProto(cx, callee, proto);
2996 :
2997 996775 : if (obj && newType) {
2998 : /*
2999 : * Reshape the object and give it a (lazily instantiated) singleton
3000 : * type before passing it as the 'this' value for the call.
3001 : */
3002 10 : obj->clear(cx);
3003 10 : if (!obj->setSingletonType(cx))
3004 0 : return NULL;
3005 :
3006 10 : JSScript *calleeScript = callee->toFunction()->script();
3007 10 : TypeScript::SetThis(cx, calleeScript, types::Type::ObjectType(obj));
3008 : }
3009 :
3010 996775 : return obj;
3011 : }
3012 :
3013 : /*
3014 : * Given pc pointing after a property accessing bytecode, return true if the
3015 : * access is "object-detecting" in the sense used by web scripts, e.g., when
3016 : * checking whether document.all is defined.
3017 : */
3018 : static bool
3019 3182503 : Detecting(JSContext *cx, jsbytecode *pc)
3020 : {
3021 : /* General case: a branch or equality op follows the access. */
3022 3182503 : JSOp op = JSOp(*pc);
3023 3182503 : if (js_CodeSpec[op].format & JOF_DETECTING)
3024 413805 : return true;
3025 :
3026 : JSAtom *atom;
3027 :
3028 2768698 : JSScript *script = cx->stack.currentScript();
3029 2768698 : jsbytecode *endpc = script->code + script->length;
3030 2768698 : JS_ASSERT(script->code <= pc && pc < endpc);
3031 :
3032 2768698 : if (op == JSOP_NULL) {
3033 : /*
3034 : * Special case #1: handle (document.all == null). Don't sweat
3035 : * about JS1.2's revision of the equality operators here.
3036 : */
3037 567 : if (++pc < endpc) {
3038 567 : op = JSOp(*pc);
3039 567 : return op == JSOP_EQ || op == JSOP_NE;
3040 : }
3041 0 : return false;
3042 : }
3043 :
3044 2768131 : if (op == JSOP_GETGNAME || op == JSOP_NAME) {
3045 : /*
3046 : * Special case #2: handle (document.all == undefined). Don't worry
3047 : * about a local variable named |undefined| shadowing the immutable
3048 : * global binding...because, really?
3049 : */
3050 204814 : atom = script->getAtom(GET_UINT32_INDEX(pc));
3051 205960 : if (atom == cx->runtime->atomState.typeAtoms[JSTYPE_VOID] &&
3052 1146 : (pc += js_CodeSpec[op].length) < endpc) {
3053 1146 : op = JSOp(*pc);
3054 1146 : return op == JSOP_EQ || op == JSOP_NE || op == JSOP_STRICTEQ || op == JSOP_STRICTNE;
3055 : }
3056 : }
3057 :
3058 2766985 : return false;
3059 : }
3060 :
3061 : /*
3062 : * Infer lookup flags from the currently executing bytecode. This does
3063 : * not attempt to infer JSRESOLVE_WITH, because the current bytecode
3064 : * does not indicate whether we are in a with statement. Return defaultFlags
3065 : * if a currently executing bytecode cannot be determined.
3066 : */
3067 : unsigned
3068 3372368 : js_InferFlags(JSContext *cx, unsigned defaultFlags)
3069 : {
3070 : const JSCodeSpec *cs;
3071 : uint32_t format;
3072 3372368 : unsigned flags = 0;
3073 :
3074 : jsbytecode *pc;
3075 3372368 : JSScript *script = cx->stack.currentScript(&pc);
3076 3372368 : if (!script || !pc)
3077 7500 : return defaultFlags;
3078 :
3079 3364868 : cs = &js_CodeSpec[*pc];
3080 3364868 : format = cs->format;
3081 3364868 : if (JOF_MODE(format) != JOF_NAME)
3082 1812870 : flags |= JSRESOLVE_QUALIFIED;
3083 3364868 : if (format & JOF_SET) {
3084 276136 : flags |= JSRESOLVE_ASSIGNING;
3085 3088732 : } else if (cs->length >= 0) {
3086 3088732 : pc += cs->length;
3087 3088732 : if (pc < script->code + script->length && Detecting(cx, pc))
3088 321066 : flags |= JSRESOLVE_DETECTING;
3089 : }
3090 3364868 : if (format & JOF_DECLARING)
3091 486177 : flags |= JSRESOLVE_DECLARING;
3092 3364868 : return flags;
3093 : }
3094 :
3095 : JSBool
3096 3840334 : JSObject::nonNativeSetProperty(JSContext *cx, jsid id, js::Value *vp, JSBool strict)
3097 : {
3098 3840334 : if (JS_UNLIKELY(watched())) {
3099 0 : id = js_CheckForStringIndex(id);
3100 0 : WatchpointMap *wpmap = cx->compartment->watchpointMap;
3101 0 : if (wpmap && !wpmap->triggerWatchpoint(cx, this, id, vp))
3102 0 : return false;
3103 : }
3104 3840334 : return getOps()->setGeneric(cx, this, id, vp, strict);
3105 : }
3106 :
3107 : JSBool
3108 13549124 : JSObject::nonNativeSetElement(JSContext *cx, uint32_t index, js::Value *vp, JSBool strict)
3109 : {
3110 13549124 : if (JS_UNLIKELY(watched())) {
3111 : jsid id;
3112 0 : if (!IndexToId(cx, index, &id))
3113 0 : return false;
3114 0 : JS_ASSERT(id == js_CheckForStringIndex(id));
3115 0 : WatchpointMap *wpmap = cx->compartment->watchpointMap;
3116 0 : if (wpmap && !wpmap->triggerWatchpoint(cx, this, id, vp))
3117 0 : return false;
3118 : }
3119 13549124 : return getOps()->setElement(cx, this, index, vp, strict);
3120 : }
3121 :
3122 : bool
3123 26852 : JSObject::deleteByValue(JSContext *cx, const Value &property, Value *rval, bool strict)
3124 : {
3125 : uint32_t index;
3126 26852 : if (IsDefinitelyIndex(property, &index))
3127 6014 : return deleteElement(cx, index, rval, strict);
3128 :
3129 20838 : Value propval = property;
3130 20838 : SpecialId sid;
3131 20838 : if (ValueIsSpecial(this, &propval, &sid, cx))
3132 0 : return deleteSpecial(cx, sid, rval, strict);
3133 :
3134 : JSAtom *name;
3135 20838 : if (!js_ValueToAtom(cx, propval, &name))
3136 0 : return false;
3137 :
3138 20838 : if (name->isIndex(&index))
3139 36 : return deleteElement(cx, index, rval, false);
3140 :
3141 20802 : return deleteProperty(cx, name->asPropertyName(), rval, false);
3142 : }
3143 :
3144 : JS_FRIEND_API(bool)
3145 0 : JS_CopyPropertiesFrom(JSContext *cx, JSObject *target, JSObject *obj)
3146 : {
3147 : // If we're not native, then we cannot copy properties.
3148 0 : JS_ASSERT(target->isNative() == obj->isNative());
3149 0 : if (!target->isNative())
3150 0 : return true;
3151 :
3152 0 : AutoShapeVector shapes(cx);
3153 0 : for (Shape::Range r(obj->lastProperty()); !r.empty(); r.popFront()) {
3154 0 : if (!shapes.append(&r.front()))
3155 0 : return false;
3156 : }
3157 :
3158 0 : size_t n = shapes.length();
3159 0 : while (n > 0) {
3160 0 : const Shape *shape = shapes[--n];
3161 0 : unsigned attrs = shape->attributes();
3162 0 : PropertyOp getter = shape->getter();
3163 0 : if ((attrs & JSPROP_GETTER) && !cx->compartment->wrap(cx, &getter))
3164 0 : return false;
3165 0 : StrictPropertyOp setter = shape->setter();
3166 0 : if ((attrs & JSPROP_SETTER) && !cx->compartment->wrap(cx, &setter))
3167 0 : return false;
3168 0 : Value v = shape->hasSlot() ? obj->getSlot(shape->slot()) : UndefinedValue();
3169 0 : if (!cx->compartment->wrap(cx, &v))
3170 0 : return false;
3171 0 : if (!target->defineGeneric(cx, shape->propid(), v, getter, setter, attrs))
3172 0 : return false;
3173 : }
3174 0 : return true;
3175 : }
3176 :
3177 : static bool
3178 0 : CopySlots(JSContext *cx, JSObject *from, JSObject *to)
3179 : {
3180 0 : JS_ASSERT(!from->isNative() && !to->isNative());
3181 0 : JS_ASSERT(from->getClass() == to->getClass());
3182 :
3183 0 : size_t n = 0;
3184 0 : if (from->isWrapper() &&
3185 0 : (Wrapper::wrapperHandler(from)->flags() & Wrapper::CROSS_COMPARTMENT)) {
3186 0 : to->setSlot(0, from->getSlot(0));
3187 0 : to->setSlot(1, from->getSlot(1));
3188 0 : n = 2;
3189 : }
3190 :
3191 0 : size_t span = JSCLASS_RESERVED_SLOTS(from->getClass());
3192 0 : for (; n < span; ++n) {
3193 0 : Value v = from->getSlot(n);
3194 0 : if (!cx->compartment->wrap(cx, &v))
3195 0 : return false;
3196 0 : to->setSlot(n, v);
3197 : }
3198 0 : return true;
3199 : }
3200 :
3201 : JS_FRIEND_API(JSObject *)
3202 0 : JS_CloneObject(JSContext *cx, JSObject *obj, JSObject *proto, JSObject *parent)
3203 : {
3204 : /*
3205 : * We can only clone native objects and proxies. Dense arrays are slowified if
3206 : * we try to clone them.
3207 : */
3208 0 : if (!obj->isNative()) {
3209 0 : if (obj->isDenseArray()) {
3210 0 : if (!obj->makeDenseArraySlow(cx))
3211 0 : return NULL;
3212 0 : } else if (!obj->isProxy()) {
3213 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3214 0 : JSMSG_CANT_CLONE_OBJECT);
3215 0 : return NULL;
3216 : }
3217 : }
3218 0 : JSObject *clone = NewObjectWithGivenProto(cx, obj->getClass(), proto, parent, obj->getAllocKind());
3219 0 : if (!clone)
3220 0 : return NULL;
3221 0 : if (obj->isNative()) {
3222 0 : if (clone->isFunction() && (obj->compartment() != clone->compartment())) {
3223 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
3224 0 : JSMSG_CANT_CLONE_OBJECT);
3225 0 : return NULL;
3226 : }
3227 :
3228 0 : if (obj->hasPrivate())
3229 0 : clone->setPrivate(obj->getPrivate());
3230 : } else {
3231 0 : JS_ASSERT(obj->isProxy());
3232 0 : if (!CopySlots(cx, obj, clone))
3233 0 : return NULL;
3234 : }
3235 :
3236 0 : return clone;
3237 : }
3238 :
3239 : struct JSObject::TradeGutsReserved {
3240 : JSContext *cx;
3241 : Vector<Value> avals;
3242 : Vector<Value> bvals;
3243 : int newafixed;
3244 : int newbfixed;
3245 : Shape *newashape;
3246 : Shape *newbshape;
3247 : HeapSlot *newaslots;
3248 : HeapSlot *newbslots;
3249 :
3250 40 : TradeGutsReserved(JSContext *cx)
3251 : : cx(cx), avals(cx), bvals(cx),
3252 : newafixed(0), newbfixed(0),
3253 : newashape(NULL), newbshape(NULL),
3254 40 : newaslots(NULL), newbslots(NULL)
3255 40 : {}
3256 :
3257 40 : ~TradeGutsReserved()
3258 40 : {
3259 40 : if (newaslots)
3260 0 : cx->free_(newaslots);
3261 40 : if (newbslots)
3262 0 : cx->free_(newbslots);
3263 40 : }
3264 : };
3265 :
3266 : bool
3267 40 : JSObject::ReserveForTradeGuts(JSContext *cx, JSObject *a, JSObject *b,
3268 : TradeGutsReserved &reserved)
3269 : {
3270 : /*
3271 : * When performing multiple swaps between objects which may have different
3272 : * numbers of fixed slots, we reserve all space ahead of time so that the
3273 : * swaps can be performed infallibly.
3274 : */
3275 :
3276 40 : if (a->sizeOfThis() == b->sizeOfThis())
3277 40 : return true;
3278 :
3279 : /*
3280 : * If either object is native, it needs a new shape to preserve the
3281 : * invariant that objects with the same shape have the same number of
3282 : * inline slots. The fixed slots will be updated in place during TradeGuts.
3283 : * Non-native objects need to be reshaped according to the new count.
3284 : */
3285 0 : if (a->isNative()) {
3286 0 : if (!a->generateOwnShape(cx))
3287 0 : return false;
3288 : } else {
3289 : reserved.newbshape = EmptyShape::getInitialShape(cx, a->getClass(),
3290 : a->getProto(), a->getParent(),
3291 0 : b->getAllocKind());
3292 0 : if (!reserved.newbshape)
3293 0 : return false;
3294 : }
3295 0 : if (b->isNative()) {
3296 0 : if (!b->generateOwnShape(cx))
3297 0 : return false;
3298 : } else {
3299 : reserved.newashape = EmptyShape::getInitialShape(cx, b->getClass(),
3300 : b->getProto(), b->getParent(),
3301 0 : a->getAllocKind());
3302 0 : if (!reserved.newashape)
3303 0 : return false;
3304 : }
3305 :
3306 : /* The avals/bvals vectors hold all original values from the objects. */
3307 :
3308 0 : if (!reserved.avals.reserve(a->slotSpan()))
3309 0 : return false;
3310 0 : if (!reserved.bvals.reserve(b->slotSpan()))
3311 0 : return false;
3312 :
3313 0 : JS_ASSERT(a->elements == emptyObjectElements);
3314 0 : JS_ASSERT(b->elements == emptyObjectElements);
3315 :
3316 : /*
3317 : * The newafixed/newbfixed hold the number of fixed slots in the objects
3318 : * after the swap. Adjust these counts according to whether the objects
3319 : * use their last fixed slot for storing private data.
3320 : */
3321 :
3322 0 : reserved.newafixed = a->numFixedSlots();
3323 0 : reserved.newbfixed = b->numFixedSlots();
3324 :
3325 0 : if (a->hasPrivate()) {
3326 0 : reserved.newafixed++;
3327 0 : reserved.newbfixed--;
3328 : }
3329 0 : if (b->hasPrivate()) {
3330 0 : reserved.newbfixed++;
3331 0 : reserved.newafixed--;
3332 : }
3333 :
3334 0 : JS_ASSERT(reserved.newafixed >= 0);
3335 0 : JS_ASSERT(reserved.newbfixed >= 0);
3336 :
3337 : /*
3338 : * The newaslots/newbslots arrays hold any dynamic slots for the objects
3339 : * if they do not have enough fixed slots to accomodate the slots in the
3340 : * other object.
3341 : */
3342 :
3343 0 : unsigned adynamic = dynamicSlotsCount(reserved.newafixed, b->slotSpan());
3344 0 : unsigned bdynamic = dynamicSlotsCount(reserved.newbfixed, a->slotSpan());
3345 :
3346 0 : if (adynamic) {
3347 0 : reserved.newaslots = (HeapSlot *) cx->malloc_(sizeof(HeapSlot) * adynamic);
3348 0 : if (!reserved.newaslots)
3349 0 : return false;
3350 0 : Debug_SetSlotRangeToCrashOnTouch(reserved.newaslots, adynamic);
3351 : }
3352 0 : if (bdynamic) {
3353 0 : reserved.newbslots = (HeapSlot *) cx->malloc_(sizeof(HeapSlot) * bdynamic);
3354 0 : if (!reserved.newbslots)
3355 0 : return false;
3356 0 : Debug_SetSlotRangeToCrashOnTouch(reserved.newbslots, bdynamic);
3357 : }
3358 :
3359 0 : return true;
3360 : }
3361 :
3362 : void
3363 40 : JSObject::TradeGuts(JSContext *cx, JSObject *a, JSObject *b, TradeGutsReserved &reserved)
3364 : {
3365 40 : JS_ASSERT(a->compartment() == b->compartment());
3366 40 : JS_ASSERT(a->isFunction() == b->isFunction());
3367 :
3368 : /* Don't try to swap a JSFunction for a plain function JSObject. */
3369 40 : JS_ASSERT_IF(a->isFunction(), a->sizeOfThis() == b->sizeOfThis());
3370 :
3371 : /*
3372 : * Regexp guts are more complicated -- we would need to migrate the
3373 : * refcounted JIT code blob for them across compartments instead of just
3374 : * swapping guts.
3375 : */
3376 40 : JS_ASSERT(!a->isRegExp() && !b->isRegExp());
3377 :
3378 : /*
3379 : * Callers should not try to swap dense arrays or ArrayBuffer objects,
3380 : * these use a different slot representation from other objects.
3381 : */
3382 40 : JS_ASSERT(!a->isDenseArray() && !b->isDenseArray());
3383 40 : JS_ASSERT(!a->isArrayBuffer() && !b->isArrayBuffer());
3384 :
3385 : #ifdef JSGC_INCREMENTAL
3386 : /*
3387 : * We need a write barrier here. If |a| was marked and |b| was not, then
3388 : * after the swap, |b|'s guts would never be marked. The write barrier
3389 : * solves this.
3390 : */
3391 40 : JSCompartment *comp = a->compartment();
3392 40 : if (comp->needsBarrier()) {
3393 0 : MarkChildren(comp->barrierTracer(), a);
3394 0 : MarkChildren(comp->barrierTracer(), b);
3395 : }
3396 : #endif
3397 :
3398 : /* Trade the guts of the objects. */
3399 40 : const size_t size = a->sizeOfThis();
3400 40 : if (size == b->sizeOfThis()) {
3401 : /*
3402 : * If the objects are the same size, then we make no assumptions about
3403 : * whether they have dynamically allocated slots and instead just copy
3404 : * them over wholesale.
3405 : */
3406 : char tmp[tl::Max<sizeof(JSFunction), sizeof(JSObject_Slots16)>::result];
3407 40 : JS_ASSERT(size <= sizeof(tmp));
3408 :
3409 40 : js_memcpy(tmp, a, size);
3410 40 : js_memcpy(a, b, size);
3411 40 : js_memcpy(b, tmp, size);
3412 :
3413 : #ifdef JSGC_GENERATIONAL
3414 : /*
3415 : * Trigger post barriers for fixed slots. JSObject bits are barriered
3416 : * below, in common with the other case.
3417 : */
3418 : JSCompartment *comp = cx->compartment;
3419 : for (size_t i = 0; i < a->numFixedSlots(); ++i) {
3420 : HeapSlot::writeBarrierPost(comp, a, i);
3421 : HeapSlot::writeBarrierPost(comp, b, i);
3422 : }
3423 : #endif
3424 : } else {
3425 : /*
3426 : * If the objects are of differing sizes, use the space we reserved
3427 : * earlier to save the slots from each object and then copy them into
3428 : * the new layout for the other object.
3429 : */
3430 :
3431 0 : unsigned acap = a->slotSpan();
3432 0 : unsigned bcap = b->slotSpan();
3433 :
3434 0 : for (size_t i = 0; i < acap; i++)
3435 0 : reserved.avals.infallibleAppend(a->getSlot(i));
3436 :
3437 0 : for (size_t i = 0; i < bcap; i++)
3438 0 : reserved.bvals.infallibleAppend(b->getSlot(i));
3439 :
3440 : /* Done with the dynamic slots. */
3441 0 : if (a->hasDynamicSlots())
3442 0 : cx->free_(a->slots);
3443 0 : if (b->hasDynamicSlots())
3444 0 : cx->free_(b->slots);
3445 :
3446 0 : void *apriv = a->hasPrivate() ? a->getPrivate() : NULL;
3447 0 : void *bpriv = b->hasPrivate() ? b->getPrivate() : NULL;
3448 :
3449 : char tmp[sizeof(JSObject)];
3450 0 : js_memcpy(&tmp, a, sizeof tmp);
3451 0 : js_memcpy(a, b, sizeof tmp);
3452 0 : js_memcpy(b, &tmp, sizeof tmp);
3453 :
3454 0 : if (a->isNative())
3455 0 : a->shape_->setNumFixedSlots(reserved.newafixed);
3456 : else
3457 0 : a->shape_ = reserved.newashape;
3458 :
3459 0 : a->slots = reserved.newaslots;
3460 0 : a->initSlotRange(0, reserved.bvals.begin(), bcap);
3461 0 : if (a->hasPrivate())
3462 0 : a->initPrivate(bpriv);
3463 :
3464 0 : if (b->isNative())
3465 0 : b->shape_->setNumFixedSlots(reserved.newbfixed);
3466 : else
3467 0 : b->shape_ = reserved.newbshape;
3468 :
3469 0 : b->slots = reserved.newbslots;
3470 0 : b->initSlotRange(0, reserved.avals.begin(), acap);
3471 0 : if (b->hasPrivate())
3472 0 : b->initPrivate(apriv);
3473 :
3474 : /* Make sure the destructor for reserved doesn't free the slots. */
3475 0 : reserved.newaslots = NULL;
3476 0 : reserved.newbslots = NULL;
3477 : }
3478 :
3479 : #ifdef JSGC_GENERATIONAL
3480 : Shape::writeBarrierPost(a->shape_, &a->shape_);
3481 : Shape::writeBarrierPost(b->shape_, &b->shape_);
3482 : types::TypeObject::writeBarrierPost(a->type_, &a->type_);
3483 : types::TypeObject::writeBarrierPost(b->type_, &b->type_);
3484 : #endif
3485 :
3486 40 : if (a->inDictionaryMode())
3487 0 : a->lastProperty()->listp = &a->shape_;
3488 40 : if (b->inDictionaryMode())
3489 0 : b->lastProperty()->listp = &b->shape_;
3490 40 : }
3491 :
3492 : /*
3493 : * Use this method with extreme caution. It trades the guts of two objects and updates
3494 : * scope ownership. This operation is not thread-safe, just as fast array to slow array
3495 : * transitions are inherently not thread-safe. Don't perform a swap operation on objects
3496 : * shared across threads or, or bad things will happen. You have been warned.
3497 : */
3498 : bool
3499 40 : JSObject::swap(JSContext *cx, JSObject *other)
3500 : {
3501 40 : if (this->compartment() == other->compartment()) {
3502 80 : TradeGutsReserved reserved(cx);
3503 40 : if (!ReserveForTradeGuts(cx, this, other, reserved))
3504 0 : return false;
3505 40 : TradeGuts(cx, this, other, reserved);
3506 40 : return true;
3507 : }
3508 :
3509 : JSObject *thisClone;
3510 : JSObject *otherClone;
3511 : {
3512 0 : AutoCompartment ac(cx, other);
3513 0 : if (!ac.enter())
3514 0 : return false;
3515 0 : thisClone = JS_CloneObject(cx, this, other->getProto(), other->getParent());
3516 0 : if (!thisClone || !JS_CopyPropertiesFrom(cx, thisClone, this))
3517 0 : return false;
3518 : }
3519 : {
3520 0 : AutoCompartment ac(cx, this);
3521 0 : if (!ac.enter())
3522 0 : return false;
3523 0 : otherClone = JS_CloneObject(cx, other, other->getProto(), other->getParent());
3524 0 : if (!otherClone || !JS_CopyPropertiesFrom(cx, otherClone, other))
3525 0 : return false;
3526 : }
3527 :
3528 0 : TradeGutsReserved reservedThis(cx);
3529 0 : TradeGutsReserved reservedOther(cx);
3530 :
3531 0 : if (!ReserveForTradeGuts(cx, this, otherClone, reservedThis) ||
3532 0 : !ReserveForTradeGuts(cx, other, thisClone, reservedOther)) {
3533 0 : return false;
3534 : }
3535 :
3536 0 : TradeGuts(cx, this, otherClone, reservedThis);
3537 0 : TradeGuts(cx, other, thisClone, reservedOther);
3538 :
3539 0 : return true;
3540 : }
3541 :
3542 : static bool
3543 202823 : DefineStandardSlot(JSContext *cx, JSObject *obj, JSProtoKey key, JSAtom *atom,
3544 : const Value &v, uint32_t attrs, bool &named)
3545 : {
3546 202823 : jsid id = ATOM_TO_JSID(atom);
3547 :
3548 202823 : if (key != JSProto_Null) {
3549 : /*
3550 : * Initializing an actual standard class on a global object. If the
3551 : * property is not yet present, force it into a new one bound to a
3552 : * reserved slot. Otherwise, go through the normal property path.
3553 : */
3554 0 : JS_ASSERT(obj->isGlobal());
3555 0 : JS_ASSERT(obj->isNative());
3556 :
3557 0 : const Shape *shape = obj->nativeLookup(cx, id);
3558 0 : if (!shape) {
3559 0 : uint32_t slot = 2 * JSProto_LIMIT + key;
3560 0 : obj->setReservedSlot(slot, v);
3561 0 : if (!obj->addProperty(cx, id, JS_PropertyStub, JS_StrictPropertyStub, slot, attrs, 0, 0))
3562 0 : return false;
3563 0 : AddTypePropertyId(cx, obj, id, v);
3564 :
3565 0 : named = true;
3566 0 : return true;
3567 : }
3568 : }
3569 :
3570 202823 : named = obj->defineGeneric(cx, id, v, JS_PropertyStub, JS_StrictPropertyStub, attrs);
3571 202823 : return named;
3572 : }
3573 :
3574 : namespace js {
3575 :
3576 : static void
3577 0 : SetClassObject(JSObject *obj, JSProtoKey key, JSObject *cobj, JSObject *proto)
3578 : {
3579 0 : JS_ASSERT(!obj->getParent());
3580 0 : if (!obj->isGlobal())
3581 0 : return;
3582 :
3583 0 : obj->setReservedSlot(key, ObjectOrNullValue(cobj));
3584 0 : obj->setReservedSlot(JSProto_LIMIT + key, ObjectOrNullValue(proto));
3585 : }
3586 :
3587 : static void
3588 0 : ClearClassObject(JSContext *cx, JSObject *obj, JSProtoKey key)
3589 : {
3590 0 : JS_ASSERT(!obj->getParent());
3591 0 : if (!obj->isGlobal())
3592 0 : return;
3593 :
3594 0 : obj->setSlot(key, UndefinedValue());
3595 0 : obj->setSlot(JSProto_LIMIT + key, UndefinedValue());
3596 : }
3597 :
3598 : JSObject *
3599 202823 : DefineConstructorAndPrototype(JSContext *cx, HandleObject obj, JSProtoKey key, HandleAtom atom,
3600 : JSObject *protoProto, Class *clasp,
3601 : Native constructor, unsigned nargs,
3602 : JSPropertySpec *ps, JSFunctionSpec *fs,
3603 : JSPropertySpec *static_ps, JSFunctionSpec *static_fs,
3604 : JSObject **ctorp, AllocKind ctorKind)
3605 : {
3606 : /*
3607 : * Create a prototype object for this class.
3608 : *
3609 : * FIXME: lazy standard (built-in) class initialization and even older
3610 : * eager boostrapping code rely on all of these properties:
3611 : *
3612 : * 1. NewObject attempting to compute a default prototype object when
3613 : * passed null for proto; and
3614 : *
3615 : * 2. NewObject tolerating no default prototype (null proto slot value)
3616 : * due to this js_InitClass call coming from js_InitFunctionClass on an
3617 : * otherwise-uninitialized global.
3618 : *
3619 : * 3. NewObject allocating a JSFunction-sized GC-thing when clasp is
3620 : * &FunctionClass, not a JSObject-sized (smaller) GC-thing.
3621 : *
3622 : * The JS_NewObjectForGivenProto and JS_NewObject APIs also allow clasp to
3623 : * be &FunctionClass (we could break compatibility easily). But fixing
3624 : * (3) is not enough without addressing the bootstrapping dependency on (1)
3625 : * and (2).
3626 : */
3627 :
3628 : /*
3629 : * Create the prototype object. (GlobalObject::createBlankPrototype isn't
3630 : * used because it parents the prototype object to the global and because
3631 : * it uses WithProto::Given. FIXME: Undo dependencies on this parentage
3632 : * [which already needs to happen for bug 638316], figure out nicer
3633 : * semantics for null-protoProto, and use createBlankPrototype.)
3634 : */
3635 405646 : RootedVarObject proto(cx);
3636 202823 : proto = NewObjectWithClassProto(cx, clasp, protoProto, obj);
3637 202823 : if (!proto)
3638 0 : return NULL;
3639 :
3640 202823 : if (!proto->setSingletonType(cx))
3641 0 : return NULL;
3642 :
3643 202823 : if (clasp == &ArrayClass && !proto->makeDenseArraySlow(cx))
3644 0 : return NULL;
3645 :
3646 : /* After this point, control must exit via label bad or out. */
3647 405646 : RootedVarObject ctor(cx);
3648 202823 : bool named = false;
3649 202823 : bool cached = false;
3650 202823 : if (!constructor) {
3651 : /*
3652 : * Lacking a constructor, name the prototype (e.g., Math) unless this
3653 : * class (a) is anonymous, i.e. for internal use only; (b) the class
3654 : * of obj (the global object) is has a reserved slot indexed by key;
3655 : * and (c) key is not the null key.
3656 : */
3657 2 : if (!(clasp->flags & JSCLASS_IS_ANONYMOUS) || !obj->isGlobal() || key == JSProto_Null) {
3658 : uint32_t attrs = (clasp->flags & JSCLASS_IS_ANONYMOUS)
3659 : ? JSPROP_READONLY | JSPROP_PERMANENT
3660 2 : : 0;
3661 2 : if (!DefineStandardSlot(cx, obj, key, atom, ObjectValue(*proto), attrs, named))
3662 0 : goto bad;
3663 : }
3664 :
3665 2 : ctor = proto;
3666 : } else {
3667 : /*
3668 : * Create the constructor, not using GlobalObject::createConstructor
3669 : * because the constructor currently must have |obj| as its parent.
3670 : * (FIXME: remove this dependency on the exact identity of the parent,
3671 : * perhaps as part of bug 638316.)
3672 : */
3673 405642 : RootedVarFunction fun(cx);
3674 : fun = js_NewFunction(cx, NULL, constructor, nargs, JSFUN_CONSTRUCTOR, obj, atom,
3675 202821 : ctorKind);
3676 202821 : if (!fun)
3677 : goto bad;
3678 202821 : fun->setConstructorClass(clasp);
3679 :
3680 : /*
3681 : * Set the class object early for standard class constructors. Type
3682 : * inference may need to access these, and js_GetClassPrototype will
3683 : * fail if it tries to do a reentrant reconstruction of the class.
3684 : */
3685 202821 : if (key != JSProto_Null) {
3686 0 : SetClassObject(obj, key, fun, proto);
3687 0 : cached = true;
3688 : }
3689 :
3690 405642 : AutoValueRooter tvr2(cx, ObjectValue(*fun));
3691 202821 : if (!DefineStandardSlot(cx, obj, key, atom, tvr2.value(), 0, named))
3692 : goto bad;
3693 :
3694 : /*
3695 : * Optionally construct the prototype object, before the class has
3696 : * been fully initialized. Allow the ctor to replace proto with a
3697 : * different object, as is done for operator new -- and as at least
3698 : * XML support requires.
3699 : */
3700 202821 : ctor = fun;
3701 202821 : if (!LinkConstructorAndPrototype(cx, ctor, proto))
3702 : goto bad;
3703 :
3704 : /* Bootstrap Function.prototype (see also JS_InitStandardClasses). */
3705 202821 : if (ctor->getClass() == clasp && !ctor->splicePrototype(cx, proto))
3706 : goto bad;
3707 : }
3708 :
3709 608467 : if (!DefinePropertiesAndBrand(cx, proto, ps, fs) ||
3710 405644 : (ctor != proto && !DefinePropertiesAndBrand(cx, ctor, static_ps, static_fs)))
3711 : {
3712 0 : goto bad;
3713 : }
3714 :
3715 202823 : if (clasp->flags & (JSCLASS_FREEZE_PROTO|JSCLASS_FREEZE_CTOR)) {
3716 0 : JS_ASSERT_IF(ctor == proto, !(clasp->flags & JSCLASS_FREEZE_CTOR));
3717 0 : if (proto && (clasp->flags & JSCLASS_FREEZE_PROTO) && !proto->freeze(cx))
3718 0 : goto bad;
3719 0 : if (ctor && (clasp->flags & JSCLASS_FREEZE_CTOR) && !ctor->freeze(cx))
3720 0 : goto bad;
3721 : }
3722 :
3723 : /* If this is a standard class, cache its prototype. */
3724 202823 : if (!cached && key != JSProto_Null)
3725 0 : SetClassObject(obj, key, ctor, proto);
3726 :
3727 202823 : if (ctorp)
3728 22873 : *ctorp = ctor;
3729 202823 : return proto;
3730 :
3731 : bad:
3732 0 : if (named) {
3733 : Value rval;
3734 0 : obj->deleteByValue(cx, StringValue(atom), &rval, false);
3735 : }
3736 0 : if (cached)
3737 0 : ClearClassObject(cx, obj, key);
3738 0 : return NULL;
3739 : }
3740 :
3741 : /*
3742 : * Lazy standard classes need a way to indicate if they have been initialized.
3743 : * Otherwise, when we delete them, we might accidentally recreate them via a
3744 : * lazy initialization. We use the presence of a ctor or proto in the
3745 : * globalObject's slot to indicate that they've been constructed, but this only
3746 : * works for classes which have a proto and ctor. Classes which don't have one
3747 : * can call MarkStandardClassInitializedNoProto(), and we can always check
3748 : * whether a class is initialized by calling IsStandardClassResolved().
3749 : */
3750 : bool
3751 2758058 : IsStandardClassResolved(JSObject *obj, js::Class *clasp)
3752 : {
3753 2758058 : JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(clasp);
3754 :
3755 : /* If the constructor is undefined, then it hasn't been initialized. */
3756 2758058 : return (obj->getReservedSlot(key) != UndefinedValue());
3757 : }
3758 :
3759 : void
3760 11056 : MarkStandardClassInitializedNoProto(JSObject *obj, js::Class *clasp)
3761 : {
3762 11056 : JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(clasp);
3763 :
3764 : /*
3765 : * We use True so that it's obvious what we're doing (instead of, say,
3766 : * Null, which might be miscontrued as an error in setting Undefined).
3767 : */
3768 11056 : if (obj->getReservedSlot(key) == UndefinedValue())
3769 6617 : obj->setSlot(key, BooleanValue(true));
3770 11056 : }
3771 :
3772 : }
3773 :
3774 : JSObject *
3775 202823 : js_InitClass(JSContext *cx, HandleObject obj, JSObject *protoProto,
3776 : Class *clasp, Native constructor, unsigned nargs,
3777 : JSPropertySpec *ps, JSFunctionSpec *fs,
3778 : JSPropertySpec *static_ps, JSFunctionSpec *static_fs,
3779 : JSObject **ctorp, AllocKind ctorKind)
3780 : {
3781 405646 : RootObject rootProto(cx, &protoProto);
3782 :
3783 405646 : RootedVarAtom atom(cx);
3784 202823 : atom = js_Atomize(cx, clasp->name, strlen(clasp->name));
3785 202823 : if (!atom)
3786 0 : return NULL;
3787 :
3788 : /*
3789 : * All instances of the class will inherit properties from the prototype
3790 : * object we are about to create (in DefineConstructorAndPrototype), which
3791 : * in turn will inherit from protoProto.
3792 : *
3793 : * When initializing a standard class (other than Object), if protoProto is
3794 : * null, default to the Object prototype object. The engine's internal uses
3795 : * of js_InitClass depend on this nicety. Note that in
3796 : * js_InitFunctionAndObjectClasses, we specially hack the resolving table
3797 : * and then depend on js_GetClassPrototype here leaving protoProto NULL and
3798 : * returning true.
3799 : */
3800 202823 : JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(clasp);
3801 202823 : if (key != JSProto_Null &&
3802 0 : !protoProto &&
3803 0 : !js_GetClassPrototype(cx, obj, JSProto_Object, &protoProto)) {
3804 0 : return NULL;
3805 : }
3806 :
3807 : return DefineConstructorAndPrototype(cx, obj, key, atom, protoProto, clasp, constructor, nargs,
3808 202823 : ps, fs, static_ps, static_fs, ctorp, ctorKind);
3809 : }
3810 :
3811 : void
3812 5488 : JSObject::initSlotRange(size_t start, const Value *vector, size_t length)
3813 : {
3814 5488 : JSCompartment *comp = compartment();
3815 : HeapSlot *fixedStart, *fixedEnd, *slotsStart, *slotsEnd;
3816 5488 : getSlotRange(start, length, &fixedStart, &fixedEnd, &slotsStart, &slotsEnd);
3817 5488 : for (HeapSlot *sp = fixedStart; sp != fixedEnd; sp++)
3818 0 : sp->init(comp, this, start++, *vector++);
3819 9783 : for (HeapSlot *sp = slotsStart; sp != slotsEnd; sp++)
3820 4295 : sp->init(comp, this, start++, *vector++);
3821 5488 : }
3822 :
3823 : void
3824 195804 : JSObject::copySlotRange(size_t start, const Value *vector, size_t length)
3825 : {
3826 195804 : JSCompartment *comp = compartment();
3827 : HeapSlot *fixedStart, *fixedEnd, *slotsStart, *slotsEnd;
3828 195804 : getSlotRange(start, length, &fixedStart, &fixedEnd, &slotsStart, &slotsEnd);
3829 545256 : for (HeapSlot *sp = fixedStart; sp != fixedEnd; sp++)
3830 349452 : sp->set(comp, this, start++, *vector++);
3831 854730 : for (HeapSlot *sp = slotsStart; sp != slotsEnd; sp++)
3832 658926 : sp->set(comp, this, start++, *vector++);
3833 195804 : }
3834 :
3835 : inline void
3836 2542 : JSObject::invalidateSlotRange(size_t start, size_t length)
3837 : {
3838 : #ifdef DEBUG
3839 2542 : JS_ASSERT(!isDenseArray());
3840 :
3841 : HeapSlot *fixedStart, *fixedEnd, *slotsStart, *slotsEnd;
3842 2542 : getSlotRange(start, length, &fixedStart, &fixedEnd, &slotsStart, &slotsEnd);
3843 2542 : Debug_SetSlotRangeToCrashOnTouch(fixedStart, fixedEnd);
3844 2542 : Debug_SetSlotRangeToCrashOnTouch(slotsStart, slotsEnd);
3845 : #endif /* DEBUG */
3846 2542 : }
3847 :
3848 : inline bool
3849 30645493 : JSObject::updateSlotsForSpan(JSContext *cx, size_t oldSpan, size_t newSpan)
3850 : {
3851 30645493 : JS_ASSERT(oldSpan != newSpan);
3852 :
3853 30645493 : size_t oldCount = dynamicSlotsCount(numFixedSlots(), oldSpan);
3854 30645493 : size_t newCount = dynamicSlotsCount(numFixedSlots(), newSpan);
3855 :
3856 30645493 : if (oldSpan < newSpan) {
3857 30642951 : if (oldCount < newCount && !growSlots(cx, oldCount, newCount))
3858 0 : return false;
3859 :
3860 30642951 : if (newSpan == oldSpan + 1)
3861 27721180 : initSlotUnchecked(oldSpan, UndefinedValue());
3862 : else
3863 2921771 : initializeSlotRange(oldSpan, newSpan - oldSpan);
3864 : } else {
3865 : /* Trigger write barriers on the old slots before reallocating. */
3866 2542 : prepareSlotRangeForOverwrite(newSpan, oldSpan);
3867 2542 : invalidateSlotRange(newSpan, oldSpan - newSpan);
3868 :
3869 2542 : if (oldCount > newCount)
3870 58 : shrinkSlots(cx, oldCount, newCount);
3871 : }
3872 :
3873 30645493 : return true;
3874 : }
3875 :
3876 : bool
3877 41663641 : JSObject::setLastProperty(JSContext *cx, const js::Shape *shape)
3878 : {
3879 41663641 : JS_ASSERT(!inDictionaryMode());
3880 41663641 : JS_ASSERT(!shape->inDictionary());
3881 41663641 : JS_ASSERT(shape->compartment() == compartment());
3882 41663641 : JS_ASSERT(shape->numFixedSlots() == numFixedSlots());
3883 :
3884 41663641 : size_t oldSpan = lastProperty()->slotSpan();
3885 41663641 : size_t newSpan = shape->slotSpan();
3886 :
3887 41663641 : if (oldSpan == newSpan) {
3888 14154063 : shape_ = const_cast<js::Shape *>(shape);
3889 14154063 : return true;
3890 : }
3891 :
3892 27509578 : if (!updateSlotsForSpan(cx, oldSpan, newSpan))
3893 0 : return false;
3894 :
3895 27509578 : shape_ = const_cast<js::Shape *>(shape);
3896 27509578 : return true;
3897 : }
3898 :
3899 : bool
3900 3135915 : JSObject::setSlotSpan(JSContext *cx, uint32_t span)
3901 : {
3902 3135915 : JS_ASSERT(inDictionaryMode());
3903 3135915 : js::BaseShape *base = lastProperty()->base();
3904 :
3905 3135915 : size_t oldSpan = base->slotSpan();
3906 :
3907 3135915 : if (oldSpan == span)
3908 0 : return true;
3909 :
3910 3135915 : if (!updateSlotsForSpan(cx, oldSpan, span))
3911 0 : return false;
3912 :
3913 3135915 : base->setSlotSpan(span);
3914 3135915 : return true;
3915 : }
3916 :
3917 : bool
3918 6002296 : JSObject::growSlots(JSContext *cx, uint32_t oldCount, uint32_t newCount)
3919 : {
3920 6002296 : JS_ASSERT(newCount > oldCount);
3921 6002296 : JS_ASSERT(newCount >= SLOT_CAPACITY_MIN);
3922 6002296 : JS_ASSERT(!isDenseArray());
3923 :
3924 : /*
3925 : * Slots are only allocated for call objects when new properties are
3926 : * added to them, which can only happen while the call is still on the
3927 : * stack (and an eval, DEFFUN, etc. happens). We thus do not need to
3928 : * worry about updating any active outer function args/vars.
3929 : */
3930 6002296 : JS_ASSERT_IF(isCall(), asCall().maybeStackFrame() != NULL);
3931 :
3932 : /*
3933 : * Slot capacities are determined by the span of allocated objects. Due to
3934 : * the limited number of bits to store shape slots, object growth is
3935 : * throttled well before the slot capacity can overflow.
3936 : */
3937 6002296 : JS_ASSERT(newCount < NELEMENTS_LIMIT);
3938 :
3939 6002296 : size_t oldSize = Probes::objectResizeActive() ? computedSizeOfThisSlotsElements() : 0;
3940 6002296 : size_t newSize = oldSize + (newCount - oldCount) * sizeof(Value);
3941 :
3942 : /*
3943 : * If we are allocating slots for an object whose type is always created
3944 : * by calling 'new' on a particular script, bump the GC kind for that
3945 : * type to give these objects a larger number of fixed slots when future
3946 : * objects are constructed.
3947 : */
3948 6002296 : if (!hasLazyType() && !oldCount && type()->newScript) {
3949 22 : gc::AllocKind kind = type()->newScript->allocKind;
3950 22 : unsigned newScriptSlots = gc::GetGCKindSlots(kind);
3951 22 : if (newScriptSlots == numFixedSlots() && gc::TryIncrementAllocKind(&kind)) {
3952 : JSObject *obj = NewReshapedObject(cx, type(), getParent(), kind,
3953 17 : type()->newScript->shape);
3954 17 : if (!obj)
3955 0 : return false;
3956 :
3957 17 : type()->newScript->allocKind = kind;
3958 17 : type()->newScript->shape = obj->lastProperty();
3959 17 : type()->markStateChange(cx);
3960 : }
3961 : }
3962 :
3963 6002296 : if (!oldCount) {
3964 5276477 : slots = (HeapSlot *) cx->malloc_(newCount * sizeof(HeapSlot));
3965 5276477 : if (!slots)
3966 0 : return false;
3967 5276477 : Debug_SetSlotRangeToCrashOnTouch(slots, newCount);
3968 5276477 : if (Probes::objectResizeActive())
3969 0 : Probes::resizeObject(cx, this, oldSize, newSize);
3970 5276477 : return true;
3971 : }
3972 :
3973 : HeapSlot *newslots = (HeapSlot*) cx->realloc_(slots, oldCount * sizeof(HeapSlot),
3974 725819 : newCount * sizeof(HeapSlot));
3975 725819 : if (!newslots)
3976 0 : return false; /* Leave slots at its old size. */
3977 :
3978 725819 : bool changed = slots != newslots;
3979 725819 : slots = newslots;
3980 :
3981 725819 : Debug_SetSlotRangeToCrashOnTouch(slots + oldCount, newCount - oldCount);
3982 :
3983 : /* Changes in the slots of global objects can trigger recompilation. */
3984 725819 : if (changed && isGlobal())
3985 38570 : types::MarkObjectStateChange(cx, this);
3986 :
3987 725819 : if (Probes::objectResizeActive())
3988 0 : Probes::resizeObject(cx, this, oldSize, newSize);
3989 :
3990 725819 : return true;
3991 : }
3992 :
3993 : void
3994 58 : JSObject::shrinkSlots(JSContext *cx, uint32_t oldCount, uint32_t newCount)
3995 : {
3996 58 : JS_ASSERT(newCount < oldCount);
3997 58 : JS_ASSERT(!isDenseArray());
3998 :
3999 : /*
4000 : * Refuse to shrink slots for call objects. This only happens in a very
4001 : * obscure situation (deleting names introduced by a direct 'eval') and
4002 : * allowing the slots pointer to change may require updating pointers in
4003 : * the function's active args/vars information.
4004 : */
4005 58 : if (isCall())
4006 0 : return;
4007 :
4008 58 : size_t oldSize = Probes::objectResizeActive() ? computedSizeOfThisSlotsElements() : 0;
4009 58 : size_t newSize = oldSize - (oldCount - newCount) * sizeof(Value);
4010 :
4011 58 : if (newCount == 0) {
4012 58 : cx->free_(slots);
4013 58 : slots = NULL;
4014 58 : if (Probes::objectResizeActive())
4015 0 : Probes::resizeObject(cx, this, oldSize, newSize);
4016 58 : return;
4017 : }
4018 :
4019 0 : JS_ASSERT(newCount >= SLOT_CAPACITY_MIN);
4020 :
4021 0 : HeapSlot *newslots = (HeapSlot *) cx->realloc_(slots, newCount * sizeof(HeapSlot));
4022 0 : if (!newslots)
4023 0 : return; /* Leave slots at its old size. */
4024 :
4025 0 : bool changed = slots != newslots;
4026 0 : slots = newslots;
4027 :
4028 : /* Watch for changes in global object slots, as for growSlots. */
4029 0 : if (changed && isGlobal())
4030 0 : types::MarkObjectStateChange(cx, this);
4031 :
4032 0 : if (Probes::objectResizeActive())
4033 0 : Probes::resizeObject(cx, this, oldSize, newSize);
4034 : }
4035 :
4036 : bool
4037 304451 : JSObject::growElements(JSContext *cx, unsigned newcap)
4038 : {
4039 304451 : JS_ASSERT(isDenseArray());
4040 :
4041 : /*
4042 : * When an object with CAPACITY_DOUBLING_MAX or fewer elements needs to
4043 : * grow, double its capacity, to add N elements in amortized O(N) time.
4044 : *
4045 : * Above this limit, grow by 12.5% each time. Speed is still amortized
4046 : * O(N), with a higher constant factor, and we waste less space.
4047 : */
4048 : static const size_t CAPACITY_DOUBLING_MAX = 1024 * 1024;
4049 : static const size_t CAPACITY_CHUNK = CAPACITY_DOUBLING_MAX / sizeof(Value);
4050 :
4051 304451 : uint32_t oldcap = getDenseArrayCapacity();
4052 304451 : JS_ASSERT(oldcap <= newcap);
4053 :
4054 304451 : size_t oldSize = Probes::objectResizeActive() ? computedSizeOfThisSlotsElements() : 0;
4055 :
4056 : uint32_t nextsize = (oldcap <= CAPACITY_DOUBLING_MAX)
4057 : ? oldcap * 2
4058 304451 : : oldcap + (oldcap >> 3);
4059 :
4060 304451 : uint32_t actualCapacity = JS_MAX(newcap, nextsize);
4061 304451 : if (actualCapacity >= CAPACITY_CHUNK)
4062 39 : actualCapacity = JS_ROUNDUP(actualCapacity, CAPACITY_CHUNK);
4063 304412 : else if (actualCapacity < SLOT_CAPACITY_MIN)
4064 8288 : actualCapacity = SLOT_CAPACITY_MIN;
4065 :
4066 : /* Don't let nelements get close to wrapping around uint32_t. */
4067 304451 : if (actualCapacity >= NELEMENTS_LIMIT || actualCapacity < oldcap || actualCapacity < newcap) {
4068 0 : JS_ReportOutOfMemory(cx);
4069 0 : return false;
4070 : }
4071 :
4072 304451 : uint32_t initlen = getDenseArrayInitializedLength();
4073 304451 : uint32_t newAllocated = actualCapacity + ObjectElements::VALUES_PER_HEADER;
4074 :
4075 : ObjectElements *newheader;
4076 304451 : if (hasDynamicElements()) {
4077 101967 : uint32_t oldAllocated = oldcap + ObjectElements::VALUES_PER_HEADER;
4078 : newheader = (ObjectElements *)
4079 101967 : cx->realloc_(getElementsHeader(), oldAllocated * sizeof(Value),
4080 203934 : newAllocated * sizeof(Value));
4081 101967 : if (!newheader)
4082 0 : return false; /* Leave elements as its old size. */
4083 : } else {
4084 202484 : newheader = (ObjectElements *) cx->malloc_(newAllocated * sizeof(Value));
4085 202484 : if (!newheader)
4086 0 : return false; /* Ditto. */
4087 202484 : js_memcpy(newheader, getElementsHeader(),
4088 404968 : (ObjectElements::VALUES_PER_HEADER + initlen) * sizeof(Value));
4089 : }
4090 :
4091 304451 : newheader->capacity = actualCapacity;
4092 304451 : elements = newheader->elements();
4093 :
4094 304451 : Debug_SetSlotRangeToCrashOnTouch(elements + initlen, actualCapacity - initlen);
4095 :
4096 304451 : if (Probes::objectResizeActive())
4097 0 : Probes::resizeObject(cx, this, oldSize, computedSizeOfThisSlotsElements());
4098 :
4099 304451 : return true;
4100 : }
4101 :
4102 : void
4103 53698 : JSObject::shrinkElements(JSContext *cx, unsigned newcap)
4104 : {
4105 53698 : JS_ASSERT(isDenseArray());
4106 :
4107 53698 : uint32_t oldcap = getDenseArrayCapacity();
4108 53698 : JS_ASSERT(newcap <= oldcap);
4109 :
4110 53698 : size_t oldSize = Probes::objectResizeActive() ? computedSizeOfThisSlotsElements() : 0;
4111 :
4112 : /* Don't shrink elements below the minimum capacity. */
4113 53698 : if (oldcap <= SLOT_CAPACITY_MIN || !hasDynamicElements())
4114 11281 : return;
4115 :
4116 42417 : newcap = Max(newcap, SLOT_CAPACITY_MIN);
4117 :
4118 42417 : uint32_t newAllocated = newcap + ObjectElements::VALUES_PER_HEADER;
4119 :
4120 : ObjectElements *newheader = (ObjectElements *)
4121 42417 : cx->realloc_(getElementsHeader(), newAllocated * sizeof(Value));
4122 42417 : if (!newheader)
4123 0 : return; /* Leave elements at its old size. */
4124 :
4125 42417 : newheader->capacity = newcap;
4126 42417 : elements = newheader->elements();
4127 :
4128 42417 : if (Probes::objectResizeActive())
4129 0 : Probes::resizeObject(cx, this, oldSize, computedSizeOfThisSlotsElements());
4130 : }
4131 :
4132 : #ifdef DEBUG
4133 : bool
4134 742308194 : JSObject::slotInRange(unsigned slot, SentinelAllowed sentinel) const
4135 : {
4136 742308194 : size_t capacity = numFixedSlots() + numDynamicSlots();
4137 742308193 : if (sentinel == SENTINEL_ALLOWED)
4138 190432606 : return slot <= capacity;
4139 551875587 : return slot < capacity;
4140 : }
4141 : #endif /* DEBUG */
4142 :
4143 : static JSObject *
4144 0 : js_InitNullClass(JSContext *cx, JSObject *obj)
4145 : {
4146 0 : JS_ASSERT(0);
4147 0 : return NULL;
4148 : }
4149 :
4150 : #define JS_PROTO(name,code,init) extern JSObject *init(JSContext *, JSObject *);
4151 : #include "jsproto.tbl"
4152 : #undef JS_PROTO
4153 :
4154 : static JSObjectOp lazy_prototype_init[JSProto_LIMIT] = {
4155 : #define JS_PROTO(name,code,init) init,
4156 : #include "jsproto.tbl"
4157 : #undef JS_PROTO
4158 : };
4159 :
4160 : namespace js {
4161 :
4162 : bool
4163 51196 : SetProto(JSContext *cx, JSObject *obj, JSObject *proto, bool checkForCycles)
4164 : {
4165 51196 : JS_ASSERT_IF(!checkForCycles, obj != proto);
4166 51196 : JS_ASSERT(obj->isExtensible());
4167 :
4168 51196 : if (proto && proto->isXML()) {
4169 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_XML_PROTO_FORBIDDEN);
4170 0 : return false;
4171 : }
4172 :
4173 : /*
4174 : * Regenerate shapes for all of the scopes along the old prototype chain,
4175 : * in case any entries were filled by looking up through obj. Stop when a
4176 : * non-native object is found, prototype lookups will not be cached across
4177 : * these.
4178 : *
4179 : * How this shape change is done is very delicate; the change can be made
4180 : * either by marking the object's prototype as uncacheable (such that the
4181 : * property cache and JIT'ed ICs cannot assume the shape determines the
4182 : * prototype) or by just generating a new shape for the object. Choosing
4183 : * the former is bad if the object is on the prototype chain of other
4184 : * objects, as the uncacheable prototype can inhibit iterator caches on
4185 : * those objects and slow down prototype accesses. Choosing the latter is
4186 : * bad if there are many similar objects to this one which will have their
4187 : * prototype mutated, as the generateOwnShape forces the object into
4188 : * dictionary mode and similar property lineages will be repeatedly cloned.
4189 : *
4190 : * :XXX: bug 707717 make this code less brittle.
4191 : */
4192 51196 : JSObject *oldproto = obj;
4193 227960 : while (oldproto && oldproto->isNative()) {
4194 125568 : if (oldproto->hasSingletonType()) {
4195 38485 : if (!oldproto->generateOwnShape(cx))
4196 0 : return false;
4197 : } else {
4198 87083 : if (!oldproto->setUncacheableProto(cx))
4199 0 : return false;
4200 : }
4201 125568 : oldproto = oldproto->getProto();
4202 : }
4203 :
4204 51196 : if (checkForCycles) {
4205 48378 : for (JSObject *obj2 = proto; obj2; obj2 = obj2->getProto()) {
4206 33497 : if (obj2 == obj) {
4207 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CYCLIC_VALUE,
4208 0 : js_proto_str);
4209 0 : return false;
4210 : }
4211 : }
4212 : }
4213 :
4214 51196 : if (obj->hasSingletonType()) {
4215 : /*
4216 : * Just splice the prototype, but mark the properties as unknown for
4217 : * consistent behavior.
4218 : */
4219 12850 : if (!obj->splicePrototype(cx, proto))
4220 0 : return false;
4221 12850 : MarkTypeObjectUnknownProperties(cx, obj->type());
4222 12850 : return true;
4223 : }
4224 :
4225 38346 : if (proto && !proto->setNewTypeUnknown(cx))
4226 0 : return false;
4227 :
4228 : TypeObject *type = proto
4229 : ? proto->getNewType(cx, NULL)
4230 38346 : : cx->compartment->getEmptyType(cx);
4231 38346 : if (!type)
4232 0 : return false;
4233 :
4234 : /*
4235 : * Setting __proto__ on an object that has escaped and may be referenced by
4236 : * other heap objects can only be done if the properties of both objects
4237 : * are unknown. Type sets containing this object will contain the original
4238 : * type but not the new type of the object, so we need to go and scan the
4239 : * entire compartment for type sets which have these objects and mark them
4240 : * as containing generic objects.
4241 : */
4242 38346 : MarkTypeObjectUnknownProperties(cx, obj->type(), true);
4243 38346 : MarkTypeObjectUnknownProperties(cx, type, true);
4244 :
4245 38346 : obj->setType(type);
4246 38346 : return true;
4247 : }
4248 :
4249 : }
4250 :
4251 : JSBool
4252 104249 : js_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key,
4253 : JSObject **objp)
4254 : {
4255 208498 : RootObject objRoot(cx, &obj);
4256 :
4257 104249 : obj = &obj->global();
4258 104249 : if (!obj->isGlobal()) {
4259 0 : *objp = NULL;
4260 0 : return true;
4261 : }
4262 :
4263 104249 : Value v = obj->getReservedSlot(key);
4264 104249 : if (v.isObject()) {
4265 17538 : *objp = &v.toObject();
4266 17538 : return true;
4267 : }
4268 :
4269 173422 : AutoResolving resolving(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.classAtoms[key]));
4270 86711 : if (resolving.alreadyStarted()) {
4271 : /* Already caching id in obj -- suppress recursion. */
4272 10397 : *objp = NULL;
4273 10397 : return true;
4274 : }
4275 :
4276 76314 : JSObject *cobj = NULL;
4277 76314 : if (JSObjectOp init = lazy_prototype_init[key]) {
4278 76314 : if (!init(cx, obj))
4279 0 : return false;
4280 76314 : v = obj->getReservedSlot(key);
4281 76314 : if (v.isObject())
4282 74382 : cobj = &v.toObject();
4283 : }
4284 :
4285 76314 : *objp = cobj;
4286 76314 : return true;
4287 : }
4288 :
4289 : JSBool
4290 362065 : js_FindClassObject(JSContext *cx, JSObject *start, JSProtoKey protoKey,
4291 : Value *vp, Class *clasp)
4292 : {
4293 : JSObject *cobj, *pobj;
4294 : jsid id;
4295 : JSProperty *prop;
4296 : const Shape *shape;
4297 :
4298 724130 : RootedVarObject obj(cx);
4299 :
4300 362065 : if (start) {
4301 341925 : obj = &start->global();
4302 341925 : OBJ_TO_INNER_OBJECT(cx, *obj.address());
4303 : } else {
4304 20140 : obj = GetGlobalForScopeChain(cx);
4305 : }
4306 362065 : if (!obj)
4307 0 : return false;
4308 :
4309 362065 : if (protoKey != JSProto_Null) {
4310 96603 : JS_ASSERT(JSProto_Null < protoKey);
4311 96603 : JS_ASSERT(protoKey < JSProto_LIMIT);
4312 96603 : if (!js_GetClassObject(cx, obj, protoKey, &cobj))
4313 0 : return false;
4314 96603 : if (cobj) {
4315 88054 : vp->setObject(*cobj);
4316 88054 : return JS_TRUE;
4317 : }
4318 8549 : id = ATOM_TO_JSID(cx->runtime->atomState.classAtoms[protoKey]);
4319 : } else {
4320 265462 : JSAtom *atom = js_Atomize(cx, clasp->name, strlen(clasp->name));
4321 265462 : if (!atom)
4322 0 : return false;
4323 265462 : id = ATOM_TO_JSID(atom);
4324 : }
4325 :
4326 274011 : JS_ASSERT(obj->isNative());
4327 274011 : if (!LookupPropertyWithFlags(cx, obj, id, JSRESOLVE_CLASSNAME, &pobj, &prop))
4328 0 : return false;
4329 274011 : Value v = UndefinedValue();
4330 274011 : if (prop && pobj->isNative()) {
4331 3168 : shape = (Shape *) prop;
4332 3168 : if (shape->hasSlot()) {
4333 3168 : v = pobj->nativeGetSlot(shape->slot());
4334 3168 : if (v.isPrimitive())
4335 0 : v.setUndefined();
4336 : }
4337 : }
4338 274011 : *vp = v;
4339 274011 : return true;
4340 : }
4341 :
4342 : bool
4343 25306741 : JSObject::allocSlot(JSContext *cx, uint32_t *slotp)
4344 : {
4345 25306741 : uint32_t slot = slotSpan();
4346 25306741 : JS_ASSERT(slot >= JSSLOT_FREE(getClass()));
4347 :
4348 : /*
4349 : * If this object is in dictionary mode, try to pull a free slot from the
4350 : * property table's slot-number freelist.
4351 : */
4352 25306741 : if (inDictionaryMode()) {
4353 2073970 : PropertyTable &table = lastProperty()->table();
4354 2073970 : uint32_t last = table.freelist;
4355 2073970 : if (last != SHAPE_INVALID_SLOT) {
4356 : #ifdef DEBUG
4357 11331 : JS_ASSERT(last < slot);
4358 11331 : uint32_t next = getSlot(last).toPrivateUint32();
4359 11331 : JS_ASSERT_IF(next != SHAPE_INVALID_SLOT, next < slot);
4360 : #endif
4361 :
4362 11331 : *slotp = last;
4363 :
4364 11331 : const Value &vref = getSlot(last);
4365 11331 : table.freelist = vref.toPrivateUint32();
4366 11331 : setSlot(last, UndefinedValue());
4367 11331 : return true;
4368 : }
4369 : }
4370 :
4371 25295410 : if (slot >= SHAPE_MAXIMUM_SLOT) {
4372 0 : js_ReportOutOfMemory(cx);
4373 0 : return false;
4374 : }
4375 :
4376 25295410 : *slotp = slot;
4377 :
4378 25295410 : if (inDictionaryMode() && !setSlotSpan(cx, slot + 1))
4379 0 : return false;
4380 :
4381 25295410 : return true;
4382 : }
4383 :
4384 : void
4385 448137 : JSObject::freeSlot(JSContext *cx, uint32_t slot)
4386 : {
4387 448137 : JS_ASSERT(slot < slotSpan());
4388 :
4389 448137 : if (inDictionaryMode()) {
4390 445694 : uint32_t &last = lastProperty()->table().freelist;
4391 :
4392 : /* Can't afford to check the whole freelist, but let's check the head. */
4393 445694 : JS_ASSERT_IF(last != SHAPE_INVALID_SLOT, last < slotSpan() && last != slot);
4394 :
4395 : /*
4396 : * Place all freed slots other than reserved slots (bug 595230) on the
4397 : * dictionary's free list.
4398 : */
4399 445694 : if (JSSLOT_FREE(getClass()) <= slot) {
4400 388042 : JS_ASSERT_IF(last != SHAPE_INVALID_SLOT, last < slotSpan());
4401 388042 : setSlot(slot, PrivateUint32Value(last));
4402 388042 : last = slot;
4403 388042 : return;
4404 : }
4405 : }
4406 60095 : setSlot(slot, UndefinedValue());
4407 : }
4408 :
4409 : static bool
4410 1135217 : PurgeProtoChain(JSContext *cx, JSObject *obj, jsid id)
4411 : {
4412 : const Shape *shape;
4413 :
4414 2270434 : RootObject objRoot(cx, &obj);
4415 2270434 : RootId idRoot(cx, &id);
4416 :
4417 2736785 : while (obj) {
4418 582343 : if (!obj->isNative()) {
4419 20 : obj = obj->getProto();
4420 20 : continue;
4421 : }
4422 582323 : shape = obj->nativeLookup(cx, id);
4423 582323 : if (shape) {
4424 115992 : if (!obj->shadowingShapeChange(cx, *shape))
4425 0 : return false;
4426 :
4427 115992 : obj->shadowingShapeChange(cx, *shape);
4428 115992 : return true;
4429 : }
4430 466331 : obj = obj->getProto();
4431 : }
4432 :
4433 1019225 : return true;
4434 : }
4435 :
4436 : bool
4437 1135076 : js_PurgeScopeChainHelper(JSContext *cx, JSObject *obj, jsid id)
4438 : {
4439 2270152 : RootObject objRoot(cx, &obj);
4440 2270152 : RootId idRoot(cx, &id);
4441 :
4442 1135076 : JS_ASSERT(obj->isDelegate());
4443 1135076 : PurgeProtoChain(cx, obj->getProto(), id);
4444 :
4445 : /*
4446 : * We must purge the scope chain only for Call objects as they are the only
4447 : * kind of cacheable non-global object that can gain properties after outer
4448 : * properties with the same names have been cached or traced. Call objects
4449 : * may gain such properties via eval introducing new vars; see bug 490364.
4450 : */
4451 1135076 : if (obj->isCall()) {
4452 335 : while ((obj = obj->enclosingScope()) != NULL) {
4453 141 : if (!PurgeProtoChain(cx, obj, id))
4454 0 : return false;
4455 : }
4456 : }
4457 :
4458 1135076 : return true;
4459 : }
4460 :
4461 : Shape *
4462 18 : js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id,
4463 : PropertyOp getter, StrictPropertyOp setter, uint32_t slot,
4464 : unsigned attrs, unsigned flags, int shortid)
4465 : {
4466 18 : JS_ASSERT(!(flags & Shape::METHOD));
4467 :
4468 : /* Convert string indices to integers if appropriate. */
4469 18 : id = js_CheckForStringIndex(id);
4470 :
4471 : /*
4472 : * Purge the property cache of now-shadowed id in obj's scope chain. Do
4473 : * this optimistically (assuming no failure below) before locking obj, so
4474 : * we can lock the shadowed scope.
4475 : */
4476 18 : if (!js_PurgeScopeChain(cx, obj, id))
4477 0 : return NULL;
4478 :
4479 18 : return obj->putProperty(cx, id, getter, setter, slot, attrs, flags, shortid);
4480 : }
4481 :
4482 : Shape *
4483 40625 : js_ChangeNativePropertyAttrs(JSContext *cx, JSObject *obj,
4484 : Shape *shape, unsigned attrs, unsigned mask,
4485 : PropertyOp getter, StrictPropertyOp setter)
4486 : {
4487 : /*
4488 : * Check for freezing an object with shape-memoized methods here, on a
4489 : * shape-by-shape basis.
4490 : */
4491 40625 : if ((attrs & JSPROP_READONLY) && shape->isMethod()) {
4492 0 : Value v = ObjectValue(*obj->nativeGetMethod(shape));
4493 :
4494 0 : shape = obj->methodReadBarrier(cx, *shape, &v);
4495 0 : if (!shape)
4496 0 : return NULL;
4497 : }
4498 :
4499 40625 : return obj->changeProperty(cx, shape, attrs, mask, getter, setter);
4500 : }
4501 :
4502 : JSBool
4503 19102925 : js_DefineProperty(JSContext *cx, JSObject *obj, jsid id, const Value *value,
4504 : PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
4505 : {
4506 19102925 : return !!DefineNativeProperty(cx, obj, id, *value, getter, setter, attrs, 0, 0);
4507 : }
4508 :
4509 : JSBool
4510 1607268 : js_DefineElement(JSContext *cx, JSObject *obj, uint32_t index, const Value *value,
4511 : PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
4512 : {
4513 : jsid id;
4514 1607268 : if (!IndexToId(cx, index, &id))
4515 0 : return false;
4516 1607268 : return !!DefineNativeProperty(cx, obj, id, *value, getter, setter, attrs, 0, 0);
4517 : }
4518 :
4519 : /*
4520 : * Backward compatibility requires allowing addProperty hooks to mutate the
4521 : * nominal initial value of a slotful property, while GC safety wants that
4522 : * value to be stored before the call-out through the hook. Optimize to do
4523 : * both while saving cycles for classes that stub their addProperty hook.
4524 : */
4525 : static inline bool
4526 40462300 : CallAddPropertyHook(JSContext *cx, Class *clasp, JSObject *obj, const Shape *shape, Value *vp)
4527 : {
4528 40462300 : if (clasp->addProperty != JS_PropertyStub) {
4529 4570579 : Value nominal = *vp;
4530 :
4531 4570579 : if (!CallJSPropertyOp(cx, clasp->addProperty, obj, shape->propid(), vp))
4532 2 : return false;
4533 4570577 : if (*vp != nominal) {
4534 0 : if (shape->hasSlot())
4535 0 : obj->nativeSetSlotWithType(cx, shape, *vp);
4536 : }
4537 : }
4538 40462298 : return true;
4539 : }
4540 :
4541 : namespace js {
4542 :
4543 : const Shape *
4544 36788638 : DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, const Value &value_,
4545 : PropertyOp getter, StrictPropertyOp setter, unsigned attrs,
4546 : unsigned flags, int shortid, unsigned defineHow /* = 0 */)
4547 : {
4548 0 : JS_ASSERT((defineHow & ~(DNP_CACHE_RESULT | DNP_DONT_PURGE |
4549 36788638 : DNP_SET_METHOD | DNP_SKIP_TYPE)) == 0);
4550 :
4551 73577276 : RootObject objRoot(cx, &obj);
4552 73577276 : RootId idRoot(cx, &id);
4553 :
4554 : /*
4555 : * Make a local copy of value, in case a method barrier needs to update the
4556 : * value to define, and just so addProperty can mutate its inout parameter.
4557 : */
4558 73577276 : RootedVarValue value(cx);
4559 36788638 : value = value_;
4560 :
4561 : /* Convert string indices to integers if appropriate. */
4562 36788638 : id = js_CheckForStringIndex(id);
4563 :
4564 : /*
4565 : * If defining a getter or setter, we must check for its counterpart and
4566 : * update the attributes and property ops. A getter or setter is really
4567 : * only half of a property.
4568 : */
4569 36788638 : Shape *shape = NULL;
4570 36788638 : if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
4571 : JSObject *pobj;
4572 : JSProperty *prop;
4573 :
4574 : /* Type information for getter/setter properties is unknown. */
4575 2425130 : AddTypePropertyId(cx, obj, id, types::Type::UnknownType());
4576 2425130 : MarkTypePropertyConfigured(cx, obj, id);
4577 :
4578 : /*
4579 : * If we are defining a getter whose setter was already defined, or
4580 : * vice versa, finish the job via obj->changeProperty, and refresh the
4581 : * property cache line for (obj, id) to map shape.
4582 : */
4583 2425130 : if (!js_LookupProperty(cx, obj, id, &pobj, &prop))
4584 0 : return NULL;
4585 2425130 : if (prop && pobj == obj) {
4586 30010 : shape = (Shape *) prop;
4587 30010 : if (shape->isAccessorDescriptor()) {
4588 : shape = obj->changeProperty(cx, shape, attrs,
4589 : JSPROP_GETTER | JSPROP_SETTER,
4590 : (attrs & JSPROP_GETTER)
4591 : ? getter
4592 : : shape->getter(),
4593 : (attrs & JSPROP_SETTER)
4594 : ? setter
4595 29662 : : shape->setter());
4596 29662 : if (!shape)
4597 0 : return NULL;
4598 : } else {
4599 348 : shape = NULL;
4600 : }
4601 : }
4602 : }
4603 :
4604 : /*
4605 : * Purge the property cache of any properties named by id that are about
4606 : * to be shadowed in obj's scope chain unless it is known a priori that it
4607 : * is not possible. We do this before locking obj to avoid nesting locks.
4608 : */
4609 36788638 : if (!(defineHow & DNP_DONT_PURGE)) {
4610 36787765 : if (!js_PurgeScopeChain(cx, obj, id))
4611 0 : return NULL;
4612 : }
4613 :
4614 : /* Use the object's class getter and setter by default. */
4615 36788638 : Class *clasp = obj->getClass();
4616 36788638 : if (!(defineHow & DNP_SET_METHOD)) {
4617 36788638 : if (!getter && !(attrs & JSPROP_GETTER))
4618 6371213 : getter = clasp->getProperty;
4619 36788638 : if (!setter && !(attrs & JSPROP_SETTER))
4620 17675300 : setter = clasp->setProperty;
4621 : }
4622 :
4623 60001582 : if (((defineHow & DNP_SET_METHOD) || getter == JS_PropertyStub) &&
4624 23212944 : !(defineHow & DNP_SKIP_TYPE)) {
4625 : /*
4626 : * Type information for normal native properties should reflect the
4627 : * initial value of the property.
4628 : */
4629 23142974 : AddTypePropertyId(cx, obj, id, value);
4630 23142974 : if (attrs & JSPROP_READONLY)
4631 9342632 : MarkTypePropertyConfigured(cx, obj, id);
4632 : }
4633 :
4634 36788638 : if (!shape) {
4635 : /* Add a new property, or replace an existing one of the same id. */
4636 36758976 : if (defineHow & DNP_SET_METHOD) {
4637 0 : JS_ASSERT(clasp == &ObjectClass);
4638 0 : JS_ASSERT(IsFunctionObject(value));
4639 0 : JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
4640 0 : JS_ASSERT(!getter && !setter);
4641 :
4642 0 : JSObject *funobj = &value.raw().toObject();
4643 0 : if (!funobj->toFunction()->isClonedMethod())
4644 0 : flags |= Shape::METHOD;
4645 : }
4646 :
4647 36758976 : if (const Shape *existingShape = obj->nativeLookup(cx, id)) {
4648 2339149 : if (existingShape->isMethod() &&
4649 0 : ObjectValue(*obj->nativeGetMethod(existingShape)) == value)
4650 : {
4651 : /*
4652 : * Redefining an existing shape-memoized method object without
4653 : * changing the property's value, perhaps to change attributes.
4654 : * Clone now via the method read barrier.
4655 : */
4656 0 : if (!obj->methodReadBarrier(cx, *existingShape, value.address()))
4657 0 : return NULL;
4658 : }
4659 : }
4660 :
4661 : shape = obj->putProperty(cx, id, getter, setter, SHAPE_INVALID_SLOT,
4662 36758976 : attrs, flags, shortid);
4663 36758976 : if (!shape)
4664 0 : return NULL;
4665 : }
4666 :
4667 : /* Store valueCopy before calling addProperty, in case the latter GC's. */
4668 36788638 : if (shape->hasSlot())
4669 23949944 : obj->nativeSetSlot(shape->slot(), value);
4670 :
4671 36788638 : if (!CallAddPropertyHook(cx, clasp, obj, shape, value.address())) {
4672 0 : obj->removeProperty(cx, id);
4673 0 : return NULL;
4674 : }
4675 :
4676 36788638 : return shape;
4677 : }
4678 :
4679 : } /* namespace js */
4680 :
4681 : /*
4682 : * Call obj's resolve hook.
4683 : *
4684 : * cx, start, id, and flags are the parameters initially passed to the ongoing
4685 : * lookup; objp and propp are its out parameters. obj is an object along
4686 : * start's prototype chain.
4687 : *
4688 : * There are four possible outcomes:
4689 : *
4690 : * - On failure, report an error or exception and return false.
4691 : *
4692 : * - If we are already resolving a property of *curobjp, set *recursedp = true,
4693 : * and return true.
4694 : *
4695 : * - If the resolve hook finds or defines the sought property, set *objp and
4696 : * *propp appropriately, set *recursedp = false, and return true.
4697 : *
4698 : * - Otherwise no property was resolved. Set *propp = NULL and *recursedp = false
4699 : * and return true.
4700 : */
4701 : static JSBool
4702 9911440 : CallResolveOp(JSContext *cx, JSObject *start, HandleObject obj, HandleId id, unsigned flags,
4703 : JSObject **objp, JSProperty **propp, bool *recursedp)
4704 : {
4705 9911440 : Class *clasp = obj->getClass();
4706 9911440 : JSResolveOp resolve = clasp->resolve;
4707 :
4708 : /*
4709 : * Avoid recursion on (obj, id) already being resolved on cx.
4710 : *
4711 : * Once we have successfully added an entry for (obj, key) to
4712 : * cx->resolvingTable, control must go through cleanup: before
4713 : * returning. But note that JS_DHASH_ADD may find an existing
4714 : * entry, in which case we bail to suppress runaway recursion.
4715 : */
4716 19822880 : AutoResolving resolving(cx, obj, id);
4717 9911440 : if (resolving.alreadyStarted()) {
4718 : /* Already resolving id in obj -- suppress recursion. */
4719 1029648 : *recursedp = true;
4720 1029648 : return true;
4721 : }
4722 8881792 : *recursedp = false;
4723 :
4724 8881792 : *propp = NULL;
4725 :
4726 8881792 : if (clasp->flags & JSCLASS_NEW_RESOLVE) {
4727 6066714 : JSNewResolveOp newresolve = reinterpret_cast<JSNewResolveOp>(resolve);
4728 6066714 : if (flags == RESOLVE_INFER)
4729 3369173 : flags = js_InferFlags(cx, 0);
4730 :
4731 12133428 : RootedVarObject obj2(cx);
4732 6066714 : obj2 = (clasp->flags & JSCLASS_NEW_RESOLVE_GETS_START) ? start : NULL;
4733 6066714 : if (!newresolve(cx, obj, id, flags, obj2.address()))
4734 0 : return false;
4735 :
4736 : /*
4737 : * We trust the new style resolve hook to set obj2 to NULL when
4738 : * the id cannot be resolved. But, when obj2 is not null, we do
4739 : * not assume that id must exist and do full nativeLookup for
4740 : * compatibility.
4741 : */
4742 6066714 : if (!obj2)
4743 4750393 : return true;
4744 :
4745 1316321 : if (!obj2->isNative()) {
4746 : /* Whoops, newresolve handed back a foreign obj2. */
4747 0 : JS_ASSERT(obj2 != obj);
4748 0 : return obj2->lookupGeneric(cx, id, objp, propp);
4749 : }
4750 7383035 : obj = obj2;
4751 : } else {
4752 2815078 : if (!resolve(cx, obj, id))
4753 0 : return false;
4754 : }
4755 :
4756 4131399 : if (!obj->nativeEmpty()) {
4757 3127957 : if (const Shape *shape = obj->nativeLookup(cx, id)) {
4758 3010254 : *objp = obj;
4759 3010254 : *propp = (JSProperty *) shape;
4760 : }
4761 : }
4762 :
4763 4131399 : return true;
4764 : }
4765 :
4766 : static JS_ALWAYS_INLINE bool
4767 92695624 : LookupPropertyWithFlagsInline(JSContext *cx, JSObject *obj, jsid id, unsigned flags,
4768 : JSObject **objp, JSProperty **propp)
4769 : {
4770 : /* We should not get string indices which aren't already integers here. */
4771 92695624 : JS_ASSERT(id == js_CheckForStringIndex(id));
4772 :
4773 185391248 : RootObject objRoot(cx, &obj);
4774 185391248 : RootId idRoot(cx, &id);
4775 :
4776 : /* Search scopes starting with obj and following the prototype link. */
4777 92695624 : JSObject *start = obj;
4778 21619730 : while (true) {
4779 114315354 : const Shape *shape = obj->nativeLookup(cx, id);
4780 114315354 : if (shape) {
4781 74615018 : *objp = obj;
4782 74615018 : *propp = (JSProperty *) shape;
4783 74615018 : return true;
4784 : }
4785 :
4786 : /* Try obj's class resolve hook if id was not found in obj's scope. */
4787 39700336 : if (obj->getClass()->resolve != JS_ResolveStub) {
4788 : bool recursed;
4789 9911440 : if (!CallResolveOp(cx, start, objRoot, idRoot, flags, objp, propp, &recursed))
4790 0 : return false;
4791 9911440 : if (recursed)
4792 1029648 : break;
4793 8881792 : if (*propp) {
4794 : /*
4795 : * For stats we do not recalculate protoIndex even if it was
4796 : * resolved on some other object.
4797 : */
4798 3010254 : return true;
4799 : }
4800 : }
4801 :
4802 35660434 : JSObject *proto = obj->getProto();
4803 35660434 : if (!proto)
4804 14039970 : break;
4805 21620464 : if (!proto->isNative()) {
4806 734 : if (!proto->lookupGeneric(cx, id, objp, propp))
4807 0 : return false;
4808 : #ifdef DEBUG
4809 : /*
4810 : * Non-native objects must have either non-native lookup results,
4811 : * or else native results from the non-native's prototype chain.
4812 : *
4813 : * See StackFrame::getValidCalleeObject, where we depend on this
4814 : * fact to force a prototype-delegated joined method accessed via
4815 : * arguments.callee through the delegating |this| object's method
4816 : * read barrier.
4817 : */
4818 734 : if (*propp && (*objp)->isNative()) {
4819 194 : while ((proto = proto->getProto()) != *objp)
4820 20 : JS_ASSERT(proto);
4821 : }
4822 : #endif
4823 734 : return true;
4824 : }
4825 :
4826 21619730 : obj = proto;
4827 : }
4828 :
4829 15069618 : *objp = NULL;
4830 15069618 : *propp = NULL;
4831 15069618 : return true;
4832 : }
4833 :
4834 : JS_FRIEND_API(JSBool)
4835 11596912 : js_LookupProperty(JSContext *cx, JSObject *obj, jsid id, JSObject **objp,
4836 : JSProperty **propp)
4837 : {
4838 : /* Convert string indices to integers if appropriate. */
4839 11596912 : id = js_CheckForStringIndex(id);
4840 :
4841 11596912 : return LookupPropertyWithFlagsInline(cx, obj, id, cx->resolveFlags, objp, propp);
4842 : }
4843 :
4844 : JS_FRIEND_API(JSBool)
4845 0 : js_LookupElement(JSContext *cx, JSObject *obj, uint32_t index, JSObject **objp, JSProperty **propp)
4846 : {
4847 : jsid id;
4848 0 : if (!IndexToId(cx, index, &id))
4849 0 : return false;
4850 :
4851 0 : return LookupPropertyWithFlagsInline(cx, obj, id, cx->resolveFlags, objp, propp);
4852 : }
4853 :
4854 : bool
4855 31908484 : js::LookupPropertyWithFlags(JSContext *cx, JSObject *obj, jsid id, unsigned flags,
4856 : JSObject **objp, JSProperty **propp)
4857 : {
4858 : /* Convert string indices to integers if appropriate. */
4859 31908484 : id = js_CheckForStringIndex(id);
4860 :
4861 31908484 : return LookupPropertyWithFlagsInline(cx, obj, id, flags, objp, propp);
4862 : }
4863 :
4864 : bool
4865 11074255 : js::FindPropertyHelper(JSContext *cx, PropertyName *name, bool cacheResult, JSObject *scopeChain,
4866 : JSObject **objp, JSObject **pobjp, JSProperty **propp)
4867 : {
4868 11074255 : jsid id = ATOM_TO_JSID(name);
4869 : JSObject *obj, *parent, *pobj;
4870 : int scopeIndex;
4871 : JSProperty *prop;
4872 :
4873 : /* Scan entries on the scope chain that we can cache across. */
4874 11074255 : obj = scopeChain;
4875 11074255 : parent = obj->enclosingScope();
4876 23731338 : for (scopeIndex = 0;
4877 : parent
4878 : ? IsCacheableNonGlobalScope(obj)
4879 10636498 : : !obj->getOps()->lookupProperty;
4880 : ++scopeIndex) {
4881 13042870 : if (!LookupPropertyWithFlags(cx, obj, id, cx->resolveFlags, &pobj, &prop))
4882 0 : return false;
4883 :
4884 13042870 : if (prop) {
4885 : #ifdef DEBUG
4886 10867956 : if (parent) {
4887 385787 : JS_ASSERT(pobj->isNative());
4888 385787 : JS_ASSERT(pobj->getClass() == obj->getClass());
4889 385787 : if (obj->isBlock()) {
4890 : /*
4891 : * A block instance on the scope chain is immutable and
4892 : * shares its shape with the compile-time prototype. Thus
4893 : * we cannot find any property on the prototype.
4894 : */
4895 28508 : JS_ASSERT(pobj->isClonedBlock());
4896 : } else {
4897 : /* Call and DeclEnvClass objects have no prototypes. */
4898 357279 : JS_ASSERT(!obj->getProto());
4899 : }
4900 385787 : JS_ASSERT(pobj == obj);
4901 : } else {
4902 10482169 : JS_ASSERT(obj->isNative());
4903 : }
4904 : #endif
4905 :
4906 : /*
4907 : * We must check if pobj is native as a global object can have
4908 : * non-native prototype.
4909 : */
4910 10867956 : if (cacheResult && pobj->isNative()) {
4911 : JS_PROPERTY_CACHE(cx).fill(cx, scopeChain, scopeIndex, pobj,
4912 7470150 : (Shape *) prop);
4913 : }
4914 :
4915 10867956 : goto out;
4916 : }
4917 :
4918 2174914 : if (!parent) {
4919 154329 : pobj = NULL;
4920 154329 : goto out;
4921 : }
4922 2020585 : obj = parent;
4923 2020585 : parent = obj->enclosingScope();
4924 : }
4925 :
4926 49025 : for (;;) {
4927 100995 : if (!obj->lookupGeneric(cx, id, &pobj, &prop))
4928 0 : return false;
4929 100995 : if (prop)
4930 51943 : goto out;
4931 :
4932 : /*
4933 : * We conservatively assume that a resolve hook could mutate the scope
4934 : * chain during JSObject::lookupGeneric. So we read parent here again.
4935 : */
4936 49052 : parent = obj->enclosingScope();
4937 49052 : if (!parent) {
4938 27 : pobj = NULL;
4939 27 : break;
4940 : }
4941 49025 : obj = parent;
4942 : }
4943 :
4944 : out:
4945 11074255 : JS_ASSERT(!!pobj == !!prop);
4946 11074255 : *objp = obj;
4947 11074255 : *pobjp = pobj;
4948 11074255 : *propp = prop;
4949 11074255 : return true;
4950 : }
4951 :
4952 : /*
4953 : * On return, if |*pobjp| is a native object, then |*propp| is a |Shape *|.
4954 : * Otherwise, its type and meaning depends on the host object's implementation.
4955 : */
4956 : bool
4957 181735 : js::FindProperty(JSContext *cx, PropertyName *name, JSObject *scopeChain,
4958 : JSObject **objp, JSObject **pobjp, JSProperty **propp)
4959 : {
4960 181735 : return !!FindPropertyHelper(cx, name, false, scopeChain, objp, pobjp, propp);
4961 : }
4962 :
4963 : JSObject *
4964 60080 : js::FindIdentifierBase(JSContext *cx, JSObject *scopeChain, PropertyName *name)
4965 : {
4966 : /*
4967 : * This function should not be called for a global object or from the
4968 : * trace and should have a valid cache entry for native scopeChain.
4969 : */
4970 60080 : JS_ASSERT(scopeChain->enclosingScope() != NULL);
4971 :
4972 60080 : JSObject *obj = scopeChain;
4973 :
4974 : /*
4975 : * Loop over cacheable objects on the scope chain until we find a
4976 : * property. We also stop when we reach the global object skipping any
4977 : * farther checks or lookups. For details see the JSOP_BINDNAME case of
4978 : * js_Interpret.
4979 : *
4980 : * The test order here matters because IsCacheableNonGlobalScope
4981 : * must not be passed a global object (i.e. one with null parent).
4982 : */
4983 265600 : for (int scopeIndex = 0;
4984 171386 : obj->isGlobal() || IsCacheableNonGlobalScope(obj);
4985 : scopeIndex++) {
4986 : JSObject *pobj;
4987 : JSProperty *prop;
4988 93070 : if (!LookupPropertyWithFlags(cx, obj, name, cx->resolveFlags, &pobj, &prop))
4989 0 : return NULL;
4990 93070 : if (prop) {
4991 58498 : if (!pobj->isNative()) {
4992 0 : JS_ASSERT(obj->isGlobal());
4993 0 : return obj;
4994 : }
4995 58498 : JS_ASSERT_IF(obj->isScope(), pobj->getClass() == obj->getClass());
4996 58498 : JS_PROPERTY_CACHE(cx).fill(cx, scopeChain, scopeIndex, pobj, (Shape *) prop);
4997 58498 : return obj;
4998 : }
4999 :
5000 34572 : JSObject *parent = obj->enclosingScope();
5001 34572 : if (!parent)
5002 438 : return obj;
5003 34134 : obj = parent;
5004 : }
5005 :
5006 : /* Loop until we find a property or reach the global object. */
5007 693 : do {
5008 : JSObject *pobj;
5009 : JSProperty *prop;
5010 1504 : if (!obj->lookupProperty(cx, name, &pobj, &prop))
5011 0 : return NULL;
5012 1504 : if (prop)
5013 811 : break;
5014 :
5015 : /*
5016 : * We conservatively assume that a resolve hook could mutate the scope
5017 : * chain during JSObject::lookupGeneric. So we must check if parent is
5018 : * not null here even if it wasn't before the lookup.
5019 : */
5020 693 : JSObject *parent = obj->enclosingScope();
5021 693 : if (!parent)
5022 0 : break;
5023 693 : obj = parent;
5024 693 : } while (!obj->isGlobal());
5025 1144 : return obj;
5026 : }
5027 :
5028 : static JS_ALWAYS_INLINE JSBool
5029 50518653 : js_NativeGetInline(JSContext *cx, JSObject *receiver, JSObject *obj, JSObject *pobj,
5030 : const Shape *shape, unsigned getHow, Value *vp)
5031 : {
5032 50518653 : JS_ASSERT(pobj->isNative());
5033 :
5034 50518653 : if (shape->hasSlot()) {
5035 47268117 : *vp = pobj->nativeGetSlot(shape->slot());
5036 47268117 : JS_ASSERT(!vp->isMagic());
5037 121469136 : JS_ASSERT_IF(!pobj->hasSingletonType() && shape->hasDefaultGetterOrIsMethod(),
5038 121469136 : js::types::TypeHasProperty(cx, pobj->type(), shape->propid(), *vp));
5039 : } else {
5040 3250536 : vp->setUndefined();
5041 : }
5042 50518653 : if (shape->hasDefaultGetter())
5043 45205428 : return true;
5044 :
5045 5313225 : if (JS_UNLIKELY(shape->isMethod()) && (getHow & JSGET_NO_METHOD_BARRIER))
5046 0 : return true;
5047 :
5048 : jsbytecode *pc;
5049 5313225 : JSScript *script = cx->stack.currentScript(&pc);
5050 5313225 : if (script && script->hasAnalysis()) {
5051 5274167 : analyze::Bytecode *code = script->analysis()->maybeCode(pc);
5052 5274167 : if (code)
5053 5274167 : code->accessGetter = true;
5054 : }
5055 :
5056 5313225 : if (!shape->get(cx, receiver, obj, pobj, vp))
5057 3915 : return false;
5058 :
5059 : /* Update slotful shapes according to the value produced by the getter. */
5060 5309310 : if (shape->hasSlot() && pobj->nativeContains(cx, *shape)) {
5061 : /* Method shapes were removed by methodReadBarrier under shape->get(). */
5062 2062860 : JS_ASSERT(!shape->isMethod());
5063 2062860 : pobj->nativeSetSlot(shape->slot(), *vp);
5064 : }
5065 :
5066 5309310 : return true;
5067 : }
5068 :
5069 : JSBool
5070 2997218 : js_NativeGet(JSContext *cx, JSObject *obj, JSObject *pobj, const Shape *shape, unsigned getHow,
5071 : Value *vp)
5072 : {
5073 2997218 : return js_NativeGetInline(cx, obj, obj, pobj, shape, getHow, vp);
5074 : }
5075 :
5076 : JSBool
5077 17790361 : js_NativeSet(JSContext *cx, JSObject *obj, const Shape *shape, bool added, bool strict, Value *vp)
5078 : {
5079 17790361 : AddTypePropertyId(cx, obj, shape->propid(), *vp);
5080 :
5081 17790361 : JS_ASSERT(obj->isNative());
5082 :
5083 17790361 : if (shape->hasSlot()) {
5084 17672727 : uint32_t slot = shape->slot();
5085 :
5086 : /* If shape has a stub setter, just store *vp. */
5087 17672727 : if (shape->hasDefaultSetter()) {
5088 17491383 : if (!added) {
5089 13825354 : if (shape->isMethod() && !obj->methodShapeChange(cx, *shape))
5090 0 : return false;
5091 : }
5092 17491383 : obj->nativeSetSlot(slot, *vp);
5093 17491383 : return true;
5094 : }
5095 : } else {
5096 : /*
5097 : * Allow API consumers to create shared properties with stub setters.
5098 : * Such properties effectively function as data descriptors which are
5099 : * not writable, so attempting to set such a property should do nothing
5100 : * or throw if we're in strict mode.
5101 : */
5102 117634 : if (!shape->hasGetterValue() && shape->hasDefaultSetter())
5103 2 : return js_ReportGetterOnlyAssignment(cx);
5104 : }
5105 :
5106 298976 : int32_t sample = cx->runtime->propertyRemovals;
5107 298976 : if (!shape->set(cx, obj, strict, vp))
5108 294 : return false;
5109 :
5110 : /*
5111 : * Update any slot for the shape with the value produced by the setter,
5112 : * unless the setter deleted the shape.
5113 : */
5114 480025 : if (shape->hasSlot() &&
5115 181343 : (JS_LIKELY(cx->runtime->propertyRemovals == sample) ||
5116 0 : obj->nativeContains(cx, *shape))) {
5117 181343 : obj->setSlot(shape->slot(), *vp);
5118 : }
5119 :
5120 298682 : return true;
5121 : }
5122 :
5123 : static JS_ALWAYS_INLINE JSBool
5124 49190228 : js_GetPropertyHelperInline(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id,
5125 : uint32_t getHow, Value *vp)
5126 : {
5127 : JSObject *aobj, *obj2;
5128 : JSProperty *prop;
5129 : const Shape *shape;
5130 :
5131 : /* Convert string indices to integers if appropriate. */
5132 49190228 : id = js_CheckForStringIndex(id);
5133 :
5134 49190228 : aobj = js_GetProtoIfDenseArray(obj);
5135 : /* This call site is hot -- use the always-inlined variant of LookupPropertyWithFlags(). */
5136 49190228 : if (!LookupPropertyWithFlagsInline(cx, aobj, id, cx->resolveFlags, &obj2, &prop))
5137 0 : return false;
5138 :
5139 49190228 : if (!prop) {
5140 1668348 : vp->setUndefined();
5141 :
5142 1668348 : if (!CallJSPropertyOp(cx, obj->getClass()->getProperty, obj, id, vp))
5143 3 : return JS_FALSE;
5144 :
5145 : /* Record non-undefined values produced by the class getter hook. */
5146 1668345 : if (!vp->isUndefined())
5147 224590 : AddTypePropertyId(cx, obj, id, *vp);
5148 :
5149 : /*
5150 : * Give a strict warning if foo.bar is evaluated by a script for an
5151 : * object foo with no property named 'bar'.
5152 : */
5153 : jsbytecode *pc;
5154 1668345 : if (vp->isUndefined() && ((pc = js_GetCurrentBytecodePC(cx)) != NULL)) {
5155 1438593 : JSOp op = (JSOp) *pc;
5156 :
5157 1438593 : if (op == JSOP_GETXPROP) {
5158 : /* Undefined property during a name lookup, report an error. */
5159 0 : JSAutoByteString printable;
5160 0 : if (js_ValueToPrintable(cx, IdToValue(id), &printable))
5161 0 : js_ReportIsNotDefined(cx, printable.ptr());
5162 0 : return false;
5163 : }
5164 :
5165 1755345 : if (!cx->hasStrictOption() ||
5166 316752 : cx->stack.currentScript()->warnedAboutUndefinedProp ||
5167 : (op != JSOP_GETPROP && op != JSOP_GETELEM)) {
5168 1344607 : return JS_TRUE;
5169 : }
5170 :
5171 : /*
5172 : * XXX do not warn about missing __iterator__ as the function
5173 : * may be called from JS_GetMethodById. See bug 355145.
5174 : */
5175 93986 : if (JSID_IS_ATOM(id, cx->runtime->atomState.iteratorAtom))
5176 0 : return JS_TRUE;
5177 :
5178 : /* Do not warn about tests like (obj[prop] == undefined). */
5179 93986 : if (cx->resolveFlags == RESOLVE_INFER) {
5180 93960 : pc += js_CodeSpec[op].length;
5181 93960 : if (Detecting(cx, pc))
5182 93530 : return JS_TRUE;
5183 26 : } else if (cx->resolveFlags & JSRESOLVE_DETECTING) {
5184 0 : return JS_TRUE;
5185 : }
5186 :
5187 456 : unsigned flags = JSREPORT_WARNING | JSREPORT_STRICT;
5188 456 : cx->stack.currentScript()->warnedAboutUndefinedProp = true;
5189 :
5190 : /* Ok, bad undefined property reference: whine about it. */
5191 456 : if (!js_ReportValueErrorFlags(cx, flags, JSMSG_UNDEFINED_PROP,
5192 : JSDVG_IGNORE_STACK, IdToValue(id),
5193 456 : NULL, NULL, NULL))
5194 : {
5195 0 : return false;
5196 : }
5197 : }
5198 230208 : return JS_TRUE;
5199 : }
5200 :
5201 47521880 : if (!obj2->isNative()) {
5202 445 : return obj2->isProxy()
5203 58 : ? Proxy::get(cx, obj2, receiver, id, vp)
5204 503 : : obj2->getGeneric(cx, id, vp);
5205 : }
5206 :
5207 47521435 : shape = (Shape *) prop;
5208 :
5209 47521435 : if (getHow & JSGET_CACHE_RESULT)
5210 10563268 : JS_PROPERTY_CACHE(cx).fill(cx, aobj, 0, obj2, shape);
5211 :
5212 : /* This call site is hot -- use the always-inlined variant of js_NativeGet(). */
5213 47521435 : if (!js_NativeGetInline(cx, receiver, obj, obj2, shape, getHow, vp))
5214 522 : return JS_FALSE;
5215 :
5216 47520913 : return JS_TRUE;
5217 : }
5218 :
5219 : bool
5220 12744180 : js::GetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uint32_t getHow, Value *vp)
5221 : {
5222 12744180 : return !!js_GetPropertyHelperInline(cx, obj, obj, id, getHow, vp);
5223 : }
5224 :
5225 : JSBool
5226 36446048 : js_GetProperty(JSContext *cx, JSObject *obj, JSObject *receiver, jsid id, Value *vp)
5227 : {
5228 : /* This call site is hot -- use the always-inlined variant of js_GetPropertyHelper(). */
5229 36446048 : return js_GetPropertyHelperInline(cx, obj, receiver, id, JSGET_METHOD_BARRIER, vp);
5230 : }
5231 :
5232 : JSBool
5233 0 : js_GetElement(JSContext *cx, JSObject *obj, JSObject *receiver, uint32_t index, Value *vp)
5234 : {
5235 : jsid id;
5236 0 : if (!IndexToId(cx, index, &id))
5237 0 : return false;
5238 :
5239 : /* This call site is hot -- use the always-inlined variant of js_GetPropertyHelper(). */
5240 0 : return js_GetPropertyHelperInline(cx, obj, receiver, id, JSGET_METHOD_BARRIER, vp);
5241 : }
5242 :
5243 : JSBool
5244 4 : js::GetPropertyDefault(JSContext *cx, JSObject *obj, jsid id, const Value &def, Value *vp)
5245 : {
5246 : JSProperty *prop;
5247 : JSObject *obj2;
5248 4 : if (!LookupPropertyWithFlags(cx, obj, id, JSRESOLVE_QUALIFIED, &obj2, &prop))
5249 0 : return false;
5250 :
5251 4 : if (!prop) {
5252 2 : *vp = def;
5253 2 : return true;
5254 : }
5255 :
5256 2 : return js_GetProperty(cx, obj2, id, vp);
5257 : }
5258 :
5259 : JSBool
5260 2252150 : js_GetMethod(JSContext *cx, JSObject *obj, jsid id, unsigned getHow, Value *vp)
5261 : {
5262 4504300 : JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED);
5263 :
5264 2252150 : GenericIdOp op = obj->getOps()->getGeneric;
5265 2252150 : if (!op) {
5266 : #if JS_HAS_XML_SUPPORT
5267 1942342 : JS_ASSERT(!obj->isXML());
5268 : #endif
5269 1942342 : return GetPropertyHelper(cx, obj, id, getHow, vp);
5270 : }
5271 : #if JS_HAS_XML_SUPPORT
5272 309808 : if (obj->isXML())
5273 99 : return js_GetXMLMethod(cx, obj, id, vp);
5274 : #endif
5275 309709 : return op(cx, obj, obj, id, vp);
5276 : }
5277 :
5278 : JS_FRIEND_API(bool)
5279 2936 : js::CheckUndeclaredVarAssignment(JSContext *cx, JSString *propname)
5280 : {
5281 2936 : StackFrame *const fp = js_GetTopStackFrame(cx, FRAME_EXPAND_ALL);
5282 2936 : if (!fp)
5283 0 : return true;
5284 :
5285 : /* If neither cx nor the code is strict, then no check is needed. */
5286 5863 : if (!(fp->isScriptFrame() && fp->script()->strictModeCode) &&
5287 2927 : !cx->hasStrictOption()) {
5288 1458 : return true;
5289 : }
5290 :
5291 2956 : JSAutoByteString bytes(cx, propname);
5292 1478 : return !!bytes &&
5293 : JS_ReportErrorFlagsAndNumber(cx,
5294 : (JSREPORT_WARNING | JSREPORT_STRICT
5295 : | JSREPORT_STRICT_MODE_ERROR),
5296 : js_GetErrorMessage, NULL,
5297 1478 : JSMSG_UNDECLARED_VAR, bytes.ptr());
5298 : }
5299 :
5300 : static bool
5301 15 : ReportReadOnly(JSContext *cx, jsid id, unsigned report)
5302 : {
5303 : return js_ReportValueErrorFlags(cx, report, JSMSG_READ_ONLY,
5304 : JSDVG_IGNORE_STACK, IdToValue(id), NULL,
5305 15 : NULL, NULL);
5306 : }
5307 :
5308 : bool
5309 9 : JSObject::reportNotConfigurable(JSContext *cx, jsid id, unsigned report)
5310 : {
5311 : return js_ReportValueErrorFlags(cx, report, JSMSG_CANT_DELETE,
5312 : JSDVG_IGNORE_STACK, IdToValue(id), NULL,
5313 9 : NULL, NULL);
5314 : }
5315 :
5316 : bool
5317 13 : JSObject::reportNotExtensible(JSContext *cx, unsigned report)
5318 : {
5319 : return js_ReportValueErrorFlags(cx, report, JSMSG_OBJECT_NOT_EXTENSIBLE,
5320 : JSDVG_IGNORE_STACK, ObjectValue(*this),
5321 13 : NULL, NULL, NULL);
5322 : }
5323 :
5324 : bool
5325 9 : JSObject::callMethod(JSContext *cx, jsid id, unsigned argc, Value *argv, Value *vp)
5326 : {
5327 : Value fval;
5328 9 : return js_GetMethod(cx, this, id, JSGET_NO_METHOD_BARRIER, &fval) &&
5329 9 : Invoke(cx, ObjectValue(*this), fval, argc, argv, vp);
5330 : }
5331 :
5332 : static bool
5333 0 : CloneFunctionForSetMethod(JSContext *cx, Value *vp)
5334 : {
5335 0 : JSFunction *fun = vp->toObject().toFunction();
5336 :
5337 : /* Clone the fun unless it already has been. */
5338 0 : if (!fun->isClonedMethod()) {
5339 0 : fun = CloneFunctionObject(cx, fun);
5340 0 : if (!fun)
5341 0 : return false;
5342 0 : vp->setObject(*fun);
5343 : }
5344 0 : return true;
5345 : }
5346 :
5347 : JSBool
5348 17763502 : js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, unsigned defineHow,
5349 : Value *vp, JSBool strict)
5350 : {
5351 : JSObject *pobj;
5352 : JSProperty *prop;
5353 : const Shape *shape;
5354 : unsigned attrs, flags;
5355 : int shortid;
5356 : Class *clasp;
5357 : PropertyOp getter;
5358 : StrictPropertyOp setter;
5359 : bool added;
5360 :
5361 17763502 : JS_ASSERT((defineHow & ~(DNP_CACHE_RESULT | DNP_SET_METHOD | DNP_UNQUALIFIED)) == 0);
5362 :
5363 : /* Convert string indices to integers if appropriate. */
5364 17763502 : id = js_CheckForStringIndex(id);
5365 :
5366 17763502 : if (JS_UNLIKELY(obj->watched())) {
5367 : /* Fire watchpoints, if any. */
5368 4397 : WatchpointMap *wpmap = cx->compartment->watchpointMap;
5369 4397 : if (wpmap && !wpmap->triggerWatchpoint(cx, obj, id, vp))
5370 2820 : return false;
5371 :
5372 : /* A watchpoint handler may set *vp to a non-function value. */
5373 1577 : defineHow &= ~DNP_SET_METHOD;
5374 : }
5375 :
5376 17760682 : if (!LookupPropertyWithFlags(cx, obj, id, cx->resolveFlags, &pobj, &prop))
5377 0 : return false;
5378 17760682 : if (prop) {
5379 14723542 : if (!pobj->isNative()) {
5380 18 : if (pobj->isProxy()) {
5381 0 : AutoPropertyDescriptorRooter pd(cx);
5382 0 : if (!Proxy::getPropertyDescriptor(cx, pobj, id, true, &pd))
5383 0 : return false;
5384 :
5385 0 : if ((pd.attrs & (JSPROP_SHARED | JSPROP_SHADOWABLE)) == JSPROP_SHARED) {
5386 0 : return !pd.setter ||
5387 0 : CallSetter(cx, obj, id, pd.setter, pd.attrs, pd.shortid, strict, vp);
5388 : }
5389 :
5390 0 : if (pd.attrs & JSPROP_READONLY) {
5391 0 : if (strict)
5392 0 : return ReportReadOnly(cx, id, JSREPORT_ERROR);
5393 0 : if (cx->hasStrictOption())
5394 0 : return ReportReadOnly(cx, id, JSREPORT_STRICT | JSREPORT_WARNING);
5395 0 : return true;
5396 : }
5397 : }
5398 :
5399 18 : prop = NULL;
5400 : }
5401 : } else {
5402 : /* We should never add properties to lexical blocks. */
5403 3037140 : JS_ASSERT(!obj->isBlock());
5404 :
5405 3040076 : if (obj->isGlobal() &&
5406 : (defineHow & DNP_UNQUALIFIED) &&
5407 2936 : !js::CheckUndeclaredVarAssignment(cx, JSID_TO_STRING(id))) {
5408 9 : return JS_FALSE;
5409 : }
5410 : }
5411 17760673 : shape = (Shape *) prop;
5412 :
5413 : /*
5414 : * Now either shape is null, meaning id was not found in obj or one of its
5415 : * prototypes; or shape is non-null, meaning id was found directly in pobj.
5416 : */
5417 17760673 : attrs = JSPROP_ENUMERATE;
5418 17760673 : flags = 0;
5419 17760673 : shortid = 0;
5420 17760673 : clasp = obj->getClass();
5421 17760673 : getter = clasp->getProperty;
5422 17760673 : setter = clasp->setProperty;
5423 :
5424 17760673 : if (shape) {
5425 : /* ES5 8.12.4 [[Put]] step 2. */
5426 14723524 : if (shape->isAccessorDescriptor()) {
5427 130783 : if (shape->hasDefaultSetter())
5428 1076 : return js_ReportGetterOnlyAssignment(cx);
5429 : } else {
5430 14592741 : JS_ASSERT(shape->isDataDescriptor());
5431 :
5432 14592741 : if (!shape->writable()) {
5433 : /* Error in strict mode code, warn with strict option, otherwise do nothing. */
5434 888 : if (strict)
5435 9 : return ReportReadOnly(cx, id, JSREPORT_ERROR);
5436 879 : if (cx->hasStrictOption())
5437 6 : return ReportReadOnly(cx, id, JSREPORT_STRICT | JSREPORT_WARNING);
5438 873 : return JS_TRUE;
5439 : }
5440 : }
5441 :
5442 14721560 : attrs = shape->attributes();
5443 14721560 : if (pobj != obj) {
5444 : /*
5445 : * We found id in a prototype object: prepare to share or shadow.
5446 : */
5447 692530 : if (!shape->shadowable()) {
5448 56013 : if (defineHow & DNP_SET_METHOD) {
5449 0 : JS_ASSERT(!shape->isMethod());
5450 0 : if (!CloneFunctionForSetMethod(cx, vp))
5451 0 : return false;
5452 : }
5453 :
5454 56013 : if (defineHow & DNP_CACHE_RESULT)
5455 41300 : JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, pobj, shape);
5456 :
5457 56013 : if (shape->hasDefaultSetter() && !shape->hasGetterValue())
5458 0 : return JS_TRUE;
5459 :
5460 56013 : return shape->set(cx, obj, strict, vp);
5461 : }
5462 :
5463 : /*
5464 : * Preserve attrs except JSPROP_SHARED, getter, and setter when
5465 : * shadowing any property that has no slot (is shared). We must
5466 : * clear the shared attribute for the shadowing shape so that the
5467 : * property in obj that it defines has a slot to retain the value
5468 : * being set, in case the setter simply cannot operate on instances
5469 : * of obj's class by storing the value in some class-specific
5470 : * location.
5471 : *
5472 : * A subset of slotless shared properties is the set of properties
5473 : * with shortids, which must be preserved too. An old API requires
5474 : * that the property's getter and setter receive the shortid, not
5475 : * id, when they are called on the shadowing property that we are
5476 : * about to create in obj.
5477 : */
5478 636517 : if (!shape->hasSlot()) {
5479 0 : defineHow &= ~DNP_SET_METHOD;
5480 0 : if (shape->hasShortID()) {
5481 0 : flags = Shape::HAS_SHORTID;
5482 0 : shortid = shape->shortid();
5483 : }
5484 0 : attrs &= ~JSPROP_SHARED;
5485 0 : getter = shape->getter();
5486 0 : setter = shape->setter();
5487 : } else {
5488 : /* Restore attrs to the ECMA default for new properties. */
5489 636517 : attrs = JSPROP_ENUMERATE;
5490 : }
5491 :
5492 : /*
5493 : * Forget we found the proto-property now that we've copied any
5494 : * needed member values.
5495 : */
5496 636517 : shape = NULL;
5497 : }
5498 :
5499 14665547 : if (shape && (defineHow & DNP_SET_METHOD)) {
5500 : /*
5501 : * JSOP_SETMETHOD is assigning to an existing own property. If it
5502 : * is an identical method property, do nothing. Otherwise downgrade
5503 : * to ordinary assignment. Either way, do not fill the property
5504 : * cache, as the interpreter has no fast path for these unusual
5505 : * cases.
5506 : */
5507 0 : if (shape->isMethod()) {
5508 0 : if (obj->nativeGetMethod(shape) == &vp->toObject())
5509 0 : return true;
5510 0 : shape = obj->methodShapeChange(cx, *shape);
5511 0 : if (!shape)
5512 0 : return false;
5513 : }
5514 0 : if (!CloneFunctionForSetMethod(cx, vp))
5515 0 : return false;
5516 0 : return js_NativeSet(cx, obj, shape, false, strict, vp);
5517 : }
5518 : }
5519 :
5520 17702696 : added = false;
5521 17702696 : if (!shape) {
5522 3673666 : if (!obj->isExtensible()) {
5523 : /* Error in strict mode code, warn with strict option, otherwise do nothing. */
5524 4 : if (strict)
5525 1 : return obj->reportNotExtensible(cx);
5526 3 : if (cx->hasStrictOption())
5527 3 : return obj->reportNotExtensible(cx, JSREPORT_STRICT | JSREPORT_WARNING);
5528 0 : return JS_TRUE;
5529 : }
5530 :
5531 : /*
5532 : * Purge the property cache of now-shadowed id in obj's scope chain.
5533 : * Do this early, before locking obj to avoid nesting locks.
5534 : */
5535 3673662 : if (!js_PurgeScopeChain(cx, obj, id))
5536 0 : return JS_FALSE;
5537 :
5538 : /*
5539 : * Check for Object class here to avoid defining a method on a class
5540 : * with magic resolve, addProperty, getProperty, etc. hooks.
5541 : */
5542 3673662 : if ((defineHow & DNP_SET_METHOD) && obj->canHaveMethodBarrier()) {
5543 0 : JS_ASSERT(IsFunctionObject(*vp));
5544 0 : JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
5545 :
5546 0 : JSObject *funobj = &vp->toObject();
5547 0 : if (!funobj->toFunction()->isClonedMethod())
5548 0 : flags |= Shape::METHOD;
5549 : }
5550 :
5551 : shape = obj->putProperty(cx, id, getter, setter, SHAPE_INVALID_SLOT,
5552 3673662 : attrs, flags, shortid);
5553 3673662 : if (!shape)
5554 0 : return JS_FALSE;
5555 :
5556 : /*
5557 : * Initialize the new property value (passed to setter) to undefined.
5558 : * Note that we store before calling addProperty, to match the order
5559 : * in DefineNativeProperty.
5560 : */
5561 3673662 : if (shape->hasSlot())
5562 3673662 : obj->nativeSetSlot(shape->slot(), UndefinedValue());
5563 :
5564 : /* XXXbe called with obj locked */
5565 3673662 : if (!CallAddPropertyHook(cx, clasp, obj, shape, vp)) {
5566 2 : obj->removeProperty(cx, id);
5567 2 : return JS_FALSE;
5568 : }
5569 3673660 : added = true;
5570 : }
5571 :
5572 17702690 : if ((defineHow & DNP_CACHE_RESULT) && !added)
5573 650563 : JS_PROPERTY_CACHE(cx).fill(cx, obj, 0, obj, shape);
5574 :
5575 17702690 : return js_NativeSet(cx, obj, shape, added, strict, vp);
5576 : }
5577 :
5578 : JSBool
5579 1 : js_SetElementHelper(JSContext *cx, JSObject *obj, uint32_t index, unsigned defineHow,
5580 : Value *vp, JSBool strict)
5581 : {
5582 : jsid id;
5583 1 : if (!IndexToId(cx, index, &id))
5584 0 : return false;
5585 1 : return js_SetPropertyHelper(cx, obj, id, defineHow, vp, strict);
5586 : }
5587 :
5588 : JSBool
5589 92112 : js_GetAttributes(JSContext *cx, JSObject *obj, jsid id, unsigned *attrsp)
5590 : {
5591 : JSProperty *prop;
5592 92112 : if (!js_LookupProperty(cx, obj, id, &obj, &prop))
5593 0 : return false;
5594 92112 : if (!prop) {
5595 0 : *attrsp = 0;
5596 0 : return true;
5597 : }
5598 92112 : if (!obj->isNative())
5599 0 : return obj->getGenericAttributes(cx, id, attrsp);
5600 :
5601 92112 : const Shape *shape = (Shape *)prop;
5602 92112 : *attrsp = shape->attributes();
5603 92112 : return true;
5604 : }
5605 :
5606 : JSBool
5607 0 : js_GetElementAttributes(JSContext *cx, JSObject *obj, uint32_t index, unsigned *attrsp)
5608 : {
5609 : JSProperty *prop;
5610 0 : if (!js_LookupElement(cx, obj, index, &obj, &prop))
5611 0 : return false;
5612 0 : if (!prop) {
5613 0 : *attrsp = 0;
5614 0 : return true;
5615 : }
5616 0 : if (!obj->isNative())
5617 0 : return obj->getElementAttributes(cx, index, attrsp);
5618 :
5619 0 : const Shape *shape = (Shape *)prop;
5620 0 : *attrsp = shape->attributes();
5621 0 : return true;
5622 : }
5623 :
5624 : JSBool
5625 40625 : js_SetNativeAttributes(JSContext *cx, JSObject *obj, Shape *shape, unsigned attrs)
5626 : {
5627 40625 : JS_ASSERT(obj->isNative());
5628 : return !!js_ChangeNativePropertyAttrs(cx, obj, shape, attrs, 0,
5629 40625 : shape->getter(), shape->setter());
5630 : }
5631 :
5632 : JSBool
5633 40625 : js_SetAttributes(JSContext *cx, JSObject *obj, jsid id, unsigned *attrsp)
5634 : {
5635 : JSProperty *prop;
5636 40625 : if (!js_LookupProperty(cx, obj, id, &obj, &prop))
5637 0 : return false;
5638 40625 : if (!prop)
5639 0 : return true;
5640 40625 : return obj->isNative()
5641 40625 : ? js_SetNativeAttributes(cx, obj, (Shape *) prop, *attrsp)
5642 81250 : : obj->setGenericAttributes(cx, id, attrsp);
5643 : }
5644 :
5645 : JSBool
5646 0 : js_SetElementAttributes(JSContext *cx, JSObject *obj, uint32_t index, unsigned *attrsp)
5647 : {
5648 : JSProperty *prop;
5649 0 : if (!js_LookupElement(cx, obj, index, &obj, &prop))
5650 0 : return false;
5651 0 : if (!prop)
5652 0 : return true;
5653 0 : return obj->isNative()
5654 0 : ? js_SetNativeAttributes(cx, obj, (Shape *) prop, *attrsp)
5655 0 : : obj->setElementAttributes(cx, index, attrsp);
5656 : }
5657 :
5658 : JSBool
5659 245979 : js_DeleteGeneric(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool strict)
5660 : {
5661 : JSObject *proto;
5662 : JSProperty *prop;
5663 : const Shape *shape;
5664 :
5665 245979 : rval->setBoolean(true);
5666 :
5667 : /* Convert string indices to integers if appropriate. */
5668 245979 : id = js_CheckForStringIndex(id);
5669 :
5670 245979 : if (!js_LookupProperty(cx, obj, id, &proto, &prop))
5671 0 : return false;
5672 245979 : if (!prop || proto != obj) {
5673 : /*
5674 : * If no property, or the property comes from a prototype, call the
5675 : * class's delProperty hook, passing rval as the result parameter.
5676 : */
5677 194287 : return CallJSPropertyOp(cx, obj->getClass()->delProperty, obj, id, rval);
5678 : }
5679 :
5680 51692 : shape = (Shape *)prop;
5681 51692 : if (!shape->configurable()) {
5682 27 : if (strict)
5683 9 : return obj->reportNotConfigurable(cx, id);
5684 18 : rval->setBoolean(false);
5685 18 : return true;
5686 : }
5687 :
5688 51665 : if (shape->hasSlot()) {
5689 21124 : const Value &v = obj->nativeGetSlot(shape->slot());
5690 21124 : GCPoke(cx->runtime, v);
5691 :
5692 : /*
5693 : * Delete is rare enough that we can take the hit of checking for an
5694 : * active cloned method function object that must be homed to a callee
5695 : * slot on the active stack frame before this delete completes, in case
5696 : * someone saved the clone and checks it against foo.caller for a foo
5697 : * called from the active method.
5698 : *
5699 : * We do not check suspended frames. They can't be reached via caller,
5700 : * so the only way they could have the method's joined function object
5701 : * as callee is through an API abusage. We break any such edge case.
5702 : */
5703 : JSFunction *fun;
5704 21124 : if (IsFunctionObject(v, &fun) && fun->isClonedMethod()) {
5705 0 : for (StackFrame *fp = cx->maybefp(); fp; fp = fp->prev()) {
5706 0 : if (fp->isFunctionFrame() &&
5707 0 : fp->fun()->script() == fun->script() &&
5708 0 : fp->thisValue().isObject())
5709 : {
5710 0 : JSObject *tmp = &fp->thisValue().toObject();
5711 0 : do {
5712 0 : if (tmp == obj) {
5713 0 : fp->overwriteCallee(*fun);
5714 0 : break;
5715 : }
5716 0 : } while ((tmp = tmp->getProto()) != NULL);
5717 : }
5718 : }
5719 : }
5720 : }
5721 :
5722 51665 : if (!CallJSPropertyOp(cx, obj->getClass()->delProperty, obj, shape->getUserId(), rval))
5723 0 : return false;
5724 51665 : if (rval->isFalse())
5725 0 : return true;
5726 :
5727 51665 : return obj->removeProperty(cx, id) && js_SuppressDeletedProperty(cx, obj, id);
5728 : }
5729 :
5730 : JSBool
5731 48293 : js_DeleteProperty(JSContext *cx, JSObject *obj, PropertyName *name, Value *rval, JSBool strict)
5732 : {
5733 48293 : return js_DeleteGeneric(cx, obj, ATOM_TO_JSID(name), rval, strict);
5734 : }
5735 :
5736 : JSBool
5737 197245 : js_DeleteElement(JSContext *cx, JSObject *obj, uint32_t index, Value *rval, JSBool strict)
5738 : {
5739 : jsid id;
5740 197245 : if (!IndexToId(cx, index, &id))
5741 0 : return false;
5742 197245 : return js_DeleteGeneric(cx, obj, id, rval, strict);
5743 : }
5744 :
5745 : JSBool
5746 0 : js_DeleteSpecial(JSContext *cx, JSObject *obj, SpecialId sid, Value *rval, JSBool strict)
5747 : {
5748 0 : return js_DeleteGeneric(cx, obj, SPECIALID_TO_JSID(sid), rval, strict);
5749 : }
5750 :
5751 : namespace js {
5752 :
5753 : bool
5754 372046 : HasDataProperty(JSContext *cx, JSObject *obj, jsid methodid, Value *vp)
5755 : {
5756 372046 : if (const Shape *shape = obj->nativeLookup(cx, methodid)) {
5757 186032 : if (shape->hasDefaultGetterOrIsMethod() && shape->hasSlot()) {
5758 186014 : *vp = obj->nativeGetSlot(shape->slot());
5759 186014 : return true;
5760 : }
5761 : }
5762 :
5763 186032 : return false;
5764 : }
5765 :
5766 : /*
5767 : * Gets |obj[id]|. If that value's not callable, returns true and stores a
5768 : * non-primitive value in *vp. If it's callable, calls it with no arguments
5769 : * and |obj| as |this|, returning the result in *vp.
5770 : *
5771 : * This is a mini-abstraction for ES5 8.12.8 [[DefaultValue]], either steps 1-2
5772 : * or steps 3-4.
5773 : */
5774 : static bool
5775 1130697 : MaybeCallMethod(JSContext *cx, JSObject *obj, jsid id, Value *vp)
5776 : {
5777 1130697 : if (!js_GetMethod(cx, obj, id, JSGET_NO_METHOD_BARRIER, vp))
5778 135 : return false;
5779 1130562 : if (!js_IsCallable(*vp)) {
5780 20259 : *vp = ObjectValue(*obj);
5781 20259 : return true;
5782 : }
5783 1110303 : return Invoke(cx, ObjectValue(*obj), *vp, 0, NULL, vp);
5784 : }
5785 :
5786 : JSBool
5787 922009 : DefaultValue(JSContext *cx, JSObject *obj, JSType hint, Value *vp)
5788 : {
5789 922009 : JS_ASSERT(hint == JSTYPE_NUMBER || hint == JSTYPE_STRING || hint == JSTYPE_VOID);
5790 922009 : JS_ASSERT(!obj->isXML());
5791 :
5792 922009 : Class *clasp = obj->getClass();
5793 922009 : if (hint == JSTYPE_STRING) {
5794 : /* Optimize (new String(...)).toString(). */
5795 52680 : if (clasp == &StringClass &&
5796 : ClassMethodIsNative(cx, obj,
5797 : &StringClass,
5798 657 : ATOM_TO_JSID(cx->runtime->atomState.toStringAtom),
5799 657 : js_str_toString)) {
5800 657 : *vp = StringValue(obj->asString().unbox());
5801 657 : return true;
5802 : }
5803 :
5804 51366 : if (!MaybeCallMethod(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.toStringAtom), vp))
5805 5430 : return false;
5806 45936 : if (vp->isPrimitive())
5807 45387 : return true;
5808 :
5809 549 : if (!MaybeCallMethod(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.valueOfAtom), vp))
5810 0 : return false;
5811 549 : if (vp->isPrimitive())
5812 540 : return true;
5813 : } else {
5814 : /* Optimize (new String(...)).valueOf(). */
5815 1052583 : if ((clasp == &StringClass &&
5816 : ClassMethodIsNative(cx, obj, &StringClass,
5817 180863 : ATOM_TO_JSID(cx->runtime->atomState.valueOfAtom),
5818 180863 : js_str_toString)) ||
5819 : (clasp == &NumberClass &&
5820 : ClassMethodIsNative(cx, obj, &NumberClass,
5821 1734 : ATOM_TO_JSID(cx->runtime->atomState.valueOfAtom),
5822 1734 : js_num_valueOf))) {
5823 182579 : *vp = obj->isString()
5824 180845 : ? StringValue(obj->asString().unbox())
5825 363424 : : NumberValue(obj->asNumber().unbox());
5826 182579 : return true;
5827 : }
5828 :
5829 687407 : if (!MaybeCallMethod(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.valueOfAtom), vp))
5830 18 : return false;
5831 687389 : if (vp->isPrimitive())
5832 296014 : return true;
5833 :
5834 391375 : if (!MaybeCallMethod(cx, obj, ATOM_TO_JSID(cx->runtime->atomState.toStringAtom), vp))
5835 27 : return false;
5836 391348 : if (vp->isPrimitive())
5837 391276 : return true;
5838 : }
5839 :
5840 : /* Avoid recursive death when decompiling in js_ReportValueError. */
5841 : JSString *str;
5842 81 : if (hint == JSTYPE_STRING) {
5843 9 : str = JS_InternString(cx, clasp->name);
5844 9 : if (!str)
5845 0 : return false;
5846 : } else {
5847 72 : str = NULL;
5848 : }
5849 :
5850 81 : js_ReportValueError2(cx, JSMSG_CANT_CONVERT_TO, JSDVG_SEARCH_STACK, ObjectValue(*obj), str,
5851 81 : (hint == JSTYPE_VOID) ? "primitive type" : JS_TYPE_STR(hint));
5852 81 : return false;
5853 : }
5854 :
5855 : } /* namespace js */
5856 :
5857 : JS_FRIEND_API(JSBool)
5858 330 : JS_EnumerateState(JSContext *cx, JSObject *obj, JSIterateOp enum_op, Value *statep, jsid *idp)
5859 : {
5860 : /* If the class has a custom JSCLASS_NEW_ENUMERATE hook, call it. */
5861 330 : Class *clasp = obj->getClass();
5862 330 : JSEnumerateOp enumerate = clasp->enumerate;
5863 330 : if (clasp->flags & JSCLASS_NEW_ENUMERATE) {
5864 0 : JS_ASSERT(enumerate != JS_EnumerateStub);
5865 0 : return ((JSNewEnumerateOp) enumerate)(cx, obj, enum_op, statep, idp);
5866 : }
5867 :
5868 330 : if (!enumerate(cx, obj))
5869 0 : return false;
5870 :
5871 : /* Tell InitNativeIterator to treat us like a native object. */
5872 330 : JS_ASSERT(enum_op == JSENUMERATE_INIT || enum_op == JSENUMERATE_INIT_ALL);
5873 330 : statep->setMagic(JS_NATIVE_ENUMERATE);
5874 330 : return true;
5875 : }
5876 :
5877 : namespace js {
5878 :
5879 : JSBool
5880 687434 : CheckAccess(JSContext *cx, JSObject *obj, jsid id, JSAccessMode mode,
5881 : Value *vp, unsigned *attrsp)
5882 : {
5883 : JSBool writing;
5884 : JSObject *pobj;
5885 : JSProperty *prop;
5886 : const Shape *shape;
5887 :
5888 1374868 : while (JS_UNLIKELY(obj->isWith()))
5889 0 : obj = obj->getProto();
5890 :
5891 687434 : writing = (mode & JSACC_WRITE) != 0;
5892 687434 : switch (mode & JSACC_TYPEMASK) {
5893 : case JSACC_PROTO:
5894 17033 : pobj = obj;
5895 17033 : if (!writing)
5896 2152 : vp->setObjectOrNull(obj->getProto());
5897 17033 : *attrsp = JSPROP_PERMANENT;
5898 17033 : break;
5899 :
5900 : case JSACC_PARENT:
5901 0 : JS_ASSERT(!writing);
5902 0 : pobj = obj;
5903 0 : vp->setObject(*obj->getParent());
5904 0 : *attrsp = JSPROP_READONLY | JSPROP_PERMANENT;
5905 0 : break;
5906 :
5907 : default:
5908 670401 : if (!obj->lookupGeneric(cx, id, &pobj, &prop))
5909 0 : return JS_FALSE;
5910 670401 : if (!prop) {
5911 638253 : if (!writing)
5912 638253 : vp->setUndefined();
5913 638253 : *attrsp = 0;
5914 638253 : pobj = obj;
5915 638253 : break;
5916 : }
5917 :
5918 32148 : if (!pobj->isNative()) {
5919 0 : if (!writing) {
5920 0 : vp->setUndefined();
5921 0 : *attrsp = 0;
5922 : }
5923 0 : break;
5924 : }
5925 :
5926 32148 : shape = (Shape *)prop;
5927 32148 : *attrsp = shape->attributes();
5928 32148 : if (!writing) {
5929 32148 : if (shape->hasSlot())
5930 915 : *vp = pobj->nativeGetSlot(shape->slot());
5931 : else
5932 31233 : vp->setUndefined();
5933 : }
5934 : }
5935 :
5936 687434 : JS_ASSERT_IF(*attrsp & JSPROP_READONLY, !(*attrsp & (JSPROP_GETTER | JSPROP_SETTER)));
5937 :
5938 : /*
5939 : * If obj's class has a stub (null) checkAccess hook, use the per-runtime
5940 : * checkObjectAccess callback, if configured.
5941 : *
5942 : * We don't want to require all classes to supply a checkAccess hook; we
5943 : * need that hook only for certain classes used when precompiling scripts
5944 : * and functions ("brutal sharing"). But for general safety of built-in
5945 : * magic properties like __proto__, we route all access checks, even for
5946 : * classes that stub out checkAccess, through the global checkObjectAccess
5947 : * hook. This covers precompilation-based sharing and (possibly
5948 : * unintended) runtime sharing across trust boundaries.
5949 : */
5950 687434 : JSCheckAccessOp check = pobj->getClass()->checkAccess;
5951 687434 : if (!check)
5952 687434 : check = cx->runtime->securityCallbacks->checkObjectAccess;
5953 687434 : return !check || check(cx, pobj, id, mode, vp);
5954 : }
5955 :
5956 : }
5957 :
5958 : JSType
5959 6979281 : js_TypeOf(JSContext *cx, JSObject *obj)
5960 : {
5961 6979281 : return obj->isCallable() ? JSTYPE_FUNCTION : JSTYPE_OBJECT;
5962 : }
5963 :
5964 : bool
5965 1060687 : js_IsDelegate(JSContext *cx, JSObject *obj, const Value &v)
5966 : {
5967 1060687 : if (v.isPrimitive())
5968 262122 : return false;
5969 798565 : JSObject *obj2 = &v.toObject();
5970 1600195 : while ((obj2 = obj2->getProto()) != NULL) {
5971 800215 : if (obj2 == obj)
5972 797150 : return true;
5973 : }
5974 1415 : return false;
5975 : }
5976 :
5977 : bool
5978 344418 : js::FindClassPrototype(JSContext *cx, JSObject *scopeobj, JSProtoKey protoKey,
5979 : JSObject **protop, Class *clasp)
5980 : {
5981 : Value v;
5982 344418 : if (!js_FindClassObject(cx, scopeobj, protoKey, &v, clasp))
5983 0 : return false;
5984 :
5985 344418 : if (IsFunctionObject(v)) {
5986 70407 : JSObject *ctor = &v.toObject();
5987 70407 : if (!ctor->getProperty(cx, cx->runtime->atomState.classPrototypeAtom, &v))
5988 0 : return false;
5989 : }
5990 :
5991 344418 : *protop = v.isObject() ? &v.toObject() : NULL;
5992 344418 : return true;
5993 : }
5994 :
5995 : /*
5996 : * The first part of this function has been hand-expanded and optimized into
5997 : * NewBuiltinClassInstance in jsobjinlines.h.
5998 : */
5999 : JSBool
6000 9831100 : js_GetClassPrototype(JSContext *cx, JSObject *scopeobj, JSProtoKey protoKey,
6001 : JSObject **protop, Class *clasp)
6002 : {
6003 9831100 : JS_ASSERT(JSProto_Null <= protoKey);
6004 9831100 : JS_ASSERT(protoKey < JSProto_LIMIT);
6005 :
6006 9831100 : if (protoKey != JSProto_Null) {
6007 : GlobalObject *global;
6008 9565638 : if (scopeobj) {
6009 7921856 : global = &scopeobj->global();
6010 : } else {
6011 1643782 : global = GetCurrentGlobal(cx);
6012 1643782 : if (!global) {
6013 0 : *protop = NULL;
6014 0 : return true;
6015 : }
6016 : }
6017 9565638 : const Value &v = global->getReservedSlot(JSProto_LIMIT + protoKey);
6018 9565638 : if (v.isObject()) {
6019 9486682 : *protop = &v.toObject();
6020 9486682 : return true;
6021 : }
6022 : }
6023 :
6024 344418 : return FindClassPrototype(cx, scopeobj, protoKey, protop, clasp);
6025 : }
6026 :
6027 : JSObject *
6028 5063951 : PrimitiveToObject(JSContext *cx, const Value &v)
6029 : {
6030 5063951 : if (v.isString())
6031 4801288 : return StringObject::create(cx, v.toString());
6032 262663 : if (v.isNumber())
6033 256351 : return NumberObject::create(cx, v.toNumber());
6034 :
6035 6312 : JS_ASSERT(v.isBoolean());
6036 6312 : return BooleanObject::create(cx, v.toBoolean());
6037 : }
6038 :
6039 : JSBool
6040 479 : js_PrimitiveToObject(JSContext *cx, Value *vp)
6041 : {
6042 479 : JSObject *obj = PrimitiveToObject(cx, *vp);
6043 479 : if (!obj)
6044 0 : return false;
6045 :
6046 479 : vp->setObject(*obj);
6047 479 : return true;
6048 : }
6049 :
6050 : JSBool
6051 5156600 : js_ValueToObjectOrNull(JSContext *cx, const Value &v, JSObject **objp)
6052 : {
6053 : JSObject *obj;
6054 :
6055 5156600 : if (v.isObjectOrNull()) {
6056 93154 : obj = v.toObjectOrNull();
6057 5063446 : } else if (v.isUndefined()) {
6058 1267 : obj = NULL;
6059 : } else {
6060 5062179 : obj = PrimitiveToObject(cx, v);
6061 5062179 : if (!obj)
6062 0 : return false;
6063 : }
6064 5156600 : *objp = obj;
6065 5156600 : return true;
6066 : }
6067 :
6068 : namespace js {
6069 :
6070 : /* Callers must handle the already-object case . */
6071 : JSObject *
6072 1313 : ToObjectSlow(JSContext *cx, Value *vp)
6073 : {
6074 1313 : JS_ASSERT(!vp->isMagic());
6075 1313 : JS_ASSERT(!vp->isObject());
6076 :
6077 1313 : if (vp->isNullOrUndefined()) {
6078 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_CONVERT_TO,
6079 20 : vp->isNull() ? "null" : "undefined", "object");
6080 20 : return NULL;
6081 : }
6082 :
6083 1293 : JSObject *obj = PrimitiveToObject(cx, *vp);
6084 1293 : if (obj)
6085 1293 : vp->setObject(*obj);
6086 1293 : return obj;
6087 : }
6088 :
6089 : }
6090 :
6091 : JSObject *
6092 5040340 : js_ValueToNonNullObject(JSContext *cx, const Value &v)
6093 : {
6094 : JSObject *obj;
6095 :
6096 5040340 : if (!js_ValueToObjectOrNull(cx, v, &obj))
6097 0 : return NULL;
6098 5040340 : if (!obj)
6099 742 : js_ReportIsNullOrUndefined(cx, JSDVG_SEARCH_STACK, v, NULL);
6100 5040340 : return obj;
6101 : }
6102 :
6103 : #ifdef DEBUG
6104 : void
6105 359079 : js_PrintObjectSlotName(JSTracer *trc, char *buf, size_t bufsize)
6106 : {
6107 359079 : JS_ASSERT(trc->debugPrinter == js_PrintObjectSlotName);
6108 :
6109 359079 : JSObject *obj = (JSObject *)trc->debugPrintArg;
6110 359079 : uint32_t slot = uint32_t(trc->debugPrintIndex);
6111 :
6112 : const Shape *shape;
6113 359079 : if (obj->isNative()) {
6114 359079 : shape = obj->lastProperty();
6115 5357688 : while (shape && (!shape->hasSlot() || shape->slot() != slot))
6116 4639530 : shape = shape->previous();
6117 : } else {
6118 0 : shape = NULL;
6119 : }
6120 :
6121 359079 : if (!shape) {
6122 74520 : const char *slotname = NULL;
6123 74520 : if (obj->isGlobal()) {
6124 : #define JS_PROTO(name,code,init) \
6125 : if ((code) == slot) { slotname = js_##name##_str; goto found; }
6126 : #include "jsproto.tbl"
6127 : #undef JS_PROTO
6128 : }
6129 : found:
6130 74520 : if (slotname)
6131 2160 : JS_snprintf(buf, bufsize, "CLASS_OBJECT(%s)", slotname);
6132 : else
6133 72360 : JS_snprintf(buf, bufsize, "**UNKNOWN SLOT %ld**", (long)slot);
6134 : } else {
6135 284559 : jsid propid = shape->propid();
6136 284559 : if (JSID_IS_INT(propid)) {
6137 0 : JS_snprintf(buf, bufsize, "%ld", (long)JSID_TO_INT(propid));
6138 284559 : } else if (JSID_IS_ATOM(propid)) {
6139 284559 : PutEscapedString(buf, bufsize, JSID_TO_ATOM(propid), 0);
6140 : } else {
6141 0 : JS_snprintf(buf, bufsize, "**FINALIZED ATOM KEY**");
6142 : }
6143 : }
6144 359079 : }
6145 : #endif
6146 :
6147 : static const Shape *
6148 447433 : LastConfigurableShape(JSObject *obj)
6149 : {
6150 9528990 : for (Shape::Range r(obj->lastProperty()->all()); !r.empty(); r.popFront()) {
6151 9515772 : const Shape *shape = &r.front();
6152 9515772 : if (shape->configurable())
6153 434215 : return shape;
6154 : }
6155 13218 : return NULL;
6156 : }
6157 :
6158 : bool
6159 447433 : js_ClearNative(JSContext *cx, JSObject *obj)
6160 : {
6161 : /* Remove all configurable properties from obj. */
6162 881648 : while (const Shape *shape = LastConfigurableShape(obj)) {
6163 434215 : if (!obj->removeProperty(cx, shape->propid()))
6164 0 : return false;
6165 : }
6166 :
6167 : /* Set all remaining writable plain data properties to undefined. */
6168 494550 : for (Shape::Range r(obj->lastProperty()->all()); !r.empty(); r.popFront()) {
6169 481332 : const Shape *shape = &r.front();
6170 1556168 : if (shape->isDataDescriptor() &&
6171 481332 : shape->writable() &&
6172 296752 : shape->hasDefaultSetter() &&
6173 296752 : shape->hasSlot()) {
6174 296752 : obj->nativeSetSlot(shape->slot(), UndefinedValue());
6175 : }
6176 : }
6177 13218 : return true;
6178 : }
6179 :
6180 : JSBool
6181 1090 : js_ReportGetterOnlyAssignment(JSContext *cx)
6182 : {
6183 : return JS_ReportErrorFlagsAndNumber(cx,
6184 : JSREPORT_WARNING | JSREPORT_STRICT |
6185 : JSREPORT_STRICT_MODE_ERROR,
6186 : js_GetErrorMessage, NULL,
6187 1090 : JSMSG_GETTER_ONLY);
6188 : }
6189 :
6190 : JS_FRIEND_API(JSBool)
6191 0 : js_GetterOnlyPropertyStub(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp)
6192 : {
6193 0 : JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_GETTER_ONLY);
6194 0 : return JS_FALSE;
6195 : }
6196 :
6197 : #ifdef DEBUG
6198 :
6199 : /*
6200 : * Routines to print out values during debugging. These are FRIEND_API to help
6201 : * the debugger find them and to support temporarily hacking js_Dump* calls
6202 : * into other code.
6203 : */
6204 :
6205 : void
6206 0 : dumpValue(const Value &v)
6207 : {
6208 0 : if (v.isNull())
6209 0 : fprintf(stderr, "null");
6210 0 : else if (v.isUndefined())
6211 0 : fprintf(stderr, "undefined");
6212 0 : else if (v.isInt32())
6213 0 : fprintf(stderr, "%d", v.toInt32());
6214 0 : else if (v.isDouble())
6215 0 : fprintf(stderr, "%g", v.toDouble());
6216 0 : else if (v.isString())
6217 0 : v.toString()->dump();
6218 0 : else if (v.isObject() && v.toObject().isFunction()) {
6219 0 : JSFunction *fun = v.toObject().toFunction();
6220 0 : if (fun->atom) {
6221 0 : fputs("<function ", stderr);
6222 0 : FileEscapedString(stderr, fun->atom, 0);
6223 : } else {
6224 0 : fputs("<unnamed function", stderr);
6225 : }
6226 0 : if (fun->isInterpreted()) {
6227 0 : JSScript *script = fun->script();
6228 : fprintf(stderr, " (%s:%u)",
6229 0 : script->filename ? script->filename : "", script->lineno);
6230 : }
6231 0 : fprintf(stderr, " at %p>", (void *) fun);
6232 0 : } else if (v.isObject()) {
6233 0 : JSObject *obj = &v.toObject();
6234 0 : Class *clasp = obj->getClass();
6235 : fprintf(stderr, "<%s%s at %p>",
6236 : clasp->name,
6237 : (clasp == &ObjectClass) ? "" : " object",
6238 0 : (void *) obj);
6239 0 : } else if (v.isBoolean()) {
6240 0 : if (v.toBoolean())
6241 0 : fprintf(stderr, "true");
6242 : else
6243 0 : fprintf(stderr, "false");
6244 0 : } else if (v.isMagic()) {
6245 0 : fprintf(stderr, "<invalid");
6246 : #ifdef DEBUG
6247 0 : switch (v.whyMagic()) {
6248 0 : case JS_ARRAY_HOLE: fprintf(stderr, " array hole"); break;
6249 0 : case JS_ARGS_HOLE: fprintf(stderr, " args hole"); break;
6250 0 : case JS_NATIVE_ENUMERATE: fprintf(stderr, " native enumeration"); break;
6251 0 : case JS_NO_ITER_VALUE: fprintf(stderr, " no iter value"); break;
6252 0 : case JS_GENERATOR_CLOSING: fprintf(stderr, " generator closing"); break;
6253 0 : default: fprintf(stderr, " ?!"); break;
6254 : }
6255 : #endif
6256 0 : fprintf(stderr, ">");
6257 : } else {
6258 0 : fprintf(stderr, "unexpected value");
6259 : }
6260 0 : }
6261 :
6262 : JS_FRIEND_API(void)
6263 0 : js_DumpValue(const Value &val)
6264 : {
6265 0 : dumpValue(val);
6266 0 : fputc('\n', stderr);
6267 0 : }
6268 :
6269 : JS_FRIEND_API(void)
6270 0 : js_DumpId(jsid id)
6271 : {
6272 0 : fprintf(stderr, "jsid %p = ", (void *) JSID_BITS(id));
6273 0 : dumpValue(IdToValue(id));
6274 0 : fputc('\n', stderr);
6275 0 : }
6276 :
6277 : static void
6278 0 : DumpProperty(JSObject *obj, const Shape &shape)
6279 : {
6280 0 : jsid id = shape.propid();
6281 0 : uint8_t attrs = shape.attributes();
6282 :
6283 0 : fprintf(stderr, " ((Shape *) %p) ", (void *) &shape);
6284 0 : if (attrs & JSPROP_ENUMERATE) fprintf(stderr, "enumerate ");
6285 0 : if (attrs & JSPROP_READONLY) fprintf(stderr, "readonly ");
6286 0 : if (attrs & JSPROP_PERMANENT) fprintf(stderr, "permanent ");
6287 0 : if (attrs & JSPROP_SHARED) fprintf(stderr, "shared ");
6288 0 : if (shape.isMethod()) fprintf(stderr, "method ");
6289 :
6290 0 : if (shape.hasGetterValue())
6291 0 : fprintf(stderr, "getterValue=%p ", (void *) shape.getterObject());
6292 0 : else if (!shape.hasDefaultGetter())
6293 0 : fprintf(stderr, "getterOp=%p ", JS_FUNC_TO_DATA_PTR(void *, shape.getterOp()));
6294 :
6295 0 : if (shape.hasSetterValue())
6296 0 : fprintf(stderr, "setterValue=%p ", (void *) shape.setterObject());
6297 0 : else if (!shape.hasDefaultSetter())
6298 0 : fprintf(stderr, "setterOp=%p ", JS_FUNC_TO_DATA_PTR(void *, shape.setterOp()));
6299 :
6300 0 : if (JSID_IS_ATOM(id))
6301 0 : JSID_TO_STRING(id)->dump();
6302 0 : else if (JSID_IS_INT(id))
6303 0 : fprintf(stderr, "%d", (int) JSID_TO_INT(id));
6304 : else
6305 0 : fprintf(stderr, "unknown jsid %p", (void *) JSID_BITS(id));
6306 :
6307 0 : uint32_t slot = shape.hasSlot() ? shape.maybeSlot() : SHAPE_INVALID_SLOT;
6308 0 : fprintf(stderr, ": slot %d", slot);
6309 0 : if (shape.hasSlot()) {
6310 0 : fprintf(stderr, " = ");
6311 0 : dumpValue(obj->getSlot(slot));
6312 0 : } else if (slot != SHAPE_INVALID_SLOT) {
6313 0 : fprintf(stderr, " (INVALID!)");
6314 : }
6315 0 : fprintf(stderr, "\n");
6316 0 : }
6317 :
6318 : void
6319 0 : JSObject::dump()
6320 : {
6321 0 : JSObject *obj = this;
6322 0 : fprintf(stderr, "object %p\n", (void *) obj);
6323 0 : Class *clasp = obj->getClass();
6324 0 : fprintf(stderr, "class %p %s\n", (void *)clasp, clasp->name);
6325 :
6326 0 : fprintf(stderr, "flags:");
6327 0 : if (obj->isDelegate()) fprintf(stderr, " delegate");
6328 0 : if (obj->isSystem()) fprintf(stderr, " system");
6329 0 : if (!obj->isExtensible()) fprintf(stderr, " not_extensible");
6330 0 : if (obj->isIndexed()) fprintf(stderr, " indexed");
6331 :
6332 0 : if (obj->isNative()) {
6333 0 : if (obj->inDictionaryMode())
6334 0 : fprintf(stderr, " inDictionaryMode");
6335 0 : if (obj->hasPropertyTable())
6336 0 : fprintf(stderr, " hasPropertyTable");
6337 : }
6338 0 : fprintf(stderr, "\n");
6339 :
6340 0 : if (obj->isDenseArray()) {
6341 0 : unsigned slots = obj->getDenseArrayInitializedLength();
6342 0 : fprintf(stderr, "elements\n");
6343 0 : for (unsigned i = 0; i < slots; i++) {
6344 0 : fprintf(stderr, " %3d: ", i);
6345 0 : dumpValue(obj->getDenseArrayElement(i));
6346 0 : fprintf(stderr, "\n");
6347 0 : fflush(stderr);
6348 : }
6349 0 : return;
6350 : }
6351 :
6352 0 : fprintf(stderr, "proto ");
6353 0 : dumpValue(ObjectOrNullValue(obj->getProto()));
6354 0 : fputc('\n', stderr);
6355 :
6356 0 : fprintf(stderr, "parent ");
6357 0 : dumpValue(ObjectOrNullValue(obj->getParent()));
6358 0 : fputc('\n', stderr);
6359 :
6360 0 : if (clasp->flags & JSCLASS_HAS_PRIVATE)
6361 0 : fprintf(stderr, "private %p\n", obj->getPrivate());
6362 :
6363 0 : if (!obj->isNative())
6364 0 : fprintf(stderr, "not native\n");
6365 :
6366 0 : unsigned reservedEnd = JSCLASS_RESERVED_SLOTS(clasp);
6367 0 : unsigned slots = obj->slotSpan();
6368 0 : unsigned stop = obj->isNative() ? reservedEnd : slots;
6369 0 : if (stop > 0)
6370 0 : fprintf(stderr, obj->isNative() ? "reserved slots:\n" : "slots:\n");
6371 0 : for (unsigned i = 0; i < stop; i++) {
6372 0 : fprintf(stderr, " %3d ", i);
6373 0 : if (i < reservedEnd)
6374 0 : fprintf(stderr, "(reserved) ");
6375 0 : fprintf(stderr, "= ");
6376 0 : dumpValue(obj->getSlot(i));
6377 0 : fputc('\n', stderr);
6378 : }
6379 :
6380 0 : if (obj->isNative()) {
6381 0 : fprintf(stderr, "properties:\n");
6382 0 : Vector<const Shape *, 8, SystemAllocPolicy> props;
6383 0 : for (Shape::Range r = obj->lastProperty()->all(); !r.empty(); r.popFront())
6384 0 : props.append(&r.front());
6385 0 : for (size_t i = props.length(); i-- != 0;)
6386 0 : DumpProperty(obj, *props[i]);
6387 : }
6388 0 : fputc('\n', stderr);
6389 : }
6390 :
6391 : static void
6392 0 : MaybeDumpObject(const char *name, JSObject *obj)
6393 : {
6394 0 : if (obj) {
6395 0 : fprintf(stderr, " %s: ", name);
6396 0 : dumpValue(ObjectValue(*obj));
6397 0 : fputc('\n', stderr);
6398 : }
6399 0 : }
6400 :
6401 : static void
6402 0 : MaybeDumpValue(const char *name, const Value &v)
6403 : {
6404 0 : if (!v.isNull()) {
6405 0 : fprintf(stderr, " %s: ", name);
6406 0 : dumpValue(v);
6407 0 : fputc('\n', stderr);
6408 : }
6409 0 : }
6410 :
6411 : JS_FRIEND_API(void)
6412 0 : js_DumpStackFrame(JSContext *cx, StackFrame *start)
6413 : {
6414 : /* This should only called during live debugging. */
6415 0 : FrameRegsIter i(cx, StackIter::GO_THROUGH_SAVED);
6416 0 : if (!start) {
6417 0 : if (i.done()) {
6418 0 : fprintf(stderr, "no stack for cx = %p\n", (void*) cx);
6419 0 : return;
6420 : }
6421 : } else {
6422 0 : while (!i.done() && i.fp() != start)
6423 0 : ++i;
6424 :
6425 0 : if (i.done()) {
6426 : fprintf(stderr, "fp = %p not found in cx = %p\n",
6427 0 : (void *)start, (void *)cx);
6428 0 : return;
6429 : }
6430 : }
6431 :
6432 0 : for (; !i.done(); ++i) {
6433 0 : StackFrame *const fp = i.fp();
6434 :
6435 0 : fprintf(stderr, "StackFrame at %p\n", (void *) fp);
6436 0 : if (fp->isFunctionFrame()) {
6437 0 : fprintf(stderr, "callee fun: ");
6438 0 : dumpValue(ObjectValue(fp->callee()));
6439 : } else {
6440 0 : fprintf(stderr, "global frame, no callee");
6441 : }
6442 0 : fputc('\n', stderr);
6443 :
6444 0 : if (fp->isScriptFrame()) {
6445 : fprintf(stderr, "file %s line %u\n",
6446 0 : fp->script()->filename, (unsigned) fp->script()->lineno);
6447 : }
6448 :
6449 0 : if (jsbytecode *pc = i.pc()) {
6450 0 : if (!fp->isScriptFrame()) {
6451 0 : fprintf(stderr, "*** pc && !script, skipping frame\n\n");
6452 0 : continue;
6453 : }
6454 0 : fprintf(stderr, " pc = %p\n", pc);
6455 0 : fprintf(stderr, " current op: %s\n", js_CodeName[*pc]);
6456 : }
6457 0 : Value *sp = i.sp();
6458 0 : fprintf(stderr, " slots: %p\n", (void *) fp->slots());
6459 0 : fprintf(stderr, " sp: %p = slots + %u\n", (void *) sp, (unsigned) (sp - fp->slots()));
6460 0 : if (sp - fp->slots() < 10000) { // sanity
6461 0 : for (Value *p = fp->slots(); p < sp; p++) {
6462 0 : fprintf(stderr, " %p: ", (void *) p);
6463 0 : dumpValue(*p);
6464 0 : fputc('\n', stderr);
6465 : }
6466 : }
6467 0 : if (fp->hasArgs()) {
6468 0 : fprintf(stderr, " actuals: %p (%u) ", (void *) fp->actualArgs(), (unsigned) fp->numActualArgs());
6469 0 : fprintf(stderr, " formals: %p (%u)\n", (void *) fp->formalArgs(), (unsigned) fp->numFormalArgs());
6470 : }
6471 0 : if (fp->hasCallObj()) {
6472 0 : fprintf(stderr, " has call obj: ");
6473 0 : dumpValue(ObjectValue(fp->callObj()));
6474 0 : fprintf(stderr, "\n");
6475 : }
6476 0 : MaybeDumpObject("argsobj", fp->maybeArgsObj());
6477 0 : MaybeDumpObject("blockChain", fp->maybeBlockChain());
6478 0 : if (!fp->isDummyFrame()) {
6479 0 : MaybeDumpValue("this", fp->thisValue());
6480 0 : fprintf(stderr, " rval: ");
6481 0 : dumpValue(fp->returnValue());
6482 : } else {
6483 0 : fprintf(stderr, "dummy frame");
6484 : }
6485 0 : fputc('\n', stderr);
6486 :
6487 0 : fprintf(stderr, " flags:");
6488 0 : if (fp->isConstructing())
6489 0 : fprintf(stderr, " constructing");
6490 0 : if (fp->isDebuggerFrame())
6491 0 : fprintf(stderr, " debugger");
6492 0 : if (fp->isEvalFrame())
6493 0 : fprintf(stderr, " eval");
6494 0 : if (fp->isYielding())
6495 0 : fprintf(stderr, " yielding");
6496 0 : if (fp->isGeneratorFrame())
6497 0 : fprintf(stderr, " generator");
6498 0 : fputc('\n', stderr);
6499 :
6500 0 : fprintf(stderr, " scopeChain: (JSObject *) %p\n", (void *) &fp->scopeChain());
6501 :
6502 0 : fputc('\n', stderr);
6503 : }
6504 : }
6505 :
6506 : #endif /* DEBUG */
6507 :
|