LCOV - code coverage report
Current view: directory - js/src/vm - Stack.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 568 533 93.8 %
Date: 2012-04-07 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          152647 : 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          152647 :     flags_ = type | HAS_SCOPECHAIN | HAS_BLOCKCHAIN | HAS_PREVPC;
      83          152647 :     if (!(flags_ & GLOBAL))
      84           81123 :         flags_ |= (prev->flags_ & (FUNCTION | GLOBAL));
      85                 : 
      86          152647 :     Value *dstvp = (Value *)this - 2;
      87          152647 :     dstvp[1] = thisv;
      88                 : 
      89          152647 :     if (isFunctionFrame()) {
      90           78903 :         dstvp[0] = prev->calleev();
      91           78903 :         exec = prev->exec;
      92           78903 :         u.evalScript = script;
      93                 :     } else {
      94           73744 :         JS_ASSERT(isGlobalFrame());
      95           73744 :         dstvp[0] = NullValue();
      96           73744 :         exec.script = script;
      97                 : #ifdef DEBUG
      98           73744 :         u.evalScript = (JSScript *)0xbad;
      99                 : #endif
     100                 :     }
     101                 : 
     102          152647 :     scopeChain_ = &scopeChain;
     103          152647 :     prev_ = prev;
     104          152647 :     prevpc_ = regs ? regs->pc : (jsbytecode *)0xbad;
     105          152647 :     prevInline_ = regs ? regs->inlined() : NULL;
     106          152647 :     blockChain_ = NULL;
     107                 : 
     108                 : #ifdef DEBUG
     109          152647 :     ncode_ = (void *)0xbad;
     110          152647 :     Debug_SetValueRangeToCrashOnTouch(&rval_, 1);
     111          152647 :     hookData_ = (void *)0xbad;
     112          152647 :     annotation_ = (void *)0xbad;
     113                 : #endif
     114                 : 
     115          152647 :     if (prev && prev->annotation())
     116               0 :         setAnnotation(prev->annotation());
     117          152647 : }
     118                 : 
     119                 : void
     120          210004 : StackFrame::initDummyFrame(JSContext *cx, JSObject &chain)
     121                 : {
     122          210004 :     PodZero(this);
     123          210004 :     flags_ = DUMMY | HAS_PREVPC | HAS_SCOPECHAIN;
     124          210004 :     initPrev(cx);
     125          210004 :     JS_ASSERT(chain.isGlobal());
     126          210004 :     setScopeChainNoCallObj(chain);
     127          210004 : }
     128                 : 
     129                 : template <class T, class U, StackFrame::TriggerPostBarriers doPostBarrier>
     130                 : void
     131           46428 : StackFrame::stealFrameAndSlots(StackFrame *fp, T *vp, StackFrame *otherfp, U *othervp,
     132                 :                                Value *othersp)
     133                 : {
     134           46428 :     JS_ASSERT((U *)vp == (U *)this - ((U *)otherfp - othervp));
     135           46428 :     JS_ASSERT((Value *)othervp == otherfp->actualArgs() - 2);
     136           46428 :     JS_ASSERT(othersp >= otherfp->slots());
     137           46428 :     JS_ASSERT(othersp <= otherfp->base() + otherfp->numSlots());
     138           46428 :     JS_ASSERT((T *)fp - vp == (U *)otherfp - othervp);
     139                 : 
     140                 :     /* Copy args, StackFrame, and slots. */
     141           46428 :     U *srcend = (U *)otherfp->formalArgsEnd();
     142           46428 :     T *dst = vp;
     143          172836 :     for (U *src = othervp; src < srcend; src++, dst++)
     144          126408 :         *dst = *src;
     145                 : 
     146           46428 :     *fp = *otherfp;
     147                 :     if (doPostBarrier)
     148           27325 :         fp->writeBarrierPost();
     149                 : 
     150           46428 :     srcend = (U *)othersp;
     151           46428 :     dst = (T *)fp->slots();
     152          108404 :     for (U *src = (U *)otherfp->slots(); src < srcend; src++, dst++)
     153           61976 :         *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           46428 :     if (hasCallObj()) {
     162            1161 :         CallObject &obj = callObj();
     163            1161 :         obj.setStackFrame(this);
     164            1161 :         otherfp->flags_ &= ~HAS_CALL_OBJ;
     165            1161 :         if (js_IsNamedLambda(fun())) {
     166             396 :             DeclEnvObject &env = obj.enclosingScope().asDeclEnv();
     167             396 :             env.setStackFrame(this);
     168                 :         }
     169                 :     }
     170           46428 :     if (hasArgsObj()) {
     171             495 :         ArgumentsObject &argsobj = argsObj();
     172             495 :         if (argsobj.isNormalArguments())
     173             306 :             argsobj.setStackFrame(this);
     174                 :         else
     175             189 :             JS_ASSERT(!argsobj.maybeStackFrame());
     176             495 :         otherfp->flags_ &= ~HAS_ARGS_OBJ;
     177                 :     }
     178           46428 : }
     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           27325 : StackFrame::writeBarrierPost()
     190                 : {
     191                 :     /* This needs to follow the same rules as in js_TraceStackFrame. */
     192           27325 :     if (scopeChain_)
     193           27325 :         JSObject::writeBarrierPost(scopeChain_, (void *)&scopeChain_);
     194           27325 :     if (isDummyFrame())
     195               0 :         return;
     196           27325 :     if (hasArgsObj())
     197             333 :         JSObject::writeBarrierPost(argsObj_, (void *)&argsObj_);
     198           27325 :     if (isScriptFrame()) {
     199           27325 :         if (isFunctionFrame()) {
     200           27325 :             JSFunction::writeBarrierPost((JSObject *)exec.fun, (void *)&exec.fun);
     201           27325 :             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           27325 :     if (hasReturnValue())
     208           19066 :         HeapValue::writeBarrierPost(rval_, &rval_);
     209                 : }
     210                 : 
     211                 : #ifdef DEBUG
     212                 : JSObject *const StackFrame::sInvalidScopeChain = (JSObject *)0xbeef;
     213                 : #endif
     214                 : 
     215                 : jsbytecode *
     216         1168250 : StackFrame::prevpcSlow(JSInlinedSite **pinlined)
     217                 : {
     218         1168250 :     JS_ASSERT(!(flags_ & HAS_PREVPC));
     219                 : #if defined(JS_METHODJIT) && defined(JS_MONOIC)
     220         1168250 :     StackFrame *p = prev();
     221         1168250 :     mjit::JITScript *jit = p->script()->getJIT(p->isConstructing());
     222         1168250 :     prevpc_ = jit->nativeToPC(ncode_, &prevInline_);
     223         1168250 :     flags_ |= HAS_PREVPC;
     224         1168250 :     if (pinlined)
     225         1168250 :         *pinlined = prevInline_;
     226         1168250 :     return prevpc_;
     227                 : #else
     228                 :     JS_NOT_REACHED("Unknown PC for frame");
     229                 :     return NULL;
     230                 : #endif
     231                 : }
     232                 : 
     233                 : jsbytecode *
     234         3770737 : StackFrame::pcQuadratic(const ContextStack &stack, StackFrame *next, JSInlinedSite **pinlined)
     235                 : {
     236         3770737 :     JS_ASSERT_IF(next, next->prev() == this);
     237                 : 
     238         3770737 :     StackSegment &seg = stack.space().containingSegment(this);
     239         3770737 :     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         3770737 :     if (regs.fp() == this) {
     246         3768174 :         if (pinlined)
     247               0 :             *pinlined = regs.inlined();
     248         3768174 :         return regs.pc;
     249                 :     }
     250                 : 
     251            2563 :     if (!next)
     252            1121 :         next = seg.computeNextFrame(this);
     253            2563 :     return next->prevpc(pinlined);
     254                 : }
     255                 : 
     256                 : void
     257          610556 : 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          610556 :     if (flags_ & HAS_SCOPECHAIN)
     265          610553 :         gc::MarkObjectUnbarriered(trc, &scopeChain_, "scope chain");
     266          610556 :     if (isDummyFrame())
     267            4492 :         return;
     268          606064 :     if (hasArgsObj())
     269            1182 :         gc::MarkObjectUnbarriered(trc, &argsObj_, "arguments");
     270          606064 :     if (isFunctionFrame()) {
     271          580220 :         gc::MarkObjectUnbarriered(trc, &exec.fun, "fun");
     272          580220 :         if (isEvalFrame())
     273             168 :             gc::MarkScriptUnbarriered(trc, &u.evalScript, "eval script");
     274                 :     } else {
     275           25844 :         gc::MarkScriptUnbarriered(trc, &exec.script, "script");
     276                 :     }
     277          606064 :     if (IS_GC_MARKING_TRACER(trc))
     278          597475 :         script()->compartment()->active = true;
     279          606064 :     gc::MarkValueUnbarriered(trc, &returnValue(), "rval");
     280                 : }
     281                 : 
     282                 : /*****************************************************************************/
     283                 : 
     284                 : bool
     285        72538416 : StackSegment::contains(const StackFrame *fp) const
     286                 : {
     287                 :     /* NB: this depends on the continuity of segments in memory. */
     288        72538416 :     return (Value *)fp >= slotsBegin() && (Value *)fp <= (Value *)maybefp();
     289                 : }
     290                 : 
     291                 : bool
     292        48736260 : StackSegment::contains(const FrameRegs *regs) const
     293                 : {
     294        48736260 :     return regs && contains(regs->fp());
     295                 : }
     296                 : 
     297                 : bool
     298        67450991 : StackSegment::contains(const CallArgsList *call) const
     299                 : {
     300        67450991 :     if (!call || !calls_)
     301        52583536 :         return false;
     302                 : 
     303                 :     /* NB: this depends on the continuity of segments in memory. */
     304        14867455 :     Value *vp = call->array();
     305        14867455 :     return vp > slotsBegin() && vp <= calls_->array();
     306                 : }
     307                 : 
     308                 : StackFrame *
     309            1126 : StackSegment::computeNextFrame(const StackFrame *f) const
     310                 : {
     311            1126 :     JS_ASSERT(contains(f) && f != fp());
     312                 : 
     313            1126 :     StackFrame *next = fp();
     314                 :     StackFrame *prev;
     315            3535 :     while ((prev = next->prev()) != f)
     316            1283 :         next = prev;
     317            1126 :     return next;
     318                 : }
     319                 : 
     320                 : Value *
     321        60901494 : StackSegment::end() const
     322                 : {
     323                 :     /* NB: this depends on the continuity of segments in memory. */
     324        60901494 :     JS_ASSERT_IF(calls_ || regs_, contains(calls_) || contains(regs_));
     325                 :     Value *p = calls_
     326                 :                ? regs_
     327        13934217 :                  ? Max(regs_->sp, calls_->end())
     328            1471 :                  : calls_->end()
     329                 :                : regs_
     330                 :                  ? regs_->sp
     331        74837182 :                  : slotsBegin();
     332        60901494 :     JS_ASSERT(p >= slotsBegin());
     333        60901494 :     return p;
     334                 : }
     335                 : 
     336                 : FrameRegs *
     337         1757483 : StackSegment::pushRegs(FrameRegs &regs)
     338                 : {
     339         1757483 :     JS_ASSERT_IF(contains(regs_), regs.fp()->prev() == regs_->fp());
     340         1757483 :     FrameRegs *prev = regs_;
     341         1757483 :     regs_ = &regs;
     342         1757483 :     return prev;
     343                 : }
     344                 : 
     345                 : void
     346         1757483 : StackSegment::popRegs(FrameRegs *regs)
     347                 : {
     348         1757483 :     JS_ASSERT_IF(regs && contains(regs->fp()), regs->fp() == regs_->fp()->prev());
     349         1757483 :     regs_ = regs;
     350         1757483 : }
     351                 : 
     352                 : void
     353         2142483 : StackSegment::pushCall(CallArgsList &callList)
     354                 : {
     355         2142483 :     callList.prev_ = calls_;
     356         2142483 :     calls_ = &callList;
     357         2142483 : }
     358                 : 
     359                 : void
     360            2160 : StackSegment::pointAtCall(CallArgsList &callList)
     361                 : {
     362            2160 :     calls_ = &callList;
     363            2160 : }
     364                 : 
     365                 : void
     366         2142483 : StackSegment::popCall()
     367                 : {
     368         2142483 :     calls_ = calls_->prev_;
     369         2142483 : }
     370                 : 
     371                 : /*****************************************************************************/
     372                 : 
     373           18761 : StackSpace::StackSpace()
     374                 :   : seg_(NULL),
     375                 :     base_(NULL),
     376                 :     conservativeEnd_(NULL),
     377                 : #ifdef XP_WIN
     378                 :     commitEnd_(NULL),
     379                 : #endif
     380                 :     defaultEnd_(NULL),
     381           18761 :     trustedEnd_(NULL)
     382                 : {
     383           18761 :     assertInvariants();
     384           18761 : }
     385                 : 
     386                 : bool
     387           18761 : 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           18761 :     JS_ASSERT(CAPACITY_BYTES % getpagesize() == 0);
     410           18761 :     p = mmap(NULL, CAPACITY_BYTES, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
     411           18761 :     if (p == MAP_FAILED)
     412               0 :         return false;
     413           18761 :     base_ = reinterpret_cast<Value *>(p);
     414           18761 :     trustedEnd_ = base_ + CAPACITY_VALS;
     415           18761 :     conservativeEnd_ = defaultEnd_ = trustedEnd_ - BUFFER_VALS;
     416                 : #endif
     417           18761 :     assertInvariants();
     418           18761 :     return true;
     419                 : }
     420                 : 
     421           18761 : StackSpace::~StackSpace()
     422                 : {
     423           18761 :     assertInvariants();
     424           18761 :     JS_ASSERT(!seg_);
     425           18761 :     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           18761 :     munmap(base_, CAPACITY_BYTES);
     437                 : #endif
     438                 : #endif
     439           18761 : }
     440                 : 
     441                 : StackSegment &
     442         3770742 : StackSpace::containingSegment(const StackFrame *target) const
     443                 : {
     444         3770742 :     for (StackSegment *s = seg_; s; s = s->prevInMemory()) {
     445         3770742 :         if (s->contains(target))
     446         3770742 :             return *s;
     447                 :     }
     448               0 :     JS_NOT_REACHED("frame not in stack space");
     449                 :     return *(StackSegment *)NULL;
     450                 : }
     451                 : 
     452                 : void
     453          607741 : StackSpace::markFrameSlots(JSTracer *trc, StackFrame *fp, Value *slotsEnd, jsbytecode *pc)
     454                 : {
     455          607741 :     Value *slotsBegin = fp->slots();
     456                 : 
     457          607741 :     if (!fp->isScriptFrame()) {
     458            4492 :         JS_ASSERT(fp->isDummyFrame());
     459            4492 :         gc::MarkValueRootRange(trc, slotsBegin, slotsEnd, "vm_stack");
     460            4492 :         return;
     461                 :     }
     462                 : 
     463                 :     /* If it's a scripted frame, we should have a pc. */
     464          603249 :     JS_ASSERT(pc);
     465                 : 
     466          603249 :     JSScript *script = fp->script();
     467          603249 :     if (!script->hasAnalysis() || !script->analysis()->ranLifetimes()) {
     468          596855 :         gc::MarkValueRootRange(trc, slotsBegin, slotsEnd, "vm_stack");
     469          596855 :         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           12788 :     analyze::AutoEnterAnalysis aea(script->compartment());
     481            6394 :     analyze::ScriptAnalysis *analysis = script->analysis();
     482            6394 :     uint32_t offset = pc - script->code;
     483            6394 :     Value *fixedEnd = slotsBegin + script->nfixed;
     484           13911 :     for (Value *vp = slotsBegin; vp < fixedEnd; vp++) {
     485            7517 :         uint32_t slot = analyze::LocalSlot(script, vp - slotsBegin);
     486                 : 
     487                 :         /* Will this slot be synced by the JIT? */
     488            7517 :         if (!analysis->trackSlot(slot) || analysis->liveness(slot).live(offset))
     489            5094 :             gc::MarkValueRoot(trc, vp, "vm_stack");
     490                 :         else
     491            2423 :             *vp = UndefinedValue();
     492                 :     }
     493                 : 
     494            6394 :     gc::MarkValueRootRange(trc, fixedEnd, slotsEnd, "vm_stack");
     495                 : }
     496                 : 
     497                 : void
     498           41836 : StackSpace::mark(JSTracer *trc)
     499                 : {
     500                 :     /*
     501                 :      * JIT code can leave values in an incoherent (i.e., unsafe for precise
     502                 :      * marking) state, hence MarkStackRangeConservatively.
     503                 :      */
     504                 : 
     505                 :     /* NB: this depends on the continuity of segments in memory. */
     506           41836 :     Value *nextSegEnd = firstUnused();
     507           64687 :     for (StackSegment *seg = seg_; seg; seg = seg->prevInMemory()) {
     508                 :         /*
     509                 :          * A segment describes a linear region of memory that contains a stack
     510                 :          * of native and interpreted calls. For marking purposes, though, we
     511                 :          * only need to distinguish between frames and values and mark
     512                 :          * accordingly. Since native calls only push values on the stack, we
     513                 :          * can effectively lump them together and just iterate over interpreted
     514                 :          * calls. Thus, marking can view the stack as the regex:
     515                 :          *   (segment slots (frame slots)*)*
     516                 :          * which gets marked in reverse order.
     517                 :          */
     518           22851 :         Value *slotsEnd = nextSegEnd;
     519           22851 :         jsbytecode *pc = seg->maybepc();
     520          630592 :         for (StackFrame *fp = seg->maybefp(); (Value *)fp > (Value *)seg; fp = fp->prev()) {
     521                 :             /* Mark from fp->slots() to slotsEnd. */
     522          607741 :             markFrameSlots(trc, fp, slotsEnd, pc);
     523                 : 
     524          607741 :             fp->mark(trc);
     525          607741 :             slotsEnd = (Value *)fp;
     526                 : 
     527                 :             JSInlinedSite *site;
     528          607741 :             pc = fp->prevpc(&site);
     529          607741 :             JS_ASSERT_IF(fp->prev(), !site);
     530                 :         }
     531           22851 :         gc::MarkValueRootRange(trc, seg->slotsBegin(), slotsEnd, "vm_stack");
     532           22851 :         nextSegEnd = (Value *)seg;
     533                 :     }
     534           41836 : }
     535                 : 
     536                 : void
     537              63 : StackSpace::markActiveCompartments()
     538                 : {
     539             126 :     for (StackSegment *seg = seg_; seg; seg = seg->prevInMemory()) {
     540             126 :         for (StackFrame *fp = seg->maybefp(); (Value *)fp > (Value *)seg; fp = fp->prev())
     541              63 :             MarkCompartmentActive(fp);
     542                 :     }
     543              63 : }
     544                 : 
     545                 : JS_FRIEND_API(bool)
     546             231 : StackSpace::ensureSpaceSlow(JSContext *cx, MaybeReportError report, Value *from, ptrdiff_t nvals,
     547                 :                             JSCompartment *dest) const
     548                 : {
     549             231 :     assertInvariants();
     550                 : 
     551                 :     /* See CX_COMPARTMENT comment. */
     552             231 :     if (dest == (JSCompartment *)CX_COMPARTMENT)
     553             231 :         dest = cx->compartment;
     554                 : 
     555             231 :     bool trusted = !dest || dest->principals == cx->runtime->trustedPrincipals();
     556             231 :     Value *end = trusted ? trustedEnd_ : defaultEnd_;
     557                 : 
     558                 :     /*
     559                 :      * conservativeEnd_ must stay below defaultEnd_: if conservativeEnd_ were
     560                 :      * to be bumped past defaultEnd_, untrusted JS would be able to consume the
     561                 :      * buffer space at the end of the stack reserved for trusted JS.
     562                 :      */
     563                 : 
     564             231 :     if (end - from < nvals) {
     565             129 :         if (report)
     566             101 :             js_ReportOverRecursed(cx);
     567             129 :         return false;
     568                 :     }
     569                 : 
     570                 : #ifdef XP_WIN
     571                 :     if (commitEnd_ - from < nvals) {
     572                 :         Value *newCommit = commitEnd_;
     573                 :         Value *request = from + nvals;
     574                 : 
     575                 :         /* Use a dumb loop; will probably execute once. */
     576                 :         JS_ASSERT((trustedEnd_ - newCommit) % COMMIT_VALS == 0);
     577                 :         do {
     578                 :             newCommit += COMMIT_VALS;
     579                 :             JS_ASSERT((trustedEnd_ - newCommit) >= 0);
     580                 :         } while (newCommit < request);
     581                 : 
     582                 :         /* The cast is safe because CAPACITY_BYTES is small. */
     583                 :         int32_t size = static_cast<int32_t>(newCommit - commitEnd_) * sizeof(Value);
     584                 : 
     585                 :         if (!VirtualAlloc(commitEnd_, size, MEM_COMMIT, PAGE_READWRITE)) {
     586                 :             if (report)
     587                 :                 js_ReportOverRecursed(cx);
     588                 :             return false;
     589                 :         }
     590                 : 
     591                 :         commitEnd_ = newCommit;
     592                 :         conservativeEnd_ = Min(commitEnd_, defaultEnd_);
     593                 :         assertInvariants();
     594                 :     }
     595                 : #endif
     596                 : 
     597             102 :     return true;
     598                 : }
     599                 : 
     600                 : bool
     601               0 : StackSpace::tryBumpLimit(JSContext *cx, Value *from, unsigned nvals, Value **limit)
     602                 : {
     603               0 :     if (!ensureSpace(cx, REPORT_ERROR, from, nvals))
     604               0 :         return false;
     605               0 :     *limit = conservativeEnd_;
     606               0 :     return true;
     607                 : }
     608                 : 
     609                 : size_t
     610               0 : StackSpace::sizeOfCommitted()
     611                 : {
     612                 : #ifdef XP_WIN
     613                 :     return (commitEnd_ - base_) * sizeof(Value);
     614                 : #else
     615               0 :     return (trustedEnd_ - base_) * sizeof(Value);
     616                 : #endif
     617                 : }
     618                 : 
     619                 : /*****************************************************************************/
     620                 : 
     621          100686 : ContextStack::ContextStack(JSContext *cx)
     622                 :   : seg_(NULL),
     623                 :     space_(&cx->runtime->stackSpace),
     624          100686 :     cx_(cx)
     625          100686 : {}
     626                 : 
     627          100686 : ContextStack::~ContextStack()
     628                 : {
     629          100686 :     JS_ASSERT(!seg_);
     630          100686 : }
     631                 : 
     632                 : bool
     633        40602042 : ContextStack::onTop() const
     634                 : {
     635        40602042 :     return seg_ && seg_ == space().seg_;
     636                 : }
     637                 : 
     638                 : bool
     639           21618 : ContextStack::containsSlow(const StackFrame *target) const
     640                 : {
     641           22104 :     for (StackSegment *s = seg_; s; s = s->prevInContext()) {
     642           22104 :         if (s->contains(target))
     643           21618 :             return true;
     644                 :     }
     645               0 :     return false;
     646                 : }
     647                 : 
     648                 : /*
     649                 :  * This helper function brings the ContextStack to the top of the thread stack
     650                 :  * (so that it can be extended to push a frame and/or arguments) by potentially
     651                 :  * pushing a StackSegment. The 'pushedSeg' outparam indicates whether such a
     652                 :  * segment was pushed (and hence whether the caller needs to call popSegment).
     653                 :  *
     654                 :  * Additionally, to minimize calls to ensureSpace, ensureOnTop ensures that
     655                 :  * there is space for nvars slots on top of the stack.
     656                 :  */
     657                 : Value *
     658         2524382 : ContextStack::ensureOnTop(JSContext *cx, MaybeReportError report, unsigned nvars,
     659                 :                           MaybeExtend extend, bool *pushedSeg, JSCompartment *dest)
     660                 : {
     661         2524382 :     Value *firstUnused = space().firstUnused();
     662                 : 
     663                 : #ifdef JS_METHODJIT
     664                 :     /*
     665                 :      * The only calls made by inlined methodjit frames can be to other JIT
     666                 :      * frames associated with the same VMFrame. If we try to Invoke(),
     667                 :      * Execute() or so forth, any topmost inline frame will need to be
     668                 :      * expanded (along with other inline frames in the compartment).
     669                 :      * To avoid pathological behavior here, make sure to mark any topmost
     670                 :      * function as uninlineable, which will expand inline frames if there are
     671                 :      * any and prevent the function from being inlined in the future.
     672                 :      */
     673         2524382 :     if (FrameRegs *regs = cx->maybeRegs()) {
     674         2448391 :         JSFunction *fun = NULL;
     675         2448391 :         if (JSInlinedSite *site = regs->inlined()) {
     676               1 :             mjit::JITChunk *chunk = regs->fp()->jit()->chunk(regs->pc);
     677               1 :             fun = chunk->inlineFrames()[site->inlineIndex].fun;
     678                 :         } else {
     679         2448390 :             StackFrame *fp = regs->fp();
     680         2448390 :             if (fp->isFunctionFrame()) {
     681         2216430 :                 JSFunction *f = fp->fun();
     682         2216430 :                 if (f->isInterpreted())
     683         2216430 :                     fun = f;
     684                 :             }
     685                 :         }
     686                 : 
     687         2448391 :         if (fun) {
     688         2216431 :             fun->script()->uninlineable = true;
     689         2216431 :             types::MarkTypeObjectFlags(cx, fun, types::OBJECT_FLAG_UNINLINEABLE);
     690                 :         }
     691                 :     }
     692         2524382 :     JS_ASSERT_IF(cx->hasfp(), !cx->regs().inlined());
     693                 : #endif
     694                 : 
     695         2524382 :     if (onTop() && extend) {
     696         2445593 :         if (!space().ensureSpace(cx, report, firstUnused, nvars, dest))
     697               0 :             return NULL;
     698         2445593 :         return firstUnused;
     699                 :     }
     700                 : 
     701           78789 :     if (!space().ensureSpace(cx, report, firstUnused, VALUES_PER_STACK_SEGMENT + nvars, dest))
     702               0 :         return NULL;
     703                 : 
     704                 :     FrameRegs *regs;
     705                 :     CallArgsList *calls;
     706           78789 :     if (seg_ && extend) {
     707               0 :         regs = seg_->maybeRegs();
     708               0 :         calls = seg_->maybeCalls();
     709                 :     } else {
     710           78789 :         regs = NULL;
     711           78789 :         calls = NULL;
     712                 :     }
     713                 : 
     714           78789 :     seg_ = new(firstUnused) StackSegment(seg_, space().seg_, regs, calls);
     715           78789 :     space().seg_ = seg_;
     716           78789 :     *pushedSeg = true;
     717           78789 :     return seg_->slotsBegin();
     718                 : }
     719                 : 
     720                 : void
     721           78789 : ContextStack::popSegment()
     722                 : {
     723           78789 :     space().seg_ = seg_->prevInMemory();
     724           78789 :     seg_ = seg_->prevInContext();
     725                 : 
     726           78789 :     if (!seg_)
     727           75845 :         cx_->maybeMigrateVersionOverride();
     728           78789 : }
     729                 : 
     730                 : bool
     731         2142483 : ContextStack::pushInvokeArgs(JSContext *cx, unsigned argc, InvokeArgsGuard *iag)
     732                 : {
     733         2142483 :     JS_ASSERT(argc <= StackSpace::ARGS_LENGTH_MAX);
     734                 : 
     735         2142483 :     unsigned nvars = 2 + argc;
     736         2142483 :     Value *firstUnused = ensureOnTop(cx, REPORT_ERROR, nvars, CAN_EXTEND, &iag->pushedSeg_);
     737         2142483 :     if (!firstUnused)
     738               0 :         return false;
     739                 : 
     740         2142483 :     MakeRangeGCSafe(firstUnused, nvars);
     741                 : 
     742         2142483 :     ImplicitCast<CallArgs>(*iag) = CallArgsFromVp(argc, firstUnused);
     743                 : 
     744         2142483 :     seg_->pushCall(*iag);
     745         2142483 :     JS_ASSERT(space().firstUnused() == iag->end());
     746         2142483 :     iag->setPushed(*this);
     747         2142483 :     return true;
     748                 : }
     749                 : 
     750                 : void
     751         2142483 : ContextStack::popInvokeArgs(const InvokeArgsGuard &iag)
     752                 : {
     753         2142483 :     JS_ASSERT(iag.pushed());
     754         2142483 :     JS_ASSERT(onTop());
     755         2142483 :     JS_ASSERT(space().firstUnused() == seg_->calls().end());
     756                 : 
     757         2142483 :     seg_->popCall();
     758         2142483 :     if (iag.pushedSeg_)
     759             702 :         popSegment();
     760         2142483 : }
     761                 : 
     762                 : bool
     763         1375757 : ContextStack::pushInvokeFrame(JSContext *cx, const CallArgs &args,
     764                 :                               InitialFrameFlags initial, InvokeFrameGuard *ifg)
     765                 : {
     766         1375757 :     JS_ASSERT(onTop());
     767         1375757 :     JS_ASSERT(space().firstUnused() == args.end());
     768                 : 
     769         1375757 :     JSObject &callee = args.callee();
     770         1375757 :     JSFunction *fun = callee.toFunction();
     771         1375757 :     JSScript *script = fun->script();
     772                 : 
     773         1375757 :     StackFrame::Flags flags = ToFrameFlags(initial);
     774         1375757 :     StackFrame *fp = getCallFrame(cx, REPORT_ERROR, args, fun, script, &flags);
     775         1375757 :     if (!fp)
     776              28 :         return false;
     777                 : 
     778         1375729 :     fp->initCallFrame(cx, *fun, script, args.length(), flags);
     779         1375729 :     ifg->regs_.prepareToRun(*fp, script);
     780                 : 
     781         1375729 :     ifg->prevRegs_ = seg_->pushRegs(ifg->regs_);
     782         1375729 :     JS_ASSERT(space().firstUnused() == ifg->regs_.sp);
     783         1375729 :     ifg->setPushed(*this);
     784         1375729 :     return true;
     785                 : }
     786                 : 
     787                 : bool
     788          152647 : ContextStack::pushExecuteFrame(JSContext *cx, JSScript *script, const Value &thisv,
     789                 :                                JSObject &scopeChain, ExecuteType type,
     790                 :                                StackFrame *evalInFrame, ExecuteFrameGuard *efg)
     791                 : {
     792                 :     /*
     793                 :      * Even though global code and indirect eval do not execute in the context
     794                 :      * of the current frame, prev-link these to the current frame so that the
     795                 :      * callstack looks right to the debugger (via CAN_EXTEND). This is safe
     796                 :      * since the scope chain is what determines name lookup and access, not
     797                 :      * prev-links.
     798                 :      *
     799                 :      * Eval-in-frame is the exception since it prev-links to an arbitrary frame
     800                 :      * (possibly in the middle of some previous segment). Thus pass CANT_EXTEND
     801                 :      * (to start a new segment) and link the frame and call chain manually
     802                 :      * below.
     803                 :      */
     804          152647 :     CallArgsList *evalInFrameCalls = NULL;  /* quell overwarning */
     805                 :     StackFrame *prev;
     806                 :     MaybeExtend extend;
     807          152647 :     if (evalInFrame) {
     808                 :         /* Though the prev-frame is given, need to search for prev-call. */
     809            2799 :         StackIter iter(cx, StackIter::GO_THROUGH_SAVED);
     810           11646 :         while (!iter.isScript() || iter.fp() != evalInFrame)
     811            6048 :             ++iter;
     812            2799 :         evalInFrameCalls = iter.calls_;
     813            2799 :         prev = evalInFrame;
     814            2799 :         extend = CANT_EXTEND;
     815                 :     } else {
     816          149848 :         prev = maybefp();
     817          149848 :         extend = CAN_EXTEND;
     818                 :     }
     819                 : 
     820          152647 :     unsigned nvars = 2 /* callee, this */ + VALUES_PER_STACK_FRAME + script->nslots;
     821          152647 :     Value *firstUnused = ensureOnTop(cx, REPORT_ERROR, nvars, extend, &efg->pushedSeg_);
     822          152647 :     if (!firstUnused)
     823               0 :         return NULL;
     824                 : 
     825          152647 :     StackFrame *fp = reinterpret_cast<StackFrame *>(firstUnused + 2);
     826          152647 :     fp->initExecuteFrame(script, prev, seg_->maybeRegs(), thisv, scopeChain, type);
     827          152647 :     SetValueRangeToUndefined(fp->slots(), script->nfixed);
     828          152647 :     efg->regs_.prepareToRun(*fp, script);
     829                 : 
     830                 :     /* pushRegs() below links the prev-frame; manually link the prev-call. */
     831          152647 :     if (evalInFrame && evalInFrameCalls)
     832            2160 :         seg_->pointAtCall(*evalInFrameCalls);
     833                 : 
     834          152647 :     efg->prevRegs_ = seg_->pushRegs(efg->regs_);
     835          152647 :     JS_ASSERT(space().firstUnused() == efg->regs_.sp);
     836          152647 :     efg->setPushed(*this);
     837          152647 :     return true;
     838                 : }
     839                 : 
     840                 : bool
     841          210004 : ContextStack::pushDummyFrame(JSContext *cx, JSCompartment *dest, JSObject &scopeChain, DummyFrameGuard *dfg)
     842                 : {
     843          210004 :     JS_ASSERT(dest == scopeChain.compartment());
     844                 : 
     845          210004 :     unsigned nvars = VALUES_PER_STACK_FRAME;
     846          210004 :     Value *firstUnused = ensureOnTop(cx, REPORT_ERROR, nvars, CAN_EXTEND, &dfg->pushedSeg_, dest);
     847          210004 :     if (!firstUnused)
     848               0 :         return false;
     849                 : 
     850          210004 :     StackFrame *fp = reinterpret_cast<StackFrame *>(firstUnused);
     851          210004 :     fp->initDummyFrame(cx, scopeChain);
     852          210004 :     dfg->regs_.initDummyFrame(*fp);
     853                 : 
     854          210004 :     cx->setCompartment(dest);
     855          210004 :     dfg->prevRegs_ = seg_->pushRegs(dfg->regs_);
     856          210004 :     JS_ASSERT(space().firstUnused() == dfg->regs_.sp);
     857          210004 :     dfg->setPushed(*this);
     858          210004 :     return true;
     859                 : }
     860                 : 
     861                 : void
     862         1757483 : ContextStack::popFrame(const FrameGuard &fg)
     863                 : {
     864         1757483 :     JS_ASSERT(fg.pushed());
     865         1757483 :     JS_ASSERT(onTop());
     866         1757483 :     JS_ASSERT(space().firstUnused() == fg.regs_.sp);
     867         1757483 :     JS_ASSERT(&fg.regs_ == &seg_->regs());
     868                 : 
     869         1757483 :     if (fg.regs_.fp()->isNonEvalFunctionFrame())
     870         1394832 :         fg.regs_.fp()->functionEpilogue();
     871                 : 
     872         1757483 :     seg_->popRegs(fg.prevRegs_);
     873         1757483 :     if (fg.pushedSeg_)
     874           77942 :         popSegment();
     875                 : 
     876                 :     /*
     877                 :      * NB: this code can call out and observe the stack (e.g., through GC), so
     878                 :      * it should only be called from a consistent stack state.
     879                 :      */
     880         1757483 :     if (!hasfp())
     881           75309 :         cx_->resetCompartment();
     882         1757483 : }
     883                 : 
     884                 : bool
     885           19103 : ContextStack::pushGeneratorFrame(JSContext *cx, JSGenerator *gen, GeneratorFrameGuard *gfg)
     886                 : {
     887           19103 :     StackFrame *genfp = gen->floatingFrame();
     888           19103 :     HeapValue *genvp = gen->floatingStack;
     889           19103 :     unsigned vplen = (HeapValue *)genfp - genvp;
     890                 : 
     891           19103 :     unsigned nvars = vplen + VALUES_PER_STACK_FRAME + genfp->numSlots();
     892           19103 :     Value *firstUnused = ensureOnTop(cx, REPORT_ERROR, nvars, CAN_EXTEND, &gfg->pushedSeg_);
     893           19103 :     if (!firstUnused)
     894               0 :         return false;
     895                 : 
     896           19103 :     StackFrame *stackfp = reinterpret_cast<StackFrame *>(firstUnused + vplen);
     897           19103 :     Value *stackvp = (Value *)stackfp - vplen;
     898                 : 
     899                 :     /* Save this for popGeneratorFrame. */
     900           19103 :     gfg->gen_ = gen;
     901           19103 :     gfg->stackvp_ = stackvp;
     902                 : 
     903                 :     /*
     904                 :      * Trigger incremental barrier on the floating frame's generator object.
     905                 :      * This is normally traced through only by associated arguments/call
     906                 :      * objects, but only when the generator is not actually on the stack.
     907                 :      * We don't need to worry about generational barriers as the generator
     908                 :      * object has a trace hook and cannot be nursery allocated.
     909                 :      */
     910           19103 :     JSObject *genobj = js_FloatingFrameToGenerator(genfp)->obj;
     911           19103 :     JS_ASSERT(genobj->getClass()->trace);
     912           19103 :     JSObject::writeBarrierPre(genobj);
     913                 : 
     914                 :     /* Copy from the generator's floating frame to the stack. */
     915                 :     stackfp->stealFrameAndSlots<Value, HeapValue, StackFrame::NoPostBarrier>(
     916           19103 :                                 stackfp, stackvp, genfp, genvp, gen->regs.sp);
     917           19103 :     stackfp->resetGeneratorPrev(cx);
     918           19103 :     stackfp->unsetFloatingGenerator();
     919           19103 :     gfg->regs_.rebaseFromTo(gen->regs, *stackfp);
     920                 : 
     921           19103 :     gfg->prevRegs_ = seg_->pushRegs(gfg->regs_);
     922           19103 :     JS_ASSERT(space().firstUnused() == gfg->regs_.sp);
     923           19103 :     gfg->setPushed(*this);
     924           19103 :     return true;
     925                 : }
     926                 : 
     927                 : void
     928           19103 : ContextStack::popGeneratorFrame(const GeneratorFrameGuard &gfg)
     929                 : {
     930           19103 :     JSGenerator *gen = gfg.gen_;
     931           19103 :     StackFrame *genfp = gen->floatingFrame();
     932           19103 :     HeapValue *genvp = gen->floatingStack;
     933                 : 
     934           19103 :     const FrameRegs &stackRegs = gfg.regs_;
     935           19103 :     StackFrame *stackfp = stackRegs.fp();
     936           19103 :     Value *stackvp = gfg.stackvp_;
     937                 : 
     938                 :     /* Copy from the stack to the generator's floating frame. */
     939           19103 :     gen->regs.rebaseFromTo(stackRegs, *genfp);
     940                 :     genfp->stealFrameAndSlots<HeapValue, Value, StackFrame::DoPostBarrier>(
     941           19103 :                               genfp, genvp, stackfp, stackvp, stackRegs.sp);
     942           19103 :     genfp->setFloatingGenerator();
     943                 : 
     944                 :     /* ~FrameGuard/popFrame will finish the popping. */
     945           19103 :     JS_ASSERT(ImplicitCast<const FrameGuard>(gfg).pushed());
     946           19103 : }
     947                 : 
     948                 : bool
     949             145 : ContextStack::saveFrameChain()
     950                 : {
     951             145 :     JSCompartment *dest = NULL;
     952                 : 
     953                 :     bool pushedSeg;
     954             145 :     if (!ensureOnTop(cx_, REPORT_ERROR, 0, CANT_EXTEND, &pushedSeg, dest))
     955               0 :         return false;
     956                 : 
     957             145 :     JS_ASSERT(pushedSeg);
     958             145 :     JS_ASSERT(!hasfp());
     959             145 :     JS_ASSERT(onTop() && seg_->isEmpty());
     960                 : 
     961             145 :     cx_->resetCompartment();
     962             145 :     return true;
     963                 : }
     964                 : 
     965                 : void
     966             145 : ContextStack::restoreFrameChain()
     967                 : {
     968             145 :     JS_ASSERT(onTop() && seg_->isEmpty());
     969                 : 
     970             145 :     popSegment();
     971             145 :     cx_->resetCompartment();
     972             145 : }
     973                 : 
     974                 : /*****************************************************************************/
     975                 : 
     976                 : void
     977           13870 : StackIter::poisonRegs()
     978                 : {
     979           13870 :     sp_ = (Value *)0xbad;
     980           13870 :     pc_ = (jsbytecode *)0xbad;
     981           13870 :     script_ = (JSScript *)0xbad;
     982           13870 : }
     983                 : 
     984                 : void
     985         5821654 : StackIter::popFrame()
     986                 : {
     987         5821654 :     StackFrame *oldfp = fp_;
     988         5821654 :     JS_ASSERT(seg_->contains(oldfp));
     989         5821654 :     fp_ = fp_->prev();
     990         5821654 :     if (seg_->contains(fp_)) {
     991                 :         JSInlinedSite *inline_;
     992         5808049 :         pc_ = oldfp->prevpc(&inline_);
     993         5808049 :         JS_ASSERT(!inline_);
     994                 : 
     995                 :         /*
     996                 :          * If there is a CallArgsList element between oldfp and fp_, then sp_
     997                 :          * is ignored, so we only consider the case where there is no
     998                 :          * intervening CallArgsList. The stack representation is not optimized
     999                 :          * for this operation so we need to do a full case analysis of how
    1000                 :          * frames are pushed by considering each ContextStack::push*Frame.
    1001                 :          */
    1002         5808049 :         if (oldfp->isGeneratorFrame()) {
    1003                 :             /* Generator's args do not overlap with the caller's expr stack. */
    1004            3195 :             sp_ = (Value *)oldfp->actualArgs() - 2;
    1005         5804854 :         } else if (oldfp->isNonEvalFunctionFrame()) {
    1006                 :             /*
    1007                 :              * When Invoke is called from a native, there will be an enclosing
    1008                 :              * pushInvokeArgs which pushes a CallArgsList element so we can
    1009                 :              * ignore that case. The other two cases of function call frames are
    1010                 :              * Invoke called directly from script and pushInlineFrmae. In both
    1011                 :              * cases, the actual arguments of the callee should be included in
    1012                 :              * the caller's expr stack.
    1013                 :              */
    1014         5767190 :             sp_ = oldfp->actualArgsEnd();
    1015           37664 :         } else if (oldfp->isFramePushedByExecute()) {
    1016                 :             /* pushExecuteFrame pushes exactly (callee, this) before frame. */
    1017            1551 :             sp_ = (Value *)oldfp - 2;
    1018                 :         } else {
    1019                 :             /* pushDummyFrame pushes exactly 0 slots before frame. */
    1020           36113 :             JS_ASSERT(oldfp->isDummyFrame());
    1021           36113 :             sp_ = (Value *)oldfp;
    1022                 :         }
    1023                 : 
    1024         5808049 :         script_ = fp_->maybeScript();
    1025                 :     } else {
    1026           13605 :         poisonRegs();
    1027                 :     }
    1028         5821654 : }
    1029                 : 
    1030                 : void
    1031          108888 : StackIter::popCall()
    1032                 : {
    1033          108888 :     CallArgsList *oldCall = calls_;
    1034          108888 :     JS_ASSERT(seg_->contains(oldCall));
    1035          108888 :     calls_ = calls_->prev();
    1036          108888 :     if (seg_->contains(fp_)) {
    1037                 :         /* pc_ keeps its same value. */
    1038          108884 :         sp_ = oldCall->base();
    1039                 :     } else {
    1040               4 :         poisonRegs();
    1041                 :     }
    1042          108888 : }
    1043                 : 
    1044                 : void
    1045          523719 : StackIter::settleOnNewSegment()
    1046                 : {
    1047          523719 :     if (FrameRegs *regs = seg_->maybeRegs()) {
    1048          523458 :         sp_ = regs->sp;
    1049          523458 :         pc_ = regs->pc;
    1050          523458 :         if (fp_)
    1051          523458 :             script_ = fp_->maybeScript();
    1052                 :     } else {
    1053             261 :         poisonRegs();
    1054                 :     }
    1055          523719 : }
    1056                 : 
    1057                 : void
    1058          523476 : StackIter::startOnSegment(StackSegment *seg)
    1059                 : {
    1060          523476 :     seg_ = seg;
    1061          523476 :     fp_ = seg_->maybefp();
    1062          523476 :     calls_ = seg_->maybeCalls();
    1063          523476 :     settleOnNewSegment();
    1064          523476 : }
    1065                 : 
    1066                 : static void JS_NEVER_INLINE
    1067         5506821 : CrashIfInvalidSlot(StackFrame *fp, Value *vp)
    1068                 : {
    1069         5506821 :     if (vp < fp->slots() || vp >= fp->slots() + fp->script()->nslots) {
    1070               0 :         JS_ASSERT(false && "About to dereference invalid slot");
    1071               0 :         *(int *)0xbad = 0;  // show up nicely in crash-stats
    1072               0 :         MOZ_Assert("About to dereference invalid slot", __FILE__, __LINE__);
    1073                 :     }
    1074         5506821 : }
    1075                 : 
    1076                 : void
    1077         6453757 : StackIter::settleOnNewState()
    1078                 : {
    1079                 :     /*
    1080                 :      * There are elements of the calls_ and fp_ chains that we want to skip
    1081                 :      * over so iterate until we settle on one or until there are no more.
    1082                 :      */
    1083           97148 :     while (true) {
    1084         6453757 :         if (!fp_ && !calls_) {
    1085           13362 :             if (savedOption_ == GO_THROUGH_SAVED && seg_->prevInContext()) {
    1086             144 :                 startOnSegment(seg_->prevInContext());
    1087             144 :                 continue;
    1088                 :             }
    1089           13218 :             state_ = DONE;
    1090           13218 :             return;
    1091                 :         }
    1092                 : 
    1093                 :         /* Check if popFrame/popCall changed segment. */
    1094         6440395 :         bool containsFrame = seg_->contains(fp_);
    1095         6440395 :         bool containsCall = seg_->contains(calls_);
    1096        12881033 :         while (!containsFrame && !containsCall) {
    1097             504 :             seg_ = seg_->prevInContext();
    1098             504 :             containsFrame = seg_->contains(fp_);
    1099             504 :             containsCall = seg_->contains(calls_);
    1100                 : 
    1101                 :             /* Eval-in-frame allows jumping into the middle of a segment. */
    1102             504 :             if (containsFrame && seg_->fp() != fp_) {
    1103                 :                 /* Avoid duplicating logic; seg_ contains fp_, so no iloop. */
    1104             261 :                 StackIter tmp = *this;
    1105             261 :                 tmp.startOnSegment(seg_);
    1106            1026 :                 while (!tmp.isScript() || tmp.fp() != fp_)
    1107             504 :                     ++tmp;
    1108             261 :                 JS_ASSERT(tmp.state_ == SCRIPTED && tmp.seg_ == seg_ && tmp.fp_ == fp_);
    1109             261 :                 *this = tmp;
    1110             261 :                 return;
    1111                 :             }
    1112                 :             /* There is no eval-in-frame equivalent for native calls. */
    1113             243 :             JS_ASSERT_IF(containsCall, &seg_->calls() == calls_);
    1114             243 :             settleOnNewSegment();
    1115                 :         }
    1116                 : 
    1117                 :         /*
    1118                 :          * In case of both a scripted frame and call record, use linear memory
    1119                 :          * ordering to decide which was the most recent.
    1120                 :          */
    1121         6440134 :         if (containsFrame && (!containsCall || (Value *)fp_ >= calls_->array())) {
    1122                 :             /* Nobody wants to see dummy frames. */
    1123         6331246 :             if (fp_->isDummyFrame()) {
    1124           36027 :                 popFrame();
    1125           36027 :                 continue;
    1126                 :             }
    1127                 : 
    1128                 :             /*
    1129                 :              * As an optimization, there is no CallArgsList element pushed for
    1130                 :              * natives called directly by a script (compiled or interpreted).
    1131                 :              * We catch these by inspecting the bytecode and stack. This check
    1132                 :              * relies on the property that, at a call opcode,
    1133                 :              *
    1134                 :              *   regs.sp == vp + 2 + argc
    1135                 :              *
    1136                 :              * The Function.prototype.call optimization leaves no record when
    1137                 :              * 'this' is a native function. Thus, if the following expression
    1138                 :              * runs and breaks in the debugger, the call to 'replace' will not
    1139                 :              * appear on the callstack.
    1140                 :              *
    1141                 :              *   (String.prototype.replace).call('a',/a/,function(){debugger});
    1142                 :              *
    1143                 :              * Function.prototype.call will however appear, hence the debugger
    1144                 :              * can, by inspecting 'args.thisv', give some useful information.
    1145                 :              *
    1146                 :              * For Function.prototype.apply, the situation is even worse: since
    1147                 :              * a dynamic number of arguments have been pushed onto the stack
    1148                 :              * (see SplatApplyArgs), there is no efficient way to know how to
    1149                 :              * find the callee. Thus, calls to apply are lost completely.
    1150                 :              */
    1151         6295219 :             JSOp op = JSOp(*pc_);
    1152         6295219 :             if (op == JSOP_CALL || op == JSOP_FUNCALL) {
    1153         5506821 :                 unsigned argc = GET_ARGC(pc_);
    1154        11013642 :                 DebugOnly<unsigned> spoff = sp_ - fp_->base();
    1155        11013642 :                 JS_ASSERT_IF(cx_->stackIterAssertionEnabled,
    1156        16520463 :                              spoff == js_ReconstructStackDepth(cx_, fp_->script(), pc_));
    1157         5506821 :                 Value *vp = sp_ - (2 + argc);
    1158                 : 
    1159         5506821 :                 CrashIfInvalidSlot(fp_, vp);
    1160         5506821 :                 if (IsNativeFunction(*vp)) {
    1161           58571 :                     state_ = IMPLICIT_NATIVE;
    1162           58571 :                     args_ = CallArgsFromVp(argc, vp);
    1163                 :                     return;
    1164                 :                 }
    1165                 :             }
    1166                 : 
    1167         6236648 :             state_ = SCRIPTED;
    1168        12473296 :             DebugOnly<JSScript *> script = fp_->script();
    1169        12463672 :             JS_ASSERT_IF(op != JSOP_FUNAPPLY,
    1170        18700320 :                          sp_ >= fp_->base() && sp_ <= fp_->slots() + script->nslots);
    1171         6236648 :             JS_ASSERT(pc_ >= script->code && pc_ < script->code + script->length);
    1172                 :             return;
    1173                 :         }
    1174                 : 
    1175                 :         /*
    1176                 :          * A CallArgsList element is pushed for any call to Invoke, regardless
    1177                 :          * of whether the callee is a scripted function or even a callable
    1178                 :          * object. Thus, it is necessary to filter calleev for natives.
    1179                 :          *
    1180                 :          * Second, stuff can happen after the args are pushed but before/after
    1181                 :          * the actual call, so only consider "active" calls. (Since Invoke
    1182                 :          * necessarily clobbers the callee, "active" is also necessary to
    1183                 :          * ensure that the callee slot is valid.)
    1184                 :          */
    1185          108888 :         if (calls_->active() && IsNativeFunction(calls_->calleev())) {
    1186           47911 :             state_ = NATIVE;
    1187           47911 :             args_ = *calls_;
    1188           47911 :             return;
    1189                 :         }
    1190                 : 
    1191                 :         /* Pop the call and keep looking. */
    1192           60977 :         popCall();
    1193                 :     }
    1194                 : }
    1195                 : 
    1196          523272 : StackIter::StackIter(JSContext *cx, SavedOption savedOption)
    1197                 :   : cx_(cx),
    1198          523272 :     savedOption_(savedOption)
    1199                 : {
    1200                 : #ifdef JS_METHODJIT
    1201          523272 :     mjit::ExpandInlineFrames(cx->compartment);
    1202                 : #endif
    1203                 : 
    1204          523272 :     if (StackSegment *seg = cx->stack.seg_) {
    1205          523071 :         startOnSegment(seg);
    1206          523071 :         settleOnNewState();
    1207                 :     } else {
    1208             201 :         state_ = DONE;
    1209                 :     }
    1210          523272 : }
    1211                 : 
    1212                 : StackIter &
    1213         5892109 : StackIter::operator++()
    1214                 : {
    1215         5892109 :     JS_ASSERT(!done());
    1216         5892109 :     switch (state_) {
    1217                 :       case DONE:
    1218               0 :         JS_NOT_REACHED("");
    1219                 :       case SCRIPTED:
    1220         5785627 :         popFrame();
    1221         5785627 :         settleOnNewState();
    1222         5785627 :         break;
    1223                 :       case NATIVE:
    1224           47911 :         popCall();
    1225           47911 :         settleOnNewState();
    1226           47911 :         break;
    1227                 :       case IMPLICIT_NATIVE:
    1228           58571 :         state_ = SCRIPTED;
    1229           58571 :         break;
    1230                 :     }
    1231         5892109 :     return *this;
    1232                 : }
    1233                 : 
    1234                 : bool
    1235               0 : StackIter::operator==(const StackIter &rhs) const
    1236                 : {
    1237               0 :     return done() == rhs.done() &&
    1238               0 :            (done() ||
    1239               0 :             (isScript() == rhs.isScript() &&
    1240               0 :              ((isScript() && fp() == rhs.fp()) ||
    1241               0 :               (!isScript() && nativeArgs().base() == rhs.nativeArgs().base()))));
    1242                 : }
    1243                 : 
    1244                 : /*****************************************************************************/
    1245                 : 
    1246           44245 : AllFramesIter::AllFramesIter(StackSpace &space)
    1247                 :   : seg_(space.seg_),
    1248           44245 :     fp_(seg_ ? seg_->maybefp() : NULL)
    1249                 : {
    1250           44245 :     settle();
    1251           44245 : }
    1252                 : 
    1253                 : AllFramesIter&
    1254           82078 : AllFramesIter::operator++()
    1255                 : {
    1256           82078 :     JS_ASSERT(!done());
    1257           82078 :     fp_ = fp_->prev();
    1258           82078 :     settle();
    1259           82078 :     return *this;
    1260                 : }
    1261                 : 
    1262                 : void
    1263          126323 : AllFramesIter::settle()
    1264                 : {
    1265          257053 :     while (seg_ && (!fp_ || !seg_->contains(fp_))) {
    1266            4407 :         seg_ = seg_->prevInMemory();
    1267            4407 :         fp_ = seg_ ? seg_->maybefp() : NULL;
    1268                 :     }
    1269                 : 
    1270          126323 :     JS_ASSERT(!!seg_ == !!fp_);
    1271          126323 :     JS_ASSERT_IF(fp_, seg_->contains(fp_));
    1272          126323 : }

Generated by: LCOV version 1.7