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
17 * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
20 * The Initial Developer of the Original Code is
21 * Brendan Eich <brendan@mozilla.org>
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 ***** */
44 #include "jslibmath.h"
48 #include "jsstaticcheck.h"
50 #include "assembler/assembler/MacroAssemblerCodeRef.h"
53 #include "methodjit/StubCalls.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"
67 using namespace js::mjit
;
72 void *ptr = JS_FUNC_TO_DATA_PTR(void *, JaegerThrowpoline); \
73 f.setReturnAddress(ReturnAddressPtr(FunctionPtr(ptr))); \
79 void *ptr = JS_FUNC_TO_DATA_PTR(void *, JaegerThrowpoline); \
80 f.setReturnAddress(ReturnAddressPtr(FunctionPtr(ptr))); \
85 InlineReturn(JSContext
*cx
, JSBool ok
);
88 FindExceptionHandler(JSContext
*cx
)
90 JSStackFrame
*fp
= cx
->fp
;
91 JSScript
*script
= fp
->script
;
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
)
104 if (tn
->stackDepth
> cx
->regs
->sp
- fp
->base())
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
);
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
)))
122 * Don't clear cx->throwing to save cx->exception from GC
123 * until it is pushed to the stack via [exception] in the
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
;
136 cx
->throwing
= JS_FALSE
;
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
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());
155 cx
->throwing
= JS_TRUE
;
156 cx
->exception
= tvr
.value();
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
);
183 /* Allocate the frame. */
184 StackSpace
&stack
= cx
->stack();
185 uintN nslots
= newscript
->nslots
;
186 uintN funargs
= fun
->nargs
;
187 Value
*argv
= vp
+ 2;
189 if (argc
< funargs
) {
190 uintN missing
= funargs
- argc
;
191 newfp
= stack
.getInlineFrame(cx
, f
.regs
.sp
, missing
, nslots
);
194 for (Value
*v
= argv
+ argc
, *end
= v
+ missing
; v
!= end
; ++v
)
197 newfp
= stack
.getInlineFrame(cx
, f
.regs
.sp
, 0, nslots
);
202 /* Initialize the frame. */
204 newfp
->callobj
= NULL
;
205 newfp
->argsobj
= NULL
;
206 newfp
->script
= newscript
;
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
)
225 /* Scope with a call object parented by callee's parent. */
226 if (fun
->isHeavyweight() && !js_GetCallObject(cx
, newfp
))
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();
238 newfp
->hookData
= NULL
;
241 stack
.pushInlineFrame(cx
, fp
, cx
->regs
->pc
, newfp
);
247 FixVMFrame(VMFrame
&f
, JSStackFrame
*fp
)
250 f
.fp
->ncode
= f
.scriptedReturn
;
251 JS_ASSERT(f
.fp
== fp
->down
);
256 InlineCall(VMFrame
&f
, uint32 flags
, void **pret
, uint32 argc
)
258 if (!CreateFrame(f
, flags
, argc
))
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
)
269 JS_ASSERT(script
->ncode
);
270 if (script
->ncode
!= JS_UNJITTABLE_METHOD
) {
272 *pret
= script
->nmap
[-1];
277 f
.regs
.pc
= script
->code
;
278 f
.regs
.sp
= fp
->base();
280 bool ok
= !!Interpret(cx
);
281 InlineReturn(cx
, JS_TRUE
);
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
;
301 hook
= cx
->debugHooks
->callHook
;
304 * Do not pass &ok directly as exposing the address inhibits
305 * optimizations and uninitialised warnings.
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
;
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]))
340 JSObject
*proto
= vp
[1].isObject() ? &vp
[1].toObject() : NULL
;
341 return NewObject(cx
, &js_ObjectClass
, proto
, funobj
->getParent());
345 stubs::SlowCall(VMFrame
&f
, uint32 argc
)
347 JSContext
*cx
= f
.cx
;
348 Value
*vp
= f
.regs
.sp
- (argc
+ 2);
351 if (IsFunctionObject(*vp
, &obj
)) {
352 JSFunction
*fun
= GET_FUNCTION_PRIVATE(cx
, obj
);
354 if (fun
->isInterpreted()) {
357 if (fun
->u
.i
.script
->isEmpty()) {
363 if (!InlineCall(f
, 0, &ret
, argc
))
369 if (fun
->isFastNative()) {
370 FastNative fn
= (FastNative
)fun
->u
.n
.native
;
371 if (!fn(cx
, argc
, vp
))
377 if (!Invoke(f
.cx
, InvokeArgsGuard(vp
, argc
), 0))
384 stubs::SlowNew(VMFrame
&f
, uint32 argc
)
386 JSContext
*cx
= f
.cx
;
387 Value
*vp
= f
.regs
.sp
- (argc
+ 2);
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
);
399 if (script
->isEmpty()) {
400 vp
[0].setObject(*obj2
);
405 vp
[1].setObject(*obj2
);
406 if (!InlineCall(f
, JSFRAME_CONSTRUCTING
, &ret
, argc
))
413 if (!InvokeConstructor(cx
, InvokeArgsGuard(vp
, argc
)))
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
);
437 /* Allocate the frame. */
438 StackSpace
&stack
= cx
->stack();
439 uintN nslots
= newscript
->nslots
;
440 uintN funargs
= fun
->nargs
;
441 Value
*argv
= vp
+ 2;
443 if (argc
< funargs
) {
444 uintN missing
= funargs
- argc
;
445 newfp
= stack
.getInlineFrame(cx
, f
.regs
.sp
, missing
, nslots
);
448 for (Value
*v
= argv
+ argc
, *end
= v
+ missing
; v
!= end
; ++v
)
451 newfp
= stack
.getInlineFrame(cx
, f
.regs
.sp
, 0, nslots
);
456 /* Initialize the frame. */
458 newfp
->callobj
= NULL
;
459 newfp
->argsobj
= NULL
;
460 newfp
->script
= newscript
;
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
;
475 /* :TODO: Switch version if currentVersion wasn't overridden. */
476 newfp
->callerVersion
= (JSVersion
)cx
->version
;
480 newfp
->savedPC
= JSStackFrame::sInvalidPC
;
483 fp
->savedPC
= f
.regs
.pc
;
484 cx
->setCurrentFrame(newfp
);
490 * stubs::Call is guaranteed to be called on a scripted call with JIT'd code.
493 stubs::Call(VMFrame
&f
, uint32 argc
)
495 if (!CreateLightFrame(f
, 0, argc
))
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.
507 stubs::New(VMFrame
&f
, uint32 argc
)
509 JSObject
*obj
= InlineConstruct(f
, argc
);
513 f
.regs
.sp
[-int(argc
+ 1)].setObject(*obj
);
514 if (!CreateLightFrame(f
, JSFRAME_CONSTRUCTING
, argc
))
517 FixVMFrame(f
, f
.cx
->fp
);
519 return f
.fp
->script
->ncode
;
523 stubs::PutCallObject(VMFrame
&f
)
525 JS_ASSERT(f
.fp
->callobj
);
526 js_PutCallObject(f
.cx
, f
.fp
);
527 JS_ASSERT(!f
.fp
->argsobj
);
531 stubs::PutArgsObject(VMFrame
&f
)
533 js_PutArgsObject(f
.cx
, f
.fp
);
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
;
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
;
554 pc
= FindExceptionHandler(cx
);
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
);
567 JS_ASSERT(f
.regs
.sp
== cx
->regs
->sp
);
568 InlineReturn(f
.cx
, JS_FALSE
);
571 f
.scriptedReturn
= cx
->fp
->ncode
;
574 JS_ASSERT(f
.regs
.sp
== cx
->regs
->sp
);
578 f
.cx
->setCurrentRegs(f
.oldRegs
);
582 return cx
->fp
->script
->pcToNative(pc
);
586 stubs::GetCallObject(VMFrame
&f
)
588 JS_ASSERT(f
.fp
->fun
->isHeavyweight());
589 if (!js_GetCallObject(f
.cx
, f
.fp
))