LCOV - code coverage report
Current view: directory - js/src/methodjit - InvokeHelpers.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 460 392 85.2 %
Date: 2012-06-02 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         1276061 : FindExceptionHandler(JSContext *cx)
      77                 : {
      78         1276061 :     StackFrame *fp = cx->fp();
      79         1276061 :     JSScript *script = fp->script();
      80                 : 
      81         1276061 :     if (!JSScript::isValidOffset(script->trynotesOffset))
      82         1271085 :         return NULL;
      83                 : 
      84                 :   error:
      85            4979 :     if (cx->isExceptionPending()) {
      86            5000 :         for (TryNoteIter tni(cx->regs()); !tni.done(); ++tni) {
      87            4866 :             JSTryNote *tn = *tni;
      88                 : 
      89            4866 :             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            4866 :             jsbytecode *pc = script->main() + tn->start + tn->length;
      97            4866 :             cx->regs().pc = pc;
      98            4866 :             cx->regs().sp = fp->base() + tn->stackDepth;
      99                 : 
     100            4866 :             switch (tn->kind) {
     101                 :                 case JSTRY_CATCH:
     102            4835 :                   JS_ASSERT(JSOp(*pc) == JSOP_ENTERBLOCK);
     103                 : 
     104                 : #if JS_HAS_GENERATORS
     105                 :                   /* Catch cannot intercept the closing of a generator. */
     106            4835 :                   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            4835 :                   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              31 :                   JS_ASSERT(JSOp(*pc) == JSOP_ENDITER);
     138              31 :                   bool ok = UnwindIteratorForException(cx, &cx->regs().sp[-1].toObject());
     139              31 :                   cx->regs().sp -= 1;
     140              31 :                   if (!ok)
     141               3 :                       goto error;
     142                 :                 }
     143                 :             }
     144                 :         }
     145                 :     } else {
     146               7 :         UnwindForUncatchableException(cx, cx->regs());
     147                 :     }
     148                 : 
     149             141 :     return NULL;
     150                 : }
     151                 : 
     152                 : /*
     153                 :  * Clean up a frame and return.
     154                 :  */
     155                 : static void
     156         1255615 : InlineReturn(VMFrame &f)
     157                 : {
     158         1255615 :     JS_ASSERT(f.fp() != f.entryfp);
     159         1255615 :     JS_ASSERT(!IsActiveWithOrBlock(f.cx, f.fp()->scopeChain(), 0));
     160         1255615 :     JS_ASSERT(!f.fp()->hasBlockChain());
     161         1255615 :     f.cx->stack.popInlineFrame(f.regs);
     162                 : 
     163         2511230 :     DebugOnly<JSOp> op = JSOp(*f.regs.pc);
     164         1467672 :     JS_ASSERT(op == JSOP_CALL ||
     165                 :               op == JSOP_NEW ||
     166                 :               op == JSOP_EVAL ||
     167                 :               op == JSOP_FUNCALL ||
     168         1467672 :               op == JSOP_FUNAPPLY);
     169         1255615 :     f.regs.pc += JSOP_CALL_LENGTH;
     170         1255615 : }
     171                 : 
     172                 : void JS_FASTCALL
     173          490310 : stubs::SlowCall(VMFrame &f, uint32_t argc)
     174                 : {
     175          490310 :     CallArgs args = CallArgsFromSp(argc, f.regs.sp);
     176          490310 :     if (!InvokeKernel(f.cx, args))
     177             139 :         THROW();
     178                 : 
     179          490171 :     types::TypeScript::Monitor(f.cx, f.script(), f.pc(), args.rval());
     180                 : }
     181                 : 
     182                 : void JS_FASTCALL
     183            6048 : stubs::SlowNew(VMFrame &f, uint32_t argc)
     184                 : {
     185            6048 :     CallArgs args = CallArgsFromSp(argc, f.regs.sp);
     186            6048 :     if (!InvokeConstructorKernel(f.cx, args))
     187              16 :         THROW();
     188                 : 
     189            6032 :     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         1022194 : stubs::FixupArity(VMFrame &f, uint32_t nactual)
     225                 : {
     226         1022194 :     JSContext *cx = f.cx;
     227         1022194 :     StackFrame *oldfp = f.fp();
     228                 : 
     229         1022194 :     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         1022194 :     InitialFrameFlags initial = oldfp->initialFlags();
     237         1022194 :     JSFunction *fun           = oldfp->fun();
     238         1022194 :     JSScript *script          = fun->script();
     239         1022194 :     void *ncode               = oldfp->nativeReturnAddress();
     240                 : 
     241                 :     /* Pop the inline frame. */
     242         1022194 :     f.regs.popPartialFrame((Value *)oldfp);
     243                 : 
     244                 :     /* Reserve enough space for a callee frame. */
     245         1022194 :     CallArgs args = CallArgsFromSp(nactual, f.regs.sp);
     246                 :     StackFrame *fp = cx->stack.getFixupFrame(cx, DONT_REPORT_ERROR, args, fun,
     247         1022194 :                                              script, ncode, initial, &f.stackLimit);
     248                 : 
     249         1022194 :     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         1022182 :     return fp;
     257                 : }
     258                 : 
     259                 : struct ResetStubRejoin {
     260                 :     VMFrame &f;
     261            4563 :     ResetStubRejoin(VMFrame &f) : f(f) {}
     262            4563 :     ~ResetStubRejoin() { f.stubRejoin = 0; }
     263                 : };
     264                 : 
     265                 : void * JS_FASTCALL
     266            4563 : 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            4563 :     JS_ASSERT_IF(f.cx->typeInferenceEnabled(), f.stubRejoin);
     274            9126 :     ResetStubRejoin reset(f);
     275                 : 
     276            4563 :     InitialFrameFlags initial = f.fp()->initialFlags();
     277            4563 :     f.regs.popPartialFrame((Value *)f.fp());
     278                 : 
     279            4563 :     if (InitialFrameFlagsAreConstructing(initial))
     280               7 :         return UncachedNew(f, argc);
     281            4556 :     else if (InitialFrameFlagsAreLowered(initial))
     282             279 :         return UncachedLoweredCall(f, argc);
     283                 :     else
     284            4277 :         return UncachedCall(f, argc);
     285                 : }
     286                 : 
     287                 : static inline bool
     288        11875216 : UncachedInlineCall(VMFrame &f, InitialFrameFlags initial,
     289                 :                    void **pret, bool *unjittable, uint32_t argc)
     290                 : {
     291        11875216 :     JSContext *cx = f.cx;
     292        11875216 :     CallArgs args = CallArgsFromSp(argc, f.regs.sp);
     293        11875216 :     JSFunction *newfun = args.callee().toFunction();
     294        11875216 :     JSScript *newscript = newfun->script();
     295                 : 
     296        11875216 :     bool construct = InitialFrameFlagsAreConstructing(initial);
     297                 : 
     298         1310983 :     bool newType = construct && cx->typeInferenceEnabled() &&
     299        13186199 :         types::UseNewType(cx, f.script(), f.pc());
     300                 : 
     301        11875216 :     types::TypeMonitorCall(cx, args, construct);
     302                 : 
     303                 :     /* Try to compile if not already compiled. */
     304        11875216 :     CompileStatus status = CanMethodJIT(cx, newscript, newscript->code, construct, CompileRequest_Interpreter);
     305        11875216 :     if (status == Compile_Error) {
     306                 :         /* A runtime exception was thrown, get out. */
     307               0 :         return false;
     308                 :     }
     309        11875216 :     if (status == Compile_Abort)
     310           48897 :         *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        11875216 :     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        11875216 :     FrameRegs regs = f.regs;
     330                 : 
     331                 :     /* Get pointer to new frame/slots, prepare arguments. */
     332        11875216 :     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        23750390 :     PreserveRegsGuard regsGuard(cx, regs);
     337                 : 
     338                 :     /* Scope with a call object parented by callee's parent. */
     339        11875195 :     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        11875195 :     if (!newType) {
     347        11875185 :         if (JITScript *jit = newscript->getJIT(regs.fp()->isConstructing())) {
     348        11830955 :             if (jit->invokeEntry) {
     349        11776526 :                 *pret = jit->invokeEntry;
     350                 : 
     351                 :                 /* Restore the old fp around and let the JIT code repush the new fp. */
     352        11776526 :                 regs.popFrame((Value *) regs.fp());
     353        11776526 :                 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           98669 :     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           98669 :     bool ok = !!Interpret(cx, cx->fp());
     371           98669 :     f.cx->stack.popInlineFrame(regs);
     372                 : 
     373           98669 :     if (ok)
     374           97778 :         types::TypeScript::Monitor(f.cx, f.script(), f.pc(), args.rval());
     375                 : 
     376           98669 :     *pret = NULL;
     377           98669 :     return ok;
     378                 : }
     379                 : 
     380                 : void * JS_FASTCALL
     381         1384221 : stubs::UncachedNew(VMFrame &f, uint32_t argc)
     382                 : {
     383                 :     UncachedCallResult ucr;
     384         1384221 :     UncachedNewHelper(f, argc, &ucr);
     385         1384221 :     return ucr.codeAddr;
     386                 : }
     387                 : 
     388                 : void
     389         1388570 : stubs::UncachedNewHelper(VMFrame &f, uint32_t argc, UncachedCallResult *ucr)
     390                 : {
     391         1388570 :     ucr->init();
     392         1388570 :     JSContext *cx = f.cx;
     393         1388570 :     CallArgs args = CallArgsFromSp(argc, f.regs.sp);
     394                 : 
     395                 :     /* Try to do a fast inline call before the general Invoke path. */
     396         1388570 :     if (IsFunctionObject(args.calleev(), &ucr->fun) && ucr->fun->isInterpretedConstructor()) {
     397         1310983 :         if (!UncachedInlineCall(f, INITIAL_CONSTRUCT, &ucr->codeAddr, &ucr->unjittable, argc))
     398               7 :             THROW();
     399                 :     } else {
     400           77587 :         if (!InvokeConstructorKernel(cx, args))
     401             131 :             THROW();
     402           77456 :         types::TypeScript::Monitor(f.cx, f.script(), f.pc(), args.rval());
     403                 :     }
     404                 : }
     405                 : 
     406                 : void * JS_FASTCALL
     407        19295846 : stubs::UncachedCall(VMFrame &f, uint32_t argc)
     408                 : {
     409                 :     UncachedCallResult ucr;
     410        19295846 :     UncachedCallHelper(f, argc, false, &ucr);
     411        19295846 :     return ucr.codeAddr;
     412                 : }
     413                 : 
     414                 : void * JS_FASTCALL
     415             279 : stubs::UncachedLoweredCall(VMFrame &f, uint32_t argc)
     416                 : {
     417                 :     UncachedCallResult ucr;
     418             279 :     UncachedCallHelper(f, argc, true, &ucr);
     419             279 :     return ucr.codeAddr;
     420                 : }
     421                 : 
     422                 : void JS_FASTCALL
     423           61564 : stubs::Eval(VMFrame &f, uint32_t argc)
     424                 : {
     425           61564 :     CallArgs args = CallArgsFromSp(argc, f.regs.sp);
     426                 : 
     427           61564 :     if (!IsBuiltinEvalForScope(&f.fp()->scopeChain(), args.calleev())) {
     428               6 :         if (!InvokeKernel(f.cx, args))
     429               0 :             THROW();
     430                 : 
     431               6 :         types::TypeScript::Monitor(f.cx, f.script(), f.pc(), args.rval());
     432               6 :         return;
     433                 :     }
     434                 : 
     435           61558 :     JS_ASSERT(f.fp() == f.cx->fp());
     436           61558 :     if (!DirectEval(f.cx, args))
     437              96 :         THROW();
     438                 : 
     439           61462 :     types::TypeScript::Monitor(f.cx, f.script(), f.pc(), args.rval());
     440                 : }
     441                 : 
     442                 : void
     443        19344755 : stubs::UncachedCallHelper(VMFrame &f, uint32_t argc, bool lowered, UncachedCallResult *ucr)
     444                 : {
     445        19344755 :     ucr->init();
     446                 : 
     447        19344755 :     JSContext *cx = f.cx;
     448        19344755 :     CallArgs args = CallArgsFromSp(argc, f.regs.sp);
     449                 : 
     450        19344755 :     if (IsFunctionObject(args.calleev(), &ucr->fun)) {
     451        19341810 :         if (ucr->fun->isInterpreted()) {
     452        10564233 :             InitialFrameFlags initial = lowered ? INITIAL_LOWERED : INITIAL_NONE;
     453        10564233 :             if (!UncachedInlineCall(f, initial, &ucr->codeAddr, &ucr->unjittable, argc))
     454             905 :                 THROW();
     455        10563328 :             return;
     456                 :         }
     457                 : 
     458         8777577 :         if (ucr->fun->isNative()) {
     459         8777577 :             if (!CallJSNative(cx, ucr->fun->u.n.native, args))
     460            4793 :                 THROW();
     461         8772784 :             types::TypeScript::Monitor(f.cx, f.script(), f.pc(), args.rval());
     462         8772784 :             return;
     463                 :         }
     464                 :     }
     465                 : 
     466            2945 :     if (!InvokeKernel(f.cx, args))
     467              87 :         THROW();
     468                 : 
     469            2858 :     types::TypeScript::Monitor(f.cx, f.script(), f.pc(), args.rval());
     470            2858 :     return;
     471                 : }
     472                 : 
     473                 : static void
     474           21909 : RemoveOrphanedNative(JSContext *cx, StackFrame *fp)
     475                 : {
     476                 :     /*
     477                 :      * Remove fp from the list of frames holding a reference on the orphaned
     478                 :      * native pools. If all the references have been removed, release all the
     479                 :      * pools. We don't release pools piecemeal as a pool can be referenced by
     480                 :      * multiple frames.
     481                 :      */
     482           21909 :     JaegerCompartment *jc = cx->compartment->jaegerCompartment();
     483           21909 :     if (jc->orphanedNativeFrames.empty())
     484           20456 :         return;
     485           88478 :     for (unsigned i = 0; i < jc->orphanedNativeFrames.length(); i++) {
     486           88478 :         if (fp == jc->orphanedNativeFrames[i]) {
     487            1453 :             jc->orphanedNativeFrames[i] = jc->orphanedNativeFrames.back();
     488            1453 :             jc->orphanedNativeFrames.popBack();
     489            1453 :             break;
     490                 :         }
     491                 :     }
     492            1453 :     if (jc->orphanedNativeFrames.empty()) {
     493            1726 :         for (unsigned i = 0; i < jc->orphanedNativePools.length(); i++)
     494             863 :             jc->orphanedNativePools[i]->release();
     495             863 :         jc->orphanedNativePools.clear();
     496                 :     }
     497                 : }
     498                 : 
     499                 : extern "C" void *
     500           20458 : js_InternalThrow(VMFrame &f)
     501                 : {
     502           20458 :     JSContext *cx = f.cx;
     503                 : 
     504           20458 :     ExpandInlineFrames(cx->compartment);
     505                 : 
     506                 :     // The current frame may have an associated orphaned native, if the native
     507                 :     // or SplatApplyArgs threw an exception.
     508           20458 :     RemoveOrphanedNative(cx, f.fp());
     509                 : 
     510           20458 :     JS_ASSERT(!f.fp()->finishedInInterpreter());
     511                 : 
     512                 :     // Make sure sp is up to date.
     513           20458 :     JS_ASSERT(&cx->regs() == &f.regs);
     514                 : 
     515           20458 :     jsbytecode *pc = NULL;
     516         1255615 :     for (;;) {
     517         1276073 :         if (cx->isExceptionPending()) {
     518                 :             // Call the throw hook if necessary
     519         1274244 :             JSThrowHook handler = cx->runtime->debugHooks.throwHook;
     520         1274244 :             if (handler || !cx->compartment->getDebuggees().empty()) {
     521                 :                 Value rval;
     522             641 :                 JSTrapStatus st = Debugger::onExceptionUnwind(cx, &rval);
     523             641 :                 if (st == JSTRAP_CONTINUE && handler) {
     524             261 :                     st = handler(cx, cx->fp()->script(), cx->regs().pc, &rval,
     525             522 :                                  cx->runtime->debugHooks.throwHookData);
     526                 :                 }
     527                 : 
     528             641 :                 switch (st) {
     529                 :                 case JSTRAP_ERROR:
     530              16 :                     cx->clearPendingException();
     531              16 :                     break;
     532                 : 
     533                 :                 case JSTRAP_CONTINUE:
     534             609 :                     break;
     535                 : 
     536                 :                 case JSTRAP_RETURN:
     537              12 :                     cx->clearPendingException();
     538              12 :                     cx->fp()->setReturnValue(rval);
     539              12 :                     return cx->jaegerCompartment()->forceReturnFromExternC();
     540                 : 
     541                 :                 case JSTRAP_THROW:
     542               4 :                     cx->setPendingException(rval);
     543               4 :                     break;
     544                 : 
     545                 :                 default:
     546               0 :                     JS_NOT_REACHED("bad onExceptionUnwind status");
     547                 :                 }
     548                 :             }
     549                 :         }
     550                 : 
     551         1276061 :         pc = FindExceptionHandler(cx);
     552         1276061 :         if (pc)
     553            4835 :             break;
     554                 : 
     555                 :         // The JIT guarantees that ScriptDebugEpilogue() and ScriptEpilogue()
     556                 :         // have always been run upon exiting to its caller. This is important
     557                 :         // for consistency, where execution modes make similar guarantees about
     558                 :         // prologues and epilogues. Interpret(), and Invoke() all rely on this
     559                 :         // property.
     560         1271226 :         JS_ASSERT(!f.fp()->finishedInInterpreter());
     561         1271226 :         UnwindScope(cx, 0);
     562         1271226 :         f.regs.sp = f.fp()->base();
     563                 : 
     564         1271226 :         if (cx->compartment->debugMode()) {
     565                 :             // This can turn a throw or error into a healthy return. Note that
     566                 :             // we will run ScriptDebugEpilogue again (from AnyFrameEpilogue);
     567                 :             // ScriptDebugEpilogue is prepared for this eventuality.
     568          576941 :             if (js::ScriptDebugEpilogue(cx, f.fp(), false))
     569              34 :                 return cx->jaegerCompartment()->forceReturnFromExternC();
     570                 :         }
     571                 :                 
     572                 : 
     573         1271192 :         ScriptEpilogue(f.cx, f.fp(), false);
     574                 : 
     575                 :         // Don't remove the last frame, this is the responsibility of
     576                 :         // JaegerShot()'s caller. We only guarantee that ScriptEpilogue()
     577                 :         // has been run.
     578         1271192 :         if (f.entryfp == f.fp())
     579           15577 :             break;
     580                 : 
     581         1255615 :         JS_ASSERT(&cx->regs() == &f.regs);
     582         1255615 :         InlineReturn(f);
     583                 :     }
     584                 : 
     585           20412 :     JS_ASSERT(&cx->regs() == &f.regs);
     586                 : 
     587           20412 :     if (!pc)
     588           15577 :         return NULL;
     589                 : 
     590            4835 :     StackFrame *fp = cx->fp();
     591            4835 :     JSScript *script = fp->script();
     592                 : 
     593                 :     /*
     594                 :      * Fall back to EnterMethodJIT and finish the frame in the interpreter.
     595                 :      * With type inference enabled, we may wipe out all JIT code on the
     596                 :      * stack without patching ncode values to jump to the interpreter, and
     597                 :      * thus can only enter JIT code via EnterMethodJIT (which overwrites
     598                 :      * its entry frame's ncode). See ClearAllFrames.
     599                 :      */
     600            4835 :     cx->compartment->jaegerCompartment()->setLastUnfinished(Jaeger_Unfinished);
     601                 : 
     602            4835 :     if (!script->ensureRanAnalysis(cx, NULL)) {
     603               0 :         js_ReportOutOfMemory(cx);
     604               0 :         return NULL;
     605                 :     }
     606                 : 
     607            9670 :     analyze::AutoEnterAnalysis enter(cx);
     608                 : 
     609                 :     /*
     610                 :      * Interpret the ENTERBLOCK and EXCEPTION opcodes, so that we don't go
     611                 :      * back into the interpreter with a pending exception. This will cause
     612                 :      * it to immediately rethrow.
     613                 :      */
     614            4835 :     if (cx->isExceptionPending()) {
     615            4835 :         JS_ASSERT(JSOp(*pc) == JSOP_ENTERBLOCK);
     616            4835 :         StaticBlockObject &blockObj = script->getObject(GET_UINT32_INDEX(pc))->asStaticBlock();
     617            4835 :         Value *vp = cx->regs().sp + blockObj.slotCount();
     618            4835 :         SetValueRangeToUndefined(cx->regs().sp, vp);
     619            4835 :         cx->regs().sp = vp;
     620            4835 :         JS_ASSERT(JSOp(pc[JSOP_ENTERBLOCK_LENGTH]) == JSOP_EXCEPTION);
     621            4835 :         cx->regs().sp[0] = cx->getPendingException();
     622            4835 :         cx->clearPendingException();
     623            4835 :         cx->regs().sp++;
     624            4835 :         cx->regs().pc = pc + JSOP_ENTERBLOCK_LENGTH + JSOP_EXCEPTION_LENGTH;
     625            4835 :         cx->regs().fp()->setBlockChain(&blockObj);
     626                 :     }
     627                 : 
     628            4835 :     *f.oldregs = f.regs;
     629                 : 
     630            4835 :     return NULL;
     631                 : }
     632                 : 
     633                 : void JS_FASTCALL
     634         1661068 : stubs::CreateThis(VMFrame &f, JSObject *proto)
     635                 : {
     636         1661068 :     JSContext *cx = f.cx;
     637         1661068 :     StackFrame *fp = f.fp();
     638         1661068 :     JSObject *callee = &fp->callee();
     639         1661068 :     JSObject *obj = js_CreateThisForFunctionWithProto(cx, callee, proto);
     640         1661068 :     if (!obj)
     641               0 :         THROW();
     642         1661068 :     fp->formalArgs()[-1].setObject(*obj);
     643                 : }
     644                 : 
     645                 : void JS_FASTCALL
     646        13321419 : stubs::ScriptDebugPrologue(VMFrame &f)
     647                 : {
     648        13321419 :     Probes::enterJSFun(f.cx, f.fp()->maybeFun(), f.fp()->script());
     649        13321419 :     JSTrapStatus status = js::ScriptDebugPrologue(f.cx, f.fp());
     650        13321419 :     switch (status) {
     651                 :       case JSTRAP_CONTINUE:
     652        13319727 :         break;
     653                 :       case JSTRAP_RETURN:
     654              20 :         *f.returnAddressLocation() = f.cx->jaegerCompartment()->forceReturnFromFastCall();
     655              20 :         return;
     656                 :       case JSTRAP_ERROR:
     657                 :       case JSTRAP_THROW:
     658            1672 :         THROW();
     659                 :       default:
     660               0 :         JS_NOT_REACHED("bad ScriptDebugPrologue status");
     661                 :     }
     662                 : }
     663                 : 
     664                 : void JS_FASTCALL
     665        12424896 : stubs::ScriptDebugEpilogue(VMFrame &f)
     666                 : {
     667        12424896 :     Probes::exitJSFun(f.cx, f.fp()->maybeFun(), f.fp()->script());
     668        12424896 :     if (!js::ScriptDebugEpilogue(f.cx, f.fp(), JS_TRUE))
     669              32 :         THROW();
     670                 : }
     671                 : 
     672                 : void JS_FASTCALL
     673               0 : stubs::ScriptProbeOnlyPrologue(VMFrame &f)
     674                 : {
     675               0 :     Probes::enterJSFun(f.cx, f.fp()->fun(), f.fp()->script());
     676               0 : }
     677                 : 
     678                 : void JS_FASTCALL
     679               0 : stubs::ScriptProbeOnlyEpilogue(VMFrame &f)
     680                 : {
     681               0 :     Probes::exitJSFun(f.cx, f.fp()->fun(), f.fp()->script());
     682               0 : }
     683                 : 
     684                 : void JS_FASTCALL
     685            5575 : stubs::CrossChunkShim(VMFrame &f, void *edge_)
     686                 : {
     687           11150 :     DebugOnly<CrossChunkEdge*> edge = (CrossChunkEdge *) edge_;
     688                 : 
     689            5575 :     mjit::ExpandInlineFrames(f.cx->compartment);
     690                 : 
     691            5575 :     JSScript *script = f.script();
     692            5575 :     JS_ASSERT(edge->target < script->length);
     693            5575 :     JS_ASSERT(script->code + edge->target == f.pc());
     694                 : 
     695            5575 :     CompileStatus status = CanMethodJIT(f.cx, script, f.pc(), f.fp()->isConstructing(),
     696            5575 :                                         CompileRequest_Interpreter);
     697            5575 :     if (status == Compile_Error)
     698               0 :         THROW();
     699                 : 
     700            5575 :     void **addr = f.returnAddressLocation();
     701            5575 :     *addr = JS_FUNC_TO_DATA_PTR(void *, JaegerInterpoline);
     702                 : 
     703            5575 :     f.fp()->setRejoin(StubRejoin(REJOIN_RESUME));
     704                 : }
     705                 : 
     706                 : JS_STATIC_ASSERT(JSOP_NOP == 0);
     707                 : 
     708                 : /* :XXX: common out with identical copy in Compiler.cpp */
     709                 : #if defined(JS_METHODJIT_SPEW)
     710                 : static const char *OpcodeNames[] = {
     711                 : # define OPDEF(op,val,name,token,length,nuses,ndefs,prec,format) #name,
     712                 : # include "jsopcode.tbl"
     713                 : # undef OPDEF
     714                 : };
     715                 : #endif
     716                 : 
     717                 : static void
     718              17 : FinishVarIncOp(VMFrame &f, RejoinState rejoin, Value ov, Value nv, Value *vp)
     719                 : {
     720                 :     /* Finish an increment operation on a LOCAL or ARG. These do not involve property accesses. */
     721              17 :     JS_ASSERT(rejoin == REJOIN_POS || rejoin == REJOIN_BINARY);
     722                 : 
     723              17 :     JSContext *cx = f.cx;
     724                 : 
     725              17 :     JSOp op = JSOp(*f.pc());
     726               0 :     JS_ASSERT(op == JSOP_LOCALINC || op == JSOP_INCLOCAL ||
     727                 :               op == JSOP_LOCALDEC || op == JSOP_DECLOCAL ||
     728                 :               op == JSOP_ARGINC || op == JSOP_INCARG ||
     729              17 :               op == JSOP_ARGDEC || op == JSOP_DECARG);
     730              17 :     const JSCodeSpec *cs = &js_CodeSpec[op];
     731                 : 
     732              17 :     unsigned i = GET_SLOTNO(f.pc());
     733              17 :     Value *var = (JOF_TYPE(cs->format) == JOF_LOCAL) ? f.fp()->slots() + i : &f.fp()->formalArg(i);
     734                 : 
     735              17 :     if (rejoin == REJOIN_POS) {
     736               5 :         double d = ov.toNumber();
     737               5 :         double N = (cs->format & JOF_INC) ? 1 : -1;
     738               5 :         if (!nv.setNumber(d + N))
     739               5 :             types::TypeScript::MonitorOverflow(cx, f.script(), f.pc());
     740                 :     }
     741                 : 
     742              17 :     *var = nv;
     743              17 :     *vp = (cs->format & JOF_POST) ? ov : nv;
     744              17 : }
     745                 : 
     746                 : extern "C" void *
     747           45090 : js_InternalInterpret(void *returnData, void *returnType, void *returnReg, js::VMFrame &f)
     748                 : {
     749           45090 :     JSRejoinState jsrejoin = f.fp()->rejoin();
     750                 :     RejoinState rejoin;
     751           45090 :     if (jsrejoin & 0x1) {
     752                 :         /* Rejoin after a scripted call finished. Restore f.regs.pc and f.regs.inlined (NULL) */
     753             284 :         uint32_t pcOffset = jsrejoin >> 1;
     754             284 :         f.regs.pc = f.fp()->script()->code + pcOffset;
     755             284 :         f.regs.clearInlined();
     756             284 :         rejoin = REJOIN_SCRIPTED;
     757                 :     } else {
     758           44806 :         rejoin = (RejoinState) (jsrejoin >> 1);
     759                 :     }
     760                 : 
     761           45090 :     JSContext *cx = f.cx;
     762           45090 :     StackFrame *fp = f.regs.fp();
     763           45090 :     JSScript *script = fp->script();
     764                 : 
     765           45090 :     jsbytecode *pc = f.regs.pc;
     766                 : 
     767           45090 :     JSOp op = JSOp(*pc);
     768           45090 :     const JSCodeSpec *cs = &js_CodeSpec[op];
     769                 : 
     770           45090 :     if (!script->ensureRanAnalysis(cx, NULL)) {
     771               0 :         js_ReportOutOfMemory(cx);
     772               0 :         return js_InternalThrow(f);
     773                 :     }
     774                 : 
     775           90180 :     analyze::AutoEnterAnalysis enter(cx);
     776           45090 :     analyze::ScriptAnalysis *analysis = script->analysis();
     777                 : 
     778                 :     /*
     779                 :      * f.regs.sp is not normally maintained by stubs (except for call prologues
     780                 :      * where it indicates the new frame), so is not expected to be coherent
     781                 :      * here. Update it to its value at the start of the opcode.
     782                 :      */
     783           45090 :     Value *oldsp = f.regs.sp;
     784           45090 :     f.regs.sp = fp->base() + analysis->getCode(pc).stackDepth;
     785                 : 
     786           45090 :     jsbytecode *nextpc = pc + GetBytecodeLength(pc);
     787           45090 :     Value *nextsp = NULL;
     788           45090 :     if (nextpc != script->code + script->length && analysis->maybeCode(nextpc))
     789           45027 :         nextsp = fp->base() + analysis->getCode(nextpc).stackDepth;
     790                 : 
     791           45090 :     JS_ASSERT(&cx->regs() == &f.regs);
     792                 : 
     793                 : #ifdef JS_METHODJIT_SPEW
     794                 :     JaegerSpew(JSpew_Recompile, "interpreter rejoin (file \"%s\") (line \"%d\") (op %s) (opline \"%d\")\n",
     795           45090 :                script->filename, script->lineno, OpcodeNames[op], PCToLineNumber(script, pc));
     796                 : #endif
     797                 : 
     798           45090 :     uint32_t nextDepth = UINT32_MAX;
     799           45090 :     bool skipTrap = false;
     800                 : 
     801           45090 :     if ((cs->format & (JOF_INC | JOF_DEC)) &&
     802                 :         (rejoin == REJOIN_POS || rejoin == REJOIN_BINARY)) {
     803                 :         /*
     804                 :          * We may reenter the interpreter while finishing the INC/DEC operation
     805                 :          * on a local or arg (property INC/DEC operations will rejoin into the
     806                 :          * decomposed version of the op.
     807                 :          */
     808              17 :         JS_ASSERT(cs->format & (JOF_LOCAL | JOF_QARG));
     809                 : 
     810              17 :         nextDepth = analysis->getCode(nextpc).stackDepth;
     811              17 :         enter.leave();
     812                 : 
     813              17 :         if (rejoin != REJOIN_BINARY || !analysis->incrementInitialValueObserved(pc)) {
     814                 :             /* Stack layout is 'V', 'N' or 'N+1' (only if the N is not needed) */
     815              14 :             FinishVarIncOp(f, rejoin, nextsp[-1], nextsp[-1], &nextsp[-1]);
     816                 :         } else {
     817                 :             /* Stack layout is 'N N+1' */
     818               3 :             FinishVarIncOp(f, rejoin, nextsp[-1], nextsp[0], &nextsp[-1]);
     819                 :         }
     820                 : 
     821              17 :         rejoin = REJOIN_FALLTHROUGH;
     822                 :     }
     823                 : 
     824           45090 :     switch (rejoin) {
     825                 :       case REJOIN_SCRIPTED: {
     826                 :         jsval_layout rval;
     827                 : #ifdef JS_NUNBOX32
     828             284 :         rval.asBits = ((uint64_t)returnType << 32) | (uint32_t)returnData;
     829                 : #elif JS_PUNBOX64
     830                 :         rval.asBits = (uint64_t)returnType | (uint64_t)returnData;
     831                 : #else
     832                 : #error "Unknown boxing format"
     833                 : #endif
     834                 : 
     835             284 :         nextsp[-1] = IMPL_TO_JSVAL(rval);
     836                 : 
     837                 :         /*
     838                 :          * When making a scripted call at monitored sites, it is the caller's
     839                 :          * responsibility to update the pushed type set.
     840                 :          */
     841             284 :         types::TypeScript::Monitor(cx, script, pc, nextsp[-1]);
     842             284 :         f.regs.pc = nextpc;
     843             284 :         break;
     844                 :       }
     845                 : 
     846                 :       case REJOIN_NONE:
     847               0 :         JS_NOT_REACHED("Unpossible rejoin!");
     848                 :         break;
     849                 : 
     850                 :       case REJOIN_RESUME:
     851            6088 :         break;
     852                 : 
     853                 :       case REJOIN_TRAP:
     854                 :         /*
     855                 :          * Make sure when resuming in the interpreter we do not execute the
     856                 :          * trap again. Watch out for the case where the trap removed itself.
     857                 :          */
     858              24 :         if (script->hasBreakpointsAt(pc))
     859               8 :             skipTrap = true;
     860              24 :         break;
     861                 : 
     862                 :       case REJOIN_FALLTHROUGH:
     863            5453 :         f.regs.pc = nextpc;
     864            5453 :         break;
     865                 : 
     866                 :       case REJOIN_NATIVE:
     867                 :       case REJOIN_NATIVE_LOWERED:
     868                 :       case REJOIN_NATIVE_GETTER: {
     869                 :         /*
     870                 :          * We don't rejoin until after the native stub finishes execution, in
     871                 :          * which case the return value will be in memory. For lowered natives,
     872                 :          * the return value will be in the 'this' value's slot.
     873                 :          */
     874            1451 :         if (rejoin != REJOIN_NATIVE)
     875               0 :             nextsp[-1] = nextsp[0];
     876                 : 
     877                 :         /* Release this reference on the orphaned native stub. */
     878            1451 :         RemoveOrphanedNative(cx, fp);
     879                 : 
     880            1451 :         f.regs.pc = nextpc;
     881            1451 :         break;
     882                 :       }
     883                 : 
     884                 :       case REJOIN_PUSH_BOOLEAN:
     885               3 :         nextsp[-1].setBoolean(returnReg != NULL);
     886               3 :         f.regs.pc = nextpc;
     887               3 :         break;
     888                 : 
     889                 :       case REJOIN_PUSH_OBJECT:
     890               0 :         nextsp[-1].setObject(* (JSObject *) returnReg);
     891               0 :         f.regs.pc = nextpc;
     892               0 :         break;
     893                 : 
     894                 :       case REJOIN_DEFLOCALFUN:
     895               0 :         fp->slots()[GET_SLOTNO(pc)].setObject(* (JSObject *) returnReg);
     896               0 :         f.regs.pc = nextpc;
     897               0 :         break;
     898                 : 
     899                 :       case REJOIN_THIS_PROTOTYPE: {
     900               0 :         JSObject *callee = &fp->callee();
     901               0 :         JSObject *proto = f.regs.sp[0].isObject() ? &f.regs.sp[0].toObject() : NULL;
     902               0 :         JSObject *obj = js_CreateThisForFunctionWithProto(cx, callee, proto);
     903               0 :         if (!obj)
     904               0 :             return js_InternalThrow(f);
     905               0 :         fp->formalArgs()[-1].setObject(*obj);
     906                 : 
     907               0 :         if (Probes::callTrackingActive(cx))
     908               0 :             Probes::enterJSFun(f.cx, f.fp()->maybeFun(), f.fp()->script());
     909                 : 
     910               0 :         if (script->debugMode) {
     911               0 :             JSTrapStatus status = js::ScriptDebugPrologue(f.cx, f.fp());
     912               0 :             switch (status) {
     913                 :               case JSTRAP_CONTINUE:
     914                 :                 break;
     915                 :               case JSTRAP_RETURN:
     916               0 :                 *f.returnAddressLocation() = f.cx->jaegerCompartment()->forceReturnFromExternC();
     917               0 :                 return NULL;
     918                 :               case JSTRAP_THROW:
     919                 :               case JSTRAP_ERROR:
     920               0 :                 return js_InternalThrow(f);
     921                 :               default:
     922               0 :                 JS_NOT_REACHED("bad ScriptDebugPrologue status");
     923                 :             }
     924                 :         }
     925                 : 
     926               0 :         break;
     927                 :       }
     928                 : 
     929                 :       case REJOIN_CHECK_ARGUMENTS:
     930                 :         /*
     931                 :          * Do all the work needed in arity check JIT prologues after the
     932                 :          * arguments check occurs (FixupArity has been called if needed, but
     933                 :          * the stack check and late prologue have not been performed.
     934                 :          */
     935              20 :         if (!CheckStackQuota(f))
     936               0 :             return js_InternalThrow(f);
     937                 : 
     938              20 :         SetValueRangeToUndefined(fp->slots(), script->nfixed);
     939                 : 
     940              20 :         if (!fp->functionPrologue(cx))
     941               0 :             return js_InternalThrow(f);
     942                 :         /* FALLTHROUGH */
     943                 : 
     944                 :       case REJOIN_FUNCTION_PROLOGUE:
     945              21 :         fp->scopeChain();
     946                 : 
     947                 :         /* Construct the 'this' object for the frame if necessary. */
     948              21 :         if (!ScriptPrologueOrGeneratorResume(cx, fp, types::UseNewTypeAtEntry(cx, fp)))
     949               0 :             return js_InternalThrow(f);
     950                 : 
     951                 :         /* 
     952                 :          * Having called ScriptPrologueOrGeneratorResume, we would normally call
     953                 :          * ScriptDebugPrologue here. But in debug mode, we only use JITted
     954                 :          * functions' invokeEntry entry point, whereas CheckArgumentTypes
     955                 :          * (REJOIN_CHECK_ARGUMENTS) and FunctionFramePrologue
     956                 :          * (REJOIN_FUNCTION_PROLOGUE) are only reachable via the other entry
     957                 :          * points. So we should never need either of these rejoin tails in debug
     958                 :          * mode.
     959                 :          *
     960                 :          * If we fix bug 699196 ("Debug mode code could use inline caches
     961                 :          * now"), then these cases will become reachable again.
     962                 :          */
     963              21 :         JS_ASSERT(!cx->compartment->debugMode());
     964                 : 
     965              21 :         break;
     966                 : 
     967                 :       case REJOIN_CALL_PROLOGUE:
     968                 :       case REJOIN_CALL_PROLOGUE_LOWERED_CALL:
     969                 :       case REJOIN_CALL_PROLOGUE_LOWERED_APPLY:
     970            9363 :         if (returnReg) {
     971             852 :             uint32_t argc = 0;
     972             852 :             if (rejoin == REJOIN_CALL_PROLOGUE)
     973             852 :                 argc = GET_ARGC(pc);
     974               0 :             else if (rejoin == REJOIN_CALL_PROLOGUE_LOWERED_CALL)
     975               0 :                 argc = GET_ARGC(pc) - 1;
     976                 :             else
     977               0 :                 argc = f.u.call.dynamicArgc;
     978                 : 
     979                 :             /*
     980                 :              * The caller frame's code was discarded, but we still need to
     981                 :              * execute the callee and have a JIT code pointer to do so.
     982                 :              * Set the argc and frame registers as the call path does, but set
     983                 :              * the callee frame's return address to jump back into the
     984                 :              * Interpoline, and change the caller frame's rejoin to reflect the
     985                 :              * state after the call.
     986                 :              */
     987             852 :             f.regs.restorePartialFrame(oldsp); /* f.regs.sp stored the new frame */
     988             852 :             f.scratch = (void *) uintptr_t(argc); /* The interpoline will load f.scratch into argc */
     989             852 :             f.fp()->setNativeReturnAddress(JS_FUNC_TO_DATA_PTR(void *, JaegerInterpolineScripted));
     990             852 :             fp->setRejoin(REJOIN_SCRIPTED | ((pc - script->code) << 1));
     991             852 :             return returnReg;
     992                 :         } else {
     993                 :             /*
     994                 :              * The call has already finished, and the return value is on the
     995                 :              * stack. For lowered call/apply, the return value has been stored
     996                 :              * in the wrong slot, so adjust it here.
     997                 :              */
     998            8511 :             f.regs.pc = nextpc;
     999            8511 :             if (rejoin != REJOIN_CALL_PROLOGUE) {
    1000                 :                 /* Same offset return value as for lowered native calls. */
    1001              30 :                 nextsp[-1] = nextsp[0];
    1002                 :             }
    1003                 :         }
    1004            8511 :         break;
    1005                 : 
    1006                 :       case REJOIN_CALL_SPLAT: {
    1007                 :         /* Leave analysis early and do the Invoke which SplatApplyArgs prepared. */
    1008               1 :         nextDepth = analysis->getCode(nextpc).stackDepth;
    1009               1 :         enter.leave();
    1010               1 :         f.regs.sp = nextsp + 2 + f.u.call.dynamicArgc;
    1011               1 :         if (!InvokeKernel(cx, CallArgsFromSp(f.u.call.dynamicArgc, f.regs.sp)))
    1012               0 :             return js_InternalThrow(f);
    1013               1 :         nextsp[-1] = nextsp[0];
    1014               1 :         f.regs.pc = nextpc;
    1015               1 :         break;
    1016                 :       }
    1017                 : 
    1018                 :       case REJOIN_GETTER:
    1019                 :         /*
    1020                 :          * Match the PC to figure out whether this property fetch is part of a
    1021                 :          * fused opcode which needs to be finished.
    1022                 :          */
    1023           21362 :         switch (op) {
    1024                 :           case JSOP_INSTANCEOF: {
    1025                 :             /*
    1026                 :              * If we recompiled from a getprop used within JSOP_INSTANCEOF,
    1027                 :              * the stack looks like 'LHS RHS protov'. Inline the remaining
    1028                 :              * portion of fun_hasInstance.
    1029                 :              */
    1030               0 :             if (f.regs.sp[0].isPrimitive()) {
    1031               0 :                 js_ReportValueError(cx, JSMSG_BAD_PROTOTYPE, -1, f.regs.sp[-1], NULL);
    1032               0 :                 return js_InternalThrow(f);
    1033                 :             }
    1034               0 :             nextsp[-1].setBoolean(js_IsDelegate(cx, &f.regs.sp[0].toObject(), f.regs.sp[-2]));
    1035               0 :             f.regs.pc = nextpc;
    1036               0 :             break;
    1037                 :           }
    1038                 : 
    1039                 :           default:
    1040           21362 :             f.regs.pc = nextpc;
    1041           21362 :             break;
    1042                 :         }
    1043           21362 :         break;
    1044                 : 
    1045                 :       case REJOIN_POS:
    1046                 :         /* Convert-to-number which might be part of an INC* op. */
    1047              14 :         JS_ASSERT(op == JSOP_POS);
    1048              14 :         f.regs.pc = nextpc;
    1049              14 :         break;
    1050                 : 
    1051                 :       case REJOIN_BINARY:
    1052                 :         /* Binary arithmetic op which might be part of an INC* op. */
    1053             168 :         JS_ASSERT(op == JSOP_ADD || op == JSOP_SUB || op == JSOP_MUL || op == JSOP_DIV);
    1054             168 :         f.regs.pc = nextpc;
    1055             168 :         break;
    1056                 : 
    1057                 :       case REJOIN_BRANCH: {
    1058                 :         /*
    1059                 :          * This must be an opcode fused with IFNE/IFEQ. Unfused IFNE/IFEQ are
    1060                 :          * implemented in terms of ValueToBoolean, which is infallible and
    1061                 :          * cannot trigger recompilation.
    1062                 :          */
    1063             858 :         bool takeBranch = false;
    1064             858 :         switch (JSOp(*nextpc)) {
    1065                 :           case JSOP_IFNE:
    1066             854 :             takeBranch = returnReg != NULL;
    1067             854 :             break;
    1068                 :           case JSOP_IFEQ:
    1069               4 :             takeBranch = returnReg == NULL;
    1070               4 :             break;
    1071                 :           default:
    1072               0 :             JS_NOT_REACHED("Bad branch op");
    1073                 :         }
    1074             858 :         if (takeBranch)
    1075             851 :             f.regs.pc = nextpc + GET_JUMP_OFFSET(nextpc);
    1076                 :         else
    1077               7 :             f.regs.pc = nextpc + GetBytecodeLength(nextpc);
    1078             858 :         break;
    1079                 :       }
    1080                 : 
    1081                 :       default:
    1082               0 :         JS_NOT_REACHED("Missing rejoin");
    1083                 :     }
    1084                 : 
    1085           44238 :     if (nextDepth == UINT32_MAX)
    1086           44220 :         nextDepth = analysis->getCode(f.regs.pc).stackDepth;
    1087           44238 :     f.regs.sp = fp->base() + nextDepth;
    1088                 : 
    1089                 :     /*
    1090                 :      * Monitor the result of the previous op when finishing a JOF_TYPESET op.
    1091                 :      * The result may not have been marked if we bailed out while inside a stub
    1092                 :      * for the op.
    1093                 :      */
    1094           44238 :     if (f.regs.pc == nextpc && (js_CodeSpec[op].format & JOF_TYPESET))
    1095           36600 :         types::TypeScript::Monitor(cx, script, pc, f.regs.sp[-1]);
    1096                 : 
    1097                 :     /* Mark the entry frame as unfinished, and update the regs to resume at. */
    1098           44238 :     JaegerStatus status = skipTrap ? Jaeger_UnfinishedAtTrap : Jaeger_Unfinished;
    1099           44238 :     cx->compartment->jaegerCompartment()->setLastUnfinished(status);
    1100           44238 :     *f.oldregs = f.regs;
    1101                 : 
    1102           44238 :     return NULL;
    1103                 : }

Generated by: LCOV version 1.7