[JAEGER] Merge from tracemonkey.
[mozilla-central.git] / js / src / methodjit / InvokeHelpers.cpp
bloba1a5abe63009d107106776be1f4a3479270f6fd3
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=4 sw=4 et tw=99:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
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/
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.
17 * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
18 * May 28, 2008.
20 * The Initial Developer of the Original Code is
21 * Brendan Eich <brendan@mozilla.org>
23 * Contributor(s):
24 * David Anderson <danderson@mozilla.com>
25 * David Mandelin <dmandelin@mozilla.com>
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.
39 * ***** END LICENSE BLOCK ***** */
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 "jsstaticcheck.h"
49 #include "jsbool.h"
50 #include "assembler/assembler/MacroAssemblerCodeRef.h"
51 #include "jsiter.h"
52 #include "jstypes.h"
53 #include "methodjit/StubCalls.h"
54 #include "jstracer.h"
55 #include "jspropertycache.h"
56 #include "jspropertycacheinlines.h"
57 #include "jsscopeinlines.h"
58 #include "jsscriptinlines.h"
59 #include "jsstrinlines.h"
60 #include "jsobjinlines.h"
61 #include "jscntxtinlines.h"
62 #include "jsatominlines.h"
64 #include "jsautooplen.h"
66 using namespace js;
67 using namespace js::mjit;
68 using namespace JSC;
70 #define THROW() \
71 do { \
72 void *ptr = JS_FUNC_TO_DATA_PTR(void *, JaegerThrowpoline); \
73 f.setReturnAddress(ReturnAddressPtr(FunctionPtr(ptr))); \
74 return; \
75 } while (0)
77 #define THROWV(v) \
78 do { \
79 void *ptr = JS_FUNC_TO_DATA_PTR(void *, JaegerThrowpoline); \
80 f.setReturnAddress(ReturnAddressPtr(FunctionPtr(ptr))); \
81 return v; \
82 } while (0)
84 static bool
85 InlineReturn(JSContext *cx, JSBool ok);
87 static jsbytecode *
88 FindExceptionHandler(JSContext *cx)
90 JSStackFrame *fp = cx->fp;
91 JSScript *script = fp->script;
93 top:
94 if (cx->throwing && script->trynotesOffset) {
95 // The PC is updated before every stub call, so we can use it here.
96 unsigned offset = cx->regs->pc - script->main;
98 JSTryNoteArray *tnarray = script->trynotes();
99 for (unsigned i = 0; i < tnarray->length; ++i) {
100 JSTryNote *tn = &tnarray->vector[i];
101 JS_ASSERT(offset < script->length);
102 if (offset - tn->start >= tn->length)
103 continue;
104 if (tn->stackDepth > cx->regs->sp - fp->base())
105 continue;
107 jsbytecode *pc = script->main + tn->start + tn->length;
108 JSBool ok = js_UnwindScope(cx, tn->stackDepth, JS_TRUE);
109 JS_ASSERT(cx->regs->sp == fp->base() + tn->stackDepth);
111 switch (tn->kind) {
112 case JSTRY_CATCH:
113 JS_ASSERT(js_GetOpcode(cx, fp->script, pc) == JSOP_ENTERBLOCK);
115 #if JS_HAS_GENERATORS
116 /* Catch cannot intercept the closing of a generator. */
117 if (JS_UNLIKELY(cx->exception.isMagic(JS_GENERATOR_CLOSING)))
118 break;
119 #endif
122 * Don't clear cx->throwing to save cx->exception from GC
123 * until it is pushed to the stack via [exception] in the
124 * catch block.
126 return pc;
128 case JSTRY_FINALLY:
130 * Push (true, exception) pair for finally to indicate that
131 * [retsub] should rethrow the exception.
133 cx->regs->sp[0].setBoolean(true);
134 cx->regs->sp[1] = cx->exception;
135 cx->regs->sp += 2;
136 cx->throwing = JS_FALSE;
137 return pc;
139 case JSTRY_ITER:
142 * This is similar to JSOP_ENDITER in the interpreter loop,
143 * except the code now uses the stack slot normally used by
144 * JSOP_NEXTITER, namely regs.sp[-1] before the regs.sp -= 2
145 * adjustment and regs.sp[1] after, to save and restore the
146 * pending exception.
148 AutoValueRooter tvr(cx, cx->exception);
149 JS_ASSERT(js_GetOpcode(cx, fp->script, pc) == JSOP_ENDITER);
150 cx->throwing = JS_FALSE;
151 ok = !!js_CloseIterator(cx, &cx->regs->sp[-1].toObject());
152 cx->regs->sp -= 1;
153 if (!ok)
154 goto top;
155 cx->throwing = JS_TRUE;
156 cx->exception = tvr.value();
162 return NULL;
165 static inline bool
166 CreateFrame(VMFrame &f, uint32 flags, uint32 argc)
168 JSContext *cx = f.cx;
169 JSStackFrame *fp = f.fp;
170 Value *vp = f.regs.sp - (argc + 2);
171 JSObject *funobj = &vp->toObject();
172 JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
174 JS_ASSERT(FUN_INTERPRETED(fun));
176 JSScript *newscript = fun->u.i.script;
178 if (f.inlineCallCount >= JS_MAX_INLINE_CALL_COUNT) {
179 js_ReportOverRecursed(cx);
180 return false;
183 /* Allocate the frame. */
184 StackSpace &stack = cx->stack();
185 uintN nslots = newscript->nslots;
186 uintN funargs = fun->nargs;
187 Value *argv = vp + 2;
188 JSStackFrame *newfp;
189 if (argc < funargs) {
190 uintN missing = funargs - argc;
191 newfp = stack.getInlineFrame(cx, f.regs.sp, missing, nslots);
192 if (!newfp)
193 return false;
194 for (Value *v = argv + argc, *end = v + missing; v != end; ++v)
195 v->setUndefined();
196 } else {
197 newfp = stack.getInlineFrame(cx, f.regs.sp, 0, nslots);
198 if (!newfp)
199 return false;
202 /* Initialize the frame. */
203 newfp->ncode = NULL;
204 newfp->callobj = NULL;
205 newfp->argsobj = NULL;
206 newfp->script = newscript;
207 newfp->fun = fun;
208 newfp->argc = argc;
209 newfp->argv = vp + 2;
210 newfp->rval.setUndefined();
211 newfp->annotation = NULL;
212 newfp->scopeChain = funobj->getParent();
213 newfp->flags = flags;
214 newfp->blockChain = NULL;
215 JS_ASSERT(!JSFUN_BOUND_METHOD_TEST(fun->flags));
216 newfp->thisv = vp[1];
217 newfp->imacpc = NULL;
219 /* Push void to initialize local variables. */
220 Value *newslots = newfp->slots();
221 Value *newsp = newslots + fun->u.i.nvars;
222 for (Value *v = newslots; v != newsp; ++v)
223 v->setUndefined();
225 /* Scope with a call object parented by callee's parent. */
226 if (fun->isHeavyweight() && !js_GetCallObject(cx, newfp))
227 return false;
229 /* :TODO: Switch version if currentVersion wasn't overridden. */
230 newfp->callerVersion = (JSVersion)cx->version;
232 // Marker for debug support.
233 if (JSInterpreterHook hook = cx->debugHooks->callHook) {
234 newfp->hookData = hook(cx, fp, JS_TRUE, 0,
235 cx->debugHooks->callHookData);
236 // CHECK_INTERRUPT_HANDLER();
237 } else {
238 newfp->hookData = NULL;
241 stack.pushInlineFrame(cx, fp, cx->regs->pc, newfp);
243 return true;
246 static inline void
247 FixVMFrame(VMFrame &f, JSStackFrame *fp)
249 f.inlineCallCount++;
250 f.fp->ncode = f.scriptedReturn;
251 JS_ASSERT(f.fp == fp->down);
252 f.fp = fp;
255 static inline bool
256 InlineCall(VMFrame &f, uint32 flags, void **pret, uint32 argc)
258 if (!CreateFrame(f, flags, argc))
259 return false;
261 JSContext *cx = f.cx;
262 JSStackFrame *fp = cx->fp;
263 JSScript *script = fp->script;
264 if (cx->options & JSOPTION_METHODJIT) {
265 if (!script->ncode) {
266 if (mjit::TryCompile(cx, script, fp->fun, fp->scopeChain) == Compile_Error)
267 return false;
269 JS_ASSERT(script->ncode);
270 if (script->ncode != JS_UNJITTABLE_METHOD) {
271 FixVMFrame(f, fp);
272 *pret = script->nmap[-1];
273 return true;
277 f.regs.pc = script->code;
278 f.regs.sp = fp->base();
280 bool ok = !!Interpret(cx);
281 InlineReturn(cx, JS_TRUE);
283 *pret = NULL;
284 return ok;
287 static bool
288 InlineReturn(JSContext *cx, JSBool ok)
290 JSStackFrame *fp = cx->fp;
292 JS_ASSERT(!fp->blockChain);
293 JS_ASSERT(!js_IsActiveWithOrBlock(cx, fp->scopeChain, 0));
295 // Marker for debug support.
296 void *hookData = fp->hookData;
297 if (JS_UNLIKELY(hookData != NULL)) {
298 JSInterpreterHook hook;
299 JSBool status;
301 hook = cx->debugHooks->callHook;
302 if (hook) {
304 * Do not pass &ok directly as exposing the address inhibits
305 * optimizations and uninitialised warnings.
307 status = ok;
308 hook(cx, fp, JS_FALSE, &status, hookData);
309 ok = (status == JS_TRUE);
310 // CHECK_INTERRUPT_HANDLER();
314 fp->putActivationObjects(cx);
316 /* :TODO: version stuff */
318 if (fp->flags & JSFRAME_CONSTRUCTING && fp->rval.isPrimitive())
319 fp->rval = fp->thisv;
321 cx->stack().popInlineFrame(cx, fp, fp->down);
322 cx->regs->sp[-1] = fp->rval;
324 return ok;
327 static inline JSObject *
328 InlineConstruct(VMFrame &f, uint32 argc)
330 JSContext *cx = f.cx;
331 Value *vp = f.regs.sp - (argc + 2);
333 JSObject *funobj = &vp[0].toObject();
334 JS_ASSERT(funobj->isFunction());
336 jsid id = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom);
337 if (!funobj->getProperty(cx, id, &vp[1]))
338 return NULL;
340 JSObject *proto = vp[1].isObject() ? &vp[1].toObject() : NULL;
341 return NewObject(cx, &js_ObjectClass, proto, funobj->getParent());
344 void * JS_FASTCALL
345 stubs::SlowCall(VMFrame &f, uint32 argc)
347 JSContext *cx = f.cx;
348 Value *vp = f.regs.sp - (argc + 2);
350 JSObject *obj;
351 if (IsFunctionObject(*vp, &obj)) {
352 JSFunction *fun = GET_FUNCTION_PRIVATE(cx, obj);
354 if (fun->isInterpreted()) {
355 void *ret;
357 if (fun->u.i.script->isEmpty()) {
358 vp->setUndefined();
359 f.regs.sp = vp + 1;
360 return NULL;
363 if (!InlineCall(f, 0, &ret, argc))
364 THROWV(NULL);
366 return ret;
369 if (fun->isFastNative()) {
370 FastNative fn = (FastNative)fun->u.n.native;
371 if (!fn(cx, argc, vp))
372 THROWV(NULL);
373 return NULL;
377 if (!Invoke(f.cx, InvokeArgsGuard(vp, argc), 0))
378 THROWV(NULL);
380 return NULL;
383 void * JS_FASTCALL
384 stubs::SlowNew(VMFrame &f, uint32 argc)
386 JSContext *cx = f.cx;
387 Value *vp = f.regs.sp - (argc + 2);
389 JSObject *obj;
390 if (IsFunctionObject(*vp, &obj)) {
391 JSFunction *fun = GET_FUNCTION_PRIVATE(cx, obj);
393 if (fun->isInterpreted()) {
394 JSScript *script = fun->u.i.script;
395 JSObject *obj2 = InlineConstruct(f, argc);
396 if (!obj2)
397 THROWV(NULL);
399 if (script->isEmpty()) {
400 vp[0].setObject(*obj2);
401 return NULL;
404 void *ret;
405 vp[1].setObject(*obj2);
406 if (!InlineCall(f, JSFRAME_CONSTRUCTING, &ret, argc))
407 THROWV(NULL);
409 return ret;
413 if (!InvokeConstructor(cx, InvokeArgsGuard(vp, argc)))
414 THROWV(NULL);
416 return NULL;
419 static inline bool
420 CreateLightFrame(VMFrame &f, uint32 flags, uint32 argc)
422 JSContext *cx = f.cx;
423 JSStackFrame *fp = f.fp;
424 Value *vp = f.regs.sp - (argc + 2);
425 JSObject *funobj = &vp->toObject();
426 JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
428 JS_ASSERT(FUN_INTERPRETED(fun));
430 JSScript *newscript = fun->u.i.script;
432 if (f.inlineCallCount >= JS_MAX_INLINE_CALL_COUNT) {
433 js_ReportOverRecursed(cx);
434 return false;
437 /* Allocate the frame. */
438 StackSpace &stack = cx->stack();
439 uintN nslots = newscript->nslots;
440 uintN funargs = fun->nargs;
441 Value *argv = vp + 2;
442 JSStackFrame *newfp;
443 if (argc < funargs) {
444 uintN missing = funargs - argc;
445 newfp = stack.getInlineFrame(cx, f.regs.sp, missing, nslots);
446 if (!newfp)
447 return false;
448 for (Value *v = argv + argc, *end = v + missing; v != end; ++v)
449 v->setUndefined();
450 } else {
451 newfp = stack.getInlineFrame(cx, f.regs.sp, 0, nslots);
452 if (!newfp)
453 return false;
456 /* Initialize the frame. */
457 newfp->ncode = NULL;
458 newfp->callobj = NULL;
459 newfp->argsobj = NULL;
460 newfp->script = newscript;
461 newfp->fun = fun;
462 newfp->argc = argc;
463 newfp->argv = vp + 2;
464 newfp->rval.setUndefined();
465 newfp->annotation = NULL;
466 newfp->scopeChain = funobj->getParent();
467 newfp->flags = flags;
468 newfp->blockChain = NULL;
469 JS_ASSERT(!JSFUN_BOUND_METHOD_TEST(fun->flags));
470 newfp->thisv = vp[1];
471 newfp->imacpc = NULL;
472 newfp->hookData = NULL;
474 #if 0
475 /* :TODO: Switch version if currentVersion wasn't overridden. */
476 newfp->callerVersion = (JSVersion)cx->version;
477 #endif
479 #ifdef DEBUG
480 newfp->savedPC = JSStackFrame::sInvalidPC;
481 #endif
482 newfp->down = fp;
483 fp->savedPC = f.regs.pc;
484 cx->setCurrentFrame(newfp);
486 return true;
490 * stubs::Call is guaranteed to be called on a scripted call with JIT'd code.
492 void * JS_FASTCALL
493 stubs::Call(VMFrame &f, uint32 argc)
495 if (!CreateLightFrame(f, 0, argc))
496 THROWV(NULL);
498 FixVMFrame(f, f.cx->fp);
500 return f.fp->script->ncode;
504 * stubs::New is guaranteed to be called on a scripted call with JIT'd code.
506 void * JS_FASTCALL
507 stubs::New(VMFrame &f, uint32 argc)
509 JSObject *obj = InlineConstruct(f, argc);
510 if (!obj)
511 THROWV(NULL);
513 f.regs.sp[-int(argc + 1)].setObject(*obj);
514 if (!CreateLightFrame(f, JSFRAME_CONSTRUCTING, argc))
515 THROWV(NULL);
517 FixVMFrame(f, f.cx->fp);
519 return f.fp->script->ncode;
522 void JS_FASTCALL
523 stubs::PutCallObject(VMFrame &f)
525 JS_ASSERT(f.fp->callobj);
526 js_PutCallObject(f.cx, f.fp);
527 JS_ASSERT(!f.fp->argsobj);
530 void JS_FASTCALL
531 stubs::PutArgsObject(VMFrame &f)
533 js_PutArgsObject(f.cx, f.fp);
536 void JS_FASTCALL
537 stubs::CopyThisv(VMFrame &f)
539 JS_ASSERT(f.fp->flags & JSFRAME_CONSTRUCTING);
540 if (f.fp->rval.isPrimitive())
541 f.fp->rval = f.fp->thisv;
544 extern "C" void *
545 js_InternalThrow(VMFrame &f)
547 JSContext *cx = f.cx;
549 // Make sure sp is up to date.
550 JS_ASSERT(cx->regs == &f.regs);
552 jsbytecode *pc = NULL;
553 for (;;) {
554 pc = FindExceptionHandler(cx);
555 if (pc)
556 break;
558 // If |f.inlineCallCount == 0|, then we are on the 'topmost' frame (where
559 // topmost means the first frame called into through js_Interpret). In this
560 // case, we still unwind, but we shouldn't return from a JS function, because
561 // we're not in a JS function.
562 bool lastFrame = (f.inlineCallCount == 0);
563 js_UnwindScope(cx, 0, cx->throwing);
564 if (lastFrame)
565 break;
567 JS_ASSERT(f.regs.sp == cx->regs->sp);
568 InlineReturn(f.cx, JS_FALSE);
569 f.inlineCallCount--;
570 f.fp = cx->fp;
571 f.scriptedReturn = cx->fp->ncode;
574 JS_ASSERT(f.regs.sp == cx->regs->sp);
576 if (!pc) {
577 *f.oldRegs = f.regs;
578 f.cx->setCurrentRegs(f.oldRegs);
579 return NULL;
582 return cx->fp->script->pcToNative(pc);
585 void JS_FASTCALL
586 stubs::GetCallObject(VMFrame &f)
588 JS_ASSERT(f.fp->fun->isHeavyweight());
589 if (!js_GetCallObject(f.cx, f.fp))
590 THROW();