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