Imported Upstream version 1.0.0
[platform/upstream/js.git] / js / src / methodjit / InvokeHelpers.cpp
1 /* -*- Mode: C++; tab-width: 4; 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 Mozilla SpiderMonkey JavaScript 1.9 code, released
18  * May 28, 2008.
19  *
20  * The Initial Developer of the Original Code is
21  *   Brendan Eich <brendan@mozilla.org>
22  *
23  * Contributor(s):
24  *   David Anderson <danderson@mozilla.com>
25  *   David Mandelin <dmandelin@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 #include "jscntxt.h"
42 #include "jsscope.h"
43 #include "jsobj.h"
44 #include "jslibmath.h"
45 #include "jsiter.h"
46 #include "jsnum.h"
47 #include "jsxml.h"
48 #include "jsstaticcheck.h"
49 #include "jsbool.h"
50 #include "assembler/assembler/MacroAssemblerCodeRef.h"
51 #include "assembler/assembler/CodeLocation.h"
52 #include "jsiter.h"
53 #include "jstypes.h"
54 #include "methodjit/StubCalls.h"
55 #include "jstracer.h"
56 #include "jspropertycache.h"
57 #include "methodjit/MonoIC.h"
58 #include "jsanalyze.h"
59 #include "methodjit/BaseCompiler.h"
60 #include "methodjit/ICRepatcher.h"
61
62 #include "jsinterpinlines.h"
63 #include "jspropertycacheinlines.h"
64 #include "jsscopeinlines.h"
65 #include "jsscriptinlines.h"
66 #include "jsstrinlines.h"
67 #include "jsobjinlines.h"
68 #include "jscntxtinlines.h"
69 #include "jsatominlines.h"
70 #include "StubCalls-inl.h"
71 #include "MethodJIT-inl.h"
72
73 #include "jsautooplen.h"
74
75 using namespace js;
76 using namespace js::mjit;
77 using namespace JSC;
78
79 using ic::Repatcher;
80
81 static jsbytecode *
82 FindExceptionHandler(JSContext *cx)
83 {
84     JSStackFrame *fp = cx->fp();
85     JSScript *script = fp->script();
86
87 top:
88     if (cx->isExceptionPending() && JSScript::isValidOffset(script->trynotesOffset)) {
89         // The PC is updated before every stub call, so we can use it here.
90         unsigned offset = cx->regs->pc - script->main;
91
92         JSTryNoteArray *tnarray = script->trynotes();
93         for (unsigned i = 0; i < tnarray->length; ++i) {
94             JSTryNote *tn = &tnarray->vector[i];
95
96             // The following if condition actually tests two separate conditions:
97             //   (1) offset - tn->start >= tn->length
98             //       means the PC is not in the range of this try note, so we
99             //       should continue searching, after considering:
100             //   (2) offset - tn->start == tn->length
101             //       means the PC is at the first op of the exception handler
102             //       for this try note. This happens when an exception is thrown
103             //       during recording: the interpreter sets the PC to the handler
104             //       and then exits. In this case, we are in fact at the right
105             //       exception handler. 
106             //      
107             //       Hypothetically, the op we are at might have thrown an
108             //       exception, in which case this would not be the right handler.
109             //       But the first ops of exception handlers generated by our
110             //       bytecode compiler cannot throw, so this is not possible.
111             if (offset - tn->start > tn->length)
112                 continue;
113             if (tn->stackDepth > cx->regs->sp - fp->base())
114                 continue;
115
116             jsbytecode *pc = script->main + tn->start + tn->length;
117             JSBool ok = js_UnwindScope(cx, tn->stackDepth, JS_TRUE);
118             JS_ASSERT(cx->regs->sp == fp->base() + tn->stackDepth);
119
120             switch (tn->kind) {
121                 case JSTRY_CATCH:
122                   JS_ASSERT(js_GetOpcode(cx, fp->script(), pc) == JSOP_ENTERBLOCK);
123
124 #if JS_HAS_GENERATORS
125                   /* Catch cannot intercept the closing of a generator. */
126                   if (JS_UNLIKELY(cx->getPendingException().isMagic(JS_GENERATOR_CLOSING)))
127                       break;
128 #endif
129
130                   /*
131                    * Don't clear cx->throwing to save cx->exception from GC
132                    * until it is pushed to the stack via [exception] in the
133                    * catch block.
134                    */
135                   return pc;
136
137                 case JSTRY_FINALLY:
138                   /*
139                    * Push (true, exception) pair for finally to indicate that
140                    * [retsub] should rethrow the exception.
141                    */
142                   cx->regs->sp[0].setBoolean(true);
143                   cx->regs->sp[1] = cx->getPendingException();
144                   cx->regs->sp += 2;
145                   cx->clearPendingException();
146                   return pc;
147
148                 case JSTRY_ITER:
149                 {
150                   /*
151                    * This is similar to JSOP_ENDITER in the interpreter loop,
152                    * except the code now uses the stack slot normally used by
153                    * JSOP_NEXTITER, namely regs.sp[-1] before the regs.sp -= 2
154                    * adjustment and regs.sp[1] after, to save and restore the
155                    * pending exception.
156                    */
157                   Value v = cx->getPendingException();
158                   JS_ASSERT(js_GetOpcode(cx, fp->script(), pc) == JSOP_ENDITER);
159                   cx->clearPendingException();
160                   ok = !!js_CloseIterator(cx, &cx->regs->sp[-1].toObject());
161                   cx->regs->sp -= 1;
162                   if (!ok)
163                       goto top;
164                   cx->setPendingException(v);
165                 }
166             }
167         }
168     }
169
170     return NULL;
171 }
172
173 /*
174  * Clean up a frame and return.
175  */
176 static void
177 InlineReturn(VMFrame &f)
178 {
179     JSContext *cx = f.cx;
180     JSStackFrame *fp = f.regs.fp;
181
182     JS_ASSERT(f.fp() != f.entryfp);
183
184     JS_ASSERT(!js_IsActiveWithOrBlock(cx, &fp->scopeChain(), 0));
185
186     Value *newsp = fp->actualArgs() - 1;
187     newsp[-1] = fp->returnValue();
188     cx->stack().popInlineFrame(cx, fp->prev(), newsp);
189 }
190
191 void JS_FASTCALL
192 stubs::SlowCall(VMFrame &f, uint32 argc)
193 {
194     Value *vp = f.regs.sp - (argc + 2);
195
196     if (!Invoke(f.cx, InvokeArgsAlreadyOnTheStack(vp, argc), 0))
197         THROW();
198 }
199
200 void JS_FASTCALL
201 stubs::SlowNew(VMFrame &f, uint32 argc)
202 {
203     JSContext *cx = f.cx;
204     Value *vp = f.regs.sp - (argc + 2);
205
206     if (!InvokeConstructor(cx, InvokeArgsAlreadyOnTheStack(vp, argc)))
207         THROW();
208 }
209
210 /*
211  * This function must only be called after the early prologue, since it depends
212  * on fp->exec.fun.
213  */
214 static inline void
215 RemovePartialFrame(JSContext *cx, JSStackFrame *fp)
216 {
217     JSStackFrame *prev = fp->prev();
218     Value *newsp = (Value *)fp;
219     cx->stack().popInlineFrame(cx, prev, newsp);
220 }
221
222 /*
223  * HitStackQuota is called after the early prologue pushing the new frame would
224  * overflow f.stackLimit.
225  */
226 void JS_FASTCALL
227 stubs::HitStackQuota(VMFrame &f)
228 {
229     /* Include space to push another frame. */
230     uintN nvals = f.fp()->script()->nslots + VALUES_PER_STACK_FRAME;
231     JS_ASSERT(f.regs.sp == f.fp()->base());
232     if (f.cx->stack().bumpCommitAndLimit(f.entryfp, f.regs.sp, nvals, &f.stackLimit))
233         return;
234
235     /* Remove the current partially-constructed frame before throwing. */
236     RemovePartialFrame(f.cx, f.fp());
237     js_ReportOverRecursed(f.cx);
238     THROW();
239 }
240
241 /*
242  * This function must only be called after the early prologue, since it depends
243  * on fp->exec.fun.
244  */
245 void * JS_FASTCALL
246 stubs::FixupArity(VMFrame &f, uint32 nactual)
247 {
248     JSContext *cx = f.cx;
249     JSStackFrame *oldfp = f.fp();
250
251     JS_ASSERT(nactual != oldfp->numFormalArgs());
252
253     /*
254      * Grossssss! *move* the stack frame. If this ends up being perf-critical,
255      * we can figure out how to spot-optimize it. Be careful to touch only the
256      * members that have been initialized by initCallFrameCallerHalf and the
257      * early prologue.
258      */
259     uint32 flags         = oldfp->isConstructingFlag();
260     JSFunction *fun      = oldfp->fun();
261     void *ncode          = oldfp->nativeReturnAddress();
262
263     /* Pop the inline frame. */
264     f.fp() = oldfp->prev();
265     f.regs.sp = (Value*) oldfp;
266
267     /* Reserve enough space for a callee frame. */
268     JSStackFrame *newfp = cx->stack().getInlineFrameWithinLimit(cx, (Value*) oldfp, nactual,
269                                                                 fun, fun->script(), &flags,
270                                                                 f.entryfp, &f.stackLimit);
271     if (!newfp) {
272         /*
273          * The PC is not coherent with the current frame, so fix it up for
274          * exception handling.
275          */
276         f.regs.pc = f.jit()->nativeToPC(ncode);
277         THROWV(NULL);
278     }
279
280     /* Reset the part of the stack frame set by the caller. */
281     newfp->initCallFrameCallerHalf(cx, flags, ncode);
282
283     /* Reset the part of the stack frame set by the prologue up to now. */
284     newfp->initCallFrameEarlyPrologue(fun, nactual);
285
286     /* The caller takes care of assigning fp to regs. */
287     return newfp;
288 }
289
290 void * JS_FASTCALL
291 stubs::CompileFunction(VMFrame &f, uint32 nactual)
292 {
293     /*
294      * We have a partially constructed frame. That's not really good enough to
295      * compile though because we could throw, so get a full, adjusted frame.
296      */
297     JSContext *cx = f.cx;
298     JSStackFrame *fp = f.fp();
299
300     /*
301      * Since we can only use members set by initCallFrameCallerHalf,
302      * we must carefully extract the callee from the nactual.
303      */
304     JSObject &callee = fp->formalArgsEnd()[-(int(nactual) + 2)].toObject();
305     JSFunction *fun = callee.getFunctionPrivate();
306     JSScript *script = fun->script();
307
308     /*
309      * FixupArity/RemovePartialFrame expect to be called after the early
310      * prologue.
311      */
312     fp->initCallFrameEarlyPrologue(fun, nactual);
313
314     if (nactual != fp->numFormalArgs()) {
315         fp = (JSStackFrame *)FixupArity(f, nactual);
316         if (!fp)
317             return NULL;
318     }
319
320     /* Finish frame initialization. */
321     fp->initCallFrameLatePrologue();
322
323     /* These would have been initialized by the prologue. */
324     f.regs.fp = fp;
325     f.regs.sp = fp->base();
326     f.regs.pc = script->code;
327
328     if (fun->isHeavyweight() && !js_GetCallObject(cx, fp))
329         THROWV(NULL);
330
331     CompileStatus status = CanMethodJIT(cx, script, fp, CompileRequest_JIT);
332     if (status == Compile_Okay)
333         return script->getJIT(fp->isConstructing())->invokeEntry;
334
335     /* Function did not compile... interpret it. */
336     JSBool ok = Interpret(cx, fp);
337     InlineReturn(f);
338
339     if (!ok)
340         THROWV(NULL);
341
342     return NULL;
343 }
344
345 static inline bool
346 UncachedInlineCall(VMFrame &f, uint32 flags, void **pret, bool *unjittable, uint32 argc)
347 {
348     JSContext *cx = f.cx;
349     Value *vp = f.regs.sp - (argc + 2);
350     JSObject &callee = vp->toObject();
351     JSFunction *newfun = callee.getFunctionPrivate();
352     JSScript *newscript = newfun->script();
353
354     /* Get pointer to new frame/slots, prepare arguments. */
355     StackSpace &stack = cx->stack();
356     JSStackFrame *newfp = stack.getInlineFrameWithinLimit(cx, f.regs.sp, argc,
357                                                           newfun, newscript, &flags,
358                                                           f.entryfp, &f.stackLimit);
359     if (JS_UNLIKELY(!newfp))
360         return false;
361
362     /* Initialize frame, locals. */
363     newfp->initCallFrame(cx, callee, newfun, argc, flags);
364     SetValueRangeToUndefined(newfp->slots(), newscript->nfixed);
365
366     /* Officially push the frame. */
367     stack.pushInlineFrame(cx, newscript, newfp, &f.regs);
368     JS_ASSERT(newfp == f.regs.fp);
369
370     /* Try to compile if not already compiled. */
371     if (newscript->getJITStatus(newfp->isConstructing()) == JITScript_None) {
372         CompileStatus status = CanMethodJIT(cx, newscript, newfp, CompileRequest_Interpreter);
373         if (status == Compile_Error) {
374             /* A runtime exception was thrown, get out. */
375             InlineReturn(f);
376             return false;
377         }
378         if (status == Compile_Abort)
379             *unjittable = true;
380     }
381
382     /* Create call object now that we can't fail entering callee. */
383     if (newfun->isHeavyweight() && !js_GetCallObject(cx, newfp))
384         return false;
385
386     /* If newscript was successfully compiled, run it. */
387     if (JITScript *jit = newscript->getJIT(newfp->isConstructing())) {
388         *pret = jit->invokeEntry;
389         return true;
390     }
391
392     /* Otherwise, run newscript in the interpreter. */
393     bool ok = !!Interpret(cx, cx->fp());
394     InlineReturn(f);
395
396     *pret = NULL;
397     return ok;
398 }
399
400 void * JS_FASTCALL
401 stubs::UncachedNew(VMFrame &f, uint32 argc)
402 {
403     UncachedCallResult ucr;
404     UncachedNewHelper(f, argc, &ucr);
405     return ucr.codeAddr;
406 }
407
408 void
409 stubs::UncachedNewHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr)
410 {
411     ucr->init();
412
413     JSContext *cx = f.cx;
414     Value *vp = f.regs.sp - (argc + 2);
415
416     /* Try to do a fast inline call before the general Invoke path. */
417     if (IsFunctionObject(*vp, &ucr->fun) && ucr->fun->isInterpreted()) {
418         ucr->callee = &vp->toObject();
419         if (!UncachedInlineCall(f, JSFRAME_CONSTRUCTING, &ucr->codeAddr, &ucr->unjittable, argc))
420             THROW();
421     } else {
422         if (!InvokeConstructor(cx, InvokeArgsAlreadyOnTheStack(vp, argc)))
423             THROW();
424     }
425 }
426
427 void * JS_FASTCALL
428 stubs::UncachedCall(VMFrame &f, uint32 argc)
429 {
430     UncachedCallResult ucr;
431     UncachedCallHelper(f, argc, &ucr);
432     return ucr.codeAddr;
433 }
434
435 void JS_FASTCALL
436 stubs::Eval(VMFrame &f, uint32 argc)
437 {
438     Value *vp = f.regs.sp - (argc + 2);
439
440     JSObject *callee;
441     JSFunction *fun;
442
443     if (!IsFunctionObject(*vp, &callee) ||
444         !IsBuiltinEvalFunction((fun = callee->getFunctionPrivate())))
445     {
446         if (!Invoke(f.cx, InvokeArgsAlreadyOnTheStack(vp, argc), 0))
447             THROW();
448         return;
449     }
450
451     JS_ASSERT(f.regs.fp == f.cx->fp());
452     if (!DirectEval(f.cx, fun, argc, vp))
453         THROW();
454 }
455
456 void
457 stubs::UncachedCallHelper(VMFrame &f, uint32 argc, UncachedCallResult *ucr)
458 {
459     ucr->init();
460
461     JSContext *cx = f.cx;
462     Value *vp = f.regs.sp - (argc + 2);
463
464     if (IsFunctionObject(*vp, &ucr->callee)) {
465         ucr->callee = &vp->toObject();
466         ucr->fun = GET_FUNCTION_PRIVATE(cx, ucr->callee);
467
468         if (ucr->fun->isInterpreted()) {
469             if (!UncachedInlineCall(f, 0, &ucr->codeAddr, &ucr->unjittable, argc))
470                 THROW();
471             return;
472         }
473
474         if (ucr->fun->isNative()) {
475             if (!CallJSNative(cx, ucr->fun->u.n.native, argc, vp))
476                 THROW();
477             return;
478         }
479     }
480
481     if (!Invoke(f.cx, InvokeArgsAlreadyOnTheStack(vp, argc), 0))
482         THROW();
483
484     return;
485 }
486
487 void JS_FASTCALL
488 stubs::PutStrictEvalCallObject(VMFrame &f)
489 {
490     JS_ASSERT(f.fp()->isEvalFrame());
491     JS_ASSERT(f.fp()->script()->strictModeCode);
492     JS_ASSERT(f.fp()->hasCallObj());
493     js_PutCallObject(f.cx, f.fp());
494 }
495
496 void JS_FASTCALL
497 stubs::PutActivationObjects(VMFrame &f)
498 {
499     JS_ASSERT(f.fp()->hasCallObj() || f.fp()->hasArgsObj());
500     js::PutActivationObjects(f.cx, f.fp());
501 }
502
503 extern "C" void *
504 js_InternalThrow(VMFrame &f)
505 {
506     JSContext *cx = f.cx;
507
508     // It's possible that from within RunTracer(), Interpret() returned with
509     // an error and finished the frame (i.e., called ScriptEpilogue), but has
510     // not yet performed an inline return.
511     //
512     // In this case, RunTracer() has no choice but to propagate the error
513     // up to the method JIT, and thus to this function. But ScriptEpilogue()
514     // has already been called. Detect this, and avoid double-finishing the
515     // frame. See HandleErrorInExcessFrame() and bug 624100.
516     if (f.fp()->finishedInInterpreter()) {
517         // If it's the last frame, just propagate the failure up again.
518         if (f.fp() == f.entryfp)
519             return NULL;
520
521         InlineReturn(f);
522     }
523
524     // Make sure sp is up to date.
525     JS_ASSERT(cx->regs == &f.regs);
526
527     // Call the throw hook if necessary
528     JSThrowHook handler = f.cx->debugHooks->throwHook;
529     if (handler) {
530         Value rval;
531         switch (handler(cx, cx->fp()->script(), cx->regs->pc, Jsvalify(&rval),
532                         cx->debugHooks->throwHookData)) {
533           case JSTRAP_ERROR:
534             cx->clearPendingException();
535             return NULL;
536
537           case JSTRAP_RETURN:
538             cx->clearPendingException();
539             cx->fp()->setReturnValue(rval);
540             return JS_FUNC_TO_DATA_PTR(void *,
541                    cx->jaegerCompartment()->forceReturnTrampoline());
542
543           case JSTRAP_THROW:
544             cx->setPendingException(rval);
545             break;
546
547           default:
548             break;
549         }
550     }
551
552     jsbytecode *pc = NULL;
553     for (;;) {
554         pc = FindExceptionHandler(cx);
555         if (pc)
556             break;
557
558         // The JIT guarantees that ScriptEpilogue() has always been run
559         // upon exiting to its caller. This is important for consistency,
560         // where execution modes make similar guarantees about prologues
561         // and epilogues. RunTracer(), Interpret(), and Invoke() all
562         // rely on this property.
563         JS_ASSERT(!f.fp()->finishedInInterpreter());
564         js_UnwindScope(cx, 0, cx->isExceptionPending());
565         ScriptEpilogue(f.cx, f.fp(), false);
566
567         // Don't remove the last frame, this is the responsibility of
568         // JaegerShot()'s caller. We only guarantee that ScriptEpilogue()
569         // has been run.
570         if (f.entryfp == f.fp())
571             break;
572
573         JS_ASSERT(f.regs.sp == cx->regs->sp);
574         InlineReturn(f);
575     }
576
577     JS_ASSERT(f.regs.sp == cx->regs->sp);
578
579     if (!pc)
580         return NULL;
581
582     JSStackFrame *fp = cx->fp();
583     JSScript *script = fp->script();
584     return script->nativeCodeForPC(fp->isConstructing(), pc);
585 }
586
587 void JS_FASTCALL
588 stubs::GetCallObject(VMFrame &f)
589 {
590     JS_ASSERT(f.fp()->fun()->isHeavyweight());
591     if (!js_GetCallObject(f.cx, f.fp()))
592         THROW();
593 }
594
595 void JS_FASTCALL
596 stubs::CreateThis(VMFrame &f, JSObject *proto)
597 {
598     JSContext *cx = f.cx;
599     JSStackFrame *fp = f.fp();
600     JSObject *callee = &fp->callee();
601     JSObject *obj = js_CreateThisForFunctionWithProto(cx, callee, proto);
602     if (!obj)
603         THROW();
604     fp->formalArgs()[-1].setObject(*obj);
605 }
606
607 void JS_FASTCALL
608 stubs::EnterScript(VMFrame &f)
609 {
610     JSStackFrame *fp = f.fp();
611     JSContext *cx = f.cx;
612
613     if (fp->script()->debugMode) {
614         if (fp->isExecuteFrame()) {
615             JSInterpreterHook hook = cx->debugHooks->executeHook;
616             if (JS_UNLIKELY(hook != NULL))
617                 fp->setHookData(hook(cx, fp, JS_TRUE, 0, cx->debugHooks->executeHookData));
618         } else {
619             JSInterpreterHook hook = cx->debugHooks->callHook;
620             if (JS_UNLIKELY(hook != NULL))
621                 fp->setHookData(hook(cx, fp, JS_TRUE, 0, cx->debugHooks->callHookData));
622         }
623     }
624
625     Probes::enterJSFun(cx, fp->maybeFun(), fp->script());
626 }
627
628 void JS_FASTCALL
629 stubs::LeaveScript(VMFrame &f)
630 {
631     JSStackFrame *fp = f.fp();
632     JSContext *cx = f.cx;
633     Probes::exitJSFun(cx, fp->maybeFun(), fp->maybeScript());
634
635     if (fp->script()->debugMode) {
636         void *hookData;
637         JSInterpreterHook hook =
638             fp->isExecuteFrame() ? cx->debugHooks->executeHook : cx->debugHooks->callHook;
639
640         if (JS_UNLIKELY(hook != NULL) && (hookData = fp->maybeHookData())) {
641             JSBool ok = JS_TRUE;
642             hook(cx, fp, JS_FALSE, &ok, hookData);
643             if (!ok)
644                 THROW();
645         }
646     }
647 }
648
649 #ifdef JS_TRACER
650
651 /*
652  * Called when an error is in progress and the topmost frame could not handle
653  * it. This will unwind to a given frame, or find and align to an exception
654  * handler in the process.
655  */
656 static inline bool
657 HandleErrorInExcessFrame(VMFrame &f, JSStackFrame *stopFp, bool searchedTopmostFrame = true)
658 {
659     JSContext *cx = f.cx;
660
661     /*
662      * Callers of this called either Interpret() or JaegerShot(), which would
663      * have searched for exception handlers already. If we see stopFp, just
664      * return false. Otherwise, pop the frame, since it's guaranteed useless.
665      *
666      * Note that this also guarantees ScriptEpilogue() has been called.
667      */
668     JSStackFrame *fp = cx->fp();
669     if (searchedTopmostFrame) {
670         /*
671          * This is a special case meaning that fp->finishedInInterpreter() is
672          * true. If so, and fp == stopFp, our only choice is to propagate this
673          * error up, back to the method JIT, and then to js_InternalThrow,
674          * where this becomes a special case. See the comment there and bug
675          * 624100.
676          */
677         if (fp == stopFp)
678             return false;
679
680         /*
681          * Otherwise, the protocol here (like Invoke) is to assume that the
682          * execution mode finished the frame, and to just pop it.
683          */
684         InlineReturn(f);
685     }
686
687     /* Remove the bottom frame. */
688     bool returnOK = false;
689     for (;;) {
690         fp = cx->fp();
691
692         /* Clear imacros. */
693         if (fp->hasImacropc()) {
694             cx->regs->pc = fp->imacropc();
695             fp->clearImacropc();
696         }
697         JS_ASSERT(!fp->hasImacropc());
698
699         /* If there's an exception and a handler, set the pc and leave. */
700         if (cx->isExceptionPending()) {
701             jsbytecode *pc = FindExceptionHandler(cx);
702             if (pc) {
703                 cx->regs->pc = pc;
704                 returnOK = true;
705                 break;
706             }
707         }
708
709         /* Don't unwind if this was the entry frame. */
710         if (fp == stopFp)
711             break;
712
713         /* Unwind and return. */
714         returnOK &= bool(js_UnwindScope(cx, 0, returnOK || cx->isExceptionPending()));
715         returnOK = ScriptEpilogue(cx, fp, returnOK);
716         InlineReturn(f);
717     }
718
719     JS_ASSERT(&f.regs == cx->regs);
720     JS_ASSERT_IF(!returnOK, cx->fp() == stopFp);
721
722     return returnOK;
723 }
724
725 /* Returns whether the current PC has method JIT'd code. */
726 static inline void *
727 AtSafePoint(JSContext *cx)
728 {
729     JSStackFrame *fp = cx->fp();
730     if (fp->hasImacropc())
731         return false;
732
733     JSScript *script = fp->script();
734     return script->maybeNativeCodeForPC(fp->isConstructing(), cx->regs->pc);
735 }
736
737 /*
738  * Interprets until either a safe point is reached that has method JIT'd
739  * code, or the current frame tries to return.
740  */
741 static inline JSBool
742 PartialInterpret(VMFrame &f)
743 {
744     JSContext *cx = f.cx;
745     JSStackFrame *fp = cx->fp();
746
747 #ifdef DEBUG
748     JSScript *script = fp->script();
749     JS_ASSERT(!fp->finishedInInterpreter());
750     JS_ASSERT(fp->hasImacropc() ||
751               !script->maybeNativeCodeForPC(fp->isConstructing(), cx->regs->pc));
752 #endif
753
754     JSBool ok = JS_TRUE;
755     ok = Interpret(cx, fp, 0, JSINTERP_SAFEPOINT);
756
757     return ok;
758 }
759
760 JS_STATIC_ASSERT(JSOP_NOP == 0);
761
762 /*
763  * Returns whether the current PC would return, or if the frame has already
764  * been completed. This distinction avoids re-entering the interpreter or JIT
765  * to complete a JSOP_RETURN. Instead, that edge case is handled in
766  * HandleFinishedFrame. We could consider reducing complexity, and making this
767  * function return only "finishedInInterpreter", and always using the full VM
768  * machinery to fully finish frames.
769  */
770 static inline bool
771 FrameIsFinished(JSContext *cx)
772 {
773     JSOp op = JSOp(*cx->regs->pc);
774     return (op == JSOP_RETURN ||
775             op == JSOP_RETRVAL ||
776             op == JSOP_STOP)
777         ? true
778         : cx->fp()->finishedInInterpreter();
779 }
780
781
782 /* Simulate an inline_return by advancing the pc. */
783 static inline void
784 AdvanceReturnPC(JSContext *cx)
785 {
786     JS_ASSERT(*cx->regs->pc == JSOP_CALL ||
787               *cx->regs->pc == JSOP_NEW ||
788               *cx->regs->pc == JSOP_EVAL ||
789               *cx->regs->pc == JSOP_FUNCALL ||
790               *cx->regs->pc == JSOP_FUNAPPLY);
791     cx->regs->pc += JSOP_CALL_LENGTH;
792 }
793
794
795 /*
796  * Given a frame that is about to return, make sure its return value and
797  * activation objects are fixed up. Then, pop the frame and advance the
798  * current PC. Note that while we could enter the JIT at this point, the
799  * logic would still be necessary for the interpreter, so it's easier
800  * (and faster) to finish frames in C++ even if at a safe point here.
801  */
802 static bool
803 HandleFinishedFrame(VMFrame &f, JSStackFrame *entryFrame)
804 {
805     JSContext *cx = f.cx;
806
807     JS_ASSERT(FrameIsFinished(cx));
808
809     /*
810      * This is the most difficult and complicated piece of the tracer
811      * integration, and historically has been very buggy. The problem is that
812      * although this frame has to be popped (see RemoveExcessFrames), it may
813      * be at a JSOP_RETURN opcode, and it might not have ever been executed.
814      * That is, fp->rval may not be set to the top of the stack, and if it
815      * has, the stack has already been decremented. Note that fp->rval is not
816      * the only problem: the epilogue may never have been executed.
817      *
818      * Here are the edge cases and whether the frame has been exited cleanly:
819      *  1. No: A trace exited directly before a RETURN op, and the
820      *         interpreter never ran.
821      *  2. Yes: The interpreter exited cleanly.
822      *  3. No: The interpreter exited on a safe point. LEAVE_ON_SAFE_POINT
823      *         is not used in between JSOP_RETURN and advancing the PC,
824      *         therefore, it cannot have been run if at a safe point.
825      *  4. No: Somewhere in the RunTracer call tree, we removed a frame,
826      *         and we returned to a JSOP_RETURN opcode. Note carefully
827      *         that in this situation, FrameIsFinished() returns true!
828      *  5. Yes: The function exited in the method JIT, during
829      *         FinishExcessFrames() However, in this case, we'll never enter
830      *         HandleFinishedFrame(): we always immediately pop JIT'd frames.
831      *
832      * Since the only scenario where this fixup is NOT needed is a normal exit
833      * from the interpreter, we can cleanly check for this scenario by checking
834      * a bit it sets in the frame.
835      */
836     bool returnOK = true;
837     if (!cx->fp()->finishedInInterpreter()) {
838         if (JSOp(*cx->regs->pc) == JSOP_RETURN)
839             cx->fp()->setReturnValue(f.regs.sp[-1]);
840
841         returnOK = ScriptEpilogue(cx, cx->fp(), true);
842     }
843
844     JS_ASSERT_IF(cx->fp()->isFunctionFrame() &&
845                  !cx->fp()->isEvalFrame(),
846                  !cx->fp()->hasCallObj());
847
848     if (cx->fp() != entryFrame) {
849         InlineReturn(f);
850         AdvanceReturnPC(cx);
851     }
852
853     return returnOK;
854 }
855
856 /*
857  * Given a frame newer than the entry frame, try to finish it. If it's at a
858  * return position, pop the frame. If it's at a safe point, execute it in
859  * Jaeger code. Otherwise, try to interpret until a safe point.
860  *
861  * While this function is guaranteed to make progress, it may not actually
862  * finish or pop the current frame. It can either:
863  *   1) Finalize a finished frame, or
864  *   2) Finish and finalize the frame in the Method JIT, or
865  *   3) Interpret, which can:
866  *     a) Propagate an error, or
867  *     b) Finish the frame, but not finalize it, or
868  *     c) Abruptly leave at any point in the frame, or in a newer frame
869  *        pushed by a call, that has method JIT'd code.
870  */
871 static bool
872 EvaluateExcessFrame(VMFrame &f, JSStackFrame *entryFrame)
873 {
874     JSContext *cx = f.cx;
875     JSStackFrame *fp = cx->fp();
876
877     /*
878      * A "finished" frame is when the interpreter rested on a STOP,
879      * RETURN, RETRVAL, etc. We check for finished frames BEFORE looking
880      * for a safe point. If the frame was finished, we could have already
881      * called ScriptEpilogue(), and entering the JIT could call it twice.
882      */
883     if (!fp->hasImacropc() && FrameIsFinished(cx))
884         return HandleFinishedFrame(f, entryFrame);
885
886     if (void *ncode = AtSafePoint(cx)) {
887         if (!JaegerShotAtSafePoint(cx, ncode))
888             return false;
889         InlineReturn(f);
890         AdvanceReturnPC(cx);
891         return true;
892     }
893
894     return PartialInterpret(f);
895 }
896
897 /*
898  * Evaluate frames newer than the entry frame until all are gone. This will
899  * always leave f.regs.fp == entryFrame.
900  */
901 static bool
902 FinishExcessFrames(VMFrame &f, JSStackFrame *entryFrame)
903 {
904     JSContext *cx = f.cx;
905
906     while (cx->fp() != entryFrame || entryFrame->hasImacropc()) {
907         if (!EvaluateExcessFrame(f, entryFrame)) {
908             if (!HandleErrorInExcessFrame(f, entryFrame))
909                 return false;
910         }
911     }
912
913     return true;
914 }
915
916 #if defined JS_MONOIC
917 static void
918 UpdateTraceHintSingle(Repatcher &repatcher, JSC::CodeLocationJump jump, JSC::CodeLocationLabel target)
919 {
920     /*
921      * Hack: The value that will be patched is before the executable address,
922      * so to get protection right, just unprotect the general region around
923      * the jump.
924      */
925     repatcher.relink(jump, target);
926
927     JaegerSpew(JSpew_PICs, "relinking trace hint %p to %p\n",
928                jump.executableAddress(), target.executableAddress());
929 }
930
931 static void
932 DisableTraceHint(JITScript *jit, ic::TraceICInfo &ic)
933 {
934     Repatcher repatcher(jit);
935     UpdateTraceHintSingle(repatcher, ic.traceHint, ic.jumpTarget);
936
937     if (ic.hasSlowTraceHint)
938         UpdateTraceHintSingle(repatcher, ic.slowTraceHint, ic.jumpTarget);
939 }
940
941 static void
942 ResetTraceHintAt(JSScript *script, js::mjit::JITScript *jit,
943                  jsbytecode *pc, uint16_t index, bool full)
944 {
945     if (index >= jit->nTraceICs)
946         return;
947     ic::TraceICInfo &ic = jit->traceICs()[index];
948     if (!ic.initialized)
949         return;
950     
951     JS_ASSERT(ic.jumpTargetPC == pc);
952
953     JaegerSpew(JSpew_PICs, "Enabling trace IC %u in script %p\n", index, script);
954
955     Repatcher repatcher(jit);
956
957     UpdateTraceHintSingle(repatcher, ic.traceHint, ic.stubEntry);
958
959     if (ic.hasSlowTraceHint)
960         UpdateTraceHintSingle(repatcher, ic.slowTraceHint, ic.stubEntry);
961
962     if (full) {
963         ic.traceData = NULL;
964         ic.loopCounterStart = 1;
965         ic.loopCounter = ic.loopCounterStart;
966     }
967 }
968 #endif
969
970 void
971 js::mjit::ResetTraceHint(JSScript *script, jsbytecode *pc, uint16_t index, bool full)
972 {
973 #if JS_MONOIC
974     if (script->jitNormal)
975         ResetTraceHintAt(script, script->jitNormal, pc, index, full);
976
977     if (script->jitCtor)
978         ResetTraceHintAt(script, script->jitCtor, pc, index, full);
979 #endif
980 }
981
982 #if JS_MONOIC
983 void *
984 RunTracer(VMFrame &f, ic::TraceICInfo &ic)
985 #else
986 void *
987 RunTracer(VMFrame &f)
988 #endif
989 {
990     JSContext *cx = f.cx;
991     JSStackFrame *entryFrame = f.fp();
992     TracePointAction tpa;
993
994     /* :TODO: nuke PIC? */
995     if (!cx->traceJitEnabled)
996         return NULL;
997
998     /*
999      * Force initialization of the entry frame's scope chain and return value,
1000      * if necessary.  The tracer can query the scope chain without needing to
1001      * check the HAS_SCOPECHAIN flag, and the frame is guaranteed to have the
1002      * correct return value stored if we trace/interpret through to the end
1003      * of the frame.
1004      */
1005     entryFrame->scopeChain();
1006     entryFrame->returnValue();
1007
1008     bool blacklist;
1009     uintN inlineCallCount = 0;
1010     void **traceData;
1011     uintN *traceEpoch;
1012     uint32 *loopCounter;
1013     uint32 hits;
1014 #if JS_MONOIC
1015     traceData = &ic.traceData;
1016     traceEpoch = &ic.traceEpoch;
1017     loopCounter = &ic.loopCounter;
1018     *loopCounter = 1;
1019     hits = ic.loopCounterStart;
1020 #else
1021     traceData = NULL;
1022     traceEpoch = NULL;
1023     loopCounter = NULL;
1024     hits = 1;
1025 #endif
1026     tpa = MonitorTracePoint(f.cx, inlineCallCount, &blacklist, traceData, traceEpoch,
1027                             loopCounter, hits);
1028     JS_ASSERT(!TRACE_RECORDER(cx));
1029
1030 #if JS_MONOIC
1031     ic.loopCounterStart = *loopCounter;
1032     if (blacklist)
1033         DisableTraceHint(entryFrame->jit(), ic);
1034 #endif
1035
1036     // Even though ExecuteTree() bypasses the interpreter, it should propagate
1037     // error failures correctly.
1038     JS_ASSERT_IF(cx->isExceptionPending(), tpa == TPA_Error);
1039
1040         f.fp() = cx->fp();
1041     JS_ASSERT(f.fp() == cx->fp());
1042     switch (tpa) {
1043       case TPA_Nothing:
1044         return NULL;
1045
1046       case TPA_Error:
1047         if (!HandleErrorInExcessFrame(f, entryFrame, f.fp()->finishedInInterpreter()))
1048             THROWV(NULL);
1049         JS_ASSERT(!cx->fp()->hasImacropc());
1050         break;
1051
1052       case TPA_RanStuff:
1053       case TPA_Recorded:
1054         break;
1055     }
1056
1057     /*
1058      * The tracer could have dropped us off on any frame at any position.
1059      * Well, it could not have removed frames (recursion is disabled).
1060      *
1061      * Frames after the entryFrame cannot be entered via JaegerShotAtSafePoint()
1062      * unless each is at a safe point. We can JaegerShotAtSafePoint these
1063      * frames individually, but we must unwind to the entryFrame.
1064      *
1065      * Note carefully that JaegerShotAtSafePoint can resume methods at
1066      * arbitrary safe points whereas JaegerShot cannot.
1067      *
1068      * If we land on entryFrame without a safe point in sight, we'll end up
1069      * at the RETURN op. This is an edge case with two paths:
1070      *
1071      * 1) The entryFrame is the last inline frame. If it fell on a RETURN,
1072      *    move the return value down.
1073      * 2) The entryFrame is NOT the last inline frame. Pop the frame.
1074      *
1075      * In both cases, we hijack the stub to return to InjectJaegerReturn. This
1076      * moves |oldFp->rval| into the scripted return registers.
1077      */
1078
1079   restart:
1080     /* Step 1. Finish frames created after the entry frame. */
1081     if (!FinishExcessFrames(f, entryFrame))
1082         THROWV(NULL);
1083
1084     /* IMacros are guaranteed to have been removed by now. */
1085     JS_ASSERT(f.fp() == entryFrame);
1086     JS_ASSERT(!entryFrame->hasImacropc());
1087
1088     /* Step 2. If entryFrame is done, use a special path to return to EnterMethodJIT(). */
1089     if (FrameIsFinished(cx)) {
1090         if (!HandleFinishedFrame(f, entryFrame))
1091             THROWV(NULL);
1092
1093         void *retPtr = JS_FUNC_TO_DATA_PTR(void *, InjectJaegerReturn);
1094         *f.returnAddressLocation() = retPtr;
1095         return NULL;
1096     }
1097
1098     /* Step 3. If entryFrame is at a safe point, just leave. */
1099     if (void *ncode = AtSafePoint(cx))
1100         return ncode;
1101
1102     /* Step 4. Do a partial interp, then restart the whole process. */
1103     if (!PartialInterpret(f)) {
1104         if (!HandleErrorInExcessFrame(f, entryFrame))
1105             THROWV(NULL);
1106     }
1107
1108     goto restart;
1109 }
1110
1111 #endif /* JS_TRACER */
1112
1113 #if defined JS_TRACER
1114 # if defined JS_MONOIC
1115 void *JS_FASTCALL
1116 stubs::InvokeTracer(VMFrame &f, ic::TraceICInfo *ic)
1117 {
1118     return RunTracer(f, *ic);
1119 }
1120
1121 # else
1122
1123 void *JS_FASTCALL
1124 stubs::InvokeTracer(VMFrame &f)
1125 {
1126     return RunTracer(f);
1127 }
1128 # endif /* JS_MONOIC */
1129 #endif /* JS_TRACER */
1130