LCOV - code coverage report
Current view: directory - js/src/methodjit - InvokeHelpers.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 461 393 85.2 %
Date: 2012-04-07 Functions: 27 25 92.6 %

       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 "jsbool.h"
      49                 : #include "assembler/assembler/MacroAssemblerCodeRef.h"
      50                 : #include "assembler/assembler/CodeLocation.h"
      51                 : #include "jstypes.h"
      52                 : #include "methodjit/StubCalls.h"
      53                 : #include "methodjit/MonoIC.h"
      54                 : #include "jsanalyze.h"
      55                 : #include "methodjit/BaseCompiler.h"
      56                 : #include "methodjit/ICRepatcher.h"
      57                 : #include "vm/Debugger.h"
      58                 : 
      59                 : #include "jsinterpinlines.h"
      60                 : #include "jsscopeinlines.h"
      61                 : #include "jsscriptinlines.h"
      62                 : #include "jsobjinlines.h"
      63                 : #include "jscntxtinlines.h"
      64                 : #include "jsatominlines.h"
      65                 : #include "StubCalls-inl.h"
      66                 : 
      67                 : #include "jsautooplen.h"
      68                 : 
      69                 : using namespace js;
      70                 : using namespace js::mjit;
      71                 : using namespace JSC;
      72                 : 
      73                 : using ic::Repatcher;
      74                 : 
      75                 : static jsbytecode *
      76         1273550 : FindExceptionHandler(JSContext *cx)
      77                 : {
      78         1273550 :     StackFrame *fp = cx->fp();
      79         1273550 :     JSScript *script = fp->script();
      80                 : 
      81         1273550 :     if (!JSScript::isValidOffset(script->trynotesOffset))
      82         1270499 :         return NULL;
      83                 : 
      84                 :   error:
      85            3054 :     if (cx->isExceptionPending()) {
      86            3081 :         for (TryNoteIter tni(cx->regs()); !tni.done(); ++tni) {
      87            2942 :             JSTryNote *tn = *tni;
      88                 : 
      89            2942 :             UnwindScope(cx, tn->stackDepth);
      90                 : 
      91                 :             /*
      92                 :              * Set pc to the first bytecode after the the try note to point
      93                 :              * to the beginning of catch or finally or to [enditer] closing
      94                 :              * the for-in loop.
      95                 :              */
      96            2942 :             jsbytecode *pc = script->main() + tn->start + tn->length;
      97            2942 :             cx->regs().pc = pc;
      98            2942 :             cx->regs().sp = fp->base() + tn->stackDepth;
      99                 : 
     100            2942 :             switch (tn->kind) {
     101                 :                 case JSTRY_CATCH:
     102            2905 :                   JS_ASSERT(JSOp(*pc) == JSOP_ENTERBLOCK);
     103                 : 
     104                 : #if JS_HAS_GENERATORS
     105                 :                   /* Catch cannot intercept the closing of a generator. */
     106            2905 :                   if (JS_UNLIKELY(cx->getPendingException().isMagic(JS_GENERATOR_CLOSING)))
     107               0 :                       break;
     108                 : #endif
     109                 : 
     110                 :                   /*
     111                 :                    * Don't clear cx->throwing to save cx->exception from GC
     112                 :                    * until it is pushed to the stack via [exception] in the
     113                 :                    * catch block.
     114                 :                    */
     115            2905 :                   return pc;
     116                 : 
     117                 :                 case JSTRY_FINALLY:
     118                 :                   /*
     119                 :                    * Push (true, exception) pair for finally to indicate that
     120                 :                    * [retsub] should rethrow the exception.
     121                 :                    */
     122               0 :                   cx->regs().sp[0].setBoolean(true);
     123               0 :                   cx->regs().sp[1] = cx->getPendingException();
     124               0 :                   cx->regs().sp += 2;
     125               0 :                   cx->clearPendingException();
     126               0 :                   return pc;
     127                 : 
     128                 :                 case JSTRY_ITER:
     129                 :                 {
     130                 :                   /*
     131                 :                    * This is similar to JSOP_ENDITER in the interpreter loop,
     132                 :                    * except the code now uses the stack slot normally used by
     133                 :                    * JSOP_NEXTITER, namely regs.sp[-1] before the regs.sp -= 2
     134                 :                    * adjustment and regs.sp[1] after, to save and restore the
     135                 :                    * pending exception.
     136                 :                    */
     137              37 :                   JS_ASSERT(JSOp(*pc) == JSOP_ENDITER);
     138              37 :                   bool ok = UnwindIteratorForException(cx, &cx->regs().sp[-1].toObject());
     139              37 :                   cx->regs().sp -= 1;
     140              37 :                   if (!ok)
     141               3 :                       goto error;
     142                 :                 }
     143                 :             }
     144                 :         }
     145                 :     } else {
     146               7 :         UnwindForUncatchableException(cx, cx->regs());
     147                 :     }
     148                 : 
     149             146 :     return NULL;
     150                 : }
     151                 : 
     152                 : /*
     153                 :  * Clean up a frame and return.
     154                 :  */
     155                 : static void
     156         1255589 : InlineReturn(VMFrame &f)
     157                 : {
     158         1255589 :     JS_ASSERT(f.fp() != f.entryfp);
     159         1255589 :     JS_ASSERT(!IsActiveWithOrBlock(f.cx, f.fp()->scopeChain(), 0));
     160         1255589 :     JS_ASSERT(!f.fp()->hasBlockChain());
     161         1255589 :     f.cx->stack.popInlineFrame(f.regs);
     162                 : 
     163         2511178 :     DebugOnly<JSOp> op = JSOp(*f.regs.pc);
     164         1467644 :     JS_ASSERT(op == JSOP_CALL ||
     165                 :               op == JSOP_NEW ||
     166                 :               op == JSOP_EVAL ||
     167                 :               op == JSOP_FUNCALL ||
     168         1467644 :               op == JSOP_FUNAPPLY);
     169         1255589 :     f.regs.pc += JSOP_CALL_LENGTH;
     170         1255589 : }
     171                 : 
     172                 : void JS_FASTCALL
     173          408168 : stubs::SlowCall(VMFrame &f, uint32_t argc)
     174                 : {
     175          408168 :     CallArgs args = CallArgsFromSp(argc, f.regs.sp);
     176          408168 :     if (!InvokeKernel(f.cx, args))
     177              86 :         THROW();
     178                 : 
     179          408082 :     types::TypeScript::Monitor(f.cx, f.script(), f.pc(), args.rval());
     180                 : }
     181                 : 
     182                 : void JS_FASTCALL
     183              28 : stubs::SlowNew(VMFrame &f, uint32_t argc)
     184                 : {
     185              28 :     CallArgs args = CallArgsFromSp(argc, f.regs.sp);
     186              28 :     if (!InvokeConstructorKernel(f.cx, args))
     187              16 :         THROW();
     188                 : 
     189              12 :     types::TypeScript::Monitor(f.cx, f.script(), f.pc(), args.rval());
     190                 : }
     191                 : 
     192                 : static inline bool
     193              40 : CheckStackQuota(VMFrame &f)
     194                 : {
     195              40 :     JS_ASSERT(f.regs.sp == f.fp()->base());
     196                 : 
     197              40 :     f.stackLimit = f.cx->stack.space().getStackLimit(f.cx, DONT_REPORT_ERROR);
     198              40 :     if (f.stackLimit)
     199              24 :         return true;
     200                 : 
     201                 :     /* Remove the current partially-constructed frame before throwing. */
     202              16 :     f.cx->stack.popFrameAfterOverflow();
     203              16 :     js_ReportOverRecursed(f.cx);
     204                 : 
     205              16 :     return false;
     206                 : }
     207                 : 
     208                 : /*
     209                 :  * HitStackQuota is called after the early prologue pushing the new frame would
     210                 :  * overflow f.stackLimit.
     211                 :  */
     212                 : void JS_FASTCALL
     213              20 : stubs::HitStackQuota(VMFrame &f)
     214                 : {
     215              20 :     if (!CheckStackQuota(f))
     216              16 :         THROW();
     217                 : }
     218                 : 
     219                 : /*
     220                 :  * This function must only be called after the early prologue, since it depends
     221                 :  * on fp->exec.fun.
     222                 :  */
     223                 : void * JS_FASTCALL
     224          935060 : stubs::FixupArity(VMFrame &f, uint32_t nactual)
     225                 : {
     226          935060 :     JSContext *cx = f.cx;
     227          935060 :     StackFrame *oldfp = f.fp();
     228                 : 
     229          935060 :     JS_ASSERT(nactual != oldfp->numFormalArgs());
     230                 : 
     231                 :     /*
     232                 :      * Grossssss! *move* the stack frame. If this ends up being perf-critical,
     233                 :      * we can figure out how to spot-optimize it. Be careful to touch only the
     234                 :      * members that have been initialized by the caller and early prologue.
     235                 :      */
     236          935060 :     InitialFrameFlags initial = oldfp->initialFlags();
     237          935060 :     JSFunction *fun           = oldfp->fun();
     238          935060 :     JSScript *script          = fun->script();
     239          935060 :     void *ncode               = oldfp->nativeReturnAddress();
     240                 : 
     241                 :     /* Pop the inline frame. */
     242          935060 :     f.regs.popPartialFrame((Value *)oldfp);
     243                 : 
     244                 :     /* Reserve enough space for a callee frame. */
     245          935060 :     CallArgs args = CallArgsFromSp(nactual, f.regs.sp);
     246                 :     StackFrame *fp = cx->stack.getFixupFrame(cx, DONT_REPORT_ERROR, args, fun,
     247          935060 :                                              script, ncode, initial, &f.stackLimit);
     248                 : 
     249          935060 :     if (!fp) {
     250              12 :         f.regs.updateForNcode(f.jit(), ncode);
     251              12 :         js_ReportOverRecursed(cx);
     252              12 :         THROWV(NULL);
     253                 :     }
     254                 : 
     255                 :     /* The caller takes care of assigning fp to regs. */
     256          935048 :     return fp;
     257                 : }
     258                 : 
     259                 : struct ResetStubRejoin {
     260                 :     VMFrame &f;
     261            3392 :     ResetStubRejoin(VMFrame &f) : f(f) {}
     262            3392 :     ~ResetStubRejoin() { f.stubRejoin = 0; }
     263                 : };
     264                 : 
     265                 : void * JS_FASTCALL
     266            3392 : stubs::CompileFunction(VMFrame &f, uint32_t argc)
     267                 : {
     268                 :     /*
     269                 :      * Note: the stubRejoin kind for the frame was written before the call, and
     270                 :      * needs to be cleared out on all return paths (doing this directly in the
     271                 :      * IC stub will not handle cases where we recompiled or threw).
     272                 :      */
     273            3392 :     JS_ASSERT_IF(f.cx->typeInferenceEnabled(), f.stubRejoin);
     274            6784 :     ResetStubRejoin reset(f);
     275                 : 
     276            3392 :     InitialFrameFlags initial = f.fp()->initialFlags();
     277            3392 :     f.regs.popPartialFrame((Value *)f.fp());
     278                 : 
     279            3392 :     if (InitialFrameFlagsAreConstructing(initial))
     280               1 :         return UncachedNew(f, argc);
     281            3391 :     else if (InitialFrameFlagsAreLowered(initial))
     282             273 :         return UncachedLoweredCall(f, argc);
     283                 :     else
     284            3118 :         return UncachedCall(f, argc);
     285                 : }
     286                 : 
     287                 : static inline bool
     288        11195172 : UncachedInlineCall(VMFrame &f, InitialFrameFlags initial,
     289                 :                    void **pret, bool *unjittable, uint32_t argc)
     290                 : {
     291        11195172 :     JSContext *cx = f.cx;
     292        11195172 :     CallArgs args = CallArgsFromSp(argc, f.regs.sp);
     293        11195172 :     JSFunction *newfun = args.callee().toFunction();
     294        11195172 :     JSScript *newscript = newfun->script();
     295                 : 
     296        11195172 :     bool construct = InitialFrameFlagsAreConstructing(initial);
     297                 : 
     298         1300013 :     bool newType = construct && cx->typeInferenceEnabled() &&
     299        12495185 :         types::UseNewType(cx, f.script(), f.pc());
     300                 : 
     301        11195172 :     types::TypeMonitorCall(cx, args, construct);
     302                 : 
     303                 :     /* Try to compile if not already compiled. */
     304        11195172 :     CompileStatus status = CanMethodJIT(cx, newscript, newscript->code, construct, CompileRequest_Interpreter);
     305        11195172 :     if (status == Compile_Error) {
     306                 :         /* A runtime exception was thrown, get out. */
     307               0 :         return false;
     308                 :     }
     309        11195172 :     if (status == Compile_Abort)
     310            2816 :         *unjittable = true;
     311                 : 
     312                 :     /*
     313                 :      * Make sure we are not calling from an inline frame if we need to make a
     314                 :      * call object for the callee, as doing so could trigger GC and cause
     315                 :      * jitcode discarding / frame expansion.
     316                 :      */
     317        11195172 :     if (f.regs.inlined() && newfun->isHeavyweight()) {
     318               0 :         ExpandInlineFrames(cx->compartment);
     319               0 :         JS_ASSERT(!f.regs.inlined());
     320                 :     }
     321                 : 
     322                 :     /*
     323                 :      * Preserve f.regs.fp while pushing the new frame, for the invariant that
     324                 :      * f.regs reflects the state when we entered the stub call. This handoff is
     325                 :      * tricky: we need to make sure that f.regs is not updated to the new
     326                 :      * frame, and we also need to ensure that cx->regs still points to f.regs
     327                 :      * when space is reserved, in case doing so throws an exception.
     328                 :      */
     329        11195172 :     FrameRegs regs = f.regs;
     330                 : 
     331                 :     /* Get pointer to new frame/slots, prepare arguments. */
     332        11195172 :     if (!cx->stack.pushInlineFrame(cx, regs, args, *newfun, newscript, initial, &f.stackLimit))
     333              21 :         return false;
     334                 : 
     335                 :     /* Finish the handoff to the new frame regs. */
     336        22390302 :     PreserveRegsGuard regsGuard(cx, regs);
     337                 : 
     338                 :     /* Scope with a call object parented by callee's parent. */
     339        11195151 :     if (!regs.fp()->functionPrologue(cx))
     340               0 :         return false;
     341                 : 
     342                 :     /*
     343                 :      * If newscript was successfully compiled, run it. Skip for calls which
     344                 :      * will be constructing a new type object for 'this'.
     345                 :      */
     346        11195151 :     if (!newType) {
     347        11195141 :         if (JITScript *jit = newscript->getJIT(regs.fp()->isConstructing())) {
     348        11181028 :             if (jit->invokeEntry) {
     349        11175538 :                 *pret = jit->invokeEntry;
     350                 : 
     351                 :                 /* Restore the old fp around and let the JIT code repush the new fp. */
     352        11175538 :                 regs.popFrame((Value *) regs.fp());
     353        11175538 :                 return true;
     354                 :             }
     355                 :         }
     356                 :     }
     357                 : 
     358                 :     /*
     359                 :      * Otherwise, run newscript in the interpreter. Expand any inlined frame we
     360                 :      * are calling from, as the new frame is not associated with the VMFrame
     361                 :      * and will not have its prevpc info updated if frame expansion is
     362                 :      * triggered while interpreting.
     363                 :      */
     364           19613 :     if (f.regs.inlined()) {
     365               0 :         ExpandInlineFrames(cx->compartment);
     366               0 :         JS_ASSERT(!f.regs.inlined());
     367               0 :         regs.fp()->resetInlinePrev(f.fp(), f.regs.pc);
     368                 :     }
     369                 : 
     370           19613 :     JS_CHECK_RECURSION(cx, return false);
     371                 : 
     372           19610 :     bool ok = Interpret(cx, cx->fp());
     373           19610 :     f.cx->stack.popInlineFrame(regs);
     374                 : 
     375           19610 :     if (ok)
     376           19132 :         types::TypeScript::Monitor(f.cx, f.script(), f.pc(), args.rval());
     377                 : 
     378           19610 :     *pret = NULL;
     379           19610 :     return ok;
     380                 : }
     381                 : 
     382                 : void * JS_FASTCALL
     383         1373343 : stubs::UncachedNew(VMFrame &f, uint32_t argc)
     384                 : {
     385                 :     UncachedCallResult ucr;
     386         1373343 :     UncachedNewHelper(f, argc, &ucr);
     387         1373343 :     return ucr.codeAddr;
     388                 : }
     389                 : 
     390                 : void
     391         1377158 : stubs::UncachedNewHelper(VMFrame &f, uint32_t argc, UncachedCallResult *ucr)
     392                 : {
     393         1377158 :     ucr->init();
     394         1377158 :     JSContext *cx = f.cx;
     395         1377158 :     CallArgs args = CallArgsFromSp(argc, f.regs.sp);
     396                 : 
     397                 :     /* Try to do a fast inline call before the general Invoke path. */
     398         1377158 :     if (IsFunctionObject(args.calleev(), &ucr->fun) && ucr->fun->isInterpretedConstructor()) {
     399         1300013 :         if (!UncachedInlineCall(f, INITIAL_CONSTRUCT, &ucr->codeAddr, &ucr->unjittable, argc))
     400               7 :             THROW();
     401                 :     } else {
     402           77145 :         if (!InvokeConstructorKernel(cx, args))
     403             131 :             THROW();
     404           77014 :         types::TypeScript::Monitor(f.cx, f.script(), f.pc(), args.rval());
     405                 :     }
     406                 : }
     407                 : 
     408                 : void * JS_FASTCALL
     409        16100857 : stubs::UncachedCall(VMFrame &f, uint32_t argc)
     410                 : {
     411                 :     UncachedCallResult ucr;
     412        16100857 :     UncachedCallHelper(f, argc, false, &ucr);
     413        16100857 :     return ucr.codeAddr;
     414                 : }
     415                 : 
     416                 : void * JS_FASTCALL
     417             273 : stubs::UncachedLoweredCall(VMFrame &f, uint32_t argc)
     418                 : {
     419                 :     UncachedCallResult ucr;
     420             273 :     UncachedCallHelper(f, argc, true, &ucr);
     421             273 :     return ucr.codeAddr;
     422                 : }
     423                 : 
     424                 : void JS_FASTCALL
     425           61436 : stubs::Eval(VMFrame &f, uint32_t argc)
     426                 : {
     427           61436 :     CallArgs args = CallArgsFromSp(argc, f.regs.sp);
     428                 : 
     429           61436 :     if (!IsBuiltinEvalForScope(&f.fp()->scopeChain(), args.calleev())) {
     430               6 :         if (!InvokeKernel(f.cx, args))
     431               0 :             THROW();
     432                 : 
     433               6 :         types::TypeScript::Monitor(f.cx, f.script(), f.pc(), args.rval());
     434               6 :         return;
     435                 :     }
     436                 : 
     437           61430 :     JS_ASSERT(f.fp() == f.cx->fp());
     438           61430 :     if (!DirectEval(f.cx, args))
     439              96 :         THROW();
     440                 : 
     441           61334 :     types::TypeScript::Monitor(f.cx, f.script(), f.pc(), args.rval());
     442                 : }
     443                 : 
     444                 : void
     445        16129843 : stubs::UncachedCallHelper(VMFrame &f, uint32_t argc, bool lowered, UncachedCallResult *ucr)
     446                 : {
     447        16129843 :     ucr->init();
     448                 : 
     449        16129843 :     JSContext *cx = f.cx;
     450        16129843 :     CallArgs args = CallArgsFromSp(argc, f.regs.sp);
     451                 : 
     452        16129843 :     if (IsFunctionObject(args.calleev(), &ucr->fun)) {
     453        16126808 :         if (ucr->fun->isInterpreted()) {
     454         9895159 :             InitialFrameFlags initial = lowered ? INITIAL_LOWERED : INITIAL_NONE;
     455         9895159 :             if (!UncachedInlineCall(f, initial, &ucr->codeAddr, &ucr->unjittable, argc))
     456             495 :                 THROW();
     457         9894664 :             return;
     458                 :         }
     459                 : 
     460         6231649 :         if (ucr->fun->isNative()) {
     461         6231649 :             if (!CallJSNative(cx, ucr->fun->native(), args))
     462            4723 :                 THROW();
     463         6226926 :             types::TypeScript::Monitor(f.cx, f.script(), f.pc(), args.rval());
     464         6226926 :             return;
     465                 :         }
     466                 :     }
     467                 : 
     468            3035 :     if (!InvokeKernel(f.cx, args))
     469              84 :         THROW();
     470                 : 
     471            2951 :     types::TypeScript::Monitor(f.cx, f.script(), f.pc(), args.rval());
     472            2951 :     return;
     473                 : }
     474                 : 
     475                 : static void
     476           18853 : RemoveOrphanedNative(JSContext *cx, StackFrame *fp)
     477                 : {
     478                 :     /*
     479                 :      * Remove fp from the list of frames holding a reference on the orphaned
     480                 :      * native pools. If all the references have been removed, release all the
     481                 :      * pools. We don't release pools piecemeal as a pool can be referenced by
     482                 :      * multiple frames.
     483                 :      */
     484           18853 :     JaegerCompartment *jc = cx->compartment->jaegerCompartment();
     485           18853 :     if (jc->orphanedNativeFrames.empty())
     486           17969 :         return;
     487           87909 :     for (unsigned i = 0; i < jc->orphanedNativeFrames.length(); i++) {
     488           87909 :         if (fp == jc->orphanedNativeFrames[i]) {
     489             884 :             jc->orphanedNativeFrames[i] = jc->orphanedNativeFrames.back();
     490             884 :             jc->orphanedNativeFrames.popBack();
     491             884 :             break;
     492                 :         }
     493                 :     }
     494             884 :     if (jc->orphanedNativeFrames.empty()) {
     495             588 :         for (unsigned i = 0; i < jc->orphanedNativePools.length(); i++)
     496             294 :             jc->orphanedNativePools[i]->release();
     497             294 :         jc->orphanedNativePools.clear();
     498                 :     }
     499                 : }
     500                 : 
     501                 : extern "C" void *
     502           17973 : js_InternalThrow(VMFrame &f)
     503                 : {
     504           17973 :     JSContext *cx = f.cx;
     505                 : 
     506           17973 :     ExpandInlineFrames(cx->compartment);
     507                 : 
     508                 :     // The current frame may have an associated orphaned native, if the native
     509                 :     // or SplatApplyArgs threw an exception.
     510           17973 :     RemoveOrphanedNative(cx, f.fp());
     511                 : 
     512           17973 :     JS_ASSERT(!f.fp()->finishedInInterpreter());
     513                 : 
     514                 :     // Make sure sp is up to date.
     515           17973 :     JS_ASSERT(&cx->regs() == &f.regs);
     516                 : 
     517           17973 :     jsbytecode *pc = NULL;
     518         1255589 :     for (;;) {
     519         1273562 :         if (cx->isExceptionPending()) {
     520                 :             // Call the throw hook if necessary
     521         1271744 :             JSThrowHook handler = cx->runtime->debugHooks.throwHook;
     522         1271744 :             if (handler || !cx->compartment->getDebuggees().empty()) {
     523                 :                 Value rval;
     524             398 :                 JSTrapStatus st = Debugger::onExceptionUnwind(cx, &rval);
     525             398 :                 if (st == JSTRAP_CONTINUE && handler) {
     526              18 :                     st = handler(cx, cx->fp()->script(), cx->regs().pc, &rval,
     527              36 :                                  cx->runtime->debugHooks.throwHookData);
     528                 :                 }
     529                 : 
     530             398 :                 switch (st) {
     531                 :                 case JSTRAP_ERROR:
     532              16 :                     cx->clearPendingException();
     533              16 :                     break;
     534                 : 
     535                 :                 case JSTRAP_CONTINUE:
     536             366 :                     break;
     537                 : 
     538                 :                 case JSTRAP_RETURN:
     539              12 :                     cx->clearPendingException();
     540              12 :                     cx->fp()->setReturnValue(rval);
     541              12 :                     return cx->jaegerCompartment()->forceReturnFromExternC();
     542                 : 
     543                 :                 case JSTRAP_THROW:
     544               4 :                     cx->setPendingException(rval);
     545               4 :                     break;
     546                 : 
     547                 :                 default:
     548               0 :                     JS_NOT_REACHED("bad onExceptionUnwind status");
     549                 :                 }
     550                 :             }
     551                 :         }
     552                 : 
     553         1273550 :         pc = FindExceptionHandler(cx);
     554         1273550 :         if (pc)
     555            2905 :             break;
     556                 : 
     557                 :         // The JIT guarantees that ScriptDebugEpilogue() and ScriptEpilogue()
     558                 :         // have always been run upon exiting to its caller. This is important
     559                 :         // for consistency, where execution modes make similar guarantees about
     560                 :         // prologues and epilogues. Interpret(), and Invoke() all rely on this
     561                 :         // property.
     562         1270645 :         JS_ASSERT(!f.fp()->finishedInInterpreter());
     563         1270645 :         UnwindScope(cx, 0);
     564         1270645 :         f.regs.sp = f.fp()->base();
     565                 : 
     566         1270645 :         if (cx->compartment->debugMode()) {
     567                 :             // This can turn a throw or error into a healthy return. Note that
     568                 :             // we will run ScriptDebugEpilogue again (from AnyFrameEpilogue);
     569                 :             // ScriptDebugEpilogue is prepared for this eventuality.
     570          576909 :             if (js::ScriptDebugEpilogue(cx, f.fp(), false))
     571              34 :                 return cx->jaegerCompartment()->forceReturnFromExternC();
     572                 :         }
     573                 :                 
     574                 : 
     575         1270611 :         ScriptEpilogue(f.cx, f.fp(), false);
     576                 : 
     577                 :         // Don't remove the last frame, this is the responsibility of
     578                 :         // JaegerShot()'s caller. We only guarantee that ScriptEpilogue()
     579                 :         // has been run.
     580         1270611 :         if (f.entryfp == f.fp())
     581           15022 :             break;
     582                 : 
     583         1255589 :         JS_ASSERT(&cx->regs() == &f.regs);
     584         1255589 :         InlineReturn(f);
     585                 :     }
     586                 : 
     587           17927 :     JS_ASSERT(&cx->regs() == &f.regs);
     588                 : 
     589           17927 :     if (!pc)
     590           15022 :         return NULL;
     591                 : 
     592            2905 :     StackFrame *fp = cx->fp();
     593            2905 :     JSScript *script = fp->script();
     594                 : 
     595                 :     /*
     596                 :      * Fall back to EnterMethodJIT and finish the frame in the interpreter.
     597                 :      * With type inference enabled, we may wipe out all JIT code on the
     598                 :      * stack without patching ncode values to jump to the interpreter, and
     599                 :      * thus can only enter JIT code via EnterMethodJIT (which overwrites
     600                 :      * its entry frame's ncode). See ClearAllFrames.
     601                 :      */
     602            2905 :     cx->compartment->jaegerCompartment()->setLastUnfinished(Jaeger_Unfinished);
     603                 : 
     604            2905 :     if (!script->ensureRanAnalysis(cx, NULL)) {
     605               0 :         js_ReportOutOfMemory(cx);
     606               0 :         return NULL;
     607                 :     }
     608                 : 
     609            5810 :     analyze::AutoEnterAnalysis enter(cx);
     610                 : 
     611                 :     /*
     612                 :      * Interpret the ENTERBLOCK and EXCEPTION opcodes, so that we don't go
     613                 :      * back into the interpreter with a pending exception. This will cause
     614                 :      * it to immediately rethrow.
     615                 :      */
     616            2905 :     if (cx->isExceptionPending()) {
     617            2905 :         JS_ASSERT(JSOp(*pc) == JSOP_ENTERBLOCK);
     618            2905 :         StaticBlockObject &blockObj = script->getObject(GET_UINT32_INDEX(pc))->asStaticBlock();
     619            2905 :         Value *vp = cx->regs().sp + blockObj.slotCount();
     620            2905 :         SetValueRangeToUndefined(cx->regs().sp, vp);
     621            2905 :         cx->regs().sp = vp;
     622            2905 :         JS_ASSERT(JSOp(pc[JSOP_ENTERBLOCK_LENGTH]) == JSOP_EXCEPTION);
     623            2905 :         cx->regs().sp[0] = cx->getPendingException();
     624            2905 :         cx->clearPendingException();
     625            2905 :         cx->regs().sp++;
     626            2905 :         cx->regs().pc = pc + JSOP_ENTERBLOCK_LENGTH + JSOP_EXCEPTION_LENGTH;
     627            2905 :         cx->regs().fp()->setBlockChain(&blockObj);
     628                 :     }
     629                 : 
     630            2905 :     *f.oldregs = f.regs;
     631                 : 
     632            2905 :     return NULL;
     633                 : }
     634                 : 
     635                 : void JS_FASTCALL
     636         1646803 : stubs::CreateThis(VMFrame &f, JSObject *proto)
     637                 : {
     638         1646803 :     JSContext *cx = f.cx;
     639         1646803 :     StackFrame *fp = f.fp();
     640         1646803 :     JSObject *callee = &fp->callee();
     641         1646803 :     JSObject *obj = js_CreateThisForFunctionWithProto(cx, callee, proto);
     642         1646803 :     if (!obj)
     643               0 :         THROW();
     644         1646803 :     fp->formalArgs()[-1].setObject(*obj);
     645                 : }
     646                 : 
     647                 : void JS_FASTCALL
     648        11798837 : stubs::ScriptDebugPrologue(VMFrame &f)
     649                 : {
     650        11798837 :     Probes::enterJSFun(f.cx, f.fp()->maybeFun(), f.fp()->script());
     651        11798837 :     JSTrapStatus status = js::ScriptDebugPrologue(f.cx, f.fp());
     652        11798837 :     switch (status) {
     653                 :       case JSTRAP_CONTINUE:
     654        11797156 :         break;
     655                 :       case JSTRAP_RETURN:
     656              20 :         *f.returnAddressLocation() = f.cx->jaegerCompartment()->forceReturnFromFastCall();
     657              20 :         return;
     658                 :       case JSTRAP_ERROR:
     659                 :       case JSTRAP_THROW:
     660            1661 :         THROW();
     661                 :       default:
     662               0 :         JS_NOT_REACHED("bad ScriptDebugPrologue status");
     663                 :     }
     664                 : }
     665                 : 
     666                 : void JS_FASTCALL
     667        10898779 : stubs::ScriptDebugEpilogue(VMFrame &f)
     668                 : {
     669        10898779 :     Probes::exitJSFun(f.cx, f.fp()->maybeFun(), f.fp()->script());
     670        10898779 :     if (!js::ScriptDebugEpilogue(f.cx, f.fp(), JS_TRUE))
     671              32 :         THROW();
     672                 : }
     673                 : 
     674                 : void JS_FASTCALL
     675               0 : stubs::ScriptProbeOnlyPrologue(VMFrame &f)
     676                 : {
     677               0 :     Probes::enterJSFun(f.cx, f.fp()->fun(), f.fp()->script());
     678               0 : }
     679                 : 
     680                 : void JS_FASTCALL
     681               0 : stubs::ScriptProbeOnlyEpilogue(VMFrame &f)
     682                 : {
     683               0 :     Probes::exitJSFun(f.cx, f.fp()->fun(), f.fp()->script());
     684               0 : }
     685                 : 
     686                 : void JS_FASTCALL
     687            5575 : stubs::CrossChunkShim(VMFrame &f, void *edge_)
     688                 : {
     689           11150 :     DebugOnly<CrossChunkEdge*> edge = (CrossChunkEdge *) edge_;
     690                 : 
     691            5575 :     mjit::ExpandInlineFrames(f.cx->compartment);
     692                 : 
     693            5575 :     JSScript *script = f.script();
     694            5575 :     JS_ASSERT(edge->target < script->length);
     695            5575 :     JS_ASSERT(script->code + edge->target == f.pc());
     696                 : 
     697            5575 :     CompileStatus status = CanMethodJIT(f.cx, script, f.pc(), f.fp()->isConstructing(),
     698            5575 :                                         CompileRequest_Interpreter);
     699            5575 :     if (status == Compile_Error)
     700               0 :         THROW();
     701                 : 
     702            5575 :     void **addr = f.returnAddressLocation();
     703            5575 :     *addr = JS_FUNC_TO_DATA_PTR(void *, JaegerInterpoline);
     704                 : 
     705            5575 :     f.fp()->setRejoin(StubRejoin(REJOIN_RESUME));
     706                 : }
     707                 : 
     708                 : JS_STATIC_ASSERT(JSOP_NOP == 0);
     709                 : 
     710                 : /* :XXX: common out with identical copy in Compiler.cpp */
     711                 : #if defined(JS_METHODJIT_SPEW)
     712                 : static const char *OpcodeNames[] = {
     713                 : # define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) #name,
     714                 : # include "jsopcode.tbl"
     715                 : # undef OPDEF
     716                 : };
     717                 : #endif
     718                 : 
     719                 : static void
     720              17 : FinishVarIncOp(VMFrame &f, RejoinState rejoin, Value ov, Value nv, Value *vp)
     721                 : {
     722                 :     /* Finish an increment operation on a LOCAL or ARG. These do not involve property accesses. */
     723              17 :     JS_ASSERT(rejoin == REJOIN_POS || rejoin == REJOIN_BINARY);
     724                 : 
     725              17 :     JSContext *cx = f.cx;
     726                 : 
     727              17 :     JSOp op = JSOp(*f.pc());
     728               0 :     JS_ASSERT(op == JSOP_LOCALINC || op == JSOP_INCLOCAL ||
     729                 :               op == JSOP_LOCALDEC || op == JSOP_DECLOCAL ||
     730                 :               op == JSOP_ARGINC || op == JSOP_INCARG ||
     731              17 :               op == JSOP_ARGDEC || op == JSOP_DECARG);
     732              17 :     const JSCodeSpec *cs = &js_CodeSpec[op];
     733                 : 
     734              17 :     unsigned i = GET_SLOTNO(f.pc());
     735              17 :     Value *var = (JOF_TYPE(cs->format) == JOF_LOCAL) ? f.fp()->slots() + i : &f.fp()->formalArg(i);
     736                 : 
     737              17 :     if (rejoin == REJOIN_POS) {
     738               5 :         double d = ov.toNumber();
     739               5 :         double N = (cs->format & JOF_INC) ? 1 : -1;
     740               5 :         if (!nv.setNumber(d + N))
     741               5 :             types::TypeScript::MonitorOverflow(cx, f.script(), f.pc());
     742                 :     }
     743                 : 
     744              17 :     *var = nv;
     745              17 :     *vp = (cs->format & JOF_POST) ? ov : nv;
     746              17 : }
     747                 : 
     748                 : extern "C" void *
     749           44126 : js_InternalInterpret(void *returnData, void *returnType, void *returnReg, js::VMFrame &f)
     750                 : {
     751           44126 :     JSRejoinState jsrejoin = f.fp()->rejoin();
     752                 :     RejoinState rejoin;
     753           44126 :     if (jsrejoin & 0x1) {
     754                 :         /* Rejoin after a scripted call finished. Restore f.regs.pc and f.regs.inlined (NULL) */
     755             270 :         uint32_t pcOffset = jsrejoin >> 1;
     756             270 :         f.regs.pc = f.fp()->script()->code + pcOffset;
     757             270 :         f.regs.clearInlined();
     758             270 :         rejoin = REJOIN_SCRIPTED;
     759                 :     } else {
     760           43856 :         rejoin = (RejoinState) (jsrejoin >> 1);
     761                 :     }
     762                 : 
     763           44126 :     JSContext *cx = f.cx;
     764           44126 :     StackFrame *fp = f.regs.fp();
     765           44126 :     JSScript *script = fp->script();
     766                 : 
     767           44126 :     jsbytecode *pc = f.regs.pc;
     768                 : 
     769           44126 :     JSOp op = JSOp(*pc);
     770           44126 :     const JSCodeSpec *cs = &js_CodeSpec[op];
     771                 : 
     772           44126 :     if (!script->ensureRanAnalysis(cx, NULL)) {
     773               0 :         js_ReportOutOfMemory(cx);
     774               0 :         return js_InternalThrow(f);
     775                 :     }
     776                 : 
     777           88252 :     analyze::AutoEnterAnalysis enter(cx);
     778           44126 :     analyze::ScriptAnalysis *analysis = script->analysis();
     779                 : 
     780                 :     /*
     781                 :      * f.regs.sp is not normally maintained by stubs (except for call prologues
     782                 :      * where it indicates the new frame), so is not expected to be coherent
     783                 :      * here. Update it to its value at the start of the opcode.
     784                 :      */
     785           44126 :     Value *oldsp = f.regs.sp;
     786           44126 :     f.regs.sp = fp->base() + analysis->getCode(pc).stackDepth;
     787                 : 
     788           44126 :     jsbytecode *nextpc = pc + GetBytecodeLength(pc);
     789           44126 :     Value *nextsp = NULL;
     790           44126 :     if (nextpc != script->code + script->length && analysis->maybeCode(nextpc))
     791           44063 :         nextsp = fp->base() + analysis->getCode(nextpc).stackDepth;
     792                 : 
     793           44126 :     JS_ASSERT(&cx->regs() == &f.regs);
     794                 : 
     795                 : #ifdef JS_METHODJIT_SPEW
     796                 :     JaegerSpew(JSpew_Recompile, "interpreter rejoin (file \"%s\") (line \"%d\") (op %s) (opline \"%d\")\n",
     797           44126 :                script->filename, script->lineno, OpcodeNames[op], PCToLineNumber(script, pc));
     798                 : #endif
     799                 : 
     800           44126 :     uint32_t nextDepth = UINT32_MAX;
     801           44126 :     bool skipTrap = false;
     802                 : 
     803           44126 :     if ((cs->format & (JOF_INC | JOF_DEC)) &&
     804                 :         (rejoin == REJOIN_POS || rejoin == REJOIN_BINARY)) {
     805                 :         /*
     806                 :          * We may reenter the interpreter while finishing the INC/DEC operation
     807                 :          * on a local or arg (property INC/DEC operations will rejoin into the
     808                 :          * decomposed version of the op.
     809                 :          */
     810              17 :         JS_ASSERT(cs->format & (JOF_LOCAL | JOF_QARG));
     811                 : 
     812              17 :         nextDepth = analysis->getCode(nextpc).stackDepth;
     813              17 :         enter.leave();
     814                 : 
     815              17 :         if (rejoin != REJOIN_BINARY || !analysis->incrementInitialValueObserved(pc)) {
     816                 :             /* Stack layout is 'V', 'N' or 'N+1' (only if the N is not needed) */
     817              14 :             FinishVarIncOp(f, rejoin, nextsp[-1], nextsp[-1], &nextsp[-1]);
     818                 :         } else {
     819                 :             /* Stack layout is 'N N+1' */
     820               3 :             FinishVarIncOp(f, rejoin, nextsp[-1], nextsp[0], &nextsp[-1]);
     821                 :         }
     822                 : 
     823              17 :         rejoin = REJOIN_FALLTHROUGH;
     824                 :     }
     825                 : 
     826           44126 :     switch (rejoin) {
     827                 :       case REJOIN_SCRIPTED: {
     828                 :         jsval_layout rval;
     829                 : #ifdef JS_NUNBOX32
     830             270 :         rval.asBits = ((uint64_t)returnType << 32) | (uint32_t)returnData;
     831                 : #elif JS_PUNBOX64
     832                 :         rval.asBits = (uint64_t)returnType | (uint64_t)returnData;
     833                 : #else
     834                 : #error "Unknown boxing format"
     835                 : #endif
     836                 : 
     837             270 :         nextsp[-1] = IMPL_TO_JSVAL(rval);
     838                 : 
     839                 :         /*
     840                 :          * When making a scripted call at monitored sites, it is the caller's
     841                 :          * responsibility to update the pushed type set.
     842                 :          */
     843             270 :         types::TypeScript::Monitor(cx, script, pc, nextsp[-1]);
     844             270 :         f.regs.pc = nextpc;
     845             270 :         break;
     846                 :       }
     847                 : 
     848                 :       case REJOIN_NONE:
     849               0 :         JS_NOT_REACHED("Unpossible rejoin!");
     850                 :         break;
     851                 : 
     852                 :       case REJOIN_RESUME:
     853            6088 :         break;
     854                 : 
     855                 :       case REJOIN_TRAP:
     856                 :         /*
     857                 :          * Make sure when resuming in the interpreter we do not execute the
     858                 :          * trap again. Watch out for the case where the trap removed itself.
     859                 :          */
     860              24 :         if (script->hasBreakpointsAt(pc))
     861               8 :             skipTrap = true;
     862              24 :         break;
     863                 : 
     864                 :       case REJOIN_FALLTHROUGH:
     865            5455 :         f.regs.pc = nextpc;
     866            5455 :         break;
     867                 : 
     868                 :       case REJOIN_NATIVE:
     869                 :       case REJOIN_NATIVE_LOWERED:
     870                 :       case REJOIN_NATIVE_GETTER: {
     871                 :         /*
     872                 :          * We don't rejoin until after the native stub finishes execution, in
     873                 :          * which case the return value will be in memory. For lowered natives,
     874                 :          * the return value will be in the 'this' value's slot.
     875                 :          */
     876             880 :         if (rejoin != REJOIN_NATIVE)
     877               0 :             nextsp[-1] = nextsp[0];
     878                 : 
     879                 :         /* Release this reference on the orphaned native stub. */
     880             880 :         RemoveOrphanedNative(cx, fp);
     881                 : 
     882             880 :         f.regs.pc = nextpc;
     883             880 :         break;
     884                 :       }
     885                 : 
     886                 :       case REJOIN_PUSH_BOOLEAN:
     887               3 :         nextsp[-1].setBoolean(returnReg != NULL);
     888               3 :         f.regs.pc = nextpc;
     889               3 :         break;
     890                 : 
     891                 :       case REJOIN_PUSH_OBJECT:
     892               0 :         nextsp[-1].setObject(* (JSObject *) returnReg);
     893               0 :         f.regs.pc = nextpc;
     894               0 :         break;
     895                 : 
     896                 :       case REJOIN_DEFLOCALFUN:
     897               0 :         fp->slots()[GET_SLOTNO(pc)].setObject(* (JSObject *) returnReg);
     898               0 :         f.regs.pc = nextpc;
     899               0 :         break;
     900                 : 
     901                 :       case REJOIN_THIS_PROTOTYPE: {
     902               0 :         JSObject *callee = &fp->callee();
     903               0 :         JSObject *proto = f.regs.sp[0].isObject() ? &f.regs.sp[0].toObject() : NULL;
     904               0 :         JSObject *obj = js_CreateThisForFunctionWithProto(cx, callee, proto);
     905               0 :         if (!obj)
     906               0 :             return js_InternalThrow(f);
     907               0 :         fp->formalArgs()[-1].setObject(*obj);
     908                 : 
     909               0 :         if (Probes::callTrackingActive(cx))
     910               0 :             Probes::enterJSFun(f.cx, f.fp()->maybeFun(), f.fp()->script());
     911                 : 
     912               0 :         if (script->debugMode) {
     913               0 :             JSTrapStatus status = js::ScriptDebugPrologue(f.cx, f.fp());
     914               0 :             switch (status) {
     915                 :               case JSTRAP_CONTINUE:
     916                 :                 break;
     917                 :               case JSTRAP_RETURN:
     918               0 :                 *f.returnAddressLocation() = f.cx->jaegerCompartment()->forceReturnFromExternC();
     919               0 :                 return NULL;
     920                 :               case JSTRAP_THROW:
     921                 :               case JSTRAP_ERROR:
     922               0 :                 return js_InternalThrow(f);
     923                 :               default:
     924               0 :                 JS_NOT_REACHED("bad ScriptDebugPrologue status");
     925                 :             }
     926                 :         }
     927                 : 
     928               0 :         break;
     929                 :       }
     930                 : 
     931                 :       case REJOIN_CHECK_ARGUMENTS:
     932                 :         /*
     933                 :          * Do all the work needed in arity check JIT prologues after the
     934                 :          * arguments check occurs (FixupArity has been called if needed, but
     935                 :          * the stack check and late prologue have not been performed.
     936                 :          */
     937              20 :         if (!CheckStackQuota(f))
     938               0 :             return js_InternalThrow(f);
     939                 : 
     940              20 :         SetValueRangeToUndefined(fp->slots(), script->nfixed);
     941                 : 
     942              20 :         if (!fp->functionPrologue(cx))
     943               0 :             return js_InternalThrow(f);
     944                 :         /* FALLTHROUGH */
     945                 : 
     946                 :       case REJOIN_FUNCTION_PROLOGUE:
     947              20 :         fp->scopeChain();
     948                 : 
     949                 :         /* Construct the 'this' object for the frame if necessary. */
     950              20 :         if (!ScriptPrologueOrGeneratorResume(cx, fp, types::UseNewTypeAtEntry(cx, fp)))
     951               0 :             return js_InternalThrow(f);
     952                 : 
     953                 :         /* 
     954                 :          * Having called ScriptPrologueOrGeneratorResume, we would normally call
     955                 :          * ScriptDebugPrologue here. But in debug mode, we only use JITted
     956                 :          * functions' invokeEntry entry point, whereas CheckArgumentTypes
     957                 :          * (REJOIN_CHECK_ARGUMENTS) and FunctionFramePrologue
     958                 :          * (REJOIN_FUNCTION_PROLOGUE) are only reachable via the other entry
     959                 :          * points. So we should never need either of these rejoin tails in debug
     960                 :          * mode.
     961                 :          *
     962                 :          * If we fix bug 699196 ("Debug mode code could use inline caches
     963                 :          * now"), then these cases will become reachable again.
     964                 :          */
     965              20 :         JS_ASSERT(!cx->compartment->debugMode());
     966                 : 
     967              20 :         break;
     968                 : 
     969                 :       case REJOIN_CALL_PROLOGUE:
     970                 :       case REJOIN_CALL_PROLOGUE_LOWERED_CALL:
     971                 :       case REJOIN_CALL_PROLOGUE_LOWERED_APPLY:
     972            8735 :         if (returnReg) {
     973             869 :             uint32_t argc = 0;
     974             869 :             if (rejoin == REJOIN_CALL_PROLOGUE)
     975             869 :                 argc = GET_ARGC(pc);
     976               0 :             else if (rejoin == REJOIN_CALL_PROLOGUE_LOWERED_CALL)
     977               0 :                 argc = GET_ARGC(pc) - 1;
     978                 :             else
     979               0 :                 argc = f.u.call.dynamicArgc;
     980                 : 
     981                 :             /*
     982                 :              * The caller frame's code was discarded, but we still need to
     983                 :              * execute the callee and have a JIT code pointer to do so.
     984                 :              * Set the argc and frame registers as the call path does, but set
     985                 :              * the callee frame's return address to jump back into the
     986                 :              * Interpoline, and change the caller frame's rejoin to reflect the
     987                 :              * state after the call.
     988                 :              */
     989             869 :             f.regs.restorePartialFrame(oldsp); /* f.regs.sp stored the new frame */
     990             869 :             f.scratch = (void *) uintptr_t(argc); /* The interpoline will load f.scratch into argc */
     991             869 :             f.fp()->setNativeReturnAddress(JS_FUNC_TO_DATA_PTR(void *, JaegerInterpolineScripted));
     992             869 :             fp->setRejoin(REJOIN_SCRIPTED | ((pc - script->code) << 1));
     993             869 :             return returnReg;
     994                 :         } else {
     995                 :             /*
     996                 :              * The call has already finished, and the return value is on the
     997                 :              * stack. For lowered call/apply, the return value has been stored
     998                 :              * in the wrong slot, so adjust it here.
     999                 :              */
    1000            7866 :             f.regs.pc = nextpc;
    1001            7866 :             if (rejoin != REJOIN_CALL_PROLOGUE) {
    1002                 :                 /* Same offset return value as for lowered native calls. */
    1003              30 :                 nextsp[-1] = nextsp[0];
    1004                 :             }
    1005                 :         }
    1006            7866 :         break;
    1007                 : 
    1008                 :       case REJOIN_CALL_SPLAT: {
    1009                 :         /* Leave analysis early and do the Invoke which SplatApplyArgs prepared. */
    1010               1 :         nextDepth = analysis->getCode(nextpc).stackDepth;
    1011               1 :         enter.leave();
    1012               1 :         f.regs.sp = nextsp + 2 + f.u.call.dynamicArgc;
    1013               1 :         if (!InvokeKernel(cx, CallArgsFromSp(f.u.call.dynamicArgc, f.regs.sp)))
    1014               0 :             return js_InternalThrow(f);
    1015               1 :         nextsp[-1] = nextsp[0];
    1016               1 :         f.regs.pc = nextpc;
    1017               1 :         break;
    1018                 :       }
    1019                 : 
    1020                 :       case REJOIN_GETTER:
    1021                 :         /*
    1022                 :          * Match the PC to figure out whether this property fetch is part of a
    1023                 :          * fused opcode which needs to be finished.
    1024                 :          */
    1025           21603 :         switch (op) {
    1026                 :           case JSOP_INSTANCEOF: {
    1027                 :             /*
    1028                 :              * If we recompiled from a getprop used within JSOP_INSTANCEOF,
    1029                 :              * the stack looks like 'LHS RHS protov'. Inline the remaining
    1030                 :              * portion of fun_hasInstance.
    1031                 :              */
    1032               0 :             if (f.regs.sp[0].isPrimitive()) {
    1033               0 :                 js_ReportValueError(cx, JSMSG_BAD_PROTOTYPE, -1, f.regs.sp[-1], NULL);
    1034               0 :                 return js_InternalThrow(f);
    1035                 :             }
    1036               0 :             nextsp[-1].setBoolean(js_IsDelegate(cx, &f.regs.sp[0].toObject(), f.regs.sp[-2]));
    1037               0 :             f.regs.pc = nextpc;
    1038               0 :             break;
    1039                 :           }
    1040                 : 
    1041                 :           default:
    1042           21603 :             f.regs.pc = nextpc;
    1043           21603 :             break;
    1044                 :         }
    1045           21603 :         break;
    1046                 : 
    1047                 :       case REJOIN_POS:
    1048                 :         /* Convert-to-number which might be part of an INC* op. */
    1049              14 :         JS_ASSERT(op == JSOP_POS);
    1050              14 :         f.regs.pc = nextpc;
    1051              14 :         break;
    1052                 : 
    1053                 :       case REJOIN_BINARY:
    1054                 :         /* Binary arithmetic op which might be part of an INC* op. */
    1055             158 :         JS_ASSERT(op == JSOP_ADD || op == JSOP_SUB || op == JSOP_MUL || op == JSOP_DIV);
    1056             158 :         f.regs.pc = nextpc;
    1057             158 :         break;
    1058                 : 
    1059                 :       case REJOIN_BRANCH: {
    1060                 :         /*
    1061                 :          * This must be an opcode fused with IFNE/IFEQ. Unfused IFNE/IFEQ are
    1062                 :          * implemented in terms of ValueToBoolean, which is infallible and
    1063                 :          * cannot trigger recompilation.
    1064                 :          */
    1065             875 :         bool takeBranch = false;
    1066             875 :         switch (JSOp(*nextpc)) {
    1067                 :           case JSOP_IFNE:
    1068             871 :             takeBranch = returnReg != NULL;
    1069             871 :             break;
    1070                 :           case JSOP_IFEQ:
    1071               4 :             takeBranch = returnReg == NULL;
    1072               4 :             break;
    1073                 :           default:
    1074               0 :             JS_NOT_REACHED("Bad branch op");
    1075                 :         }
    1076             875 :         if (takeBranch)
    1077             868 :             f.regs.pc = nextpc + GET_JUMP_OFFSET(nextpc);
    1078                 :         else
    1079               7 :             f.regs.pc = nextpc + GetBytecodeLength(nextpc);
    1080             875 :         break;
    1081                 :       }
    1082                 : 
    1083                 :       default:
    1084               0 :         JS_NOT_REACHED("Missing rejoin");
    1085                 :     }
    1086                 : 
    1087           43257 :     if (nextDepth == UINT32_MAX)
    1088           43239 :         nextDepth = analysis->getCode(f.regs.pc).stackDepth;
    1089           43257 :     f.regs.sp = fp->base() + nextDepth;
    1090                 : 
    1091                 :     /*
    1092                 :      * Monitor the result of the previous op when finishing a JOF_TYPESET op.
    1093                 :      * The result may not have been marked if we bailed out while inside a stub
    1094                 :      * for the op.
    1095                 :      */
    1096           43257 :     if (f.regs.pc == nextpc && (js_CodeSpec[op].format & JOF_TYPESET))
    1097           35633 :         types::TypeScript::Monitor(cx, script, pc, f.regs.sp[-1]);
    1098                 : 
    1099                 :     /* Mark the entry frame as unfinished, and update the regs to resume at. */
    1100           43257 :     JaegerStatus status = skipTrap ? Jaeger_UnfinishedAtTrap : Jaeger_Unfinished;
    1101           43257 :     cx->compartment->jaegerCompartment()->setLastUnfinished(status);
    1102           43257 :     *f.oldregs = f.regs;
    1103                 : 
    1104           43257 :     return NULL;
    1105                 : }

Generated by: LCOV version 1.7