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