LCOV - code coverage report
Current view: directory - js/src/vm - Stack.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 571 535 93.7 %
Date: 2012-06-02 Functions: 57 54 94.7 %

       1                 : /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2                 :  * vim: set ts=4 sw=4 et tw=79 ft=cpp:
       3                 :  *
       4                 :  * ***** BEGIN LICENSE BLOCK *****
       5                 :  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
       6                 :  *
       7                 :  * The contents of this file are subject to the Mozilla Public License Version
       8                 :  * 1.1 (the "License"); you may not use this file except in compliance with
       9                 :  * the License. You may obtain a copy of the License at
      10                 :  * http://www.mozilla.org/MPL/
      11                 :  *
      12                 :  * Software distributed under the License is distributed on an "AS IS" basis,
      13                 :  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
      14                 :  * for the specific language governing rights and limitations under the
      15                 :  * License.
      16                 :  *
      17                 :  * The Original Code is SpiderMonkey JavaScript engine.
      18                 :  *
      19                 :  * The Initial Developer of the Original Code is
      20                 :  * Mozilla Corporation.
      21                 :  * Portions created by the Initial Developer are Copyright (C) 2009
      22                 :  * the Initial Developer. All Rights Reserved.
      23                 :  *
      24                 :  * Contributor(s):
      25                 :  *   Luke Wagner <luke@mozilla.com>
      26                 :  *
      27                 :  * Alternatively, the contents of this file may be used under the terms of
      28                 :  * either the GNU General Public License Version 2 or later (the "GPL"), or
      29                 :  * 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 "jsgcmark.h"
      43                 : #include "methodjit/MethodJIT.h"
      44                 : #include "Stack.h"
      45                 : 
      46                 : #include "jsgcinlines.h"
      47                 : #include "jsobjinlines.h"
      48                 : 
      49                 : #include "Stack-inl.h"
      50                 : 
      51                 : /* Includes to get to low-level memory-mapping functionality. */
      52                 : #ifdef XP_WIN
      53                 : # include "jswin.h"
      54                 : #elif defined(XP_OS2)
      55                 : # define INCL_DOSMEMMGR
      56                 : # include <os2.h>
      57                 : #else
      58                 : # include <unistd.h>
      59                 : # include <sys/mman.h>
      60                 : # if !defined(MAP_ANONYMOUS)
      61                 : #  if defined(MAP_ANON)
      62                 : #   define MAP_ANONYMOUS MAP_ANON
      63                 : #  else
      64                 : #   define MAP_ANONYMOUS 0
      65                 : #  endif
      66                 : # endif
      67                 : #endif
      68                 : 
      69                 : using namespace js;
      70                 : 
      71                 : /*****************************************************************************/
      72                 : 
      73                 : void
      74          179593 : StackFrame::initExecuteFrame(JSScript *script, StackFrame *prev, FrameRegs *regs,
      75                 :                              const Value &thisv, JSObject &scopeChain, ExecuteType type)
      76                 : {
      77                 :     /*
      78                 :      * See encoding of ExecuteType. When GLOBAL isn't set, we are executing a
      79                 :      * script in the context of another frame and the frame type is determined
      80                 :      * by the context.
      81                 :      */
      82          179593 :     flags_ = type | HAS_SCOPECHAIN | HAS_BLOCKCHAIN | HAS_PREVPC;
      83          179593 :     if (!(flags_ & GLOBAL))
      84           81606 :         flags_ |= (prev->flags_ & (FUNCTION | GLOBAL));
      85                 : 
      86          179593 :     Value *dstvp = (Value *)this - 2;
      87          179593 :     dstvp[1] = thisv;
      88                 : 
      89          179593 :     if (isFunctionFrame()) {
      90           79404 :         dstvp[0] = prev->calleev();
      91           79404 :         exec = prev->exec;
      92           79404 :         u.evalScript = script;
      93                 :     } else {
      94          100189 :         JS_ASSERT(isGlobalFrame());
      95          100189 :         dstvp[0] = NullValue();
      96          100189 :         exec.script = script;
      97                 : #ifdef DEBUG
      98          100189 :         u.evalScript = (JSScript *)0xbad;
      99                 : #endif
     100                 :     }
     101                 : 
     102          179593 :     scopeChain_ = &scopeChain;
     103          179593 :     prev_ = prev;
     104          179593 :     prevpc_ = regs ? regs->pc : (jsbytecode *)0xbad;
     105          179593 :     prevInline_ = regs ? regs->inlined() : NULL;
     106          179593 :     blockChain_ = NULL;
     107                 : 
     108                 : #ifdef DEBUG
     109          179593 :     ncode_ = (void *)0xbad;
     110          179593 :     Debug_SetValueRangeToCrashOnTouch(&rval_, 1);
     111          179593 :     hookData_ = (void *)0xbad;
     112          179593 :     annotation_ = (void *)0xbad;
     113                 : #endif
     114                 : 
     115          179593 :     if (prev && prev->annotation())
     116               0 :         setAnnotation(prev->annotation());
     117          179593 : }
     118                 : 
     119                 : void
     120          259746 : StackFrame::initDummyFrame(JSContext *cx, JSObject &chain)
     121                 : {
     122          259746 :     PodZero(this);
     123          259746 :     flags_ = DUMMY | HAS_PREVPC | HAS_SCOPECHAIN;
     124          259746 :     initPrev(cx);
     125          259746 :     JS_ASSERT(chain.isGlobal());
     126          259746 :     setScopeChainNoCallObj(chain);
     127          259746 : }
     128                 : 
     129                 : template <class T, class U, StackFrame::TriggerPostBarriers doPostBarrier>
     130                 : void
     131           73931 : StackFrame::stealFrameAndSlots(StackFrame *fp, T *vp, StackFrame *otherfp, U *othervp,
     132                 :                                Value *othersp)
     133                 : {
     134           73931 :     JS_ASSERT((U *)vp == (U *)this - ((U *)otherfp - othervp));
     135           73931 :     JS_ASSERT((Value *)othervp == otherfp->actualArgs() - 2);
     136           73931 :     JS_ASSERT(othersp >= otherfp->slots());
     137           73931 :     JS_ASSERT(othersp <= otherfp->base() + otherfp->numSlots());
     138           73931 :     JS_ASSERT((T *)fp - vp == (U *)otherfp - othervp);
     139                 : 
     140                 :     /* Copy args, StackFrame, and slots. */
     141           73931 :     U *srcend = (U *)otherfp->formalArgsEnd();
     142           73931 :     T *dst = vp;
     143          281896 :     for (U *src = othervp; src < srcend; src++, dst++)
     144          207965 :         *dst = *src;
     145                 : 
     146           73931 :     *fp = *otherfp;
     147                 :     if (doPostBarrier)
     148           43097 :         fp->writeBarrierPost();
     149                 : 
     150           73931 :     srcend = (U *)othersp;
     151           73931 :     dst = (T *)fp->slots();
     152          199615 :     for (U *src = (U *)otherfp->slots(); src < srcend; src++, dst++)
     153          125684 :         *dst = *src;
     154                 : 
     155                 :     /*
     156                 :      * Repoint Call, Arguments, Block and With objects to the new live frame.
     157                 :      * Call and Arguments are done directly because we have pointers to them.
     158                 :      * Block and With objects are done indirectly through 'liveFrame'. See
     159                 :      * js_LiveFrameToFloating comment in jsiter.h.
     160                 :      */
     161           73931 :     if (hasCallObj()) {
     162            3234 :         CallObject &obj = callObj();
     163            3234 :         obj.setStackFrame(this);
     164            3234 :         otherfp->flags_ &= ~HAS_CALL_OBJ;
     165            3234 :         if (js_IsNamedLambda(fun())) {
     166             396 :             DeclEnvObject &env = obj.enclosingScope().asDeclEnv();
     167             396 :             env.setStackFrame(this);
     168                 :         }
     169                 :     }
     170           73931 :     if (hasArgsObj()) {
     171             225 :         ArgumentsObject &argsobj = argsObj();
     172             225 :         if (argsobj.isNormalArguments())
     173              54 :             argsobj.setStackFrame(this);
     174                 :         else
     175             171 :             JS_ASSERT(!argsobj.maybeStackFrame());
     176             225 :         otherfp->flags_ &= ~HAS_ARGS_OBJ;
     177                 :     }
     178           73931 : }
     179                 : 
     180                 : /* Note: explicit instantiation for js_NewGenerator located in jsiter.cpp. */
     181                 : template void StackFrame::stealFrameAndSlots<Value, HeapValue, StackFrame::NoPostBarrier>(
     182                 :                                              StackFrame *, Value *,
     183                 :                                              StackFrame *, HeapValue *, Value *);
     184                 : template void StackFrame::stealFrameAndSlots<HeapValue, Value, StackFrame::DoPostBarrier>(
     185                 :                                              StackFrame *, HeapValue *,
     186                 :                                              StackFrame *, Value *, Value *);
     187                 : 
     188                 : void
     189           43097 : StackFrame::writeBarrierPost()
     190                 : {
     191                 :     /* This needs to follow the same rules as in js_TraceStackFrame. */
     192           43097 :     if (scopeChain_)
     193           43097 :         JSObject::writeBarrierPost(scopeChain_, (void *)&scopeChain_);
     194           43097 :     if (isDummyFrame())
     195               0 :         return;
     196           43097 :     if (hasArgsObj())
     197             126 :         JSObject::writeBarrierPost(argsObj_, (void *)&argsObj_);
     198           43097 :     if (isScriptFrame()) {
     199           43097 :         if (isFunctionFrame()) {
     200           43097 :             JSFunction::writeBarrierPost((JSObject *)exec.fun, (void *)&exec.fun);
     201           43097 :             if (isEvalFrame())
     202               0 :                 JSScript::writeBarrierPost(u.evalScript, (void *)&u.evalScript);
     203                 :         } else {
     204               0 :             JSScript::writeBarrierPost(exec.script, (void *)&exec.script);
     205                 :         }
     206                 :     }
     207           43097 :     if (hasReturnValue())
     208           29191 :         HeapValue::writeBarrierPost(rval_, &rval_);
     209                 : }
     210                 : 
     211                 : #ifdef DEBUG
     212                 : JSObject *const StackFrame::sInvalidScopeChain = (JSObject *)0xbeef;
     213                 : #endif
     214                 : 
     215                 : jsbytecode *
     216         1278132 : StackFrame::prevpcSlow(JSInlinedSite **pinlined)
     217                 : {
     218         1278132 :     JS_ASSERT(!(flags_ & HAS_PREVPC));
     219                 : #if defined(JS_METHODJIT) && defined(JS_MONOIC)
     220         1278132 :     StackFrame *p = prev();
     221         1278132 :     mjit::JITScript *jit = p->script()->getJIT(p->isConstructing());
     222         1278132 :     prevpc_ = jit->nativeToPC(ncode_, &prevInline_);
     223         1278132 :     flags_ |= HAS_PREVPC;
     224         1278132 :     if (pinlined)
     225         1278132 :         *pinlined = prevInline_;
     226         1278132 :     return prevpc_;
     227                 : #else
     228                 :     JS_NOT_REACHED("Unknown PC for frame");
     229                 :     return NULL;
     230                 : #endif
     231                 : }
     232                 : 
     233                 : jsbytecode *
     234        28885692 : StackFrame::pcQuadratic(const ContextStack &stack, StackFrame *next, JSInlinedSite **pinlined)
     235                 : {
     236        28885692 :     JS_ASSERT_IF(next, next->prev() == this);
     237                 : 
     238        28885692 :     StackSegment &seg = stack.space().containingSegment(this);
     239        28885692 :     FrameRegs &regs = seg.regs();
     240                 : 
     241                 :     /*
     242                 :      * This isn't just an optimization; seg->computeNextFrame(fp) is only
     243                 :      * defined if fp != seg->currentFrame.
     244                 :      */
     245        28885692 :     if (regs.fp() == this) {
     246        27583681 :         if (pinlined)
     247               0 :             *pinlined = regs.inlined();
     248        27583681 :         return regs.pc;
     249                 :     }
     250                 : 
     251         1302011 :     if (!next)
     252         1300537 :         next = seg.computeNextFrame(this);
     253         1302011 :     return next->prevpc(pinlined);
     254                 : }
     255                 : 
     256                 : void
     257          627607 : StackFrame::mark(JSTracer *trc)
     258                 : {
     259                 :     /*
     260                 :      * Normally we would use MarkRoot here, except that generators also take
     261                 :      * this path. However, generators use a special write barrier when the stack
     262                 :      * frame is copied to the floating frame. Therefore, no barrier is needed.
     263                 :      */
     264          627607 :     if (flags_ & HAS_SCOPECHAIN)
     265          627602 :         gc::MarkObjectUnbarriered(trc, &scopeChain_, "scope chain");
     266          627607 :     if (isDummyFrame())
     267            2857 :         return;
     268          624750 :     if (hasArgsObj())
     269            1396 :         gc::MarkObjectUnbarriered(trc, &argsObj_, "arguments");
     270          624750 :     if (isFunctionFrame()) {
     271          599437 :         gc::MarkObjectUnbarriered(trc, &exec.fun, "fun");
     272          599437 :         if (isEvalFrame())
     273             170 :             gc::MarkScriptUnbarriered(trc, &u.evalScript, "eval script");
     274                 :     } else {
     275           25313 :         gc::MarkScriptUnbarriered(trc, &exec.script, "script");
     276                 :     }
     277          624750 :     if (IS_GC_MARKING_TRACER(trc))
     278          616169 :         script()->compartment()->active = true;
     279          624750 :     gc::MarkValueUnbarriered(trc, &returnValue(), "rval");
     280                 : }
     281                 : 
     282                 : /*****************************************************************************/
     283                 : 
     284                 : bool
     285       105344742 : StackSegment::contains(const StackFrame *fp) const
     286                 : {
     287                 :     /* NB: this depends on the continuity of segments in memory. */
     288       105344742 :     return (Value *)fp >= slotsBegin() && (Value *)fp <= (Value *)maybefp();
     289                 : }
     290                 : 
     291                 : bool
     292        56348344 : StackSegment::contains(const FrameRegs *regs) const
     293                 : {
     294        56348344 :     return regs && contains(regs->fp());
     295                 : }
     296                 : 
     297                 : bool
     298       110682871 : StackSegment::contains(const CallArgsList *call) const
     299                 : {
     300       110682871 :     if (!call || !calls_)
     301        54891002 :         return false;
     302                 : 
     303                 :     /* NB: this depends on the continuity of segments in memory. */
     304        55791869 :     Value *vp = call->array();
     305        55791869 :     return vp > slotsBegin() && vp <= calls_->array();
     306                 : }
     307                 : 
     308                 : StackFrame *
     309         1300542 : StackSegment::computeNextFrame(const StackFrame *f) const
     310                 : {
     311         1300542 :     JS_ASSERT(contains(f) && f != fp());
     312                 : 
     313         1300542 :     StackFrame *next = fp();
     314                 :     StackFrame *prev;
     315        11235874 :     while ((prev = next->prev()) != f)
     316         8634790 :         next = prev;
     317         1300542 :     return next;
     318                 : }
     319                 : 
     320                 : Value *
     321       106451948 : StackSegment::end() const
     322                 : {
     323                 :     /* NB: this depends on the continuity of segments in memory. */
     324       106451948 :     JS_ASSERT_IF(calls_ || regs_, contains(calls_) || contains(regs_));
     325                 :     Value *p = calls_
     326                 :                ? regs_
     327        54889566 :                  ? Max(regs_->sp, calls_->end())
     328          483529 :                  : calls_->end()
     329                 :                : regs_
     330                 :                  ? regs_->sp
     331       161825043 :                  : slotsBegin();
     332       106451948 :     JS_ASSERT(p >= slotsBegin());
     333       106451948 :     return p;
     334                 : }
     335                 : 
     336                 : FrameRegs *
     337         5688408 : StackSegment::pushRegs(FrameRegs &regs)
     338                 : {
     339         5688408 :     JS_ASSERT_IF(contains(regs_), regs.fp()->prev() == regs_->fp());
     340         5688408 :     FrameRegs *prev = regs_;
     341         5688408 :     regs_ = &regs;
     342         5688408 :     return prev;
     343                 : }
     344                 : 
     345                 : void
     346         5688408 : StackSegment::popRegs(FrameRegs *regs)
     347                 : {
     348         5688408 :     JS_ASSERT_IF(regs && contains(regs->fp()), regs->fp() == regs_->fp()->prev());
     349         5688408 :     regs_ = regs;
     350         5688408 : }
     351                 : 
     352                 : void
     353         6101043 : StackSegment::pushCall(CallArgsList &callList)
     354                 : {
     355         6101043 :     callList.prev_ = calls_;
     356         6101043 :     calls_ = &callList;
     357         6101043 : }
     358                 : 
     359                 : void
     360            2156 : StackSegment::pointAtCall(CallArgsList &callList)
     361                 : {
     362            2156 :     calls_ = &callList;
     363            2156 : }
     364                 : 
     365                 : void
     366         6101043 : StackSegment::popCall()
     367                 : {
     368         6101043 :     calls_ = calls_->prev_;
     369         6101043 : }
     370                 : 
     371                 : /*****************************************************************************/
     372                 : 
     373           19910 : StackSpace::StackSpace()
     374                 :   : seg_(NULL),
     375                 :     base_(NULL),
     376                 :     conservativeEnd_(NULL),
     377                 : #ifdef XP_WIN
     378                 :     commitEnd_(NULL),
     379                 : #endif
     380                 :     defaultEnd_(NULL),
     381           19910 :     trustedEnd_(NULL)
     382                 : {
     383           19910 :     assertInvariants();
     384           19910 : }
     385                 : 
     386                 : bool
     387           19910 : StackSpace::init()
     388                 : {
     389                 :     void *p;
     390                 : #ifdef XP_WIN
     391                 :     p = VirtualAlloc(NULL, CAPACITY_BYTES, MEM_RESERVE, PAGE_READWRITE);
     392                 :     if (!p)
     393                 :         return false;
     394                 :     void *check = VirtualAlloc(p, COMMIT_BYTES, MEM_COMMIT, PAGE_READWRITE);
     395                 :     if (p != check)
     396                 :         return false;
     397                 :     base_ = reinterpret_cast<Value *>(p);
     398                 :     conservativeEnd_ = commitEnd_ = base_ + COMMIT_VALS;
     399                 :     trustedEnd_ = base_ + CAPACITY_VALS;
     400                 :     defaultEnd_ = trustedEnd_ - BUFFER_VALS;
     401                 : #elif defined(XP_OS2)
     402                 :     if (DosAllocMem(&p, CAPACITY_BYTES, PAG_COMMIT | PAG_READ | PAG_WRITE | OBJ_ANY) &&
     403                 :         DosAllocMem(&p, CAPACITY_BYTES, PAG_COMMIT | PAG_READ | PAG_WRITE))
     404                 :         return false;
     405                 :     base_ = reinterpret_cast<Value *>(p);
     406                 :     trustedEnd_ = base_ + CAPACITY_VALS;
     407                 :     conservativeEnd_ = defaultEnd_ = trustedEnd_ - BUFFER_VALS;
     408                 : #else
     409           19910 :     JS_ASSERT(CAPACITY_BYTES % getpagesize() == 0);
     410           19910 :     p = mmap(NULL, CAPACITY_BYTES, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
     411           19910 :     if (p == MAP_FAILED)
     412               0 :         return false;
     413           19910 :     base_ = reinterpret_cast<Value *>(p);
     414           19910 :     trustedEnd_ = base_ + CAPACITY_VALS;
     415           19910 :     conservativeEnd_ = defaultEnd_ = trustedEnd_ - BUFFER_VALS;
     416                 : #endif
     417           19910 :     assertInvariants();
     418           19910 :     return true;
     419                 : }
     420                 : 
     421           19908 : StackSpace::~StackSpace()
     422                 : {
     423           19908 :     assertInvariants();
     424           19908 :     JS_ASSERT(!seg_);
     425           19908 :     if (!base_)
     426               0 :         return;
     427                 : #ifdef XP_WIN
     428                 :     VirtualFree(base_, (commitEnd_ - base_) * sizeof(Value), MEM_DECOMMIT);
     429                 :     VirtualFree(base_, 0, MEM_RELEASE);
     430                 : #elif defined(XP_OS2)
     431                 :     DosFreeMem(base_);
     432                 : #else
     433                 : #ifdef SOLARIS
     434                 :     munmap((caddr_t)base_, CAPACITY_BYTES);
     435                 : #else
     436           19908 :     munmap(base_, CAPACITY_BYTES);
     437                 : #endif
     438                 : #endif
     439           19908 : }
     440                 : 
     441                 : StackSegment &
     442        28885697 : StackSpace::containingSegment(const StackFrame *target) const
     443                 : {
     444        28885753 :     for (StackSegment *s = seg_; s; s = s->prevInMemory()) {
     445        28885753 :         if (s->contains(target))
     446        28885697 :             return *s;
     447                 :     }
     448               0 :     JS_NOT_REACHED("frame not in stack space");
     449                 :     return *(StackSegment *)NULL;
     450                 : }
     451                 : 
     452                 : void
     453          624727 : StackSpace::markFrameSlots(JSTracer *trc, StackFrame *fp, Value *slotsEnd, jsbytecode *pc)
     454                 : {
     455          624727 :     Value *slotsBegin = fp->slots();
     456                 : 
     457          624727 :     if (!fp->isScriptFrame()) {
     458            2857 :         JS_ASSERT(fp->isDummyFrame());
     459            2857 :         gc::MarkValueRootRange(trc, slotsBegin, slotsEnd, "vm_stack");
     460            2857 :         return;
     461                 :     }
     462                 : 
     463                 :     /* If it's a scripted frame, we should have a pc. */
     464          621870 :     JS_ASSERT(pc);
     465                 : 
     466          621870 :     JSScript *script = fp->script();
     467          621870 :     if (!script->hasAnalysis() || !script->analysis()->ranLifetimes()) {
     468          611608 :         gc::MarkValueRootRange(trc, slotsBegin, slotsEnd, "vm_stack");
     469          611608 :         return;
     470                 :     }
     471                 : 
     472                 :     /*
     473                 :      * If the JIT ran a lifetime analysis, then it may have left garbage in the
     474                 :      * slots considered not live. We need to avoid marking them. Additionally,
     475                 :      * in case the analysis information is thrown out later, we overwrite these
     476                 :      * dead slots with valid values so that future GCs won't crash. Analysis
     477                 :      * results are thrown away during the sweeping phase, so we always have at
     478                 :      * least one GC to do this.
     479                 :      */
     480           20524 :     analyze::AutoEnterAnalysis aea(script->compartment());
     481           10262 :     analyze::ScriptAnalysis *analysis = script->analysis();
     482           10262 :     uint32_t offset = pc - script->code;
     483           10262 :     Value *fixedEnd = slotsBegin + script->nfixed;
     484           21429 :     for (Value *vp = slotsBegin; vp < fixedEnd; vp++) {
     485           11167 :         uint32_t slot = analyze::LocalSlot(script, vp - slotsBegin);
     486                 : 
     487                 :         /*
     488                 :          * Will this slot be synced by the JIT? If not, replace with a dummy
     489                 :          * value with the same type tag.
     490                 :          */
     491           11167 :         if (!analysis->trackSlot(slot) || analysis->liveness(slot).live(offset))
     492            5018 :             gc::MarkValueRoot(trc, vp, "vm_stack");
     493            6149 :         else if (vp->isObject())
     494             552 :             *vp = ObjectValue(fp->scopeChain().global());
     495            5597 :         else if (vp->isString())
     496             121 :             *vp = StringValue(trc->runtime->atomState.nullAtom);
     497                 :     }
     498                 : 
     499           10262 :     gc::MarkValueRootRange(trc, fixedEnd, slotsEnd, "vm_stack");
     500                 : }
     501                 : 
     502                 : void
     503           54466 : StackSpace::mark(JSTracer *trc)
     504                 : {
     505                 :     /*
     506                 :      * JIT code can leave values in an incoherent (i.e., unsafe for precise
     507                 :      * marking) state, hence MarkStackRangeConservatively.
     508                 :      */
     509                 : 
     510                 :     /* NB: this depends on the continuity of segments in memory. */
     511           54466 :     Value *nextSegEnd = firstUnused();
     512           82857 :     for (StackSegment *seg = seg_; seg; seg = seg->prevInMemory()) {
     513                 :         /*
     514                 :          * A segment describes a linear region of memory that contains a stack
     515                 :          * of native and interpreted calls. For marking purposes, though, we
     516                 :          * only need to distinguish between frames and values and mark
     517                 :          * accordingly. Since native calls only push values on the stack, we
     518                 :          * can effectively lump them together and just iterate over interpreted
     519                 :          * calls. Thus, marking can view the stack as the regex:
     520                 :          *   (segment slots (frame slots)*)*
     521                 :          * which gets marked in reverse order.
     522                 :          */
     523           28391 :         Value *slotsEnd = nextSegEnd;
     524           28391 :         jsbytecode *pc = seg->maybepc();
     525          653118 :         for (StackFrame *fp = seg->maybefp(); (Value *)fp > (Value *)seg; fp = fp->prev()) {
     526                 :             /* Mark from fp->slots() to slotsEnd. */
     527          624727 :             markFrameSlots(trc, fp, slotsEnd, pc);
     528                 : 
     529          624727 :             fp->mark(trc);
     530          624727 :             slotsEnd = (Value *)fp;
     531                 : 
     532                 :             JSInlinedSite *site;
     533          624727 :             pc = fp->prevpc(&site);
     534          624727 :             JS_ASSERT_IF(fp->prev(), !site);
     535                 :         }
     536           28391 :         gc::MarkValueRootRange(trc, seg->slotsBegin(), slotsEnd, "vm_stack");
     537           28391 :         nextSegEnd = (Value *)seg;
     538                 :     }
     539           54466 : }
     540                 : 
     541                 : void
     542               0 : StackSpace::markActiveCompartments()
     543                 : {
     544               0 :     for (StackSegment *seg = seg_; seg; seg = seg->prevInMemory()) {
     545               0 :         for (StackFrame *fp = seg->maybefp(); (Value *)fp > (Value *)seg; fp = fp->prev())
     546               0 :             MarkCompartmentActive(fp);
     547                 :     }
     548               0 : }
     549                 : 
     550                 : JS_FRIEND_API(bool)
     551             231 : StackSpace::ensureSpaceSlow(JSContext *cx, MaybeReportError report, Value *from, ptrdiff_t nvals,
     552                 :                             JSCompartment *dest) const
     553                 : {
     554             231 :     assertInvariants();
     555                 : 
     556                 :     /* See CX_COMPARTMENT comment. */
     557             231 :     if (dest == (JSCompartment *)CX_COMPARTMENT)
     558             231 :         dest = cx->compartment;
     559                 : 
     560             231 :     bool trusted = !dest || dest->principals == cx->runtime->trustedPrincipals();
     561             231 :     Value *end = trusted ? trustedEnd_ : defaultEnd_;
     562                 : 
     563                 :     /*
     564                 :      * conservativeEnd_ must stay below defaultEnd_: if conservativeEnd_ were
     565                 :      * to be bumped past defaultEnd_, untrusted JS would be able to consume the
     566                 :      * buffer space at the end of the stack reserved for trusted JS.
     567                 :      */
     568                 : 
     569             231 :     if (end - from < nvals) {
     570             129 :         if (report)
     571             101 :             js_ReportOverRecursed(cx);
     572             129 :         return false;
     573                 :     }
     574                 : 
     575                 : #ifdef XP_WIN
     576                 :     if (commitEnd_ - from < nvals) {
     577                 :         Value *newCommit = commitEnd_;
     578                 :         Value *request = from + nvals;
     579                 : 
     580                 :         /* Use a dumb loop; will probably execute once. */
     581                 :         JS_ASSERT((trustedEnd_ - newCommit) % COMMIT_VALS == 0);
     582                 :         do {
     583                 :             newCommit += COMMIT_VALS;
     584                 :             JS_ASSERT((trustedEnd_ - newCommit) >= 0);
     585                 :         } while (newCommit < request);
     586                 : 
     587                 :         /* The cast is safe because CAPACITY_BYTES is small. */
     588                 :         int32_t size = static_cast<int32_t>(newCommit - commitEnd_) * sizeof(Value);
     589                 : 
     590                 :         if (!VirtualAlloc(commitEnd_, size, MEM_COMMIT, PAGE_READWRITE)) {
     591                 :             if (report)
     592                 :                 js_ReportOverRecursed(cx);
     593                 :             return false;
     594                 :         }
     595                 : 
     596                 :         commitEnd_ = newCommit;
     597                 :         conservativeEnd_ = Min(commitEnd_, defaultEnd_);
     598                 :         assertInvariants();
     599                 :     }
     600                 : #endif
     601                 : 
     602             102 :     return true;
     603                 : }
     604                 : 
     605                 : bool
     606               0 : StackSpace::tryBumpLimit(JSContext *cx, Value *from, unsigned nvals, Value **limit)
     607                 : {
     608               0 :     if (!ensureSpace(cx, REPORT_ERROR, from, nvals))
     609               0 :         return false;
     610               0 :     *limit = conservativeEnd_;
     611               0 :     return true;
     612                 : }
     613                 : 
     614                 : size_t
     615               6 : StackSpace::sizeOfCommitted()
     616                 : {
     617                 : #ifdef XP_WIN
     618                 :     return (commitEnd_ - base_) * sizeof(Value);
     619                 : #else
     620               6 :     return (trustedEnd_ - base_) * sizeof(Value);
     621                 : #endif
     622                 : }
     623                 : 
     624                 : /*****************************************************************************/
     625                 : 
     626          108725 : ContextStack::ContextStack(JSContext *cx)
     627                 :   : seg_(NULL),
     628                 :     space_(&cx->runtime->stackSpace),
     629          108725 :     cx_(cx)
     630          108725 : {}
     631                 : 
     632          108722 : ContextStack::~ContextStack()
     633                 : {
     634          108722 :     JS_ASSERT(!seg_);
     635          108722 : }
     636                 : 
     637                 : bool
     638        63054735 : ContextStack::onTop() const
     639                 : {
     640        63054735 :     return seg_ && seg_ == space().seg_;
     641                 : }
     642                 : 
     643                 : bool
     644           30602 : ContextStack::containsSlow(const StackFrame *target) const
     645                 : {
     646           31118 :     for (StackSegment *s = seg_; s; s = s->prevInContext()) {
     647           31118 :         if (s->contains(target))
     648           30602 :             return true;
     649                 :     }
     650               0 :     return false;
     651                 : }
     652                 : 
     653                 : /*
     654                 :  * This helper function brings the ContextStack to the top of the thread stack
     655                 :  * (so that it can be extended to push a frame and/or arguments) by potentially
     656                 :  * pushing a StackSegment. The 'pushedSeg' outparam indicates whether such a
     657                 :  * segment was pushed (and hence whether the caller needs to call popSegment).
     658                 :  *
     659                 :  * Additionally, to minimize calls to ensureSpace, ensureOnTop ensures that
     660                 :  * there is space for nvars slots on top of the stack.
     661                 :  */
     662                 : Value *
     663         6791683 : ContextStack::ensureOnTop(JSContext *cx, MaybeReportError report, unsigned nvars,
     664                 :                           MaybeExtend extend, bool *pushedSeg, JSCompartment *dest)
     665                 : {
     666         6791683 :     Value *firstUnused = space().firstUnused();
     667                 : 
     668                 : #ifdef JS_METHODJIT
     669                 :     /*
     670                 :      * The only calls made by inlined methodjit frames can be to other JIT
     671                 :      * frames associated with the same VMFrame. If we try to Invoke(),
     672                 :      * Execute() or so forth, any topmost inline frame will need to be
     673                 :      * expanded (along with other inline frames in the compartment).
     674                 :      * To avoid pathological behavior here, make sure to mark any topmost
     675                 :      * function as uninlineable, which will expand inline frames if there are
     676                 :      * any and prevent the function from being inlined in the future.
     677                 :      */
     678         6791683 :     if (FrameRegs *regs = cx->maybeRegs()) {
     679         6483110 :         JSFunction *fun = NULL;
     680         6483110 :         if (JSInlinedSite *site = regs->inlined()) {
     681               1 :             mjit::JITChunk *chunk = regs->fp()->jit()->chunk(regs->pc);
     682               1 :             fun = chunk->inlineFrames()[site->inlineIndex].fun;
     683                 :         } else {
     684         6483109 :             StackFrame *fp = regs->fp();
     685         6483109 :             if (fp->isFunctionFrame()) {
     686         6109743 :                 JSFunction *f = fp->fun();
     687         6109743 :                 if (f->isInterpreted())
     688         6109743 :                     fun = f;
     689                 :             }
     690                 :         }
     691                 : 
     692         6483110 :         if (fun) {
     693         6109744 :             fun->script()->uninlineable = true;
     694         6109744 :             types::MarkTypeObjectFlags(cx, fun, types::OBJECT_FLAG_UNINLINEABLE);
     695                 :         }
     696                 :     }
     697         6791683 :     JS_ASSERT_IF(cx->hasfp(), !cx->regs().inlined());
     698                 : #endif
     699                 : 
     700         6791683 :     if (onTop() && extend) {
     701         6322368 :         if (!space().ensureSpace(cx, report, firstUnused, nvars, dest))
     702               0 :             return NULL;
     703         6322368 :         return firstUnused;
     704                 :     }
     705                 : 
     706          469315 :     if (!space().ensureSpace(cx, report, firstUnused, VALUES_PER_STACK_SEGMENT + nvars, dest))
     707               0 :         return NULL;
     708                 : 
     709                 :     FrameRegs *regs;
     710                 :     CallArgsList *calls;
     711          469315 :     if (seg_ && extend) {
     712            2740 :         regs = seg_->maybeRegs();
     713            2740 :         calls = seg_->maybeCalls();
     714                 :     } else {
     715          466575 :         regs = NULL;
     716          466575 :         calls = NULL;
     717                 :     }
     718                 : 
     719          469315 :     seg_ = new(firstUnused) StackSegment(seg_, space().seg_, regs, calls);
     720          469315 :     space().seg_ = seg_;
     721          469315 :     *pushedSeg = true;
     722          469315 :     return seg_->slotsBegin();
     723                 : }
     724                 : 
     725                 : void
     726          469315 : ContextStack::popSegment()
     727                 : {
     728          469315 :     space().seg_ = seg_->prevInMemory();
     729          469315 :     seg_ = seg_->prevInContext();
     730                 : 
     731          469315 :     if (!seg_)
     732          243593 :         cx_->maybeMigrateVersionOverride();
     733          469315 : }
     734                 : 
     735                 : bool
     736         6101043 : ContextStack::pushInvokeArgs(JSContext *cx, unsigned argc, InvokeArgsGuard *iag)
     737                 : {
     738         6101043 :     JS_ASSERT(argc <= StackSpace::ARGS_LENGTH_MAX);
     739                 : 
     740         6101043 :     unsigned nvars = 2 + argc;
     741         6101043 :     Value *firstUnused = ensureOnTop(cx, REPORT_ERROR, nvars, CAN_EXTEND, &iag->pushedSeg_);
     742         6101043 :     if (!firstUnused)
     743               0 :         return false;
     744                 : 
     745         6101043 :     MakeRangeGCSafe(firstUnused, nvars);
     746                 : 
     747         6101043 :     ImplicitCast<CallArgs>(*iag) = CallArgsFromVp(argc, firstUnused);
     748                 : 
     749         6101043 :     seg_->pushCall(*iag);
     750         6101043 :     JS_ASSERT(space().firstUnused() == iag->end());
     751         6101043 :     iag->setPushed(*this);
     752         6101043 :     return true;
     753                 : }
     754                 : 
     755                 : void
     756         6101043 : ContextStack::popInvokeArgs(const InvokeArgsGuard &iag)
     757                 : {
     758         6101043 :     JS_ASSERT(iag.pushed());
     759         6101043 :     JS_ASSERT(onTop());
     760         6101043 :     JS_ASSERT(space().firstUnused() == seg_->calls().end());
     761                 : 
     762         6101043 :     seg_->popCall();
     763         6101043 :     if (iag.pushedSeg_)
     764           48314 :         popSegment();
     765         6101043 : }
     766                 : 
     767                 : bool
     768         5218263 : ContextStack::pushInvokeFrame(JSContext *cx, const CallArgs &args,
     769                 :                               InitialFrameFlags initial, InvokeFrameGuard *ifg)
     770                 : {
     771         5218263 :     JS_ASSERT(onTop());
     772         5218263 :     JS_ASSERT(space().firstUnused() == args.end());
     773                 : 
     774         5218263 :     JSObject &callee = args.callee();
     775         5218263 :     JSFunction *fun = callee.toFunction();
     776         5218263 :     JSScript *script = fun->script();
     777                 : 
     778         5218263 :     StackFrame::Flags flags = ToFrameFlags(initial);
     779         5218263 :     StackFrame *fp = getCallFrame(cx, REPORT_ERROR, args, fun, script, &flags);
     780         5218263 :     if (!fp)
     781              28 :         return false;
     782                 : 
     783         5218235 :     fp->initCallFrame(cx, *fun, script, args.length(), flags);
     784         5218235 :     ifg->regs_.prepareToRun(*fp, script);
     785                 : 
     786         5218235 :     ifg->prevRegs_ = seg_->pushRegs(ifg->regs_);
     787         5218235 :     JS_ASSERT(space().firstUnused() == ifg->regs_.sp);
     788         5218235 :     ifg->setPushed(*this);
     789         5218235 :     return true;
     790                 : }
     791                 : 
     792                 : bool
     793          179593 : ContextStack::pushExecuteFrame(JSContext *cx, JSScript *script, const Value &thisv,
     794                 :                                JSObject &scopeChain, ExecuteType type,
     795                 :                                StackFrame *evalInFrame, ExecuteFrameGuard *efg)
     796                 : {
     797                 :     /*
     798                 :      * Even though global code and indirect eval do not execute in the context
     799                 :      * of the current frame, prev-link these to the current frame so that the
     800                 :      * callstack looks right to the debugger (via CAN_EXTEND). This is safe
     801                 :      * since the scope chain is what determines name lookup and access, not
     802                 :      * prev-links.
     803                 :      *
     804                 :      * Eval-in-frame is the exception since it prev-links to an arbitrary frame
     805                 :      * (possibly in the middle of some previous segment). Thus pass CANT_EXTEND
     806                 :      * (to start a new segment) and link the frame and call chain manually
     807                 :      * below.
     808                 :      */
     809          179593 :     CallArgsList *evalInFrameCalls = NULL;  /* quell overwarning */
     810                 :     StackFrame *prev;
     811                 :     MaybeExtend extend;
     812          179593 :     if (evalInFrame) {
     813                 :         /* Though the prev-frame is given, need to search for prev-call. */
     814            2795 :         StackIter iter(cx, StackIter::GO_THROUGH_SAVED);
     815           11661 :         while (!iter.isScript() || iter.fp() != evalInFrame)
     816            6071 :             ++iter;
     817            2795 :         evalInFrameCalls = iter.calls_;
     818            2795 :         prev = evalInFrame;
     819            2795 :         extend = CANT_EXTEND;
     820                 :     } else {
     821          176798 :         prev = maybefp();
     822          176798 :         extend = CAN_EXTEND;
     823                 :     }
     824                 : 
     825          179593 :     unsigned nvars = 2 /* callee, this */ + VALUES_PER_STACK_FRAME + script->nslots;
     826          179593 :     Value *firstUnused = ensureOnTop(cx, REPORT_ERROR, nvars, extend, &efg->pushedSeg_);
     827          179593 :     if (!firstUnused)
     828               0 :         return NULL;
     829                 : 
     830          179593 :     StackFrame *fp = reinterpret_cast<StackFrame *>(firstUnused + 2);
     831          179593 :     fp->initExecuteFrame(script, prev, seg_->maybeRegs(), thisv, scopeChain, type);
     832          179593 :     SetValueRangeToUndefined(fp->slots(), script->nfixed);
     833          179593 :     efg->regs_.prepareToRun(*fp, script);
     834                 : 
     835                 :     /* pushRegs() below links the prev-frame; manually link the prev-call. */
     836          179593 :     if (evalInFrame && evalInFrameCalls)
     837            2156 :         seg_->pointAtCall(*evalInFrameCalls);
     838                 : 
     839          179593 :     efg->prevRegs_ = seg_->pushRegs(efg->regs_);
     840          179593 :     JS_ASSERT(space().firstUnused() == efg->regs_.sp);
     841          179593 :     efg->setPushed(*this);
     842          179593 :     return true;
     843                 : }
     844                 : 
     845                 : bool
     846          259746 : ContextStack::pushDummyFrame(JSContext *cx, JSCompartment *dest, JSObject &scopeChain, DummyFrameGuard *dfg)
     847                 : {
     848          259746 :     JS_ASSERT(dest == scopeChain.compartment());
     849                 : 
     850          259746 :     unsigned nvars = VALUES_PER_STACK_FRAME;
     851          259746 :     Value *firstUnused = ensureOnTop(cx, REPORT_ERROR, nvars, CAN_EXTEND, &dfg->pushedSeg_, dest);
     852          259746 :     if (!firstUnused)
     853               0 :         return false;
     854                 : 
     855          259746 :     StackFrame *fp = reinterpret_cast<StackFrame *>(firstUnused);
     856          259746 :     fp->initDummyFrame(cx, scopeChain);
     857          259746 :     dfg->regs_.initDummyFrame(*fp);
     858                 : 
     859          259746 :     cx->setCompartment(dest);
     860          259746 :     dfg->prevRegs_ = seg_->pushRegs(dfg->regs_);
     861          259746 :     JS_ASSERT(space().firstUnused() == dfg->regs_.sp);
     862          259746 :     dfg->setPushed(*this);
     863          259746 :     return true;
     864                 : }
     865                 : 
     866                 : void
     867         5688408 : ContextStack::popFrame(const FrameGuard &fg)
     868                 : {
     869         5688408 :     JS_ASSERT(fg.pushed());
     870         5688408 :     JS_ASSERT(onTop());
     871         5688408 :     JS_ASSERT(space().firstUnused() == fg.regs_.sp);
     872         5688408 :     JS_ASSERT(&fg.regs_ == &seg_->regs());
     873                 : 
     874         5688408 :     if (fg.regs_.fp()->isNonEvalFunctionFrame())
     875         5249069 :         fg.regs_.fp()->functionEpilogue();
     876                 : 
     877         5688408 :     seg_->popRegs(fg.prevRegs_);
     878         5688408 :     if (fg.pushedSeg_)
     879          200534 :         popSegment();
     880                 : 
     881                 :     /*
     882                 :      * NB: this code can call out and observe the stack (e.g., through GC), so
     883                 :      * it should only be called from a consistent stack state.
     884                 :      */
     885         5688408 :     if (!hasfp())
     886          306820 :         cx_->resetCompartment();
     887         5688408 : }
     888                 : 
     889                 : bool
     890           30834 : ContextStack::pushGeneratorFrame(JSContext *cx, JSGenerator *gen, GeneratorFrameGuard *gfg)
     891                 : {
     892           30834 :     StackFrame *genfp = gen->floatingFrame();
     893           30834 :     HeapValue *genvp = gen->floatingStack;
     894           30834 :     unsigned vplen = (HeapValue *)genfp - genvp;
     895                 : 
     896           30834 :     unsigned nvars = vplen + VALUES_PER_STACK_FRAME + genfp->numSlots();
     897           30834 :     Value *firstUnused = ensureOnTop(cx, REPORT_ERROR, nvars, CAN_EXTEND, &gfg->pushedSeg_);
     898           30834 :     if (!firstUnused)
     899               0 :         return false;
     900                 : 
     901           30834 :     StackFrame *stackfp = reinterpret_cast<StackFrame *>(firstUnused + vplen);
     902           30834 :     Value *stackvp = (Value *)stackfp - vplen;
     903                 : 
     904                 :     /* Save this for popGeneratorFrame. */
     905           30834 :     gfg->gen_ = gen;
     906           30834 :     gfg->stackvp_ = stackvp;
     907                 : 
     908                 :     /*
     909                 :      * Trigger incremental barrier on the floating frame's generator object.
     910                 :      * This is normally traced through only by associated arguments/call
     911                 :      * objects, but only when the generator is not actually on the stack.
     912                 :      * We don't need to worry about generational barriers as the generator
     913                 :      * object has a trace hook and cannot be nursery allocated.
     914                 :      */
     915           30834 :     JSObject *genobj = js_FloatingFrameToGenerator(genfp)->obj;
     916           30834 :     JS_ASSERT(genobj->getClass()->trace);
     917           30834 :     JSObject::writeBarrierPre(genobj);
     918                 : 
     919                 :     /* Copy from the generator's floating frame to the stack. */
     920                 :     stackfp->stealFrameAndSlots<Value, HeapValue, StackFrame::NoPostBarrier>(
     921           30834 :                                 stackfp, stackvp, genfp, genvp, gen->regs.sp);
     922           30834 :     stackfp->resetGeneratorPrev(cx);
     923           30834 :     stackfp->unsetFloatingGenerator();
     924           30834 :     gfg->regs_.rebaseFromTo(gen->regs, *stackfp);
     925                 : 
     926           30834 :     gfg->prevRegs_ = seg_->pushRegs(gfg->regs_);
     927           30834 :     JS_ASSERT(space().firstUnused() == gfg->regs_.sp);
     928           30834 :     gfg->setPushed(*this);
     929           30834 :     return true;
     930                 : }
     931                 : 
     932                 : void
     933           30834 : ContextStack::popGeneratorFrame(const GeneratorFrameGuard &gfg)
     934                 : {
     935           30834 :     JSGenerator *gen = gfg.gen_;
     936           30834 :     StackFrame *genfp = gen->floatingFrame();
     937           30834 :     HeapValue *genvp = gen->floatingStack;
     938                 : 
     939           30834 :     const FrameRegs &stackRegs = gfg.regs_;
     940           30834 :     StackFrame *stackfp = stackRegs.fp();
     941           30834 :     Value *stackvp = gfg.stackvp_;
     942                 : 
     943                 :     /* Copy from the stack to the generator's floating frame. */
     944           30834 :     gen->regs.rebaseFromTo(stackRegs, *genfp);
     945                 :     genfp->stealFrameAndSlots<HeapValue, Value, StackFrame::DoPostBarrier>(
     946           30834 :                               genfp, genvp, stackfp, stackvp, stackRegs.sp);
     947           30834 :     genfp->setFloatingGenerator();
     948                 : 
     949                 :     /* ~FrameGuard/popFrame will finish the popping. */
     950           30834 :     JS_ASSERT(ImplicitCast<const FrameGuard>(gfg).pushed());
     951           30834 : }
     952                 : 
     953                 : bool
     954          220467 : ContextStack::saveFrameChain()
     955                 : {
     956          220467 :     JSCompartment *dest = NULL;
     957                 : 
     958                 :     bool pushedSeg;
     959          220467 :     if (!ensureOnTop(cx_, REPORT_ERROR, 0, CANT_EXTEND, &pushedSeg, dest))
     960               0 :         return false;
     961                 : 
     962          220467 :     JS_ASSERT(pushedSeg);
     963          220467 :     JS_ASSERT(!hasfp());
     964          220467 :     JS_ASSERT(onTop() && seg_->isEmpty());
     965                 : 
     966          220467 :     cx_->resetCompartment();
     967          220467 :     return true;
     968                 : }
     969                 : 
     970                 : void
     971          220467 : ContextStack::restoreFrameChain()
     972                 : {
     973          220467 :     JS_ASSERT(onTop() && seg_->isEmpty());
     974                 : 
     975          220467 :     popSegment();
     976          220467 :     cx_->resetCompartment();
     977          220467 : }
     978                 : 
     979                 : /*****************************************************************************/
     980                 : 
     981                 : void
     982           12385 : StackIter::poisonRegs()
     983                 : {
     984           12385 :     sp_ = (Value *)0xbad;
     985           12385 :     pc_ = (jsbytecode *)0xbad;
     986           12385 :     script_ = (JSScript *)0xbad;
     987           12385 : }
     988                 : 
     989                 : void
     990         4403293 : StackIter::popFrame()
     991                 : {
     992         4403293 :     StackFrame *oldfp = fp_;
     993         4403293 :     JS_ASSERT(seg_->contains(oldfp));
     994         4403293 :     fp_ = fp_->prev();
     995         4403293 :     if (seg_->contains(fp_)) {
     996                 :         JSInlinedSite *inline_;
     997         4392593 :         pc_ = oldfp->prevpc(&inline_);
     998         4392593 :         JS_ASSERT(!inline_);
     999                 : 
    1000                 :         /*
    1001                 :          * If there is a CallArgsList element between oldfp and fp_, then sp_
    1002                 :          * is ignored, so we only consider the case where there is no
    1003                 :          * intervening CallArgsList. The stack representation is not optimized
    1004                 :          * for this operation so we need to do a full case analysis of how
    1005                 :          * frames are pushed by considering each ContextStack::push*Frame.
    1006                 :          */
    1007         4392593 :         if (oldfp->isGeneratorFrame()) {
    1008                 :             /* Generator's args do not overlap with the caller's expr stack. */
    1009            2812 :             sp_ = (Value *)oldfp->actualArgs() - 2;
    1010         4389781 :         } else if (oldfp->isNonEvalFunctionFrame()) {
    1011                 :             /*
    1012                 :              * When Invoke is called from a native, there will be an enclosing
    1013                 :              * pushInvokeArgs which pushes a CallArgsList element so we can
    1014                 :              * ignore that case. The other two cases of function call frames are
    1015                 :              * Invoke called directly from script and pushInlineFrmae. In both
    1016                 :              * cases, the actual arguments of the callee should be included in
    1017                 :              * the caller's expr stack.
    1018                 :              */
    1019         4363376 :             sp_ = oldfp->actualArgsEnd();
    1020           26405 :         } else if (oldfp->isFramePushedByExecute()) {
    1021                 :             /* pushExecuteFrame pushes exactly (callee, this) before frame. */
    1022            1068 :             sp_ = (Value *)oldfp - 2;
    1023                 :         } else {
    1024                 :             /* pushDummyFrame pushes exactly 0 slots before frame. */
    1025           25337 :             JS_ASSERT(oldfp->isDummyFrame());
    1026           25337 :             sp_ = (Value *)oldfp;
    1027                 :         }
    1028                 : 
    1029         4392593 :         script_ = fp_->maybeScript();
    1030                 :     } else {
    1031           10700 :         poisonRegs();
    1032                 :     }
    1033         4403293 : }
    1034                 : 
    1035                 : void
    1036          103391 : StackIter::popCall()
    1037                 : {
    1038          103391 :     CallArgsList *oldCall = calls_;
    1039          103391 :     JS_ASSERT(seg_->contains(oldCall));
    1040          103391 :     calls_ = calls_->prev();
    1041          103391 :     if (seg_->contains(fp_)) {
    1042                 :         /* pc_ keeps its same value. */
    1043          102205 :         sp_ = oldCall->base();
    1044                 :     } else {
    1045            1186 :         poisonRegs();
    1046                 :     }
    1047          103391 : }
    1048                 : 
    1049                 : void
    1050           63781 : StackIter::settleOnNewSegment()
    1051                 : {
    1052           63781 :     if (FrameRegs *regs = seg_->maybeRegs()) {
    1053           63282 :         sp_ = regs->sp;
    1054           63282 :         pc_ = regs->pc;
    1055           63282 :         if (fp_)
    1056           63282 :             script_ = fp_->maybeScript();
    1057                 :     } else {
    1058             499 :         poisonRegs();
    1059                 :     }
    1060           63781 : }
    1061                 : 
    1062                 : void
    1063           63538 : StackIter::startOnSegment(StackSegment *seg)
    1064                 : {
    1065           63538 :     seg_ = seg;
    1066           63538 :     fp_ = seg_->maybefp();
    1067           63538 :     calls_ = seg_->maybeCalls();
    1068           63538 :     settleOnNewSegment();
    1069           63538 : }
    1070                 : 
    1071                 : static void JS_NEVER_INLINE
    1072         4102724 : CrashIfInvalidSlot(StackFrame *fp, Value *vp)
    1073                 : {
    1074         4102724 :     if (vp < fp->slots() || vp >= fp->slots() + fp->script()->nslots) {
    1075               0 :         JS_ASSERT(false && "About to dereference invalid slot");
    1076               0 :         *(int *)0xbad = 0;  // show up nicely in crash-stats
    1077               0 :         MOZ_Assert("About to dereference invalid slot", __FILE__, __LINE__);
    1078                 :     }
    1079         4102724 : }
    1080                 : 
    1081                 : void
    1082         4569979 : StackIter::settleOnNewState()
    1083                 : {
    1084                 :     /*
    1085                 :      * There are elements of the calls_ and fp_ chains that we want to skip
    1086                 :      * over so iterate until we settle on one or until there are no more.
    1087                 :      */
    1088           84785 :     while (true) {
    1089         4569979 :         if (!fp_ && !calls_) {
    1090           10713 :             if (savedOption_ == GO_THROUGH_SAVED && seg_->prevInContext()) {
    1091             149 :                 startOnSegment(seg_->prevInContext());
    1092             149 :                 continue;
    1093                 :             }
    1094           10564 :             state_ = DONE;
    1095           10564 :             return;
    1096                 :         }
    1097                 : 
    1098                 :         /* Check if popFrame/popCall changed segment. */
    1099         4559266 :         bool containsFrame = seg_->contains(fp_);
    1100         4559266 :         bool containsCall = seg_->contains(calls_);
    1101         9118775 :         while (!containsFrame && !containsCall) {
    1102             486 :             seg_ = seg_->prevInContext();
    1103             486 :             containsFrame = seg_->contains(fp_);
    1104             486 :             containsCall = seg_->contains(calls_);
    1105                 : 
    1106                 :             /* Eval-in-frame allows jumping into the middle of a segment. */
    1107             486 :             if (containsFrame && seg_->fp() != fp_) {
    1108                 :                 /* Avoid duplicating logic; seg_ contains fp_, so no iloop. */
    1109             243 :                 StackIter tmp = *this;
    1110             243 :                 tmp.startOnSegment(seg_);
    1111             936 :                 while (!tmp.isScript() || tmp.fp() != fp_)
    1112             450 :                     ++tmp;
    1113             243 :                 JS_ASSERT(tmp.state_ == SCRIPTED && tmp.seg_ == seg_ && tmp.fp_ == fp_);
    1114             243 :                 *this = tmp;
    1115             243 :                 return;
    1116                 :             }
    1117                 :             /* There is no eval-in-frame equivalent for native calls. */
    1118             243 :             JS_ASSERT_IF(containsCall, &seg_->calls() == calls_);
    1119             243 :             settleOnNewSegment();
    1120                 :         }
    1121                 : 
    1122                 :         /*
    1123                 :          * In case of both a scripted frame and call record, use linear memory
    1124                 :          * ordering to decide which was the most recent.
    1125                 :          */
    1126         4559023 :         if (containsFrame && (!containsCall || (Value *)fp_ >= calls_->array())) {
    1127                 :             /* Nobody wants to see dummy frames. */
    1128         4455632 :             if (fp_->isDummyFrame()) {
    1129           25289 :                 popFrame();
    1130           25289 :                 continue;
    1131                 :             }
    1132                 : 
    1133                 :             /*
    1134                 :              * As an optimization, there is no CallArgsList element pushed for
    1135                 :              * natives called directly by a script (compiled or interpreted).
    1136                 :              * We catch these by inspecting the bytecode and stack. This check
    1137                 :              * relies on the property that, at a call opcode,
    1138                 :              *
    1139                 :              *   regs.sp == vp + 2 + argc
    1140                 :              *
    1141                 :              * The Function.prototype.call optimization leaves no record when
    1142                 :              * 'this' is a native function. Thus, if the following expression
    1143                 :              * runs and breaks in the debugger, the call to 'replace' will not
    1144                 :              * appear on the callstack.
    1145                 :              *
    1146                 :              *   (String.prototype.replace).call('a',/a/,function(){debugger});
    1147                 :              *
    1148                 :              * Function.prototype.call will however appear, hence the debugger
    1149                 :              * can, by inspecting 'args.thisv', give some useful information.
    1150                 :              *
    1151                 :              * For Function.prototype.apply, the situation is even worse: since
    1152                 :              * a dynamic number of arguments have been pushed onto the stack
    1153                 :              * (see SplatApplyArgs), there is no efficient way to know how to
    1154                 :              * find the callee. Thus, calls to apply are lost completely.
    1155                 :              */
    1156         4430343 :             JSOp op = JSOp(*pc_);
    1157         4430343 :             if (op == JSOP_CALL || op == JSOP_FUNCALL) {
    1158         4102724 :                 unsigned argc = GET_ARGC(pc_);
    1159         8205448 :                 DebugOnly<unsigned> spoff = sp_ - fp_->base();
    1160         8205448 :                 JS_ASSERT_IF(cx_->stackIterAssertionEnabled,
    1161        12308172 :                              spoff == js_ReconstructStackDepth(cx_, fp_->script(), pc_));
    1162         4102724 :                 Value *vp = sp_ - (2 + argc);
    1163                 : 
    1164         4102724 :                 CrashIfInvalidSlot(fp_, vp);
    1165         4102724 :                 if (IsNativeFunction(*vp)) {
    1166           58818 :                     state_ = IMPLICIT_NATIVE;
    1167           58818 :                     args_ = CallArgsFromVp(argc, vp);
    1168                 :                     return;
    1169                 :                 }
    1170                 :             }
    1171                 : 
    1172         4371525 :             state_ = SCRIPTED;
    1173         8743050 :             DebugOnly<JSScript *> script = fp_->script();
    1174         8731454 :             JS_ASSERT_IF(op != JSOP_FUNAPPLY,
    1175        13102979 :                          sp_ >= fp_->base() && sp_ <= fp_->slots() + script->nslots);
    1176         4371525 :             JS_ASSERT(pc_ >= script->code && pc_ < script->code + script->length);
    1177                 :             return;
    1178                 :         }
    1179                 : 
    1180                 :         /*
    1181                 :          * A CallArgsList element is pushed for any call to Invoke, regardless
    1182                 :          * of whether the callee is a scripted function or even a callable
    1183                 :          * object. Thus, it is necessary to filter calleev for natives.
    1184                 :          *
    1185                 :          * Second, stuff can happen after the args are pushed but before/after
    1186                 :          * the actual call, so only consider "active" calls. (Since Invoke
    1187                 :          * necessarily clobbers the callee, "active" is also necessary to
    1188                 :          * ensure that the callee slot is valid.)
    1189                 :          */
    1190          103391 :         if (calls_->active() && IsNativeFunction(calls_->calleev())) {
    1191           44044 :             state_ = NATIVE;
    1192           44044 :             args_ = *calls_;
    1193           44044 :             return;
    1194                 :         }
    1195                 : 
    1196                 :         /* Pop the call and keep looking. */
    1197           59347 :         popCall();
    1198                 :     }
    1199                 : }
    1200                 : 
    1201           63451 : StackIter::StackIter(JSContext *cx, SavedOption savedOption)
    1202                 :   : cx_(cx),
    1203           63451 :     savedOption_(savedOption)
    1204                 : {
    1205                 : #ifdef JS_METHODJIT
    1206           63451 :     mjit::ExpandInlineFrames(cx->compartment);
    1207                 : #endif
    1208                 : 
    1209           63451 :     if (StackSegment *seg = cx->stack.seg_) {
    1210           63146 :         startOnSegment(seg);
    1211           63146 :         settleOnNewState();
    1212                 :     } else {
    1213             305 :         state_ = DONE;
    1214                 :     }
    1215           63451 : }
    1216                 : 
    1217                 : StackIter &
    1218         4480866 : StackIter::operator++()
    1219                 : {
    1220         4480866 :     JS_ASSERT(!done());
    1221         4480866 :     switch (state_) {
    1222                 :       case DONE:
    1223               0 :         JS_NOT_REACHED("");
    1224                 :       case SCRIPTED:
    1225         4378004 :         popFrame();
    1226         4378004 :         settleOnNewState();
    1227         4378004 :         break;
    1228                 :       case NATIVE:
    1229           44044 :         popCall();
    1230           44044 :         settleOnNewState();
    1231           44044 :         break;
    1232                 :       case IMPLICIT_NATIVE:
    1233           58818 :         state_ = SCRIPTED;
    1234           58818 :         break;
    1235                 :     }
    1236         4480866 :     return *this;
    1237                 : }
    1238                 : 
    1239                 : bool
    1240               0 : StackIter::operator==(const StackIter &rhs) const
    1241                 : {
    1242               0 :     return done() == rhs.done() &&
    1243               0 :            (done() ||
    1244               0 :             (isScript() == rhs.isScript() &&
    1245               0 :              ((isScript() && fp() == rhs.fp()) ||
    1246               0 :               (!isScript() && nativeArgs().base() == rhs.nativeArgs().base()))));
    1247                 : }
    1248                 : 
    1249                 : /*****************************************************************************/
    1250                 : 
    1251           46886 : AllFramesIter::AllFramesIter(StackSpace &space)
    1252                 :   : seg_(space.seg_),
    1253           46886 :     fp_(seg_ ? seg_->maybefp() : NULL)
    1254                 : {
    1255           46886 :     settle();
    1256           46886 : }
    1257                 : 
    1258                 : AllFramesIter&
    1259           93358 : AllFramesIter::operator++()
    1260                 : {
    1261           93358 :     JS_ASSERT(!done());
    1262           93358 :     fp_ = fp_->prev();
    1263           93358 :     settle();
    1264           93358 :     return *this;
    1265                 : }
    1266                 : 
    1267                 : void
    1268          140244 : AllFramesIter::settle()
    1269                 : {
    1270          285481 :     while (seg_ && (!fp_ || !seg_->contains(fp_))) {
    1271            4993 :         seg_ = seg_->prevInMemory();
    1272            4993 :         fp_ = seg_ ? seg_->maybefp() : NULL;
    1273                 :     }
    1274                 : 
    1275          140244 :     JS_ASSERT(!!seg_ == !!fp_);
    1276          140244 :     JS_ASSERT_IF(fp_, seg_->contains(fp_));
    1277          140244 : }

Generated by: LCOV version 1.7