Imported Upstream version 1.0.0
[platform/upstream/js.git] / js / src / jsobjinlines.h
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim: set ts=8 sw=4 et tw=99:
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 #ifndef jsobjinlines_h___
42 #define jsobjinlines_h___
43
44 #include <new>
45 #include "jsdate.h"
46 #include "jsfun.h"
47 #include "jsiter.h"
48 #include "jslock.h"
49 #include "jsobj.h"
50 #include "jsprobes.h"
51 #include "jspropertytree.h"
52 #include "jsproxy.h"
53 #include "jsscope.h"
54 #include "jsstaticcheck.h"
55 #include "jsxml.h"
56
57 /* Headers included for inline implementations used by this header. */
58 #include "jsbool.h"
59 #include "jscntxt.h"
60 #include "jsnum.h"
61 #include "jsscopeinlines.h"
62 #include "jsstr.h"
63
64 #include "jsfuninlines.h"
65 #include "jsgcinlines.h"
66 #include "jsprobes.h"
67
68 inline bool
69 JSObject::preventExtensions(JSContext *cx, js::AutoIdVector *props)
70 {
71     JS_ASSERT(isExtensible());
72
73     if (js::FixOp fix = getOps()->fix) {
74         bool success;
75         if (!fix(cx, this, &success, props))
76             return false;
77         if (!success) {
78             JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_CHANGE_EXTENSIBILITY);
79             return false;
80         }
81     } else {
82         if (!GetPropertyNames(cx, this, JSITER_HIDDEN | JSITER_OWNONLY, props))
83             return false;
84     }
85
86     if (isNative())
87         extensibleShapeChange(cx);
88
89     flags |= NOT_EXTENSIBLE;
90     return true;
91 }
92
93 inline bool
94 JSObject::brand(JSContext *cx)
95 {
96     JS_ASSERT(!generic());
97     JS_ASSERT(!branded());
98     JS_ASSERT(isNative());
99     generateOwnShape(cx);
100     if (js_IsPropertyCacheDisabled(cx))  // check for rt->shapeGen overflow
101         return false;
102     flags |= BRANDED;
103     return true;
104 }
105
106 inline bool
107 JSObject::unbrand(JSContext *cx)
108 {
109     JS_ASSERT(isNative());
110     if (branded()) {
111         generateOwnShape(cx);
112         if (js_IsPropertyCacheDisabled(cx))  // check for rt->shapeGen overflow
113             return false;
114         flags &= ~BRANDED;
115     }
116     setGeneric();
117     return true;
118 }
119
120 inline void
121 JSObject::syncSpecialEquality()
122 {
123     if (clasp->ext.equality)
124         flags |= JSObject::HAS_EQUALITY;
125 }
126
127 inline void
128 JSObject::finalize(JSContext *cx)
129 {
130     /* Cope with stillborn objects that have no map. */
131     if (!map)
132         return;
133
134     /* Finalize obj first, in case it needs map and slots. */
135     js::Class *clasp = getClass();
136     if (clasp->finalize)
137         clasp->finalize(cx, this);
138
139     js::Probes::finalizeObject(this);
140
141     finish(cx);
142 }
143
144 /*
145  * Property read barrier for deferred cloning of compiler-created function
146  * objects optimized as typically non-escaping, ad-hoc methods in obj.
147  */
148 inline const js::Shape *
149 JSObject::methodReadBarrier(JSContext *cx, const js::Shape &shape, js::Value *vp)
150 {
151     JS_ASSERT(canHaveMethodBarrier());
152     JS_ASSERT(hasMethodBarrier());
153     JS_ASSERT(nativeContains(shape));
154     JS_ASSERT(shape.isMethod());
155     JS_ASSERT(&shape.methodObject() == &vp->toObject());
156     JS_ASSERT(shape.writable());
157     JS_ASSERT(shape.slot != SHAPE_INVALID_SLOT);
158     JS_ASSERT(shape.hasDefaultSetter() || shape.setterOp() == js_watch_set);
159     JS_ASSERT(!isGlobal());  /* i.e. we are not changing the global shape */
160
161     JSObject *funobj = &vp->toObject();
162     JSFunction *fun = funobj->getFunctionPrivate();
163     JS_ASSERT(fun == funobj);
164     JS_ASSERT(FUN_NULL_CLOSURE(fun));
165
166     funobj = CloneFunctionObject(cx, fun, funobj->getParent());
167     if (!funobj)
168         return NULL;
169     funobj->setMethodObj(*this);
170
171     /*
172      * Replace the method property with an ordinary data property. This is
173      * equivalent to this->setProperty(cx, shape.id, vp) except that any
174      * watchpoint on the property is not triggered.
175      */
176     uint32 slot = shape.slot;
177     const js::Shape *newshape = methodShapeChange(cx, shape);
178     if (!newshape)
179         return NULL;
180     JS_ASSERT(!newshape->isMethod());
181     JS_ASSERT(newshape->slot == slot);
182     vp->setObject(*funobj);
183     nativeSetSlot(slot, *vp);
184
185 #ifdef DEBUG
186     if (cx->runtime->functionMeterFilename) {
187         JS_FUNCTION_METER(cx, mreadbarrier);
188
189         typedef JSRuntime::FunctionCountMap HM;
190         HM &h = cx->runtime->methodReadBarrierCountMap;
191         HM::AddPtr p = h.lookupForAdd(fun);
192         if (!p) {
193             h.add(p, fun, 1);
194         } else {
195             JS_ASSERT(p->key == fun);
196             ++p->value;
197         }
198     }
199 #endif
200     return newshape;
201 }
202
203 static JS_ALWAYS_INLINE bool
204 ChangesMethodValue(const js::Value &prev, const js::Value &v)
205 {
206     JSObject *prevObj;
207     return prev.isObject() && (prevObj = &prev.toObject())->isFunction() &&
208            (!v.isObject() || &v.toObject() != prevObj);
209 }
210
211 inline const js::Shape *
212 JSObject::methodWriteBarrier(JSContext *cx, const js::Shape &shape, const js::Value &v)
213 {
214     if (brandedOrHasMethodBarrier() && shape.slot != SHAPE_INVALID_SLOT) {
215         const js::Value &prev = nativeGetSlot(shape.slot);
216
217         if (ChangesMethodValue(prev, v)) {
218             JS_FUNCTION_METER(cx, mwritebarrier);
219             return methodShapeChange(cx, shape);
220         }
221     }
222     return &shape;
223 }
224
225 inline bool
226 JSObject::methodWriteBarrier(JSContext *cx, uint32 slot, const js::Value &v)
227 {
228     if (brandedOrHasMethodBarrier()) {
229         const js::Value &prev = nativeGetSlot(slot);
230
231         if (ChangesMethodValue(prev, v)) {
232             JS_FUNCTION_METER(cx, mwslotbarrier);
233             return methodShapeChange(cx, slot);
234         }
235     }
236     return true;
237 }
238
239 inline bool
240 JSObject::ensureClassReservedSlots(JSContext *cx)
241 {
242     return !nativeEmpty() || ensureClassReservedSlotsForEmptyObject(cx);
243 }
244
245 inline js::Value
246 JSObject::getReservedSlot(uintN index) const
247 {
248     return (index < numSlots()) ? getSlot(index) : js::UndefinedValue();
249 }
250
251 inline bool
252 JSObject::canHaveMethodBarrier() const
253 {
254     return isObject() || isFunction() || isPrimitive() || isDate();
255 }
256
257 inline bool
258 JSObject::isPrimitive() const
259 {
260     return isNumber() || isString() || isBoolean();
261 }
262
263 inline const js::Value &
264 JSObject::getPrimitiveThis() const
265 {
266     JS_ASSERT(isPrimitive());
267     return getSlot(JSSLOT_PRIMITIVE_THIS);
268 }
269
270 inline void
271 JSObject::setPrimitiveThis(const js::Value &pthis)
272 {
273     JS_ASSERT(isPrimitive());
274     setSlot(JSSLOT_PRIMITIVE_THIS, pthis);
275 }
276
277 inline /* gc::FinalizeKind */ unsigned
278 JSObject::finalizeKind() const
279 {
280     return js::gc::FinalizeKind(arena()->header()->thingKind);
281 }
282
283 inline size_t
284 JSObject::numFixedSlots() const
285 {
286     if (isFunction())
287         return JSObject::FUN_CLASS_RESERVED_SLOTS;
288     if (!hasSlotsArray())
289         return capacity;
290     return js::gc::GetGCKindSlots(js::gc::FinalizeKind(finalizeKind()));
291 }
292
293 inline size_t
294 JSObject::slotsAndStructSize(uint32 nslots) const
295 {
296     bool isFun = isFunction() && this == (JSObject*) getPrivate();
297
298     int ndslots = hasSlotsArray() ? nslots : 0;
299     int nfslots = isFun ? 0 : numFixedSlots();
300
301     return sizeof(js::Value) * (ndslots + nfslots)
302            + isFun ? sizeof(JSFunction) : sizeof(JSObject);
303 }
304
305 inline uint32
306 JSObject::getArrayLength() const
307 {
308     JS_ASSERT(isArray());
309     return (uint32)(size_t) getPrivate();
310 }
311
312 inline void
313 JSObject::setArrayLength(uint32 length)
314 {
315     JS_ASSERT(isArray());
316     setPrivate((void*) length);
317 }
318
319 inline uint32
320 JSObject::getDenseArrayCapacity()
321 {
322     JS_ASSERT(isDenseArray());
323     return numSlots();
324 }
325
326 inline js::Value*
327 JSObject::getDenseArrayElements()
328 {
329     JS_ASSERT(isDenseArray());
330     return getSlots();
331 }
332
333 inline const js::Value &
334 JSObject::getDenseArrayElement(uintN idx)
335 {
336     JS_ASSERT(isDenseArray());
337     return getSlot(idx);
338 }
339
340 inline js::Value *
341 JSObject::addressOfDenseArrayElement(uintN idx)
342 {
343     JS_ASSERT(isDenseArray());
344     return &getSlotRef(idx);
345 }
346
347 inline void
348 JSObject::setDenseArrayElement(uintN idx, const js::Value &val)
349 {
350     JS_ASSERT(isDenseArray());
351     setSlot(idx, val);
352 }
353
354 inline void
355 JSObject::shrinkDenseArrayElements(JSContext *cx, uintN cap)
356 {
357     JS_ASSERT(isDenseArray());
358     shrinkSlots(cx, cap);
359 }
360
361 inline void
362 JSObject::setArgsLength(uint32 argc)
363 {
364     JS_ASSERT(isArguments());
365     JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX);
366     JS_ASSERT(UINT32_MAX > (uint64(argc) << ARGS_PACKED_BITS_COUNT));
367     getSlotRef(JSSLOT_ARGS_LENGTH).setInt32(argc << ARGS_PACKED_BITS_COUNT);
368     JS_ASSERT(!isArgsLengthOverridden());
369 }
370
371 inline uint32
372 JSObject::getArgsInitialLength() const
373 {
374     JS_ASSERT(isArguments());
375     uint32 argc = uint32(getSlot(JSSLOT_ARGS_LENGTH).toInt32()) >> ARGS_PACKED_BITS_COUNT;
376     JS_ASSERT(argc <= JS_ARGS_LENGTH_MAX);
377     return argc;
378 }
379
380 inline void
381 JSObject::setArgsLengthOverridden()
382 {
383     JS_ASSERT(isArguments());
384     getSlotRef(JSSLOT_ARGS_LENGTH).getInt32Ref() |= ARGS_LENGTH_OVERRIDDEN_BIT;
385 }
386
387 inline bool
388 JSObject::isArgsLengthOverridden() const
389 {
390     JS_ASSERT(isArguments());
391     const js::Value &v = getSlot(JSSLOT_ARGS_LENGTH);
392     return v.toInt32() & ARGS_LENGTH_OVERRIDDEN_BIT;
393 }
394
395 inline js::ArgumentsData *
396 JSObject::getArgsData() const
397 {
398     JS_ASSERT(isArguments());
399     return (js::ArgumentsData *) getSlot(JSSLOT_ARGS_DATA).toPrivate();
400 }
401
402 inline void
403 JSObject::setArgsData(js::ArgumentsData *data)
404 {
405     JS_ASSERT(isArguments());
406     getSlotRef(JSSLOT_ARGS_DATA).setPrivate(data);
407 }
408
409 inline const js::Value &
410 JSObject::getArgsCallee() const
411 {
412     return getArgsData()->callee;
413 }
414
415 inline void
416 JSObject::setArgsCallee(const js::Value &callee)
417 {
418     getArgsData()->callee = callee;
419 }
420
421 inline const js::Value &
422 JSObject::getArgsElement(uint32 i) const
423 {
424     JS_ASSERT(isArguments());
425     JS_ASSERT(i < getArgsInitialLength());
426     return getArgsData()->slots[i];
427 }
428
429 inline js::Value *
430 JSObject::getArgsElements() const
431 {
432     JS_ASSERT(isArguments());
433     return getArgsData()->slots;
434 }
435
436 inline js::Value *
437 JSObject::addressOfArgsElement(uint32 i)
438 {
439     JS_ASSERT(isArguments());
440     JS_ASSERT(i < getArgsInitialLength());
441     return &getArgsData()->slots[i];
442 }
443
444 inline void
445 JSObject::setArgsElement(uint32 i, const js::Value &v)
446 {
447     JS_ASSERT(isArguments());
448     JS_ASSERT(i < getArgsInitialLength());
449     getArgsData()->slots[i] = v;
450 }
451
452 inline bool
453 JSObject::callIsForEval() const
454 {
455     JS_ASSERT(isCall());
456     JS_ASSERT(getSlot(JSSLOT_CALL_CALLEE).isObjectOrNull());
457     JS_ASSERT_IF(getSlot(JSSLOT_CALL_CALLEE).isObject(),
458                  getSlot(JSSLOT_CALL_CALLEE).toObject().isFunction());
459     return getSlot(JSSLOT_CALL_CALLEE).isNull();
460 }
461
462 inline JSStackFrame *
463 JSObject::maybeCallObjStackFrame() const
464 {
465     JS_ASSERT(isCall());
466     return reinterpret_cast<JSStackFrame *>(getPrivate());
467 }
468
469 inline void
470 JSObject::setCallObjCallee(JSObject *callee)
471 {
472     JS_ASSERT(isCall());
473     JS_ASSERT_IF(callee, callee->isFunction());
474     return getSlotRef(JSSLOT_CALL_CALLEE).setObjectOrNull(callee);
475 }
476
477 inline JSObject *
478 JSObject::getCallObjCallee() const
479 {
480     JS_ASSERT(isCall());
481     return getSlot(JSSLOT_CALL_CALLEE).toObjectOrNull();
482 }
483
484 inline JSFunction *
485 JSObject::getCallObjCalleeFunction() const
486 {
487     JS_ASSERT(isCall());
488     return getSlot(JSSLOT_CALL_CALLEE).toObject().getFunctionPrivate();
489 }
490
491 inline const js::Value &
492 JSObject::getCallObjArguments() const
493 {
494     JS_ASSERT(isCall());
495     JS_ASSERT(!callIsForEval());
496     return getSlot(JSSLOT_CALL_ARGUMENTS);
497 }
498
499 inline void
500 JSObject::setCallObjArguments(const js::Value &v)
501 {
502     JS_ASSERT(isCall());
503     JS_ASSERT(!callIsForEval());
504     setSlot(JSSLOT_CALL_ARGUMENTS, v);
505 }
506
507 inline const js::Value &
508 JSObject::callObjArg(uintN i) const
509 {
510     JS_ASSERT(isCall());
511     JS_ASSERT(i < getCallObjCalleeFunction()->nargs);
512     return getSlot(JSObject::CALL_RESERVED_SLOTS + i);
513 }
514
515 inline js::Value &
516 JSObject::callObjArg(uintN i)
517 {
518     JS_ASSERT(isCall());
519     JS_ASSERT(i < getCallObjCalleeFunction()->nargs);
520     return getSlotRef(JSObject::CALL_RESERVED_SLOTS + i);
521 }
522
523 inline const js::Value &
524 JSObject::callObjVar(uintN i) const
525 {
526     JSFunction *fun = getCallObjCalleeFunction();
527     JS_ASSERT(fun->nargs == fun->script()->bindings.countArgs());
528     JS_ASSERT(i < fun->script()->bindings.countVars());
529     return getSlot(JSObject::CALL_RESERVED_SLOTS + fun->nargs + i);
530 }
531
532 inline js::Value &
533 JSObject::callObjVar(uintN i)
534 {
535     JSFunction *fun = getCallObjCalleeFunction();
536     JS_ASSERT(fun->nargs == fun->script()->bindings.countArgs());
537     JS_ASSERT(i < fun->script()->bindings.countVars());
538     return getSlotRef(JSObject::CALL_RESERVED_SLOTS + fun->nargs + i);
539 }
540
541 inline const js::Value &
542 JSObject::getDateUTCTime() const
543 {
544     JS_ASSERT(isDate());
545     return getSlot(JSSLOT_DATE_UTC_TIME);
546 }
547
548 inline void 
549 JSObject::setDateUTCTime(const js::Value &time)
550 {
551     JS_ASSERT(isDate());
552     setSlot(JSSLOT_DATE_UTC_TIME, time);
553 }
554
555 inline js::Value *
556 JSObject::getFlatClosureUpvars() const
557 {
558 #ifdef DEBUG
559     JSFunction *fun = getFunctionPrivate();
560     JS_ASSERT(fun->isFlatClosure());
561     JS_ASSERT(fun->script()->bindings.countUpvars() == fun->script()->upvars()->length);
562 #endif
563     return (js::Value *) getSlot(JSSLOT_FLAT_CLOSURE_UPVARS).toPrivate();
564 }
565
566 inline js::Value
567 JSObject::getFlatClosureUpvar(uint32 i) const
568 {
569     JS_ASSERT(i < getFunctionPrivate()->script()->bindings.countUpvars());
570     return getFlatClosureUpvars()[i];
571 }
572
573 inline js::Value &
574 JSObject::getFlatClosureUpvar(uint32 i)
575 {
576     JS_ASSERT(i < getFunctionPrivate()->script()->bindings.countUpvars());
577     return getFlatClosureUpvars()[i];
578 }
579
580 inline void
581 JSObject::setFlatClosureUpvars(js::Value *upvars)
582 {
583     JS_ASSERT(isFunction());
584     JS_ASSERT(FUN_FLAT_CLOSURE(getFunctionPrivate()));
585     getSlotRef(JSSLOT_FLAT_CLOSURE_UPVARS).setPrivate(upvars);
586 }
587
588 inline bool
589 JSObject::hasMethodObj(const JSObject& obj) const
590 {
591     return JSSLOT_FUN_METHOD_OBJ < numSlots() &&
592            getSlot(JSSLOT_FUN_METHOD_OBJ).isObject() &&
593            &getSlot(JSSLOT_FUN_METHOD_OBJ).toObject() == &obj;
594 }
595
596 inline void
597 JSObject::setMethodObj(JSObject& obj)
598 {
599     getSlotRef(JSSLOT_FUN_METHOD_OBJ).setObject(obj);
600 }
601
602 inline js::NativeIterator *
603 JSObject::getNativeIterator() const
604 {
605     return (js::NativeIterator *) getPrivate();
606 }
607
608 inline void
609 JSObject::setNativeIterator(js::NativeIterator *ni)
610 {
611     setPrivate(ni);
612 }
613
614 inline JSLinearString *
615 JSObject::getNamePrefix() const
616 {
617     JS_ASSERT(isNamespace() || isQName());
618     const js::Value &v = getSlot(JSSLOT_NAME_PREFIX);
619     return !v.isUndefined() ? v.toString()->assertIsLinear() : NULL;
620 }
621
622 inline jsval
623 JSObject::getNamePrefixVal() const
624 {
625     JS_ASSERT(isNamespace() || isQName());
626     return js::Jsvalify(getSlot(JSSLOT_NAME_PREFIX));
627 }
628
629 inline void
630 JSObject::setNamePrefix(JSLinearString *prefix)
631 {
632     JS_ASSERT(isNamespace() || isQName());
633     setSlot(JSSLOT_NAME_PREFIX, prefix ? js::StringValue(prefix) : js::UndefinedValue());
634 }
635
636 inline void
637 JSObject::clearNamePrefix()
638 {
639     JS_ASSERT(isNamespace() || isQName());
640     setSlot(JSSLOT_NAME_PREFIX, js::UndefinedValue());
641 }
642
643 inline JSLinearString *
644 JSObject::getNameURI() const
645 {
646     JS_ASSERT(isNamespace() || isQName());
647     const js::Value &v = getSlot(JSSLOT_NAME_URI);
648     return !v.isUndefined() ? v.toString()->assertIsLinear() : NULL;
649 }
650
651 inline jsval
652 JSObject::getNameURIVal() const
653 {
654     JS_ASSERT(isNamespace() || isQName());
655     return js::Jsvalify(getSlot(JSSLOT_NAME_URI));
656 }
657
658 inline void
659 JSObject::setNameURI(JSLinearString *uri)
660 {
661     JS_ASSERT(isNamespace() || isQName());
662     setSlot(JSSLOT_NAME_URI, uri ? js::StringValue(uri) : js::UndefinedValue());
663 }
664
665 inline jsval
666 JSObject::getNamespaceDeclared() const
667 {
668     JS_ASSERT(isNamespace());
669     return js::Jsvalify(getSlot(JSSLOT_NAMESPACE_DECLARED));
670 }
671
672 inline void
673 JSObject::setNamespaceDeclared(jsval decl)
674 {
675     JS_ASSERT(isNamespace());
676     setSlot(JSSLOT_NAMESPACE_DECLARED, js::Valueify(decl));
677 }
678
679 inline JSLinearString *
680 JSObject::getQNameLocalName() const
681 {
682     JS_ASSERT(isQName());
683     const js::Value &v = getSlot(JSSLOT_QNAME_LOCAL_NAME);
684     return !v.isUndefined() ? v.toString()->assertIsLinear() : NULL;
685 }
686
687 inline jsval
688 JSObject::getQNameLocalNameVal() const
689 {
690     JS_ASSERT(isQName());
691     return js::Jsvalify(getSlot(JSSLOT_QNAME_LOCAL_NAME));
692 }
693
694 inline void
695 JSObject::setQNameLocalName(JSLinearString *name)
696 {
697     JS_ASSERT(isQName());
698     setSlot(JSSLOT_QNAME_LOCAL_NAME, name ? js::StringValue(name) : js::UndefinedValue());
699 }
700
701 inline JSObject *
702 JSObject::getWithThis() const
703 {
704     return &getSlot(JSSLOT_WITH_THIS).toObject();
705 }
706
707 inline void
708 JSObject::setWithThis(JSObject *thisp)
709 {
710     getSlotRef(JSSLOT_WITH_THIS).setObject(*thisp);
711 }
712
713 inline void
714 JSObject::init(JSContext *cx, js::Class *aclasp, JSObject *proto, JSObject *parent,
715                void *priv, bool useHoles)
716 {
717     clasp = aclasp;
718     flags = 0;
719
720 #ifdef DEBUG
721     /*
722      * NB: objShape must not be set here; rather, the caller must call setMap
723      * or setSharedNonNativeMap after calling init. To defend this requirement
724      * we set map to null in DEBUG builds, and set objShape to a value we then
725      * assert obj->shape() never returns.
726      */
727     map = NULL;
728     objShape = JSObjectMap::INVALID_SHAPE;
729 #endif
730
731     setProto(proto);
732     setParent(parent);
733
734     privateData = priv;
735     slots = fixedSlots();
736
737     /*
738      * Fill the fixed slots with undefined or array holes.  This object must
739      * already have its capacity filled in, as by js_NewGCObject.
740      */
741     JS_ASSERT(capacity == numFixedSlots());
742     ClearValueRange(slots, capacity, useHoles);
743
744     emptyShapes = NULL;
745 }
746
747 inline void
748 JSObject::finish(JSContext *cx)
749 {
750 #ifdef DEBUG
751     if (isNative())
752         JS_LOCK_RUNTIME_VOID(cx->runtime, cx->runtime->liveObjectProps -= propertyCount());
753 #endif
754     if (hasSlotsArray())
755         freeSlotsArray(cx);
756     if (emptyShapes)
757         cx->free(emptyShapes);
758 }
759
760 inline bool
761 JSObject::initSharingEmptyShape(JSContext *cx,
762                                 js::Class *aclasp,
763                                 JSObject *proto,
764                                 JSObject *parent,
765                                 void *privateValue,
766                                 /* js::gc::FinalizeKind */ unsigned kind)
767 {
768     init(cx, aclasp, proto, parent, privateValue, false);
769
770     JS_ASSERT(!isDenseArray());
771
772     js::EmptyShape *empty = proto->getEmptyShape(cx, aclasp, kind);
773     if (!empty)
774         return false;
775
776     setMap(empty);
777     return true;
778 }
779
780 inline void
781 JSObject::freeSlotsArray(JSContext *cx)
782 {
783     JS_ASSERT(hasSlotsArray());
784     cx->free(slots);
785 }
786
787 inline void
788 JSObject::revertToFixedSlots(JSContext *cx)
789 {
790     JS_ASSERT(hasSlotsArray());
791     size_t fixed = numFixedSlots();
792     JS_ASSERT(capacity >= fixed);
793     memcpy(fixedSlots(), slots, fixed * sizeof(js::Value));
794     freeSlotsArray(cx);
795     slots = fixedSlots();
796     capacity = fixed;
797 }
798
799 inline bool
800 JSObject::hasProperty(JSContext *cx, jsid id, bool *foundp, uintN flags)
801 {
802     JSObject *pobj;
803     JSProperty *prop;
804     JSAutoResolveFlags rf(cx, flags);
805     if (!lookupProperty(cx, id, &pobj, &prop))
806         return false;
807     *foundp = !!prop;
808     return true;
809 }
810
811 inline bool
812 JSObject::isCallable()
813 {
814     return isFunction() || getClass()->call;
815 }
816
817 static inline bool
818 js_IsCallable(const js::Value &v)
819 {
820     return v.isObject() && v.toObject().isCallable();
821 }
822
823 namespace js {
824
825 class AutoPropDescArrayRooter : private AutoGCRooter
826 {
827   public:
828     AutoPropDescArrayRooter(JSContext *cx)
829       : AutoGCRooter(cx, DESCRIPTORS), descriptors(cx)
830     { }
831
832     PropDesc *append() {
833         if (!descriptors.append(PropDesc()))
834             return NULL;
835         return &descriptors.back();
836     }
837
838     PropDesc& operator[](size_t i) {
839         JS_ASSERT(i < descriptors.length());
840         return descriptors[i];
841     }
842
843     friend void AutoGCRooter::trace(JSTracer *trc);
844
845   private:
846     PropDescArray descriptors;
847 };
848
849 class AutoPropertyDescriptorRooter : private AutoGCRooter, public PropertyDescriptor
850 {
851   public:
852     AutoPropertyDescriptorRooter(JSContext *cx) : AutoGCRooter(cx, DESCRIPTOR) {
853         obj = NULL;
854         attrs = 0;
855         getter = (PropertyOp) NULL;
856         setter = (StrictPropertyOp) NULL;
857         value.setUndefined();
858     }
859
860     AutoPropertyDescriptorRooter(JSContext *cx, PropertyDescriptor *desc)
861       : AutoGCRooter(cx, DESCRIPTOR)
862     {
863         obj = desc->obj;
864         attrs = desc->attrs;
865         getter = desc->getter;
866         setter = desc->setter;
867         value = desc->value;
868     }
869
870     friend void AutoGCRooter::trace(JSTracer *trc);
871 };
872
873 static inline bool
874 InitScopeForObject(JSContext* cx, JSObject* obj, js::Class *clasp, JSObject* proto,
875                    gc::FinalizeKind kind)
876 {
877     JS_ASSERT(clasp->isNative());
878     JS_ASSERT(proto == obj->getProto());
879
880     /* Share proto's emptyShape only if obj is similar to proto. */
881     js::EmptyShape *empty = NULL;
882
883     if (proto) {
884         if (proto->canProvideEmptyShape(clasp)) {
885             empty = proto->getEmptyShape(cx, clasp, kind);
886             if (!empty)
887                 goto bad;
888         }
889     }
890
891     if (!empty) {
892         empty = js::EmptyShape::create(cx, clasp);
893         if (!empty)
894             goto bad;
895         uint32 freeslot = JSSLOT_FREE(clasp);
896         if (freeslot > obj->numSlots() && !obj->allocSlots(cx, freeslot))
897             goto bad;
898     }
899
900     obj->setMap(empty);
901     return true;
902
903   bad:
904     /* The GC nulls map initially. It should still be null on error. */
905     JS_ASSERT(!obj->map);
906     return false;
907 }
908
909 /*
910  * Helper optimized for creating a native instance of the given class (not the
911  * class's prototype object). Use this in preference to NewObject, but use
912  * NewBuiltinClassInstance if you need the default class prototype as proto,
913  * and its parent global as parent.
914  */
915 static inline JSObject *
916 NewNativeClassInstance(JSContext *cx, Class *clasp, JSObject *proto,
917                        JSObject *parent, gc::FinalizeKind kind)
918 {
919     JS_ASSERT(proto);
920     JS_ASSERT(parent);
921
922     /*
923      * Allocate an object from the GC heap and initialize all its fields before
924      * doing any operation that can potentially trigger GC.
925      */
926     JSObject* obj = js_NewGCObject(cx, kind);
927
928     if (obj) {
929         /*
930          * Default parent to the parent of the prototype, which was set from
931          * the parent of the prototype's constructor.
932          */
933         bool useHoles = (clasp == &js_ArrayClass);
934         obj->init(cx, clasp, proto, parent, NULL, useHoles);
935
936         JS_ASSERT(proto->canProvideEmptyShape(clasp));
937         js::EmptyShape *empty = proto->getEmptyShape(cx, clasp, kind);
938
939         if (empty)
940             obj->setMap(empty);
941         else
942             obj = NULL;
943     }
944
945     return obj;
946 }
947
948 static inline JSObject *
949 NewNativeClassInstance(JSContext *cx, Class *clasp, JSObject *proto, JSObject *parent)
950 {
951     gc::FinalizeKind kind = gc::GetGCObjectKind(JSCLASS_RESERVED_SLOTS(clasp));
952     return NewNativeClassInstance(cx, clasp, proto, parent, kind);
953 }
954
955 bool
956 FindClassPrototype(JSContext *cx, JSObject *scope, JSProtoKey protoKey, JSObject **protop,
957                    Class *clasp);
958
959 /*
960  * Helper used to create Boolean, Date, RegExp, etc. instances of built-in
961  * classes with class prototypes of the same Class. See, e.g., jsdate.cpp,
962  * jsregexp.cpp, and js_PrimitiveToObject in jsobj.cpp. Use this to get the
963  * right default proto and parent for clasp in cx.
964  */
965 static inline JSObject *
966 NewBuiltinClassInstance(JSContext *cx, Class *clasp, gc::FinalizeKind kind)
967 {
968     VOUCH_DOES_NOT_REQUIRE_STACK();
969
970     JSProtoKey protoKey = JSCLASS_CACHED_PROTO_KEY(clasp);
971     JS_ASSERT(protoKey != JSProto_Null);
972
973     /* NB: inline-expanded and specialized version of js_GetClassPrototype. */
974     JSObject *global;
975     if (!cx->hasfp()) {
976         global = cx->globalObject;
977         OBJ_TO_INNER_OBJECT(cx, global);
978         if (!global)
979             return NULL;
980     } else {
981         global = cx->fp()->scopeChain().getGlobal();
982     }
983     JS_ASSERT(global->isGlobal());
984
985     const Value &v = global->getReservedSlot(JSProto_LIMIT + protoKey);
986     JSObject *proto;
987     if (v.isObject()) {
988         proto = &v.toObject();
989         JS_ASSERT(proto->getParent() == global);
990     } else {
991         if (!FindClassPrototype(cx, global, protoKey, &proto, clasp))
992             return NULL;
993     }
994
995     return NewNativeClassInstance(cx, clasp, proto, global, kind);
996 }
997
998 static inline JSObject *
999 NewBuiltinClassInstance(JSContext *cx, Class *clasp)
1000 {
1001     gc::FinalizeKind kind = gc::GetGCObjectKind(JSCLASS_RESERVED_SLOTS(clasp));
1002     return NewBuiltinClassInstance(cx, clasp, kind);
1003 }
1004
1005 static inline JSProtoKey
1006 GetClassProtoKey(js::Class *clasp)
1007 {
1008     JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(clasp);
1009     if (key != JSProto_Null)
1010         return key;
1011     if (clasp->flags & JSCLASS_IS_ANONYMOUS)
1012         return JSProto_Object;
1013     return JSProto_Null;
1014 }
1015
1016 namespace WithProto {
1017     enum e {
1018         Class = 0,
1019         Given = 1
1020     };
1021 }
1022
1023 /*
1024  * Create an instance of any class, native or not, JSFunction-sized or not.
1025  *
1026  * If withProto is 'Class':
1027  *    If proto is null:
1028  *      for a built-in class:
1029  *        use the memoized original value of the class constructor .prototype
1030  *        property object
1031  *      else if available
1032  *        the current value of .prototype
1033  *      else
1034  *        Object.prototype.
1035  *
1036  *    If parent is null, default it to proto->getParent() if proto is non
1037  *    null, else to null.
1038  *
1039  * If withProto is 'Given':
1040  *    We allocate an object with exactly the given proto.  A null parent
1041  *    defaults to proto->getParent() if proto is non-null (else to null).
1042  *
1043  * If isFunction is true, return a JSFunction-sized object. If isFunction is
1044  * false, return a normal object.
1045  *
1046  * Note that as a template, there will be lots of instantiations, which means
1047  * the internals will be specialized based on the template parameters.
1048  */
1049 static JS_ALWAYS_INLINE bool
1050 FindProto(JSContext *cx, js::Class *clasp, JSObject *parent, JSObject ** proto)
1051 {
1052     JSProtoKey protoKey = GetClassProtoKey(clasp);
1053     if (!js_GetClassPrototype(cx, parent, protoKey, proto, clasp))
1054         return false;
1055     if (!(*proto) && !js_GetClassPrototype(cx, parent, JSProto_Object, proto))
1056         return false;
1057
1058     return true;
1059 }
1060
1061 namespace detail
1062 {
1063 template <bool withProto, bool isFunction>
1064 static JS_ALWAYS_INLINE JSObject *
1065 NewObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent,
1066           gc::FinalizeKind kind)
1067 {
1068     /* Bootstrap the ur-object, and make it the default prototype object. */
1069     if (withProto == WithProto::Class && !proto) {
1070         if (!FindProto(cx, clasp, parent, &proto))
1071           return NULL;
1072     }
1073
1074     /*
1075      * Allocate an object from the GC heap and initialize all its fields before
1076      * doing any operation that can potentially trigger GC. Functions have a
1077      * larger non-standard allocation size.
1078      *
1079      * The should be specialized by the template.
1080      */
1081     JSObject* obj = isFunction ? js_NewGCFunction(cx) : js_NewGCObject(cx, kind);
1082     if (!obj)
1083         goto out;
1084
1085     /* This needs to match up with the size of JSFunction::data_padding. */
1086     JS_ASSERT_IF(isFunction, kind == gc::FINALIZE_OBJECT2);
1087
1088     /*
1089      * Default parent to the parent of the prototype, which was set from
1090      * the parent of the prototype's constructor.
1091      */
1092     obj->init(cx, clasp, proto,
1093               (!parent && proto) ? proto->getParent() : parent,
1094               NULL, clasp == &js_ArrayClass);
1095
1096     if (clasp->isNative()) {
1097         if (!InitScopeForObject(cx, obj, clasp, proto, kind)) {
1098             obj = NULL;
1099             goto out;
1100         }
1101     } else {
1102         obj->setSharedNonNativeMap();
1103     }
1104
1105 out:
1106     Probes::createObject(cx, obj);
1107     return obj;
1108 }
1109 } /* namespace detail */
1110
1111 static JS_ALWAYS_INLINE JSObject *
1112 NewFunction(JSContext *cx, JSObject *parent)
1113 {
1114     return detail::NewObject<WithProto::Class, true>(cx, &js_FunctionClass, NULL, parent,
1115                                                      gc::FINALIZE_OBJECT2);
1116 }
1117
1118 template <WithProto::e withProto>
1119 static JS_ALWAYS_INLINE JSObject *
1120 NewNonFunction(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent,
1121                gc::FinalizeKind kind)
1122 {
1123     return detail::NewObject<withProto, false>(cx, clasp, proto, parent, kind);
1124 }
1125
1126 template <WithProto::e withProto>
1127 static JS_ALWAYS_INLINE JSObject *
1128 NewNonFunction(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent)
1129 {
1130     gc::FinalizeKind kind = gc::GetGCObjectKind(JSCLASS_RESERVED_SLOTS(clasp));
1131     return detail::NewObject<withProto, false>(cx, clasp, proto, parent, kind);
1132 }
1133
1134 template <WithProto::e withProto>
1135 static JS_ALWAYS_INLINE JSObject *
1136 NewObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent,
1137           gc::FinalizeKind kind)
1138 {
1139     if (clasp == &js_FunctionClass)
1140         return detail::NewObject<withProto, true>(cx, clasp, proto, parent, kind);
1141     return detail::NewObject<withProto, false>(cx, clasp, proto, parent, kind);
1142 }
1143
1144 template <WithProto::e withProto>
1145 static JS_ALWAYS_INLINE JSObject *
1146 NewObject(JSContext *cx, js::Class *clasp, JSObject *proto, JSObject *parent)
1147 {
1148     gc::FinalizeKind kind = gc::GetGCObjectKind(JSCLASS_RESERVED_SLOTS(clasp));
1149     return NewObject<withProto>(cx, clasp, proto, parent, kind);
1150 }
1151
1152 /*
1153  * As for gc::GetGCObjectKind, where numSlots is a guess at the final size of
1154  * the object, zero if the final size is unknown.
1155  */
1156 static inline gc::FinalizeKind
1157 GuessObjectGCKind(size_t numSlots, bool isArray)
1158 {
1159     if (numSlots)
1160         return gc::GetGCObjectKind(numSlots);
1161     return isArray ? gc::FINALIZE_OBJECT8 : gc::FINALIZE_OBJECT4;
1162 }
1163
1164 /*
1165  * Get the GC kind to use for scripted 'new' on the given class.
1166  * FIXME bug 547327: estimate the size from the allocation site.
1167  */
1168 static inline gc::FinalizeKind
1169 NewObjectGCKind(JSContext *cx, js::Class *clasp)
1170 {
1171     if (clasp == &js_ArrayClass || clasp == &js_SlowArrayClass)
1172         return gc::FINALIZE_OBJECT8;
1173     if (clasp == &js_FunctionClass)
1174         return gc::FINALIZE_OBJECT2;
1175     return gc::FINALIZE_OBJECT4;
1176 }
1177
1178 /* Make an object with pregenerated shape from a NEWOBJECT bytecode. */
1179 static inline JSObject *
1180 CopyInitializerObject(JSContext *cx, JSObject *baseobj)
1181 {
1182     JS_ASSERT(baseobj->getClass() == &js_ObjectClass);
1183     JS_ASSERT(!baseobj->inDictionaryMode());
1184
1185     gc::FinalizeKind kind = gc::FinalizeKind(baseobj->finalizeKind());
1186     JSObject *obj = NewBuiltinClassInstance(cx, &js_ObjectClass, kind);
1187
1188     if (!obj || !obj->ensureSlots(cx, baseobj->numSlots()))
1189         return NULL;
1190
1191     obj->flags = baseobj->flags;
1192     obj->lastProp = baseobj->lastProp;
1193     obj->objShape = baseobj->objShape;
1194
1195     return obj;
1196 }
1197
1198 /*
1199  * When we have an object of a builtin class, we don't quite know what its
1200  * valueOf/toString methods are, since these methods may have been overwritten
1201  * or shadowed. However, we can still do better than js_TryMethod by
1202  * hard-coding the necessary properties for us to find the native we expect.
1203  *
1204  * TODO: a per-thread shape-based cache would be faster and simpler.
1205  */
1206 static JS_ALWAYS_INLINE bool
1207 ClassMethodIsNative(JSContext *cx, JSObject *obj, Class *clasp, jsid methodid,
1208                     Native native)
1209 {
1210     JS_ASSERT(obj->getClass() == clasp);
1211
1212     if (HasNativeMethod(obj, methodid, native))
1213         return true;
1214
1215     JSObject *pobj = obj->getProto();
1216     return pobj && pobj->getClass() == clasp &&
1217            HasNativeMethod(pobj, methodid, native);
1218 }
1219
1220 } /* namespace js */
1221
1222 #endif /* jsobjinlines_h___ */