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 ®s = 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 ®s)
338 : {
339 5688408 : JS_ASSERT_IF(contains(regs_), regs.fp()->prev() == regs_->fp());
340 5688408 : FrameRegs *prev = regs_;
341 5688408 : regs_ = ®s;
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 : }
|