Imported Upstream version 1.0.0
[platform/upstream/js.git] / js / src / jsinterpinlines.h
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim: set ts=4 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 SpiderMonkey code.
18  *
19  * The Initial Developer of the Original Code is
20  * Mozilla Corporation.
21  * Portions created by the Initial Developer are Copyright (C) 2010
22  * the Initial Developer. All Rights Reserved.
23  *
24  * Contributor(s):
25  *   Luke Wagner <lw@mozilla.com>
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 jsinterpinlines_h__
42 #define jsinterpinlines_h__
43
44 #include "jsapi.h"
45 #include "jsbool.h"
46 #include "jsinterp.h"
47 #include "jsnum.h"
48 #include "jsprobes.h"
49 #include "jsstr.h"
50 #include "methodjit/MethodJIT.h"
51
52 #include "jsfuninlines.h"
53
54 inline void
55 JSStackFrame::initPrev(JSContext *cx)
56 {
57     JS_ASSERT(flags_ & JSFRAME_HAS_PREVPC);
58     if (JSFrameRegs *regs = cx->regs) {
59         prev_ = regs->fp;
60         prevpc_ = regs->pc;
61         JS_ASSERT_IF(!prev_->isDummyFrame() && !prev_->hasImacropc(),
62                      uint32(prevpc_ - prev_->script()->code) < prev_->script()->length);
63     } else {
64         prev_ = NULL;
65 #ifdef DEBUG
66         prevpc_ = (jsbytecode *)0xbadc;
67 #endif
68     }
69 }
70
71 inline void
72 JSStackFrame::resetGeneratorPrev(JSContext *cx)
73 {
74     flags_ |= JSFRAME_HAS_PREVPC;
75     initPrev(cx);
76 }
77
78 inline void
79 JSStackFrame::initCallFrame(JSContext *cx, JSObject &callee, JSFunction *fun,
80                             uint32 nactual, uint32 flagsArg)
81 {
82     JS_ASSERT((flagsArg & ~(JSFRAME_CONSTRUCTING |
83                             JSFRAME_OVERFLOW_ARGS |
84                             JSFRAME_UNDERFLOW_ARGS)) == 0);
85     JS_ASSERT(fun == callee.getFunctionPrivate());
86
87     /* Initialize stack frame members. */
88     flags_ = JSFRAME_FUNCTION | JSFRAME_HAS_PREVPC | JSFRAME_HAS_SCOPECHAIN | flagsArg;
89     exec.fun = fun;
90     args.nactual = nactual;  /* only need to write if over/under-flow */
91     scopeChain_ = callee.getParent();
92     initPrev(cx);
93     JS_ASSERT(!hasImacropc());
94     JS_ASSERT(!hasHookData());
95     JS_ASSERT(annotation() == NULL);
96
97     JS_ASSERT(!hasCallObj());
98 }
99
100 inline void
101 JSStackFrame::resetInvokeCallFrame()
102 {
103     /* Undo changes to frame made during execution; see initCallFrame */
104
105     JS_ASSERT(!(flags_ & ~(JSFRAME_FUNCTION |
106                            JSFRAME_OVERFLOW_ARGS |
107                            JSFRAME_UNDERFLOW_ARGS |
108                            JSFRAME_OVERRIDE_ARGS |
109                            JSFRAME_HAS_PREVPC |
110                            JSFRAME_HAS_RVAL |
111                            JSFRAME_HAS_SCOPECHAIN |
112                            JSFRAME_HAS_ANNOTATION |
113                            JSFRAME_FINISHED_IN_INTERPRETER)));
114     flags_ &= JSFRAME_FUNCTION |
115               JSFRAME_OVERFLOW_ARGS |
116               JSFRAME_HAS_PREVPC |
117               JSFRAME_UNDERFLOW_ARGS;
118
119     JS_ASSERT(exec.fun == callee().getFunctionPrivate());
120     scopeChain_ = callee().getParent();
121 }
122
123 inline void
124 JSStackFrame::initCallFrameCallerHalf(JSContext *cx, uint32 flagsArg,
125                                       void *ncode)
126 {
127     JS_ASSERT((flagsArg & ~(JSFRAME_CONSTRUCTING |
128                             JSFRAME_FUNCTION |
129                             JSFRAME_OVERFLOW_ARGS |
130                             JSFRAME_UNDERFLOW_ARGS)) == 0);
131
132     flags_ = JSFRAME_FUNCTION | flagsArg;
133     prev_ = cx->regs->fp;
134     ncode_ = ncode;
135 }
136
137 /*
138  * The "early prologue" refers to the members that are stored for the benefit
139  * of slow paths before initializing the rest of the members.
140  */
141 inline void
142 JSStackFrame::initCallFrameEarlyPrologue(JSFunction *fun, uint32 nactual)
143 {
144     exec.fun = fun;
145     if (flags_ & (JSFRAME_OVERFLOW_ARGS | JSFRAME_UNDERFLOW_ARGS))
146         args.nactual = nactual;
147 }
148
149 /*
150  * The "late prologue" refers to the members that are stored after having
151  * checked for stack overflow and formal/actual arg mismatch.
152  */
153 inline void
154 JSStackFrame::initCallFrameLatePrologue()
155 {
156     SetValueRangeToUndefined(slots(), script()->nfixed);
157 }
158
159 inline void
160 JSStackFrame::initEvalFrame(JSContext *cx, JSScript *script, JSStackFrame *prev, uint32 flagsArg)
161 {
162     JS_ASSERT(flagsArg & JSFRAME_EVAL);
163     JS_ASSERT((flagsArg & ~(JSFRAME_EVAL | JSFRAME_DEBUGGER)) == 0);
164     JS_ASSERT(prev->flags_ & (JSFRAME_FUNCTION | JSFRAME_GLOBAL));
165
166     /* Copy (callee, thisv). */
167     js::Value *dstvp = (js::Value *)this - 2;
168     js::Value *srcvp = prev->flags_ & (JSFRAME_GLOBAL | JSFRAME_EVAL)
169                        ? (js::Value *)prev - 2
170                        : prev->formalArgs() - 2;
171     dstvp[0] = srcvp[0];
172     dstvp[1] = srcvp[1];
173     JS_ASSERT_IF(prev->flags_ & JSFRAME_FUNCTION,
174                  dstvp[0].toObject().isFunction());
175
176     /* Initialize stack frame members. */
177     flags_ = flagsArg | JSFRAME_HAS_PREVPC | JSFRAME_HAS_SCOPECHAIN |
178              (prev->flags_ & (JSFRAME_FUNCTION | JSFRAME_GLOBAL | JSFRAME_HAS_CALL_OBJ));
179     if (isFunctionFrame()) {
180         exec = prev->exec;
181         args.script = script;
182     } else {
183         exec.script = script;
184     }
185
186     scopeChain_ = &prev->scopeChain();
187     JS_ASSERT_IF(isFunctionFrame(), &callObj() == &prev->callObj());
188
189     prev_ = prev;
190     prevpc_ = prev->pc(cx);
191     JS_ASSERT(!hasImacropc());
192     JS_ASSERT(!hasHookData());
193     setAnnotation(prev->annotation());
194 }
195
196 inline void
197 JSStackFrame::initGlobalFrame(JSScript *script, JSObject &chain, uint32 flagsArg)
198 {
199     JS_ASSERT((flagsArg & ~(JSFRAME_EVAL | JSFRAME_DEBUGGER)) == 0);
200
201     /* Initialize (callee, thisv). */
202     js::Value *vp = (js::Value *)this - 2;
203     vp[0].setUndefined();
204     vp[1].setUndefined();  /* Set after frame pushed using thisObject */
205
206     /* Initialize stack frame members. */
207     flags_ = flagsArg | JSFRAME_GLOBAL | JSFRAME_HAS_PREVPC | JSFRAME_HAS_SCOPECHAIN;
208     exec.script = script;
209     args.script = (JSScript *)0xbad;
210     scopeChain_ = &chain;
211     prev_ = NULL;
212     JS_ASSERT(!hasImacropc());
213     JS_ASSERT(!hasHookData());
214     JS_ASSERT(annotation() == NULL);
215 }
216
217 inline void
218 JSStackFrame::initDummyFrame(JSContext *cx, JSObject &chain)
219 {
220     js::PodZero(this);
221     flags_ = JSFRAME_DUMMY | JSFRAME_HAS_PREVPC | JSFRAME_HAS_SCOPECHAIN;
222     initPrev(cx);
223     chain.isGlobal();
224     setScopeChainNoCallObj(chain);
225 }
226
227 inline void
228 JSStackFrame::stealFrameAndSlots(js::Value *vp, JSStackFrame *otherfp,
229                                  js::Value *othervp, js::Value *othersp)
230 {
231     JS_ASSERT(vp == (js::Value *)this - (otherfp->formalArgsEnd() - othervp));
232     JS_ASSERT(othervp == otherfp->actualArgs() - 2);
233     JS_ASSERT(othersp >= otherfp->slots());
234     JS_ASSERT(othersp <= otherfp->base() + otherfp->numSlots());
235
236     PodCopy(vp, othervp, othersp - othervp);
237     JS_ASSERT(vp == this->actualArgs() - 2);
238
239     /* Catch bad-touching of non-canonical args (e.g., generator_trace). */
240     if (otherfp->hasOverflowArgs())
241         Debug_SetValueRangeToCrashOnTouch(othervp, othervp + 2 + otherfp->numFormalArgs());
242
243     /*
244      * Repoint Call, Arguments, Block and With objects to the new live frame.
245      * Call and Arguments are done directly because we have pointers to them.
246      * Block and With objects are done indirectly through 'liveFrame'. See
247      * js_LiveFrameToFloating comment in jsiter.h.
248      */
249     if (hasCallObj()) {
250         callObj().setPrivate(this);
251         otherfp->flags_ &= ~JSFRAME_HAS_CALL_OBJ;
252         if (js_IsNamedLambda(fun())) {
253             JSObject *env = callObj().getParent();
254             JS_ASSERT(env->getClass() == &js_DeclEnvClass);
255             env->setPrivate(this);
256         }
257     }
258     if (hasArgsObj()) {
259         JSObject &args = argsObj();
260         JS_ASSERT(args.isArguments());
261         if (args.isNormalArguments())
262             args.setPrivate(this);
263         else
264             JS_ASSERT(!args.getPrivate());
265         otherfp->flags_ &= ~JSFRAME_HAS_ARGS_OBJ;
266     }
267 }
268
269 inline js::Value &
270 JSStackFrame::canonicalActualArg(uintN i) const
271 {
272     if (i < numFormalArgs())
273         return formalArg(i);
274     JS_ASSERT(i < numActualArgs());
275     return actualArgs()[i];
276 }
277
278 template <class Op>
279 inline void
280 JSStackFrame::forEachCanonicalActualArg(Op op)
281 {
282     uintN nformal = fun()->nargs;
283     js::Value *formals = formalArgsEnd() - nformal;
284     uintN nactual = numActualArgs();
285     if (nactual <= nformal) {
286         uintN i = 0;
287         js::Value *actualsEnd = formals + nactual;
288         for (js::Value *p = formals; p != actualsEnd; ++p, ++i)
289             op(i, p);
290     } else {
291         uintN i = 0;
292         js::Value *formalsEnd = formalArgsEnd();
293         for (js::Value *p = formals; p != formalsEnd; ++p, ++i)
294             op(i, p);
295         js::Value *actuals = formalsEnd - (nactual + 2);
296         js::Value *actualsEnd = formals - 2;
297         for (js::Value *p = actuals; p != actualsEnd; ++p, ++i)
298             op(i, p);
299     }
300 }
301
302 template <class Op>
303 inline void
304 JSStackFrame::forEachFormalArg(Op op)
305 {
306     js::Value *formals = formalArgsEnd() - fun()->nargs;
307     js::Value *formalsEnd = formalArgsEnd();
308     uintN i = 0;
309     for (js::Value *p = formals; p != formalsEnd; ++p, ++i)
310         op(i, p);
311 }
312
313 namespace js {
314
315 struct STATIC_SKIP_INFERENCE CopyNonHoleArgsTo
316 {
317     CopyNonHoleArgsTo(JSObject *aobj, Value *dst) : aobj(aobj), dst(dst) {}
318     JSObject *aobj;
319     Value *dst;
320     void operator()(uintN argi, Value *src) {
321         if (aobj->getArgsElement(argi).isMagic(JS_ARGS_HOLE))
322             dst->setUndefined();
323         else
324             *dst = *src;
325         ++dst;
326     }
327 };
328
329 struct CopyTo
330 {
331     Value *dst;
332     CopyTo(Value *dst) : dst(dst) {}
333     void operator()(uintN, Value *src) {
334         *dst++ = *src;
335     }
336 };
337
338 }
339
340 JS_ALWAYS_INLINE void
341 JSStackFrame::clearMissingArgs()
342 {
343     if (flags_ & JSFRAME_UNDERFLOW_ARGS)
344         SetValueRangeToUndefined(formalArgs() + numActualArgs(), formalArgsEnd());
345 }
346
347 inline bool
348 JSStackFrame::computeThis(JSContext *cx)
349 {
350     js::Value &thisv = thisValue();
351     if (thisv.isObject())
352         return true;
353     if (isFunctionFrame()) {
354         if (fun()->inStrictMode())
355             return true;
356         /*
357          * Eval function frames have their own |this| slot, which is a copy of the function's
358          * |this| slot. If we lazily wrap a primitive |this| in an eval function frame, the
359          * eval's frame will get the wrapper, but the function's frame will not. To prevent
360          * this, we always wrap a function's |this| before pushing an eval frame, and should
361          * thus never see an unwrapped primitive in a non-strict eval function frame.
362          */
363         JS_ASSERT(!isEvalFrame());
364     }
365     if (!js::BoxThisForVp(cx, &thisv - 1))
366         return false;
367     return true;
368 }
369
370 inline JSObject &
371 JSStackFrame::varobj(js::StackSegment *seg) const
372 {
373     JS_ASSERT(seg->contains(this));
374     return isFunctionFrame() ? callObj() : seg->getInitialVarObj();
375 }
376
377 inline JSObject &
378 JSStackFrame::varobj(JSContext *cx) const
379 {
380     JS_ASSERT(cx->activeSegment()->contains(this));
381     return isFunctionFrame() ? callObj() : cx->activeSegment()->getInitialVarObj();
382 }
383
384 inline uintN
385 JSStackFrame::numActualArgs() const
386 {
387     JS_ASSERT(hasArgs());
388     if (JS_UNLIKELY(flags_ & (JSFRAME_OVERFLOW_ARGS | JSFRAME_UNDERFLOW_ARGS)))
389         return hasArgsObj() ? argsObj().getArgsInitialLength() : args.nactual;
390     return numFormalArgs();
391 }
392
393 inline js::Value *
394 JSStackFrame::actualArgs() const
395 {
396     JS_ASSERT(hasArgs());
397     js::Value *argv = formalArgs();
398     if (JS_UNLIKELY(flags_ & JSFRAME_OVERFLOW_ARGS)) {
399         uintN nactual = hasArgsObj() ? argsObj().getArgsInitialLength() : args.nactual;
400         return argv - (2 + nactual);
401     }
402     return argv;
403 }
404
405 inline js::Value *
406 JSStackFrame::actualArgsEnd() const
407 {
408     JS_ASSERT(hasArgs());
409     if (JS_UNLIKELY(flags_ & JSFRAME_OVERFLOW_ARGS))
410         return formalArgs() - 2;
411     return formalArgs() + numActualArgs();
412 }
413
414 inline void
415 JSStackFrame::setArgsObj(JSObject &obj)
416 {
417     JS_ASSERT_IF(hasArgsObj(), &obj == args.obj);
418     JS_ASSERT_IF(!hasArgsObj(), numActualArgs() == obj.getArgsInitialLength());
419     args.obj = &obj;
420     flags_ |= JSFRAME_HAS_ARGS_OBJ;
421 }
422
423 inline void
424 JSStackFrame::clearArgsObj()
425 {
426     JS_ASSERT(hasArgsObj());
427     args.nactual = args.obj->getArgsInitialLength();
428     flags_ ^= JSFRAME_HAS_ARGS_OBJ;
429 }
430
431 inline void
432 JSStackFrame::setScopeChainNoCallObj(JSObject &obj)
433 {
434 #ifdef DEBUG
435     JS_ASSERT(&obj != NULL);
436     JSObject *callObjBefore = maybeCallObj();
437     if (!hasCallObj() && &scopeChain() != sInvalidScopeChain) {
438         for (JSObject *pobj = &scopeChain(); pobj; pobj = pobj->getParent())
439             JS_ASSERT_IF(pobj->isCall(), pobj->getPrivate() != this);
440     }
441 #endif
442     scopeChain_ = &obj;
443     flags_ |= JSFRAME_HAS_SCOPECHAIN;
444     JS_ASSERT(callObjBefore == maybeCallObj());
445 }
446
447 inline void
448 JSStackFrame::setScopeChainAndCallObj(JSObject &obj)
449 {
450     JS_ASSERT(&obj != NULL);
451     JS_ASSERT(!hasCallObj() && obj.isCall() && obj.getPrivate() == this);
452     scopeChain_ = &obj;
453     flags_ |= JSFRAME_HAS_SCOPECHAIN | JSFRAME_HAS_CALL_OBJ;
454 }
455
456 inline void
457 JSStackFrame::clearCallObj()
458 {
459     JS_ASSERT(hasCallObj());
460     flags_ ^= JSFRAME_HAS_CALL_OBJ;
461 }
462
463 inline JSObject &
464 JSStackFrame::callObj() const
465 {
466     JS_ASSERT(hasCallObj());
467     JSObject *pobj = &scopeChain();
468     while (JS_UNLIKELY(pobj->getClass() != &js_CallClass)) {
469         JS_ASSERT(js::IsCacheableNonGlobalScope(pobj) || pobj->isWith());
470         pobj = pobj->getParent();
471     }
472     return *pobj;
473 }
474
475 inline JSObject *
476 JSStackFrame::maybeCallObj() const
477 {
478     return hasCallObj() ? &callObj() : NULL;
479 }
480
481 namespace js {
482
483 class AutoPreserveEnumerators {
484     JSContext *cx;
485     JSObject *enumerators;
486
487   public:
488     AutoPreserveEnumerators(JSContext *cx) : cx(cx), enumerators(cx->enumerators)
489     {
490     }
491
492     ~AutoPreserveEnumerators()
493     {
494         cx->enumerators = enumerators;
495     }
496 };
497
498 struct AutoInterpPreparer  {
499     JSContext *cx;
500     JSScript *script;
501
502     AutoInterpPreparer(JSContext *cx, JSScript *script)
503       : cx(cx), script(script)
504     {
505         cx->interpLevel++;
506     }
507
508     ~AutoInterpPreparer()
509     {
510         --cx->interpLevel;
511     }
512 };
513
514 inline void
515 PutActivationObjects(JSContext *cx, JSStackFrame *fp)
516 {
517     JS_ASSERT(!fp->isYielding());
518     JS_ASSERT(!fp->isEvalFrame() || fp->script()->strictModeCode);
519
520     /* The order is important as js_PutCallObject needs to access argsObj. */
521     if (fp->hasCallObj()) {
522         js_PutCallObject(cx, fp);
523     } else if (fp->hasArgsObj()) {
524         js_PutArgsObject(cx, fp);
525     }
526 }
527
528 /*
529  * FIXME Remove with bug 635811
530  *
531  * NB: a non-strict eval frame aliases its non-eval-parent's call/args object.
532  */
533 inline void
534 PutOwnedActivationObjects(JSContext *cx, JSStackFrame *fp)
535 {
536     JS_ASSERT(!fp->isYielding());
537     if (!fp->isEvalFrame() || fp->script()->strictModeCode)
538         PutActivationObjects(cx, fp);
539 }
540
541 class InvokeSessionGuard
542 {
543     InvokeArgsGuard args_;
544     InvokeFrameGuard frame_;
545     Value savedCallee_, savedThis_;
546     Value *formals_, *actuals_;
547     unsigned nformals_;
548     JSScript *script_;
549     Value *stackLimit_;
550     jsbytecode *stop_;
551
552     bool optimized() const { return frame_.pushed(); }
553
554   public:
555     InvokeSessionGuard() : args_(), frame_() {}
556     inline ~InvokeSessionGuard();
557
558     bool start(JSContext *cx, const Value &callee, const Value &thisv, uintN argc);
559     bool invoke(JSContext *cx) const;
560
561     bool started() const {
562         return args_.pushed();
563     }
564
565     Value &operator[](unsigned i) const {
566         JS_ASSERT(i < argc());
567         Value &arg = i < nformals_ ? formals_[i] : actuals_[i];
568         JS_ASSERT_IF(optimized(), &arg == &frame_.fp()->canonicalActualArg(i));
569         JS_ASSERT_IF(!optimized(), &arg == &args_[i]);
570         return arg;
571     }
572
573     uintN argc() const {
574         return args_.argc();
575     }
576
577     const Value &rval() const {
578         return optimized() ? frame_.fp()->returnValue() : args_.rval();
579     }
580 };
581
582 inline
583 InvokeSessionGuard::~InvokeSessionGuard()
584 {
585     if (frame_.pushed())
586         PutActivationObjects(frame_.pushedFrameContext(), frame_.fp());
587 }
588
589 inline bool
590 InvokeSessionGuard::invoke(JSContext *cx) const
591 {
592     /* N.B. Must be kept in sync with Invoke */
593
594     /* Refer to canonical (callee, this) for optimized() sessions. */
595     formals_[-2] = savedCallee_;
596     formals_[-1] = savedThis_;
597
598 #ifdef JS_METHODJIT
599     void *code;
600     if (!optimized() || !(code = script_->getJIT(false /* !constructing */)->invokeEntry))
601 #else
602     if (!optimized())
603 #endif
604         return Invoke(cx, args_, 0);
605
606     /* Clear any garbage left from the last Invoke. */
607     JSStackFrame *fp = frame_.fp();
608     fp->clearMissingArgs();
609     PutActivationObjects(cx, frame_.fp());
610     fp->resetInvokeCallFrame();
611     SetValueRangeToUndefined(fp->slots(), script_->nfixed);
612
613     JSBool ok;
614     {
615         AutoPreserveEnumerators preserve(cx);
616         Probes::enterJSFun(cx, fp->fun(), script_);
617 #ifdef JS_METHODJIT
618         AutoInterpPreparer prepareInterp(cx, script_);
619         ok = mjit::EnterMethodJIT(cx, fp, code, stackLimit_);
620         cx->regs->pc = stop_;
621 #else
622         cx->regs->pc = script_->code;
623         ok = Interpret(cx, cx->fp());
624 #endif
625         Probes::exitJSFun(cx, fp->fun(), script_);
626     }
627
628     /* Don't clobber callee with rval; rval gets read from fp->rval. */
629     return ok;
630 }
631
632 namespace detail {
633
634 template<typename T> class PrimitiveBehavior { };
635
636 template<>
637 class PrimitiveBehavior<JSString *> {
638   public:
639     static inline bool isType(const Value &v) { return v.isString(); }
640     static inline JSString *extract(const Value &v) { return v.toString(); }
641     static inline Class *getClass() { return &js_StringClass; }
642 };
643
644 template<>
645 class PrimitiveBehavior<bool> {
646   public:
647     static inline bool isType(const Value &v) { return v.isBoolean(); }
648     static inline bool extract(const Value &v) { return v.toBoolean(); }
649     static inline Class *getClass() { return &js_BooleanClass; }
650 };
651
652 template<>
653 class PrimitiveBehavior<double> {
654   public:
655     static inline bool isType(const Value &v) { return v.isNumber(); }
656     static inline double extract(const Value &v) { return v.toNumber(); }
657     static inline Class *getClass() { return &js_NumberClass; }
658 };
659
660 } // namespace detail
661
662 /*
663  * Compute the implicit |this| parameter for a call expression where the callee
664  * is an unqualified name reference.
665  *
666  * We can avoid computing |this| eagerly and push the implicit callee-coerced
667  * |this| value, undefined, according to this decision tree:
668  *
669  * 1. If the called value, funval, is not an object, bind |this| to undefined.
670  *
671  * 2. The nominal |this|, obj, has one of Block, Call, or DeclEnv class (this
672  *    is what IsCacheableNonGlobalScope tests). Such objects-as-scopes must be
673  *    censored.
674  *
675  * 3. obj is a global. There are several sub-cases:
676  *
677  * a) obj is a proxy: we try unwrapping it (see jswrapper.cpp) in order to find
678  *    a function object inside. If the proxy is not a wrapper, or else it wraps
679  *    a non-function, then bind |this| to undefined per ES5-strict/Harmony.
680  *
681  *    [Else fall through with callee pointing to an unwrapped function object.]
682  *
683  * b) If callee is a function (after unwrapping if necessary), check whether it
684  *    is interpreted and in strict mode. If so, then bind |this| to undefined
685  *    per ES5 strict.
686  *
687  * c) Now check that callee is scoped by the same global object as the object
688  *    in which its unqualified name was bound as a property. ES1-3 bound |this|
689  *    to the name's "Reference base object", which in the context of multiple
690  *    global objects may not be the callee's global. If globals match, bind
691  *    |this| to undefined.
692  *
693  *    This is a backward compatibility measure; see bug 634590.
694  *
695  * 4. Finally, obj is neither a declarative scope object to be censored, nor a
696  *    global where the callee requires no backward-compatible special handling
697  *    or future-proofing based on (explicit or imputed by Harmony status in the
698  *    proxy case) strict mode opt-in. Bind |this| to obj->thisObject().
699  *
700  * We set *vp to undefined early to reduce code size and bias this code for the
701  * common and future-friendly cases.
702  */
703 inline bool
704 ComputeImplicitThis(JSContext *cx, JSObject *obj, const Value &funval, Value *vp)
705 {
706     vp->setUndefined();
707
708     if (!funval.isObject())
709         return true;
710
711     if (!obj->isGlobal()) {
712         if (IsCacheableNonGlobalScope(obj))
713             return true;
714     } else {
715         JSObject *callee = &funval.toObject();
716
717         if (callee->isProxy()) {
718             callee = callee->unwrap();
719             if (!callee->isFunction())
720                 return true; // treat any non-wrapped-function proxy as strict
721         }
722         if (callee->isFunction()) {
723             JSFunction *fun = callee->getFunctionPrivate();
724             if (fun->isInterpreted() && fun->inStrictMode())
725                 return true;
726         }
727         if (callee->getGlobal() == cx->fp()->scopeChain().getGlobal())
728             return true;;
729     }
730
731     obj = obj->thisObject(cx);
732     if (!obj)
733         return false;
734
735     vp->setObject(*obj);
736     return true;
737 }
738
739 template <typename T>
740 bool
741 GetPrimitiveThis(JSContext *cx, Value *vp, T *v)
742 {
743     typedef detail::PrimitiveBehavior<T> Behavior;
744
745     const Value &thisv = vp[1];
746     if (Behavior::isType(thisv)) {
747         *v = Behavior::extract(thisv);
748         return true;
749     }
750
751     if (thisv.isObject() && thisv.toObject().getClass() == Behavior::getClass()) {
752         *v = Behavior::extract(thisv.toObject().getPrimitiveThis());
753         return true;
754     }
755
756     ReportIncompatibleMethod(cx, vp, Behavior::getClass());
757     return false;
758 }
759
760 /*
761  * Return an object on which we should look for the properties of |value|.
762  * This helps us implement the custom [[Get]] method that ES5's GetValue
763  * algorithm uses for primitive values, without actually constructing the
764  * temporary object that the specification does.
765  * 
766  * For objects, return the object itself. For string, boolean, and number
767  * primitive values, return the appropriate constructor's prototype. For
768  * undefined and null, throw an error and return NULL, attributing the
769  * problem to the value at |spindex| on the stack.
770  */
771 JS_ALWAYS_INLINE JSObject *
772 ValuePropertyBearer(JSContext *cx, const Value &v, int spindex)
773 {
774     if (v.isObject())
775         return &v.toObject();
776
777     JSProtoKey protoKey;
778     if (v.isString()) {
779         protoKey = JSProto_String;
780     } else if (v.isNumber()) {
781         protoKey = JSProto_Number;
782     } else if (v.isBoolean()) {
783         protoKey = JSProto_Boolean;
784     } else {
785         JS_ASSERT(v.isNull() || v.isUndefined());
786         js_ReportIsNullOrUndefined(cx, spindex, v, NULL);
787         return NULL;
788     }
789
790     JSObject *pobj;
791     if (!js_GetClassPrototype(cx, NULL, protoKey, &pobj))
792         return NULL;
793     return pobj;
794 }
795
796 static inline bool
797 ScriptEpilogue(JSContext *cx, JSStackFrame *fp, JSBool ok)
798 {
799     if (!fp->isExecuteFrame())
800         Probes::exitJSFun(cx, fp->maybeFun(), fp->maybeScript());
801
802     JSInterpreterHook hook =
803         fp->isExecuteFrame() ? cx->debugHooks->executeHook : cx->debugHooks->callHook;
804
805     void* hookData;
806     if (JS_UNLIKELY(hook != NULL) && (hookData = fp->maybeHookData()))
807         hook(cx, fp, JS_FALSE, &ok, hookData);
808
809     if (fp->isEvalFrame()) {
810         /*
811          * The parent (ancestor for nested eval) of a non-strict eval frame
812          * owns its activation objects. Strict mode eval frames own their own
813          * Call objects but never have an arguments object (the first non-eval
814          * parent frame has it).
815          */
816         if (fp->script()->strictModeCode) {
817             JS_ASSERT(!fp->isYielding());
818             JS_ASSERT(!fp->hasArgsObj());
819             JS_ASSERT(fp->hasCallObj());
820             JS_ASSERT(fp->callObj().callIsForEval());
821             js_PutCallObject(cx, fp);
822         }
823     } else {
824         /*
825          * Otherwise only function frames have activation objects. A yielding
826          * frame's activation objects are transferred to the floating frame,
827          * stored in the generator, and thus need not be synced.
828          */
829         if (fp->isFunctionFrame() && !fp->isYielding()) {
830             JS_ASSERT_IF(fp->hasCallObj(), !fp->callObj().callIsForEval());
831             PutActivationObjects(cx, fp);
832         }
833     }
834
835     /*
836      * If inline-constructing, replace primitive rval with the new object
837      * passed in via |this|, and instrument this constructor invocation.
838      */
839     if (fp->isConstructing() && ok) {
840         if (fp->returnValue().isPrimitive())
841             fp->setReturnValue(ObjectValue(fp->constructorThis()));
842         JS_RUNTIME_METER(cx->runtime, constructs);
843     }
844
845     return ok;
846 }
847
848 }
849
850 #endif /* jsinterpinlines_h__ */