зеркало из https://github.com/mozilla/pjs.git
Remove JSOP_BEGIN and fix tracer integration issues (bug 603044, r=luke+dmandelin).
This commit is contained in:
Родитель
97e0a9703a
Коммит
8fcaad8405
|
@ -855,7 +855,7 @@ interface jsdIStackFrame : jsdIEphemeral
|
|||
* Script object. In JavaScript engine terms, there's a single script for each
|
||||
* function, and one for the top level script.
|
||||
*/
|
||||
[scriptable, uuid(53dadd96-69f6-4846-8958-cc8eaa3f9f09)]
|
||||
[scriptable, uuid(7e6fb9ed-4382-421d-9a14-c80a486e983b)]
|
||||
interface jsdIScript : jsdIEphemeral
|
||||
{
|
||||
/** Internal use only. */
|
||||
|
@ -1001,17 +1001,6 @@ interface jsdIScript : jsdIEphemeral
|
|||
* The |pcmap| argument specifies which pc to source line map to use.
|
||||
*/
|
||||
boolean isLineExecutable (in unsigned long line, in unsigned long pcmap);
|
||||
/**
|
||||
* Get the first valid PC in the script. This will be either
|
||||
* (a) the first bytecode in the script, or (b) the next bytecode
|
||||
* in the script, iff the first bytecode is a JSOP_BEGIN.
|
||||
*/
|
||||
unsigned long getFirstValidPC ();
|
||||
/**
|
||||
* Return the last valid PC in the script (i.e., the PC just after
|
||||
* the last bytecode).
|
||||
*/
|
||||
unsigned long getEndValidPC ();
|
||||
/**
|
||||
* Set a breakpoint at a PC in this script.
|
||||
*/
|
||||
|
|
|
@ -478,12 +478,6 @@ jsd_GetScriptHook(JSDContext* jsdc, JSD_ScriptHookProc* hook, void** callerdata)
|
|||
extern jsuword
|
||||
jsd_GetClosestPC(JSDContext* jsdc, JSDScript* jsdscript, uintN line);
|
||||
|
||||
extern jsuword
|
||||
jsd_GetFirstValidPC(JSDContext* jsdc, JSDScript* jsdscript);
|
||||
|
||||
extern jsuword
|
||||
jsd_GetEndPC(JSDContext* jsdc, JSDScript* jsdscript);
|
||||
|
||||
extern uintN
|
||||
jsd_GetClosestLine(JSDContext* jsdc, JSDScript* jsdscript, jsuword pc);
|
||||
|
||||
|
|
|
@ -500,22 +500,20 @@ jsd_GetScriptLineExtent(JSDContext* jsdc, JSDScript *jsdscript)
|
|||
jsuword
|
||||
jsd_GetClosestPC(JSDContext* jsdc, JSDScript* jsdscript, uintN line)
|
||||
{
|
||||
#ifdef LIVEWIRE
|
||||
if( jsdscript && jsdscript->lwscript )
|
||||
{
|
||||
uintN newline;
|
||||
jsdlw_RawToProcessedLineNumber(jsdc, jsdscript, line, &newline);
|
||||
if( line != newline )
|
||||
line = newline;
|
||||
}
|
||||
#endif
|
||||
|
||||
return (jsuword) JS_LineNumberToPC(jsdc->dumbContext,
|
||||
jsdscript->script, line );
|
||||
}
|
||||
|
||||
jsuword
|
||||
jsd_GetFirstValidPC(JSDContext* jsdc, JSDScript* jsdscript)
|
||||
{
|
||||
return (jsuword) JS_FirstValidPC(jsdc->dumbContext, jsdscript->script );
|
||||
}
|
||||
|
||||
jsuword
|
||||
jsd_GetEndPC(JSDContext* jsdc, JSDScript* jsdscript)
|
||||
{
|
||||
return (jsuword) JS_EndPC(jsdc->dumbContext, jsdscript->script );
|
||||
}
|
||||
|
||||
uintN
|
||||
jsd_GetClosestLine(JSDContext* jsdc, JSDScript* jsdscript, jsuword pc)
|
||||
{
|
||||
|
|
|
@ -958,9 +958,7 @@ jsdScript::jsdScript (JSDContext *aCx, JSDScript *aScript) : mValid(PR_FALSE),
|
|||
mBaseLineNumber(0),
|
||||
mLineExtent(0),
|
||||
mPPLineMap(0),
|
||||
mFirstValidPC(0),
|
||||
mFirstPC(0),
|
||||
mEndPC(0)
|
||||
mFirstPC(0)
|
||||
{
|
||||
DEBUG_CREATE ("jsdScript", gScriptCount);
|
||||
|
||||
|
@ -974,8 +972,6 @@ jsdScript::jsdScript (JSDContext *aCx, JSDScript *aScript) : mValid(PR_FALSE),
|
|||
mBaseLineNumber = JSD_GetScriptBaseLineNumber(mCx, mScript);
|
||||
mLineExtent = JSD_GetScriptLineExtent(mCx, mScript);
|
||||
mFirstPC = JSD_GetClosestPC(mCx, mScript, 0);
|
||||
mFirstValidPC = JSD_GetFirstValidPC(mCx, mScript);
|
||||
mEndPC = JSD_GetEndPC(mCx, mScript);
|
||||
JSD_UnlockScriptSubsystem(mCx);
|
||||
|
||||
mValid = PR_TRUE;
|
||||
|
@ -1479,22 +1475,6 @@ jsdScript::IsLineExecutable(PRUint32 aLine, PRUint32 aPcmap, PRBool *_rval)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
jsdScript::GetFirstValidPC(PRUint32 *_rval)
|
||||
{
|
||||
ASSERT_VALID_EPHEMERAL;
|
||||
*_rval = PRUint32(mFirstValidPC - mFirstPC);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
jsdScript::GetEndValidPC(PRUint32 *_rval)
|
||||
{
|
||||
ASSERT_VALID_EPHEMERAL;
|
||||
*_rval = PRUint32(mEndPC - mFirstPC);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
jsdScript::SetBreakpoint(PRUint32 aPC)
|
||||
{
|
||||
|
|
|
@ -182,9 +182,7 @@ class jsdScript : public jsdIScript
|
|||
PRUint32 mBaseLineNumber, mLineExtent;
|
||||
PCMapEntry *mPPLineMap;
|
||||
PRUint32 mPCMapSize;
|
||||
jsuword mFirstPC; /* address of first PC in script */
|
||||
jsuword mFirstValidPC; /* address of first valid bkpt PC */
|
||||
jsuword mEndPC; /* address of end of script code */
|
||||
jsuword mFirstPC;
|
||||
};
|
||||
|
||||
PRUint32 jsdScript::LastTag = 0;
|
||||
|
|
|
@ -348,22 +348,6 @@ JSD_GetClosestPC(JSDContext* jsdc, JSDScript* jsdscript, uintN line)
|
|||
return jsd_GetClosestPC(jsdc, jsdscript, line);
|
||||
}
|
||||
|
||||
JSD_PUBLIC_API(jsuword)
|
||||
JSD_GetFirstValidPC(JSDContext* jsdc, JSDScript* jsdscript)
|
||||
{
|
||||
JSD_ASSERT_VALID_CONTEXT(jsdc);
|
||||
JSD_ASSERT_VALID_SCRIPT(jsdscript);
|
||||
return jsd_GetFirstValidPC(jsdc, jsdscript);
|
||||
}
|
||||
|
||||
JSD_PUBLIC_API(jsuword)
|
||||
JSD_GetEndPC(JSDContext* jsdc, JSDScript* jsdscript)
|
||||
{
|
||||
JSD_ASSERT_VALID_CONTEXT(jsdc);
|
||||
JSD_ASSERT_VALID_SCRIPT(jsdscript);
|
||||
return jsd_GetEndPC(jsdc, jsdscript);
|
||||
}
|
||||
|
||||
JSD_PUBLIC_API(uintN)
|
||||
JSD_GetClosestLine(JSDContext* jsdc, JSDScript* jsdscript, jsuword pc)
|
||||
{
|
||||
|
|
|
@ -486,19 +486,6 @@ JSD_GetScriptHook(JSDContext* jsdc, JSD_ScriptHookProc* hook, void** callerdata)
|
|||
extern JSD_PUBLIC_API(jsuword)
|
||||
JSD_GetClosestPC(JSDContext* jsdc, JSDScript* jsdscript, uintN line);
|
||||
|
||||
/*
|
||||
* Get the first 'Program Counter' value where a breakpoint can be set.
|
||||
*/
|
||||
extern JSD_PUBLIC_API(jsuword)
|
||||
JSD_GetFirstValidPC(JSDContext* jsdc, JSDScript* jsdscript);
|
||||
|
||||
/*
|
||||
* Get the 'Program Counter' value just after the last byte of the script.
|
||||
* 0 is returned for invalid scripts.
|
||||
*/
|
||||
extern JSD_PUBLIC_API(jsuword)
|
||||
JSD_GetEndPC(JSDContext* jsdc, JSDScript* jsdscript);
|
||||
|
||||
/*
|
||||
* Get the source line number for a given 'Program Counter' location.
|
||||
* Returns 0 if no source line information is appropriate (or available) for
|
||||
|
|
|
@ -48,7 +48,6 @@ MODULE = jsdebug
|
|||
include $(topsrcdir)/config/rules.mk
|
||||
|
||||
_TEST_FILES = test_bug507448.html \
|
||||
test_bug602003.html \
|
||||
$(NULL)
|
||||
|
||||
libs:: $(_TEST_FILES)
|
||||
|
|
|
@ -1,62 +0,0 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=507448
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 602003</title>
|
||||
<script type="application/javascript" src="/MochiKit/packed.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=507448">Mozilla Bug 507448</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
<script type="application/javascript">
|
||||
|
||||
/** Test for Bug 602003 **/
|
||||
|
||||
// This is somewhat unfortunate: jsd only deals with scripts that have a
|
||||
// nonzero line number, so we can't just createElement a script here.
|
||||
// So break the test up into three <script>s, of which the middle one has our test functions.
|
||||
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
var jsdIDebuggerService = Components.interfaces.jsdIDebuggerService;
|
||||
var jsd = Components.classes['@mozilla.org/js/jsd/debugger-service;1']
|
||||
.getService(jsdIDebuggerService);
|
||||
var jsdOn = jsd.isOn;
|
||||
if (!jsdOn) {
|
||||
jsd.on();
|
||||
ok(jsd.isOn, "JSD should be running.");
|
||||
}
|
||||
</script>
|
||||
<script>
|
||||
function g(a,b) { return a + b }
|
||||
</script>
|
||||
<script>
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
var script = jsd.wrapValue(g).script;
|
||||
|
||||
// Test the script start/end PC APIs.
|
||||
var start = script.getFirstValidPC();
|
||||
var end = script.getEndValidPC();
|
||||
|
||||
// Start PC should be 1 for a function because it starts with JSOP_BEGIN.
|
||||
is(start, 1, "Start PC should be 1");
|
||||
|
||||
// End PC should be something greater than 1, and not huge. Changes
|
||||
// in the bytecode will change this, so we'll just be approximate.
|
||||
ok(1 < end && end < 100, "End PC doesn't seem sane.");
|
||||
|
||||
if (!jsdOn) {
|
||||
jsd.off();
|
||||
ok(!jsd.isOn, "JSD shouldn't be running anymore.");
|
||||
}
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
|
@ -475,9 +475,7 @@ AllFramesIter::operator++()
|
|||
{
|
||||
JS_ASSERT(!done());
|
||||
if (curfp == curcs->getInitialFrame()) {
|
||||
do {
|
||||
curcs = curcs->getPreviousInMemory();
|
||||
} while (curcs && !curcs->inContext());
|
||||
curcs = curcs->getPreviousInMemory();
|
||||
curfp = curcs ? curcs->getCurrentFrame() : NULL;
|
||||
} else {
|
||||
curfp = curfp->prev();
|
||||
|
|
|
@ -2008,7 +2008,7 @@ struct JSContext
|
|||
|
||||
public:
|
||||
friend class js::StackSpace;
|
||||
friend bool js::Interpret(JSContext *, JSStackFrame *, uintN, uintN);
|
||||
friend bool js::Interpret(JSContext *, JSStackFrame *, uintN, JSInterpMode);
|
||||
|
||||
void resetCompartment();
|
||||
|
||||
|
|
|
@ -235,12 +235,6 @@ JS_SetTrap(JSContext *cx, JSScript *script, jsbytecode *pc,
|
|||
return JS_FALSE;
|
||||
}
|
||||
|
||||
if (JSOp(*pc) == JSOP_BEGIN) {
|
||||
JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage,
|
||||
NULL, JSMSG_READ_ONLY, "trap invalid on BEGIN opcode");
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
JS_ASSERT((JSOp) *pc != JSOP_TRAP);
|
||||
junk = NULL;
|
||||
rt = cx->runtime;
|
||||
|
@ -1021,13 +1015,6 @@ JS_LineNumberToPC(JSContext *cx, JSScript *script, uintN lineno)
|
|||
return js_LineNumberToPC(script, lineno);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(jsbytecode *)
|
||||
JS_FirstValidPC(JSContext *cx, JSScript *script)
|
||||
{
|
||||
jsbytecode *pc = script->code;
|
||||
return *pc == JSOP_BEGIN ? pc + JSOP_BEGIN_LENGTH : pc;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(jsbytecode *)
|
||||
JS_EndPC(JSContext *cx, JSScript *script)
|
||||
{
|
||||
|
|
|
@ -159,9 +159,6 @@ JS_PCToLineNumber(JSContext *cx, JSScript *script, jsbytecode *pc);
|
|||
extern JS_PUBLIC_API(jsbytecode *)
|
||||
JS_LineNumberToPC(JSContext *cx, JSScript *script, uintN lineno);
|
||||
|
||||
extern JS_PUBLIC_API(jsbytecode *)
|
||||
JS_FirstValidPC(JSContext *cx, JSScript *script);
|
||||
|
||||
extern JS_PUBLIC_API(jsbytecode *)
|
||||
JS_EndPC(JSContext *cx, JSScript *script);
|
||||
|
||||
|
|
|
@ -3713,15 +3713,10 @@ bad:
|
|||
JSBool
|
||||
js_EmitFunctionScript(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body)
|
||||
{
|
||||
CG_SWITCH_TO_PROLOG(cg);
|
||||
JS_ASSERT(CG_NEXT(cg) == CG_BASE(cg));
|
||||
if (js_Emit1(cx, cg, JSOP_BEGIN) < 0)
|
||||
return false;
|
||||
CG_SWITCH_TO_MAIN(cg);
|
||||
|
||||
if (cg->flags & TCF_FUN_IS_GENERATOR) {
|
||||
/* JSOP_GENERATOR must be the first real instruction. */
|
||||
/* JSOP_GENERATOR must be the first instruction. */
|
||||
CG_SWITCH_TO_PROLOG(cg);
|
||||
JS_ASSERT(CG_NEXT(cg) == CG_BASE(cg));
|
||||
if (js_Emit1(cx, cg, JSOP_GENERATOR) < 0)
|
||||
return false;
|
||||
CG_SWITCH_TO_MAIN(cg);
|
||||
|
|
|
@ -82,7 +82,6 @@
|
|||
#include "jscntxtinlines.h"
|
||||
#include "jsinterpinlines.h"
|
||||
#include "jsobjinlines.h"
|
||||
#include "jsprobes.h"
|
||||
#include "jspropertycacheinlines.h"
|
||||
#include "jsscopeinlines.h"
|
||||
#include "jsscriptinlines.h"
|
||||
|
@ -734,28 +733,13 @@ Invoke(JSContext *cx, const CallArgs &argsRef, uint32 flags)
|
|||
}
|
||||
}
|
||||
|
||||
JSInterpreterHook hook = cx->debugHooks->callHook;
|
||||
void *hookData = NULL;
|
||||
if (JS_UNLIKELY(hook != NULL))
|
||||
hookData = hook(cx, fp, JS_TRUE, 0, cx->debugHooks->callHookData);
|
||||
|
||||
/* Run function until JSOP_STOP, JSOP_RETURN or error. */
|
||||
JSBool ok;
|
||||
{
|
||||
AutoPreserveEnumerators preserve(cx);
|
||||
Probes::enterJSFun(cx, fun);
|
||||
ok = RunScript(cx, script, fp);
|
||||
Probes::exitJSFun(cx, fun);
|
||||
}
|
||||
|
||||
if (JS_UNLIKELY(hookData != NULL)) {
|
||||
hook = cx->debugHooks->callHook;
|
||||
if (hook)
|
||||
hook(cx, fp, JS_FALSE, &ok, hookData);
|
||||
}
|
||||
|
||||
PutActivationObjects(cx, fp);
|
||||
|
||||
args.rval() = fp->returnValue();
|
||||
JS_ASSERT_IF(ok && (flags & JSINVOKE_CONSTRUCT), !args.rval().isPrimitive());
|
||||
|
||||
|
@ -2161,11 +2145,28 @@ IteratorNext(JSContext *cx, JSObject *iterobj, Value *rval)
|
|||
return js_IteratorNext(cx, iterobj, rval);
|
||||
}
|
||||
|
||||
static inline bool
|
||||
ScriptPrologue(JSContext *cx, JSStackFrame *fp)
|
||||
{
|
||||
if (fp->isConstructing()) {
|
||||
JSObject *obj = js_CreateThisForFunction(cx, &fp->callee());
|
||||
if (!obj)
|
||||
return false;
|
||||
fp->functionThis().setObject(*obj);
|
||||
}
|
||||
JSInterpreterHook hook = cx->debugHooks->callHook;
|
||||
if (JS_UNLIKELY(hook != NULL) && !fp->isExecuteFrame())
|
||||
fp->setHookData(hook(cx, fp, JS_TRUE, 0, cx->debugHooks->callHookData));
|
||||
|
||||
Probes::enterJSFun(cx, fp->maybeFun());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
namespace js {
|
||||
|
||||
JS_REQUIRES_STACK JS_NEVER_INLINE bool
|
||||
Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, uintN interpFlags)
|
||||
Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, JSInterpMode interpMode)
|
||||
{
|
||||
#ifdef MOZ_TRACEVIS
|
||||
TraceVisStateObj tvso(cx, S_INTERP);
|
||||
|
@ -2396,7 +2397,7 @@ Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, uintN
|
|||
script->maybeNativeCodeForPC(regs.fp->isConstructing(), regs.pc)) { \
|
||||
JS_ASSERT(!TRACE_RECORDER(cx)); \
|
||||
interpReturnOK = true; \
|
||||
goto stop_recording; \
|
||||
goto leave_on_safe_point; \
|
||||
} \
|
||||
} while (0)
|
||||
#else
|
||||
|
@ -2432,13 +2433,25 @@ Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, uintN
|
|||
/* Check for too deep of a native thread stack. */
|
||||
JS_CHECK_RECURSION(cx, return JS_FALSE);
|
||||
|
||||
MUST_FLOW_THROUGH("exit");
|
||||
++cx->interpLevel;
|
||||
JSFrameRegs regs = *cx->regs;
|
||||
|
||||
/* Repoint cx->regs to a local variable for faster access. */
|
||||
JSFrameRegs *const prevContextRegs = cx->regs;
|
||||
JSFrameRegs regs = *cx->regs;
|
||||
cx->setCurrentRegs(®s);
|
||||
struct InterpExitGuard {
|
||||
JSContext *cx;
|
||||
const JSFrameRegs ®s;
|
||||
JSFrameRegs *prevContextRegs;
|
||||
InterpExitGuard(JSContext *cx, JSFrameRegs ®s)
|
||||
: cx(cx), regs(regs), prevContextRegs(cx->regs) {
|
||||
cx->setCurrentRegs(®s);
|
||||
++cx->interpLevel;
|
||||
}
|
||||
~InterpExitGuard() {
|
||||
--cx->interpLevel;
|
||||
JS_ASSERT(cx->regs == ®s);
|
||||
*prevContextRegs = regs;
|
||||
cx->setCurrentRegs(prevContextRegs);
|
||||
}
|
||||
} interpGuard(cx, regs);
|
||||
|
||||
/* Copy in hot values that change infrequently. */
|
||||
JSRuntime *const rt = cx->runtime;
|
||||
|
@ -2450,7 +2463,7 @@ Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, uintN
|
|||
JS_ASSERT(script->length > 1);
|
||||
|
||||
#if defined(JS_TRACER) && defined(JS_METHODJIT)
|
||||
bool leaveOnSafePoint = !!(interpFlags & JSINTERP_SAFEPOINT);
|
||||
bool leaveOnSafePoint = (interpMode == JSINTERP_SAFEPOINT);
|
||||
# define CLEAR_LEAVE_ON_TRACE_POINT() ((void) (leaveOnSafePoint = false))
|
||||
#else
|
||||
# define CLEAR_LEAVE_ON_TRACE_POINT() ((void) 0)
|
||||
|
@ -2470,7 +2483,7 @@ Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, uintN
|
|||
|
||||
#if JS_HAS_GENERATORS
|
||||
if (JS_UNLIKELY(regs.fp->isGeneratorFrame())) {
|
||||
JS_ASSERT(prevContextRegs == &cx->generatorFor(regs.fp)->regs);
|
||||
JS_ASSERT(interpGuard.prevContextRegs == &cx->generatorFor(regs.fp)->regs);
|
||||
JS_ASSERT((size_t) (regs.pc - script->code) <= script->length);
|
||||
JS_ASSERT((size_t) (regs.sp - regs.fp->base()) <= StackDepth(script));
|
||||
|
||||
|
@ -2489,7 +2502,7 @@ Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, uintN
|
|||
* there should already be a valid recorder. Otherwise...
|
||||
* we cannot reenter the interpreter while recording.
|
||||
*/
|
||||
if (interpFlags & JSINTERP_RECORD) {
|
||||
if (interpMode == JSINTERP_RECORD) {
|
||||
JS_ASSERT(TRACE_RECORDER(cx));
|
||||
ENABLE_INTERRUPTS();
|
||||
} else if (TRACE_RECORDER(cx)) {
|
||||
|
@ -2500,6 +2513,15 @@ Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, uintN
|
|||
atoms = COMMON_ATOMS_START(&rt->atomState);
|
||||
#endif
|
||||
|
||||
/* Don't call the script prologue if executing between Method and Trace JIT. */
|
||||
if (interpMode == JSINTERP_NORMAL) {
|
||||
JS_ASSERT_IF(!regs.fp->isGeneratorFrame(), regs.pc == script->code);
|
||||
if (!ScriptPrologue(cx, regs.fp))
|
||||
goto error;
|
||||
}
|
||||
|
||||
CHECK_INTERRUPT_HANDLER();
|
||||
|
||||
/* State communicated between non-local jumps: */
|
||||
JSBool interpReturnOK;
|
||||
JSAtom *atomNotDefined;
|
||||
|
@ -2585,7 +2607,8 @@ Interpret(JSContext *cx, JSStackFrame *entryFrame, uintN inlineCallCount, uintN
|
|||
AbortableRecordingStatus status = tr->monitorRecording(op);
|
||||
JS_ASSERT_IF(cx->throwing, status == ARECORD_ERROR);
|
||||
|
||||
if (interpFlags & (JSINTERP_RECORD | JSINTERP_SAFEPOINT)) {
|
||||
if (interpMode != JSINTERP_NORMAL) {
|
||||
JS_ASSERT(interpMode == JSINTERP_RECORD || JSINTERP_SAFEPOINT);
|
||||
switch (status) {
|
||||
case ARECORD_IMACRO_ABORTED:
|
||||
case ARECORD_ABORTED:
|
||||
|
@ -2760,27 +2783,11 @@ BEGIN_CASE(JSOP_STOP)
|
|||
inline_return:
|
||||
{
|
||||
JS_ASSERT(!js_IsActiveWithOrBlock(cx, ®s.fp->scopeChain(), 0));
|
||||
if (JS_UNLIKELY(regs.fp->hasHookData())) {
|
||||
if (JSInterpreterHook hook = cx->debugHooks->callHook) {
|
||||
hook(cx, regs.fp, JS_FALSE, &interpReturnOK, regs.fp->hookData());
|
||||
CHECK_INTERRUPT_HANDLER();
|
||||
}
|
||||
}
|
||||
|
||||
PutActivationObjects(cx, regs.fp);
|
||||
|
||||
Probes::exitJSFun(cx, regs.fp->maybeFun());
|
||||
|
||||
/*
|
||||
* If inline-constructing, replace primitive rval with the new object
|
||||
* passed in via |this|, and instrument this constructor invocation.
|
||||
*/
|
||||
if (regs.fp->isConstructing()) {
|
||||
if (regs.fp->returnValue().isPrimitive())
|
||||
regs.fp->setReturnValue(ObjectValue(regs.fp->constructorThis()));
|
||||
JS_RUNTIME_METER(cx->runtime, constructs);
|
||||
}
|
||||
interpReturnOK = ScriptEpilogue(cx, regs.fp, interpReturnOK);
|
||||
CHECK_INTERRUPT_HANDLER();
|
||||
|
||||
/* The JIT inlines ScriptEpilogue. */
|
||||
jit_return:
|
||||
Value *newsp = regs.fp->actualArgs() - 1;
|
||||
newsp[-1] = regs.fp->returnValue();
|
||||
cx->stack().popInlineFrame(cx, regs.fp->prev(), newsp);
|
||||
|
@ -2803,18 +2810,6 @@ BEGIN_CASE(JSOP_STOP)
|
|||
goto error;
|
||||
} else {
|
||||
JS_ASSERT(regs.sp == regs.fp->base());
|
||||
if (regs.fp->isConstructing() && regs.fp->returnValue().isPrimitive())
|
||||
regs.fp->setReturnValue(ObjectValue(regs.fp->constructorThis()));
|
||||
|
||||
#if defined(JS_TRACER) && defined(JS_METHODJIT)
|
||||
/* Hack: re-push rval so either JIT will read it properly. */
|
||||
regs.fp->setBailedAtReturn();
|
||||
if (TRACE_RECORDER(cx)) {
|
||||
AbortRecording(cx, "recording out of Interpret");
|
||||
interpReturnOK = true;
|
||||
goto stop_recording;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
interpReturnOK = true;
|
||||
goto exit;
|
||||
|
@ -4569,41 +4564,6 @@ BEGIN_CASE(JSOP_ENUMELEM)
|
|||
}
|
||||
END_CASE(JSOP_ENUMELEM)
|
||||
|
||||
BEGIN_CASE(JSOP_BEGIN)
|
||||
{
|
||||
if (regs.fp->isConstructing()) {
|
||||
JSObject *obj2 = js_CreateThisForFunction(cx, ®s.fp->callee());
|
||||
if (!obj2)
|
||||
goto error;
|
||||
regs.fp->functionThis().setObject(*obj2);
|
||||
}
|
||||
|
||||
/* Call the debugger hook if present. */
|
||||
if (JSInterpreterHook hook = cx->debugHooks->callHook) {
|
||||
regs.fp->setHookData(hook(cx, regs.fp, JS_TRUE, 0,
|
||||
cx->debugHooks->callHookData));
|
||||
CHECK_INTERRUPT_HANDLER();
|
||||
}
|
||||
|
||||
JS_RUNTIME_METER(rt, inlineCalls);
|
||||
|
||||
Probes::enterJSFun(cx, regs.fp->fun());
|
||||
|
||||
#ifdef JS_METHODJIT
|
||||
/* Try to ensure methods are method JIT'd. */
|
||||
mjit::CompileStatus status = mjit::CanMethodJIT(cx, script, regs.fp);
|
||||
if (status == mjit::Compile_Error)
|
||||
goto error;
|
||||
if (!TRACE_RECORDER(cx) && status == mjit::Compile_Okay) {
|
||||
if (!mjit::JaegerShot(cx))
|
||||
goto error;
|
||||
interpReturnOK = true;
|
||||
goto inline_return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
END_CASE(JSOP_BEGIN)
|
||||
|
||||
{
|
||||
JSFunction *newfun;
|
||||
JSObject *callee;
|
||||
|
@ -4703,12 +4663,31 @@ BEGIN_CASE(JSOP_APPLY)
|
|||
goto error;
|
||||
|
||||
inlineCallCount++;
|
||||
JS_RUNTIME_METER(rt, inlineCalls);
|
||||
|
||||
TRACE_0(EnterFrame);
|
||||
|
||||
CHECK_INTERRUPT_HANDLER();
|
||||
|
||||
#ifdef JS_METHODJIT
|
||||
/* Try to ensure methods are method JIT'd. */
|
||||
mjit::CompileStatus status = mjit::CanMethodJIT(cx, script, regs.fp);
|
||||
if (status == mjit::Compile_Error)
|
||||
goto error;
|
||||
if (!TRACE_RECORDER(cx) && status == mjit::Compile_Okay) {
|
||||
interpReturnOK = mjit::JaegerShot(cx);
|
||||
CHECK_INTERRUPT_HANDLER();
|
||||
goto jit_return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!ScriptPrologue(cx, regs.fp))
|
||||
goto error;
|
||||
|
||||
CHECK_INTERRUPT_HANDLER();
|
||||
|
||||
/* Load first op and dispatch it (safe since JSOP_STOP). */
|
||||
op = (JSOp) *regs.pc;
|
||||
JS_ASSERT(op == JSOP_BEGIN);
|
||||
DO_OP();
|
||||
}
|
||||
|
||||
|
@ -6933,6 +6912,9 @@ END_CASE(JSOP_ARRAYPUSH)
|
|||
goto inline_return;
|
||||
|
||||
exit:
|
||||
interpReturnOK = ScriptEpilogue(cx, regs.fp, interpReturnOK);
|
||||
regs.fp->setFinishedInInterpreter();
|
||||
|
||||
/*
|
||||
* At this point we are inevitably leaving an interpreted function or a
|
||||
* top-level script, and returning to one of:
|
||||
|
@ -6945,20 +6927,15 @@ END_CASE(JSOP_ARRAYPUSH)
|
|||
* frame pc.
|
||||
*/
|
||||
JS_ASSERT(entryFrame == regs.fp);
|
||||
JS_ASSERT(cx->regs == ®s);
|
||||
*prevContextRegs = regs;
|
||||
cx->setCurrentRegs(prevContextRegs);
|
||||
|
||||
#ifdef JS_TRACER
|
||||
JS_ASSERT_IF(interpReturnOK && (interpFlags & JSINTERP_RECORD), !TRACE_RECORDER(cx));
|
||||
JS_ASSERT_IF(interpReturnOK && interpMode == JSINTERP_RECORD, !TRACE_RECORDER(cx));
|
||||
if (TRACE_RECORDER(cx))
|
||||
AbortRecording(cx, "recording out of Interpret");
|
||||
#endif
|
||||
|
||||
JS_ASSERT_IF(!regs.fp->isGeneratorFrame(), !js_IsActiveWithOrBlock(cx, ®s.fp->scopeChain(), 0));
|
||||
|
||||
--cx->interpLevel;
|
||||
|
||||
return interpReturnOK;
|
||||
|
||||
atom_not_defined:
|
||||
|
@ -6971,12 +6948,13 @@ END_CASE(JSOP_ARRAYPUSH)
|
|||
goto error;
|
||||
}
|
||||
|
||||
/*
|
||||
* This path is used when it's guaranteed the method can be finished
|
||||
* inside the JIT.
|
||||
*/
|
||||
#if defined(JS_TRACER) && defined(JS_METHODJIT)
|
||||
stop_recording:
|
||||
leave_on_safe_point:
|
||||
#endif
|
||||
JS_ASSERT(cx->regs == ®s);
|
||||
*prevContextRegs = regs;
|
||||
cx->setCurrentRegs(prevContextRegs);
|
||||
return interpReturnOK;
|
||||
}
|
||||
|
||||
|
|
|
@ -59,10 +59,11 @@ struct JSFrameRegs
|
|||
};
|
||||
|
||||
/* Flags to toggle js::Interpret() execution. */
|
||||
enum JSInterpFlags
|
||||
enum JSInterpMode
|
||||
{
|
||||
JSINTERP_RECORD = 0x1, /* interpreter has been started to record/run traces */
|
||||
JSINTERP_SAFEPOINT = 0x2 /* interpreter should leave on a method JIT safe point */
|
||||
JSINTERP_NORMAL = 0, /* Interpreter is running normally. */
|
||||
JSINTERP_RECORD = 1, /* interpreter has been started to record/run traces */
|
||||
JSINTERP_SAFEPOINT = 2 /* interpreter should leave on a method JIT safe point */
|
||||
};
|
||||
|
||||
/* Flags used in JSStackFrame::flags_ */
|
||||
|
@ -83,7 +84,7 @@ enum JSFrameFlags
|
|||
/* Temporary frame states */
|
||||
JSFRAME_ASSIGNING = 0x100, /* not-JOF_ASSIGNING op is assigning */
|
||||
JSFRAME_YIELDING = 0x200, /* js::Interpret dispatched JSOP_YIELD */
|
||||
JSFRAME_BAILED_AT_RETURN = 0x400, /* bailed at JSOP_RETURN */
|
||||
JSFRAME_FINISHED_IN_INTERPRETER = 0x400, /* set if frame finished in Interpret() */
|
||||
|
||||
/* Concerning function arguments */
|
||||
JSFRAME_OVERRIDE_ARGS = 0x1000, /* overridden arguments local variable */
|
||||
|
@ -173,6 +174,10 @@ struct JSStackFrame
|
|||
return flags_ & JSFRAME_EVAL;
|
||||
}
|
||||
|
||||
bool isExecuteFrame() const {
|
||||
return !!(flags_ & (JSFRAME_GLOBAL | JSFRAME_EVAL));
|
||||
}
|
||||
|
||||
/*
|
||||
* Frame initialization
|
||||
*
|
||||
|
@ -680,12 +685,12 @@ struct JSStackFrame
|
|||
flags_ &= ~JSFRAME_YIELDING;
|
||||
}
|
||||
|
||||
bool isBailedAtReturn() const {
|
||||
return flags_ & JSFRAME_BAILED_AT_RETURN;
|
||||
void setFinishedInInterpreter() {
|
||||
flags_ |= JSFRAME_FINISHED_IN_INTERPRETER;
|
||||
}
|
||||
|
||||
void setBailedAtReturn() {
|
||||
flags_ |= JSFRAME_BAILED_AT_RETURN;
|
||||
bool finishedInInterpreter() const {
|
||||
return !!(flags_ & JSFRAME_FINISHED_IN_INTERPRETER);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -982,7 +987,7 @@ Execute(JSContext *cx, JSObject *chain, JSScript *script,
|
|||
* pointed to by cx->fp until completion or error.
|
||||
*/
|
||||
extern JS_REQUIRES_STACK JS_NEVER_INLINE bool
|
||||
Interpret(JSContext *cx, JSStackFrame *stopFp, uintN inlineCallCount = 0, uintN interpFlags = 0);
|
||||
Interpret(JSContext *cx, JSStackFrame *stopFp, uintN inlineCallCount = 0, JSInterpMode mode = JSINTERP_NORMAL);
|
||||
|
||||
extern JS_REQUIRES_STACK bool
|
||||
RunScript(JSContext *cx, JSScript *script, JSStackFrame *fp);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=4 sw=4 et tw=99:
|
||||
*
|
||||
* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
|
@ -111,7 +112,7 @@ JSStackFrame::resetInvokeCallFrame()
|
|||
JSFRAME_HAS_RVAL |
|
||||
JSFRAME_HAS_SCOPECHAIN |
|
||||
JSFRAME_HAS_ANNOTATION |
|
||||
JSFRAME_BAILED_AT_RETURN)));
|
||||
JSFRAME_FINISHED_IN_INTERPRETER)));
|
||||
flags_ &= JSFRAME_FUNCTION |
|
||||
JSFRAME_OVERFLOW_ARGS |
|
||||
JSFRAME_HAS_PREVPC |
|
||||
|
@ -262,6 +263,11 @@ JSStackFrame::stealFrameAndSlots(js::Value *vp, JSStackFrame *otherfp,
|
|||
if (hasCallObj()) {
|
||||
callObj().setPrivate(this);
|
||||
otherfp->flags_ &= ~JSFRAME_HAS_CALL_OBJ;
|
||||
if (js_IsNamedLambda(fun())) {
|
||||
JSObject *env = callObj().getParent();
|
||||
JS_ASSERT(env->getClass() == &js_DeclEnvClass);
|
||||
env->setPrivate(this);
|
||||
}
|
||||
}
|
||||
if (hasArgsObj()) {
|
||||
argsObj().setPrivate(this);
|
||||
|
@ -673,6 +679,35 @@ ValuePropertyBearer(JSContext *cx, const Value &v, int spindex)
|
|||
return pobj;
|
||||
}
|
||||
|
||||
static inline bool
|
||||
ScriptEpilogue(JSContext *cx, JSStackFrame *fp, JSBool ok)
|
||||
{
|
||||
Probes::exitJSFun(cx, fp->maybeFun());
|
||||
JSInterpreterHook hook = cx->debugHooks->callHook;
|
||||
if (hook && fp->hasHookData() && !fp->isExecuteFrame())
|
||||
hook(cx, fp, JS_FALSE, &ok, fp->hookData());
|
||||
|
||||
/*
|
||||
* An eval frame's parent owns its activation objects. A yielding frame's
|
||||
* activation objects are transferred to the floating frame, stored in the
|
||||
* generator.
|
||||
*/
|
||||
if (fp->isFunctionFrame() && !fp->isEvalFrame() && !fp->isYielding())
|
||||
PutActivationObjects(cx, fp);
|
||||
|
||||
/*
|
||||
* If inline-constructing, replace primitive rval with the new object
|
||||
* passed in via |this|, and instrument this constructor invocation.
|
||||
*/
|
||||
if (fp->isConstructing()) {
|
||||
if (fp->returnValue().isPrimitive())
|
||||
fp->setReturnValue(ObjectValue(fp->constructorThis()));
|
||||
JS_RUNTIME_METER(cx->runtime, constructs);
|
||||
}
|
||||
|
||||
return ok;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif /* jsinterpinlines_h__ */
|
||||
|
|
|
@ -1272,7 +1272,7 @@ SendToGenerator(JSContext *cx, JSGeneratorOp op, JSObject *obj,
|
|||
if (!cx->ensureGeneratorStackSpace())
|
||||
return JS_FALSE;
|
||||
|
||||
JS_ASSERT(gen->state == JSGEN_NEWBORN || gen->state == JSGEN_OPEN);
|
||||
JS_ASSERT(gen->state == JSGEN_NEWBORN || gen->state == JSGEN_OPEN);
|
||||
switch (op) {
|
||||
case JSGENOP_NEXT:
|
||||
case JSGENOP_SEND:
|
||||
|
|
|
@ -623,6 +623,4 @@ OPDEF(JSOP_FORGLOBAL, 246,"forglobal", NULL, 3, 1, 1, 19, JOF_GLOBAL
|
|||
OPDEF(JSOP_BLOCKCHAIN, 247,"blockchain", NULL, 3, 0, 0, 0, JOF_OBJECT)
|
||||
OPDEF(JSOP_NULLBLOCKCHAIN,248,"nullblockchain",NULL, 1, 0, 0, 0, JOF_BYTE)
|
||||
|
||||
OPDEF(JSOP_BEGIN, 249,"begin", NULL, 1, 0, 0, 0, JOF_BYTE|JOF_TMPSLOT)
|
||||
|
||||
/* When adding bytecodes, don't forget to update JSXDR_BYTECODE_VERSION. */
|
||||
/* When changing bytecodes, don't forget to update JSXDR_BYTECODE_VERSION. */
|
||||
|
|
|
@ -308,7 +308,7 @@ GetAtomFromBytecode(JSContext *cx, jsbytecode *pc, JSOp op, const JSCodeSpec &cs
|
|||
|
||||
// The method JIT's implementation of instanceof contains an internal lookup
|
||||
// of the prototype property.
|
||||
if (op == JSOP_INSTANCEOF || op == JSOP_BEGIN)
|
||||
if (op == JSOP_INSTANCEOF)
|
||||
return cx->runtime->atomState.classPrototypeAtom;
|
||||
|
||||
ptrdiff_t pcoff = (JOF_TYPE(cs.format) == JOF_SLOTATOM) ? SLOTNO_LEN : 0;
|
||||
|
|
|
@ -1097,7 +1097,8 @@ JSScript::NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg)
|
|||
mainLength = CG_OFFSET(cg);
|
||||
prologLength = CG_PROLOG_OFFSET(cg);
|
||||
|
||||
if (prologLength + mainLength <= 3) {
|
||||
if (prologLength + mainLength <= 3 &&
|
||||
!(cg->flags & TCF_IN_FUNCTION)) {
|
||||
/*
|
||||
* Check very short scripts to see whether they are "empty" and return
|
||||
* the const empty-script singleton if so.
|
||||
|
|
|
@ -70,6 +70,10 @@ JSScript::getRegExp(size_t index)
|
|||
inline bool
|
||||
JSScript::isEmpty() const
|
||||
{
|
||||
return (this == emptyScript());
|
||||
|
||||
// See bug 603044 comment #21.
|
||||
#if 0
|
||||
if (this == emptyScript())
|
||||
return true;
|
||||
|
||||
|
@ -82,6 +86,7 @@ JSScript::isEmpty() const
|
|||
return true;
|
||||
}
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif /* jsscriptinlines_h___ */
|
||||
|
|
|
@ -10497,6 +10497,13 @@ TraceRecorder::record_EnterFrame()
|
|||
RETURN_STOP_A("recursion started inlining");
|
||||
}
|
||||
|
||||
if (fp->isConstructing()) {
|
||||
LIns* args[] = { callee_ins, INS_CONSTPTR(&js_ObjectClass), cx_ins };
|
||||
LIns* tv_ins = lir->insCall(&js_CreateThisFromTrace_ci, args);
|
||||
guard(false, lir->insEqP_0(tv_ins), OOM_EXIT);
|
||||
set(&fp->thisValue(), tv_ins);
|
||||
}
|
||||
|
||||
return ARECORD_CONTINUE;
|
||||
}
|
||||
|
||||
|
@ -11199,20 +11206,6 @@ TraceRecorder::emitNativePropertyOp(const Shape* shape, LIns* obj_ins,
|
|||
guard(true, lir->insEqI_0(status_ins), STATUS_EXIT);
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK AbortableRecordingStatus
|
||||
TraceRecorder::record_JSOP_BEGIN()
|
||||
{
|
||||
JSStackFrame* fp = cx->fp();
|
||||
if (fp->isConstructing()) {
|
||||
LIns* callee_ins = get(&cx->fp()->calleeValue());
|
||||
LIns* args[] = { callee_ins, INS_CONSTPTR(&js_ObjectClass), cx_ins };
|
||||
LIns* tv_ins = lir->insCall(&js_CreateThisFromTrace_ci, args);
|
||||
guard(false, lir->insEqP_0(tv_ins), OOM_EXIT);
|
||||
set(&fp->thisValue(), tv_ins);
|
||||
}
|
||||
return ARECORD_CONTINUE;
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK RecordingStatus
|
||||
TraceRecorder::emitNativeCall(JSSpecializedNative* sn, uintN argc, LIns* args[], bool rooted)
|
||||
{
|
||||
|
|
|
@ -205,7 +205,7 @@ JS_XDRFindClassById(JSXDRState *xdr, uint32 id);
|
|||
* before deserialization of bytecode. If the saved version does not match
|
||||
* the current version, abort deserialization and invalidate the file.
|
||||
*/
|
||||
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 73)
|
||||
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 74)
|
||||
|
||||
/*
|
||||
* Library-private functions.
|
||||
|
|
|
@ -218,6 +218,10 @@ mjit::TryCompile(JSContext *cx, JSStackFrame *fp)
|
|||
{
|
||||
JS_ASSERT(cx->fp() == fp);
|
||||
|
||||
// Ensure that constructors have at least one slot.
|
||||
if (fp->isConstructing() && !fp->script()->nslots)
|
||||
fp->script()->nslots++;
|
||||
|
||||
Compiler cc(cx, fp);
|
||||
|
||||
return cc.compile();
|
||||
|
@ -316,6 +320,12 @@ mjit::Compiler::generatePrologue()
|
|||
}
|
||||
}
|
||||
|
||||
if (isConstructing)
|
||||
constructThis();
|
||||
|
||||
if (debugMode)
|
||||
stubCall(stubs::EnterScript);
|
||||
|
||||
return Compile_Okay;
|
||||
}
|
||||
|
||||
|
@ -1394,17 +1404,17 @@ mjit::Compiler::generateMethod()
|
|||
END_CASE(JSOP_LOCALDEC)
|
||||
|
||||
BEGIN_CASE(JSOP_BINDNAME)
|
||||
jsop_bindname(fullAtomIndex(PC));
|
||||
jsop_bindname(fullAtomIndex(PC), true);
|
||||
END_CASE(JSOP_BINDNAME)
|
||||
|
||||
BEGIN_CASE(JSOP_SETPROP)
|
||||
if (!jsop_setprop(script->getAtom(fullAtomIndex(PC))))
|
||||
if (!jsop_setprop(script->getAtom(fullAtomIndex(PC)), true))
|
||||
return Compile_Error;
|
||||
END_CASE(JSOP_SETPROP)
|
||||
|
||||
BEGIN_CASE(JSOP_SETNAME)
|
||||
BEGIN_CASE(JSOP_SETMETHOD)
|
||||
if (!jsop_setprop(script->getAtom(fullAtomIndex(PC))))
|
||||
if (!jsop_setprop(script->getAtom(fullAtomIndex(PC)), true))
|
||||
return Compile_Error;
|
||||
END_CASE(JSOP_SETNAME)
|
||||
|
||||
|
@ -1757,13 +1767,6 @@ mjit::Compiler::generateMethod()
|
|||
break;
|
||||
END_CASE(JSOP_GLOBALINC)
|
||||
|
||||
BEGIN_CASE(JSOP_BEGIN)
|
||||
if (isConstructing) {
|
||||
if (!constructThis())
|
||||
return Compile_Error;
|
||||
}
|
||||
END_CASE(JSOP_BEGIN)
|
||||
|
||||
default:
|
||||
/* Sorry, this opcode isn't implemented yet. */
|
||||
#ifdef JS_METHODJIT_SPEW
|
||||
|
@ -1987,6 +1990,11 @@ mjit::Compiler::emitReturn(FrameEntry *fe)
|
|||
/* Only the top of the stack can be returned. */
|
||||
JS_ASSERT_IF(fe, fe == frame.peek(-1));
|
||||
|
||||
if (debugMode) {
|
||||
prepareStubCall(Uses(0));
|
||||
stubCall(stubs::LeaveScript);
|
||||
}
|
||||
|
||||
/*
|
||||
* If there's a function object, deal with the fact that it can escape.
|
||||
* Note that after we've placed the call object, all tracked state can
|
||||
|
@ -2419,20 +2427,28 @@ mjit::Compiler::emitStubCmpOp(BoolStub stub, jsbytecode *target, JSOp fused)
|
|||
}
|
||||
|
||||
void
|
||||
mjit::Compiler::jsop_setprop_slow(JSAtom *atom)
|
||||
mjit::Compiler::jsop_setprop_slow(JSAtom *atom, bool usePropCache)
|
||||
{
|
||||
prepareStubCall(Uses(2));
|
||||
masm.move(ImmPtr(atom), Registers::ArgReg1);
|
||||
stubCall(STRICT_VARIANT(stubs::SetName));
|
||||
if (usePropCache)
|
||||
stubCall(STRICT_VARIANT(stubs::SetName));
|
||||
else
|
||||
stubCall(STRICT_VARIANT(stubs::SetPropNoCache));
|
||||
JS_STATIC_ASSERT(JSOP_SETNAME_LENGTH == JSOP_SETPROP_LENGTH);
|
||||
frame.shimmy(1);
|
||||
}
|
||||
|
||||
void
|
||||
mjit::Compiler::jsop_getprop_slow()
|
||||
mjit::Compiler::jsop_getprop_slow(JSAtom *atom, bool usePropCache)
|
||||
{
|
||||
prepareStubCall(Uses(1));
|
||||
stubCall(stubs::GetProp);
|
||||
if (usePropCache) {
|
||||
stubCall(stubs::GetProp);
|
||||
} else {
|
||||
masm.move(ImmPtr(atom), Registers::ArgReg1);
|
||||
stubCall(stubs::GetPropNoCache);
|
||||
}
|
||||
frame.pop();
|
||||
frame.pushSynced();
|
||||
}
|
||||
|
@ -2498,7 +2514,7 @@ mjit::Compiler::passPICAddress(PICGenInfo &pic)
|
|||
}
|
||||
|
||||
bool
|
||||
mjit::Compiler::jsop_getprop(JSAtom *atom, bool doTypeCheck)
|
||||
mjit::Compiler::jsop_getprop(JSAtom *atom, bool doTypeCheck, bool usePropCache)
|
||||
{
|
||||
FrameEntry *top = frame.peek(-1);
|
||||
|
||||
|
@ -2506,7 +2522,7 @@ mjit::Compiler::jsop_getprop(JSAtom *atom, bool doTypeCheck)
|
|||
if (top->isTypeKnown() && top->getKnownType() != JSVAL_TYPE_OBJECT) {
|
||||
JS_ASSERT_IF(atom == cx->runtime->atomState.lengthAtom,
|
||||
top->getKnownType() != JSVAL_TYPE_STRING);
|
||||
jsop_getprop_slow();
|
||||
jsop_getprop_slow(atom, usePropCache);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -2522,7 +2538,7 @@ mjit::Compiler::jsop_getprop(JSAtom *atom, bool doTypeCheck)
|
|||
shapeReg = frame.allocReg();
|
||||
}
|
||||
|
||||
PICGenInfo pic(ic::PICInfo::GET);
|
||||
PICGenInfo pic(ic::PICInfo::GET, usePropCache);
|
||||
|
||||
/* Guard that the type is an object. */
|
||||
Jump typeCheck;
|
||||
|
@ -2631,7 +2647,7 @@ bool
|
|||
mjit::Compiler::jsop_getelem_pic(FrameEntry *obj, FrameEntry *id, RegisterID objReg,
|
||||
RegisterID idReg, RegisterID shapeReg)
|
||||
{
|
||||
PICGenInfo pic(ic::PICInfo::GETELEM);
|
||||
PICGenInfo pic(ic::PICInfo::GETELEM, true);
|
||||
|
||||
pic.objRemat = frame.dataRematInfo(obj);
|
||||
pic.idRemat = frame.dataRematInfo(id);
|
||||
|
@ -2746,7 +2762,7 @@ mjit::Compiler::jsop_callprop_generic(JSAtom *atom)
|
|||
RegisterID objReg = frame.copyDataIntoReg(top);
|
||||
RegisterID shapeReg = frame.allocReg();
|
||||
|
||||
PICGenInfo pic(ic::PICInfo::CALL);
|
||||
PICGenInfo pic(ic::PICInfo::CALL, true);
|
||||
|
||||
/* Guard that the type is an object. */
|
||||
pic.typeReg = frame.copyTypeIntoReg(top);
|
||||
|
@ -2916,7 +2932,7 @@ mjit::Compiler::jsop_callprop_obj(JSAtom *atom)
|
|||
{
|
||||
FrameEntry *top = frame.peek(-1);
|
||||
|
||||
PICGenInfo pic(ic::PICInfo::CALL);
|
||||
PICGenInfo pic(ic::PICInfo::CALL, true);
|
||||
|
||||
JS_ASSERT(top->isTypeKnown());
|
||||
JS_ASSERT(top->getKnownType() == JSVAL_TYPE_OBJECT);
|
||||
|
@ -3034,20 +3050,20 @@ mjit::Compiler::jsop_callprop(JSAtom *atom)
|
|||
}
|
||||
|
||||
bool
|
||||
mjit::Compiler::jsop_setprop(JSAtom *atom)
|
||||
mjit::Compiler::jsop_setprop(JSAtom *atom, bool usePropCache)
|
||||
{
|
||||
FrameEntry *lhs = frame.peek(-2);
|
||||
FrameEntry *rhs = frame.peek(-1);
|
||||
|
||||
/* If the incoming type will never PIC, take slow path. */
|
||||
if (lhs->isTypeKnown() && lhs->getKnownType() != JSVAL_TYPE_OBJECT) {
|
||||
jsop_setprop_slow(atom);
|
||||
jsop_setprop_slow(atom, usePropCache);
|
||||
return true;
|
||||
}
|
||||
|
||||
JSOp op = JSOp(*PC);
|
||||
|
||||
PICGenInfo pic(op == JSOP_SETMETHOD ? ic::PICInfo::SETMETHOD : ic::PICInfo::SET);
|
||||
PICGenInfo pic(op == JSOP_SETMETHOD ? ic::PICInfo::SETMETHOD : ic::PICInfo::SET, usePropCache);
|
||||
pic.atom = atom;
|
||||
|
||||
/* Guard that the type is an object. */
|
||||
|
@ -3063,19 +3079,11 @@ mjit::Compiler::jsop_setprop(JSAtom *atom)
|
|||
pic.typeCheck = stubcc.linkExit(j, Uses(2));
|
||||
stubcc.leave();
|
||||
|
||||
/*
|
||||
* This gets called from PROPINC/PROPDEC which aren't compatible with
|
||||
* the normal SETNAME property cache logic.
|
||||
*/
|
||||
JSOp op = JSOp(*PC);
|
||||
stubcc.masm.move(ImmPtr(atom), Registers::ArgReg1);
|
||||
if (op == JSOP_SETNAME || op == JSOP_SETPROP || op == JSOP_SETGNAME || op ==
|
||||
JSOP_SETMETHOD) {
|
||||
if (usePropCache)
|
||||
stubcc.call(STRICT_VARIANT(stubs::SetName));
|
||||
} else {
|
||||
else
|
||||
stubcc.call(STRICT_VARIANT(stubs::SetPropNoCache));
|
||||
}
|
||||
|
||||
typeCheck = stubcc.masm.jump();
|
||||
pic.hasTypeCheck = true;
|
||||
} else {
|
||||
|
@ -3180,7 +3188,7 @@ mjit::Compiler::jsop_setprop(JSAtom *atom)
|
|||
void
|
||||
mjit::Compiler::jsop_name(JSAtom *atom)
|
||||
{
|
||||
PICGenInfo pic(ic::PICInfo::NAME);
|
||||
PICGenInfo pic(ic::PICInfo::NAME, true);
|
||||
|
||||
pic.shapeReg = frame.allocReg();
|
||||
pic.objReg = frame.allocReg();
|
||||
|
@ -3212,7 +3220,7 @@ mjit::Compiler::jsop_name(JSAtom *atom)
|
|||
bool
|
||||
mjit::Compiler::jsop_xname(JSAtom *atom)
|
||||
{
|
||||
PICGenInfo pic(ic::PICInfo::XNAME);
|
||||
PICGenInfo pic(ic::PICInfo::XNAME, true);
|
||||
|
||||
FrameEntry *fe = frame.peek(-1);
|
||||
if (fe->isNotType(JSVAL_TYPE_OBJECT)) {
|
||||
|
@ -3254,9 +3262,9 @@ mjit::Compiler::jsop_xname(JSAtom *atom)
|
|||
}
|
||||
|
||||
void
|
||||
mjit::Compiler::jsop_bindname(uint32 index)
|
||||
mjit::Compiler::jsop_bindname(uint32 index, bool usePropCache)
|
||||
{
|
||||
PICGenInfo pic(ic::PICInfo::BIND);
|
||||
PICGenInfo pic(ic::PICInfo::BIND, usePropCache);
|
||||
|
||||
pic.shapeReg = frame.allocReg();
|
||||
pic.objReg = frame.allocReg();
|
||||
|
@ -3317,9 +3325,9 @@ mjit::Compiler::jsop_xname(JSAtom *atom)
|
|||
}
|
||||
|
||||
bool
|
||||
mjit::Compiler::jsop_getprop(JSAtom *atom, bool typecheck)
|
||||
mjit::Compiler::jsop_getprop(JSAtom *atom, bool typecheck, bool usePropCache)
|
||||
{
|
||||
jsop_getprop_slow();
|
||||
jsop_getprop_slow(atom, usePropCache);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -3330,14 +3338,14 @@ mjit::Compiler::jsop_callprop(JSAtom *atom)
|
|||
}
|
||||
|
||||
bool
|
||||
mjit::Compiler::jsop_setprop(JSAtom *atom)
|
||||
mjit::Compiler::jsop_setprop(JSAtom *atom, bool usePropCache)
|
||||
{
|
||||
jsop_setprop_slow(atom);
|
||||
jsop_setprop_slow(atom, usePropCache);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
mjit::Compiler::jsop_bindname(uint32 index)
|
||||
mjit::Compiler::jsop_bindname(uint32 index, bool usePropCache)
|
||||
{
|
||||
RegisterID reg = frame.allocReg();
|
||||
Address scopeChain(JSFrameReg, JSStackFrame::offsetOfScopeChain());
|
||||
|
@ -3349,7 +3357,12 @@ mjit::Compiler::jsop_bindname(uint32 index)
|
|||
|
||||
stubcc.linkExit(j, Uses(0));
|
||||
stubcc.leave();
|
||||
stubcc.call(stubs::BindName);
|
||||
if (usePropCache) {
|
||||
stubcc.call(stubs::BindName);
|
||||
} else {
|
||||
masm.move(ImmPtr(script->getAtom(index)), Registers::ArgReg1);
|
||||
stubcc.call(stubs::BindNameNoCache);
|
||||
}
|
||||
|
||||
frame.pushTypedPayload(JSVAL_TYPE_OBJECT, reg);
|
||||
|
||||
|
@ -3492,7 +3505,7 @@ mjit::Compiler::jsop_nameinc(JSOp op, VoidStubAtom stub, uint32 index)
|
|||
jsop_binary(JSOP_SUB, stubs::Sub);
|
||||
// N+1
|
||||
|
||||
jsop_bindname(index);
|
||||
jsop_bindname(index, false);
|
||||
// V+1 OBJ
|
||||
|
||||
frame.dup2();
|
||||
|
@ -3504,7 +3517,7 @@ mjit::Compiler::jsop_nameinc(JSOp op, VoidStubAtom stub, uint32 index)
|
|||
frame.shift(-1);
|
||||
// OBJ V+1
|
||||
|
||||
if (!jsop_setprop(atom))
|
||||
if (!jsop_setprop(atom, false))
|
||||
return false;
|
||||
// V+1
|
||||
|
||||
|
@ -3528,7 +3541,7 @@ mjit::Compiler::jsop_nameinc(JSOp op, VoidStubAtom stub, uint32 index)
|
|||
jsop_binary(JSOP_ADD, stubs::Add);
|
||||
// N N+1
|
||||
|
||||
jsop_bindname(index);
|
||||
jsop_bindname(index, false);
|
||||
// N N+1 OBJ
|
||||
|
||||
frame.dup2();
|
||||
|
@ -3540,7 +3553,7 @@ mjit::Compiler::jsop_nameinc(JSOp op, VoidStubAtom stub, uint32 index)
|
|||
frame.shift(-1);
|
||||
// N OBJ N+1
|
||||
|
||||
if (!jsop_setprop(atom))
|
||||
if (!jsop_setprop(atom, false))
|
||||
return false;
|
||||
// N N+1
|
||||
|
||||
|
@ -3589,7 +3602,7 @@ mjit::Compiler::jsop_propinc(JSOp op, VoidStubAtom stub, uint32 index)
|
|||
jsop_binary(JSOP_SUB, stubs::Sub);
|
||||
// OBJ V+1
|
||||
|
||||
if (!jsop_setprop(atom))
|
||||
if (!jsop_setprop(atom, false))
|
||||
return false;
|
||||
// V+1
|
||||
|
||||
|
@ -3623,7 +3636,7 @@ mjit::Compiler::jsop_propinc(JSOp op, VoidStubAtom stub, uint32 index)
|
|||
frame.dupAt(-2);
|
||||
// OBJ N N+1 OBJ N+1
|
||||
|
||||
if (!jsop_setprop(atom))
|
||||
if (!jsop_setprop(atom, false))
|
||||
return false;
|
||||
// OBJ N N+1 N+1
|
||||
|
||||
|
@ -4424,7 +4437,7 @@ mjit::Compiler::constructThis()
|
|||
frame.pushTypedPayload(JSVAL_TYPE_OBJECT, calleeReg);
|
||||
|
||||
// Get callee.prototype.
|
||||
if (!jsop_getprop(cx->runtime->atomState.classPrototypeAtom))
|
||||
if (!jsop_getprop(cx->runtime->atomState.classPrototypeAtom, false))
|
||||
return false;
|
||||
|
||||
// Reach into the proto Value and grab a register for its data.
|
||||
|
|
|
@ -151,7 +151,8 @@ class Compiler : public BaseCompiler
|
|||
|
||||
#if defined JS_POLYIC
|
||||
struct PICGenInfo {
|
||||
PICGenInfo(ic::PICInfo::Kind kind) : kind(kind)
|
||||
PICGenInfo(ic::PICInfo::Kind kind, bool usePropCache)
|
||||
: kind(kind), usePropCache(usePropCache)
|
||||
{ }
|
||||
ic::PICInfo::Kind kind;
|
||||
Label fastPathStart;
|
||||
|
@ -163,6 +164,7 @@ class Compiler : public BaseCompiler
|
|||
RegisterID objReg;
|
||||
RegisterID idReg;
|
||||
RegisterID typeReg;
|
||||
bool usePropCache;
|
||||
Label shapeGuard;
|
||||
JSAtom *atom;
|
||||
StateRemat objRemat;
|
||||
|
@ -179,6 +181,7 @@ class Compiler : public BaseCompiler
|
|||
pi.shapeReg = shapeReg;
|
||||
pi.objReg = objReg;
|
||||
pi.atom = atom;
|
||||
pi.usePropCache = usePropCache;
|
||||
if (kind == ic::PICInfo::SET) {
|
||||
pi.u.vr = vr;
|
||||
} else if (kind != ic::PICInfo::NAME) {
|
||||
|
@ -315,10 +318,10 @@ class Compiler : public BaseCompiler
|
|||
void jsop_setelem_slow();
|
||||
void jsop_getelem_slow();
|
||||
void jsop_unbrand();
|
||||
bool jsop_getprop(JSAtom *atom, bool typeCheck = true);
|
||||
bool jsop_getprop(JSAtom *atom, bool typeCheck = true, bool usePropCache = true);
|
||||
bool jsop_length();
|
||||
bool jsop_setprop(JSAtom *atom);
|
||||
void jsop_setprop_slow(JSAtom *atom);
|
||||
bool jsop_setprop(JSAtom *atom, bool usePropCache = true);
|
||||
void jsop_setprop_slow(JSAtom *atom, bool usePropCache = true);
|
||||
bool jsop_callprop_slow(JSAtom *atom);
|
||||
bool jsop_callprop(JSAtom *atom);
|
||||
bool jsop_callprop_obj(JSAtom *atom);
|
||||
|
|
|
@ -73,9 +73,6 @@ using namespace js;
|
|||
using namespace js::mjit;
|
||||
using namespace JSC;
|
||||
|
||||
static bool
|
||||
InlineReturn(VMFrame &f, JSBool ok, JSBool popFrame = JS_TRUE);
|
||||
|
||||
static jsbytecode *
|
||||
FindExceptionHandler(JSContext *cx)
|
||||
{
|
||||
|
@ -177,8 +174,8 @@ top:
|
|||
* either because of a call into an un-JITable script, or because the call is
|
||||
* throwing an exception.
|
||||
*/
|
||||
static bool
|
||||
InlineReturn(VMFrame &f, JSBool ok, JSBool popFrame)
|
||||
static void
|
||||
InlineReturn(VMFrame &f)
|
||||
{
|
||||
JSContext *cx = f.cx;
|
||||
JSStackFrame *fp = f.regs.fp;
|
||||
|
@ -187,36 +184,9 @@ InlineReturn(VMFrame &f, JSBool ok, JSBool popFrame)
|
|||
|
||||
JS_ASSERT(!js_IsActiveWithOrBlock(cx, &fp->scopeChain(), 0));
|
||||
|
||||
// Marker for debug support.
|
||||
if (JS_UNLIKELY(fp->hasHookData())) {
|
||||
JSInterpreterHook hook;
|
||||
JSBool status;
|
||||
|
||||
hook = cx->debugHooks->callHook;
|
||||
if (hook) {
|
||||
/*
|
||||
* Do not pass &ok directly as exposing the address inhibits
|
||||
* optimizations and uninitialised warnings.
|
||||
*/
|
||||
status = ok;
|
||||
hook(cx, fp, JS_FALSE, &status, fp->hookData());
|
||||
ok = (status == JS_TRUE);
|
||||
// CHECK_INTERRUPT_HANDLER();
|
||||
}
|
||||
}
|
||||
|
||||
PutActivationObjects(cx, fp);
|
||||
|
||||
if (fp->isConstructing() && fp->returnValue().isPrimitive())
|
||||
fp->setReturnValue(fp->thisValue());
|
||||
|
||||
if (popFrame) {
|
||||
Value *newsp = fp->actualArgs() - 1;
|
||||
newsp[-1] = fp->returnValue();
|
||||
cx->stack().popInlineFrame(cx, fp->prev(), newsp);
|
||||
}
|
||||
|
||||
return ok;
|
||||
Value *newsp = fp->actualArgs() - 1;
|
||||
newsp[-1] = fp->returnValue();
|
||||
cx->stack().popInlineFrame(cx, fp->prev(), newsp);
|
||||
}
|
||||
|
||||
void JS_FASTCALL
|
||||
|
@ -372,7 +342,7 @@ stubs::CompileFunction(VMFrame &f, uint32 nactual)
|
|||
|
||||
/* Function did not compile... interpret it. */
|
||||
JSBool ok = Interpret(cx, fp);
|
||||
InlineReturn(f, ok);
|
||||
InlineReturn(f);
|
||||
|
||||
if (!ok)
|
||||
THROWV(NULL);
|
||||
|
@ -384,7 +354,6 @@ static inline bool
|
|||
UncachedInlineCall(VMFrame &f, uint32 flags, void **pret, uint32 argc)
|
||||
{
|
||||
JSContext *cx = f.cx;
|
||||
JSStackFrame *fp = f.fp();
|
||||
Value *vp = f.regs.sp - (argc + 2);
|
||||
JSObject &callee = vp->toObject();
|
||||
JSFunction *newfun = callee.getFunctionPrivate();
|
||||
|
@ -412,17 +381,11 @@ UncachedInlineCall(VMFrame &f, uint32 flags, void **pret, uint32 argc)
|
|||
if (newfun->isHeavyweight() && !js_GetCallObject(cx, newfp))
|
||||
return false;
|
||||
|
||||
/* Marker for debug support. */
|
||||
if (JSInterpreterHook hook = cx->debugHooks->callHook) {
|
||||
newfp->setHookData(hook(cx, fp, JS_TRUE, 0,
|
||||
cx->debugHooks->callHookData));
|
||||
}
|
||||
|
||||
/* Try to compile if not already compiled. */
|
||||
if (newscript->getJITStatus(newfp->isConstructing()) == JITScript_None) {
|
||||
if (mjit::TryCompile(cx, newfp) == Compile_Error) {
|
||||
/* A runtime exception was thrown, get out. */
|
||||
InlineReturn(f, JS_FALSE);
|
||||
InlineReturn(f);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -435,7 +398,7 @@ UncachedInlineCall(VMFrame &f, uint32 flags, void **pret, uint32 argc)
|
|||
|
||||
/* Otherwise, run newscript in the interpreter. */
|
||||
bool ok = !!Interpret(cx, cx->fp());
|
||||
InlineReturn(f, JS_TRUE);
|
||||
InlineReturn(f);
|
||||
|
||||
*pret = NULL;
|
||||
return ok;
|
||||
|
@ -574,11 +537,18 @@ js_InternalThrow(VMFrame &f)
|
|||
// JS function.
|
||||
bool lastFrame = (f.entryFp == f.fp());
|
||||
js_UnwindScope(cx, 0, cx->throwing);
|
||||
|
||||
// For consistency with Interpret(), always run the script epilogue.
|
||||
// This simplifies interactions with RunTracer(), since it can assume
|
||||
// no matter how a function exited (error or not), that the epilogue
|
||||
// does not need to be run.
|
||||
ScriptEpilogue(f.cx, f.fp(), false);
|
||||
|
||||
if (lastFrame)
|
||||
break;
|
||||
|
||||
JS_ASSERT(f.regs.sp == cx->regs->sp);
|
||||
InlineReturn(f, JS_FALSE);
|
||||
InlineReturn(f);
|
||||
}
|
||||
|
||||
JS_ASSERT(f.regs.sp == cx->regs->sp);
|
||||
|
@ -611,21 +581,44 @@ stubs::CreateThis(VMFrame &f, JSObject *proto)
|
|||
fp->formalArgs()[-1].setObject(*obj);
|
||||
}
|
||||
|
||||
static inline void
|
||||
AdvanceReturnPC(JSContext *cx)
|
||||
void JS_FASTCALL
|
||||
stubs::EnterScript(VMFrame &f)
|
||||
{
|
||||
/* Simulate an inline_return by advancing the pc. */
|
||||
JS_ASSERT(*cx->regs->pc == JSOP_CALL ||
|
||||
*cx->regs->pc == JSOP_NEW ||
|
||||
*cx->regs->pc == JSOP_EVAL ||
|
||||
*cx->regs->pc == JSOP_APPLY);
|
||||
cx->regs->pc += JSOP_CALL_LENGTH;
|
||||
JSStackFrame *fp = f.fp();
|
||||
JSContext *cx = f.cx;
|
||||
JSInterpreterHook hook = cx->debugHooks->callHook;
|
||||
if (JS_UNLIKELY(hook != NULL) && !fp->isExecuteFrame()) {
|
||||
fp->setHookData(hook(cx, fp, JS_TRUE, 0, cx->debugHooks->callHookData));
|
||||
}
|
||||
|
||||
Probes::enterJSFun(cx, fp->maybeFun());
|
||||
}
|
||||
|
||||
void JS_FASTCALL
|
||||
stubs::LeaveScript(VMFrame &f)
|
||||
{
|
||||
JSStackFrame *fp = f.fp();
|
||||
JSContext *cx = f.cx;
|
||||
Probes::exitJSFun(cx, fp->maybeFun());
|
||||
JSInterpreterHook hook = cx->debugHooks->callHook;
|
||||
|
||||
if (hook && fp->hasHookData() && !fp->isExecuteFrame()) {
|
||||
JSBool ok = JS_TRUE;
|
||||
hook(cx, fp, JS_FALSE, &ok, fp->hookData());
|
||||
if (!ok)
|
||||
THROW();
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef JS_TRACER
|
||||
|
||||
/*
|
||||
* Called when an error is in progress and the topmost frame could not handle
|
||||
* it. This will unwind to a given frame, or find and align to an exception
|
||||
* handler in the process.
|
||||
*/
|
||||
static inline bool
|
||||
HandleErrorInExcessFrames(VMFrame &f, JSStackFrame *stopFp)
|
||||
HandleErrorInExcessFrame(VMFrame &f, JSStackFrame *stopFp, bool searchedTopmostFrame = true)
|
||||
{
|
||||
JSContext *cx = f.cx;
|
||||
|
||||
|
@ -633,14 +626,19 @@ HandleErrorInExcessFrames(VMFrame &f, JSStackFrame *stopFp)
|
|||
* Callers of this called either Interpret() or JaegerShot(), which would
|
||||
* have searched for exception handlers already. If we see stopFp, just
|
||||
* return false. Otherwise, pop the frame, since it's guaranteed useless.
|
||||
*
|
||||
* Note that this also guarantees ScriptEpilogue() has been called.
|
||||
*/
|
||||
JSStackFrame *fp = cx->fp();
|
||||
if (fp == stopFp)
|
||||
return false;
|
||||
if (searchedTopmostFrame) {
|
||||
if (fp == stopFp)
|
||||
return false;
|
||||
|
||||
bool returnOK = InlineReturn(f, false);
|
||||
InlineReturn(f);
|
||||
}
|
||||
|
||||
/* Remove the bottom frame. */
|
||||
bool returnOK = false;
|
||||
for (;;) {
|
||||
fp = cx->fp();
|
||||
|
||||
|
@ -667,7 +665,8 @@ HandleErrorInExcessFrames(VMFrame &f, JSStackFrame *stopFp)
|
|||
|
||||
/* Unwind and return. */
|
||||
returnOK &= bool(js_UnwindScope(cx, 0, returnOK || cx->throwing));
|
||||
returnOK = InlineReturn(f, returnOK);
|
||||
returnOK = ScriptEpilogue(cx, fp, returnOK);
|
||||
InlineReturn(f);
|
||||
}
|
||||
|
||||
JS_ASSERT(&f.regs == cx->regs);
|
||||
|
@ -676,6 +675,7 @@ HandleErrorInExcessFrames(VMFrame &f, JSStackFrame *stopFp)
|
|||
return returnOK;
|
||||
}
|
||||
|
||||
/* Returns whether the current PC has method JIT'd code. */
|
||||
static inline void *
|
||||
AtSafePoint(JSContext *cx)
|
||||
{
|
||||
|
@ -687,6 +687,10 @@ AtSafePoint(JSContext *cx)
|
|||
return script->maybeNativeCodeForPC(fp->isConstructing(), cx->regs->pc);
|
||||
}
|
||||
|
||||
/*
|
||||
* Interprets until either a safe point is reached that has method JIT'd
|
||||
* code, or the current frame tries to return.
|
||||
*/
|
||||
static inline JSBool
|
||||
PartialInterpret(VMFrame &f)
|
||||
{
|
||||
|
@ -695,6 +699,7 @@ PartialInterpret(VMFrame &f)
|
|||
|
||||
#ifdef DEBUG
|
||||
JSScript *script = fp->script();
|
||||
JS_ASSERT(!fp->finishedInInterpreter());
|
||||
JS_ASSERT(fp->hasImacropc() ||
|
||||
!script->maybeNativeCodeForPC(fp->isConstructing(), cx->regs->pc));
|
||||
#endif
|
||||
|
@ -707,6 +712,7 @@ PartialInterpret(VMFrame &f)
|
|||
|
||||
JS_STATIC_ASSERT(JSOP_NOP == 0);
|
||||
|
||||
/* Returns whether the current PC would return, popping the frame. */
|
||||
static inline JSOp
|
||||
FrameIsFinished(JSContext *cx)
|
||||
{
|
||||
|
@ -718,39 +724,134 @@ FrameIsFinished(JSContext *cx)
|
|||
: JSOP_NOP;
|
||||
}
|
||||
|
||||
|
||||
/* Simulate an inline_return by advancing the pc. */
|
||||
static inline void
|
||||
AdvanceReturnPC(JSContext *cx)
|
||||
{
|
||||
JS_ASSERT(*cx->regs->pc == JSOP_CALL ||
|
||||
*cx->regs->pc == JSOP_NEW ||
|
||||
*cx->regs->pc == JSOP_EVAL ||
|
||||
*cx->regs->pc == JSOP_APPLY);
|
||||
cx->regs->pc += JSOP_CALL_LENGTH;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Given a frame that is about to return, make sure its return value and
|
||||
* activation objects are fixed up. Then, pop the frame and advance the
|
||||
* current PC. Note that while we could enter the JIT at this point, the
|
||||
* logic would still be necessary for the interpreter, so it's easier
|
||||
* (and faster) to finish frames in C++ even if at a safe point here.
|
||||
*/
|
||||
static bool
|
||||
HandleFinishedFrame(VMFrame &f, JSStackFrame *entryFrame)
|
||||
{
|
||||
JSContext *cx = f.cx;
|
||||
|
||||
JS_ASSERT(FrameIsFinished(cx));
|
||||
|
||||
/*
|
||||
* This is the most difficult and complicated piece of the tracer
|
||||
* integration, and historically has been very buggy. The problem is that
|
||||
* although this frame has to be popped (see RemoveExcessFrames), it may
|
||||
* be at a JSOP_RETURN opcode, and it might not have ever been executed.
|
||||
* That is, fp->rval may not be set to the top of the stack, and if it
|
||||
* has, the stack has already been decremented. Note that fp->rval is not
|
||||
* the only problem: the epilogue may never have been executed.
|
||||
*
|
||||
* Here are the edge cases and whether the frame has been exited cleanly:
|
||||
* 1. No: A trace exited directly before a RETURN op, and the
|
||||
* interpreter never ran.
|
||||
* 2. Yes: The interpreter exited cleanly.
|
||||
* 3. No: The interpreter exited on a safe point. LEAVE_ON_SAFE_POINT
|
||||
* is not used in between JSOP_RETURN and advancing the PC,
|
||||
* therefore, it cannot have been run if at a safe point.
|
||||
* 4. No: Somewhere in the RunTracer call tree, we removed a frame,
|
||||
* and we returned to a JSOP_RETURN opcode. Note carefully
|
||||
* that in this situation, FrameIsFinished() returns true!
|
||||
* 5. Yes: The function exited in the method JIT. However, in this
|
||||
* case, we'll never enter HandleFinishedFrame(): we always
|
||||
* immediately pop JIT'd frames.
|
||||
*
|
||||
* Since the only scenario where this fixup is NOT needed is a normal exit
|
||||
* from the interpreter, we can cleanly check for this scenario by checking
|
||||
* a bit it sets in the frame.
|
||||
*/
|
||||
bool returnOK = true;
|
||||
if (!cx->fp()->finishedInInterpreter()) {
|
||||
if (JSOp(*cx->regs->pc) == JSOP_RETURN)
|
||||
cx->fp()->setReturnValue(f.regs.sp[-1]);
|
||||
|
||||
returnOK = ScriptEpilogue(cx, cx->fp(), true);
|
||||
}
|
||||
|
||||
JS_ASSERT_IF(cx->fp()->isFunctionFrame() &&
|
||||
!cx->fp()->isEvalFrame(),
|
||||
!cx->fp()->hasCallObj());
|
||||
|
||||
if (cx->fp() != entryFrame) {
|
||||
InlineReturn(f);
|
||||
AdvanceReturnPC(cx);
|
||||
}
|
||||
|
||||
return returnOK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Given a frame newer than the entry frame, try to finish it. If it's at a
|
||||
* return position, pop the frame. If it's at a safe point, execute it in
|
||||
* Jaeger code. Otherwise, try to interpret until a safe point.
|
||||
*
|
||||
* While this function is guaranteed to make progress, it may not actually
|
||||
* finish or pop the current frame. It can either:
|
||||
* 1) Finalize a finished frame, or
|
||||
* 2) Finish and finalize the frame in the Method JIT, or
|
||||
* 3) Interpret, which can:
|
||||
* a) Propagate an error, or
|
||||
* b) Finish the frame, but not finalize it, or
|
||||
* c) Abruptly leave at any point in the frame, or in a newer frame
|
||||
* pushed by a call, that has method JIT'd code.
|
||||
*/
|
||||
static bool
|
||||
EvaluateExcessFrame(VMFrame &f, JSStackFrame *entryFrame)
|
||||
{
|
||||
JSContext *cx = f.cx;
|
||||
JSStackFrame *fp = cx->fp();
|
||||
|
||||
/*
|
||||
* A "finished" frame is when the interpreter rested on a STOP,
|
||||
* RETURN, RETRVAL, etc. We check for finished frames BEFORE looking
|
||||
* for a safe point. If the frame was finished, we could have already
|
||||
* called ScriptEpilogue(), and entering the JIT could call it twice.
|
||||
*/
|
||||
if (!fp->hasImacropc() && FrameIsFinished(cx))
|
||||
return HandleFinishedFrame(f, entryFrame);
|
||||
|
||||
if (void *ncode = AtSafePoint(cx)) {
|
||||
if (!JaegerShotAtSafePoint(cx, ncode))
|
||||
return false;
|
||||
InlineReturn(f);
|
||||
AdvanceReturnPC(cx);
|
||||
return true;
|
||||
}
|
||||
|
||||
return PartialInterpret(f);
|
||||
}
|
||||
|
||||
/*
|
||||
* Evaluate frames newer than the entry frame until all are gone. This will
|
||||
* always leave f.regs.fp == entryFrame.
|
||||
*/
|
||||
static bool
|
||||
FinishExcessFrames(VMFrame &f, JSStackFrame *entryFrame)
|
||||
{
|
||||
JSContext *cx = f.cx;
|
||||
while (cx->fp() != entryFrame || entryFrame->hasImacropc()) {
|
||||
if (void *ncode = AtSafePoint(cx)) {
|
||||
if (!JaegerShotAtSafePoint(cx, ncode)) {
|
||||
if (!HandleErrorInExcessFrames(f, entryFrame))
|
||||
return false;
|
||||
|
||||
/* Could be anywhere - restart outer loop. */
|
||||
continue;
|
||||
}
|
||||
InlineReturn(f, JS_TRUE);
|
||||
AdvanceReturnPC(cx);
|
||||
} else {
|
||||
if (!PartialInterpret(f)) {
|
||||
if (!HandleErrorInExcessFrames(f, entryFrame))
|
||||
return false;
|
||||
} else if (cx->fp() != entryFrame) {
|
||||
/*
|
||||
* Partial interpret could have dropped us anywhere. Deduce the
|
||||
* edge case: at a RETURN, needing to pop a frame.
|
||||
*/
|
||||
JS_ASSERT(!cx->fp()->hasImacropc());
|
||||
if (FrameIsFinished(cx)) {
|
||||
JSOp op = JSOp(*cx->regs->pc);
|
||||
if (op == JSOP_RETURN && !cx->fp()->isBailedAtReturn())
|
||||
cx->fp()->setReturnValue(f.regs.sp[-1]);
|
||||
InlineReturn(f, JS_TRUE);
|
||||
AdvanceReturnPC(cx);
|
||||
}
|
||||
}
|
||||
while (cx->fp() != entryFrame || entryFrame->hasImacropc()) {
|
||||
if (!EvaluateExcessFrame(f, entryFrame)) {
|
||||
if (!HandleErrorInExcessFrame(f, entryFrame))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -822,18 +923,18 @@ RunTracer(VMFrame &f)
|
|||
DisableTraceHint(f, mic);
|
||||
#endif
|
||||
|
||||
if ((tpa == TPA_RanStuff || tpa == TPA_Recorded) && cx->throwing)
|
||||
tpa = TPA_Error;
|
||||
// Even though ExecuteTree() bypasses the interpreter, it should propagate
|
||||
// error failures correctly.
|
||||
JS_ASSERT_IF(cx->throwing, tpa == TPA_Error);
|
||||
|
||||
/* Sync up the VMFrame's view of cx->fp(). */
|
||||
f.fp() = cx->fp();
|
||||
|
||||
JS_ASSERT(f.fp() == cx->fp());
|
||||
switch (tpa) {
|
||||
case TPA_Nothing:
|
||||
return NULL;
|
||||
|
||||
case TPA_Error:
|
||||
if (!HandleErrorInExcessFrames(f, entryFrame))
|
||||
if (!HandleErrorInExcessFrame(f, entryFrame, f.fp()->finishedInInterpreter()))
|
||||
THROWV(NULL);
|
||||
JS_ASSERT(!cx->fp()->hasImacropc());
|
||||
break;
|
||||
|
@ -871,32 +972,26 @@ RunTracer(VMFrame &f)
|
|||
THROWV(NULL);
|
||||
|
||||
/* IMacros are guaranteed to have been removed by now. */
|
||||
JS_ASSERT(f.fp() == entryFrame);
|
||||
JS_ASSERT(!entryFrame->hasImacropc());
|
||||
|
||||
/* Step 2. If entryFrame is at a safe point, just leave. */
|
||||
if (void *ncode = AtSafePoint(cx))
|
||||
return ncode;
|
||||
|
||||
/* Step 3. If entryFrame is at a RETURN, then leave slightly differently. */
|
||||
if (JSOp op = FrameIsFinished(cx)) {
|
||||
/* We're not guaranteed that the RETURN was run. */
|
||||
if (op == JSOP_RETURN && !entryFrame->isBailedAtReturn())
|
||||
entryFrame->setReturnValue(f.regs.sp[-1]);
|
||||
|
||||
/* Cleanup activation objects on the frame unless it's owned by an Invoke. */
|
||||
if (f.fp() != f.entryFp) {
|
||||
if (!InlineReturn(f, JS_TRUE, JS_FALSE))
|
||||
THROWV(NULL);
|
||||
}
|
||||
/* Step 2. If entryFrame is done, use a special path to return to EnterMethodJIT(). */
|
||||
if (FrameIsFinished(cx)) {
|
||||
if (!HandleFinishedFrame(f, entryFrame))
|
||||
THROWV(NULL);
|
||||
|
||||
void *retPtr = JS_FUNC_TO_DATA_PTR(void *, InjectJaegerReturn);
|
||||
*f.returnAddressLocation() = retPtr;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Step 3. If entryFrame is at a safe point, just leave. */
|
||||
if (void *ncode = AtSafePoint(cx))
|
||||
return ncode;
|
||||
|
||||
/* Step 4. Do a partial interp, then restart the whole process. */
|
||||
if (!PartialInterpret(f)) {
|
||||
if (!HandleErrorInExcessFrames(f, entryFrame))
|
||||
if (!HandleErrorInExcessFrame(f, entryFrame))
|
||||
THROWV(NULL);
|
||||
}
|
||||
|
||||
|
|
|
@ -690,10 +690,10 @@ class SetPropCompiler : public PICStubCompiler
|
|||
|
||||
class GetPropCompiler : public PICStubCompiler
|
||||
{
|
||||
JSObject *obj;
|
||||
JSAtom *atom;
|
||||
void *stub;
|
||||
int lastStubSecondShapeGuard;
|
||||
JSObject *obj;
|
||||
JSAtom *atom;
|
||||
VoidStubPIC stub;
|
||||
int lastStubSecondShapeGuard;
|
||||
|
||||
static int32 inlineShapeOffset(ic::PICInfo &pic) {
|
||||
#if defined JS_NUNBOX32
|
||||
|
@ -732,17 +732,12 @@ class GetPropCompiler : public PICStubCompiler
|
|||
}
|
||||
|
||||
public:
|
||||
GetPropCompiler(VMFrame &f, JSScript *script, JSObject *obj, ic::PICInfo &pic, JSAtom *atom,
|
||||
VoidStub stub)
|
||||
: PICStubCompiler("getprop", f, script, pic), obj(obj), atom(atom),
|
||||
stub(JS_FUNC_TO_DATA_PTR(void *, stub)),
|
||||
lastStubSecondShapeGuard(pic.secondShapeGuard)
|
||||
{ }
|
||||
|
||||
GetPropCompiler(VMFrame &f, JSScript *script, JSObject *obj, ic::PICInfo &pic, JSAtom *atom,
|
||||
VoidStubPIC stub)
|
||||
: PICStubCompiler("callprop", f, script, pic), obj(obj), atom(atom),
|
||||
stub(JS_FUNC_TO_DATA_PTR(void *, stub)),
|
||||
: PICStubCompiler(pic.kind == ic::PICInfo::CALL ? "callprop" : "getprop", f, script, pic),
|
||||
obj(obj),
|
||||
atom(atom),
|
||||
stub(stub),
|
||||
lastStubSecondShapeGuard(pic.secondShapeGuard)
|
||||
{ }
|
||||
|
||||
|
@ -2012,6 +2007,24 @@ class BindNameCompiler : public PICStubCompiler
|
|||
}
|
||||
};
|
||||
|
||||
static void JS_FASTCALL
|
||||
DisabledLengthIC(VMFrame &f, ic::PICInfo *pic)
|
||||
{
|
||||
stubs::Length(f);
|
||||
}
|
||||
|
||||
static void JS_FASTCALL
|
||||
DisabledGetPropIC(VMFrame &f, ic::PICInfo *pic)
|
||||
{
|
||||
stubs::GetProp(f);
|
||||
}
|
||||
|
||||
static void JS_FASTCALL
|
||||
DisabledGetPropICNoCache(VMFrame &f, ic::PICInfo *pic)
|
||||
{
|
||||
stubs::GetPropNoCache(f, pic->atom);
|
||||
}
|
||||
|
||||
void JS_FASTCALL
|
||||
ic::GetProp(VMFrame &f, ic::PICInfo *pic)
|
||||
{
|
||||
|
@ -2020,7 +2033,7 @@ ic::GetProp(VMFrame &f, ic::PICInfo *pic)
|
|||
JSAtom *atom = pic->atom;
|
||||
if (atom == f.cx->runtime->atomState.lengthAtom) {
|
||||
if (f.regs.sp[-1].isString()) {
|
||||
GetPropCompiler cc(f, script, NULL, *pic, NULL, stubs::Length);
|
||||
GetPropCompiler cc(f, script, NULL, *pic, NULL, DisabledLengthIC);
|
||||
if (!cc.generateStringLengthStub()) {
|
||||
cc.disable("error");
|
||||
THROW();
|
||||
|
@ -2031,7 +2044,7 @@ ic::GetProp(VMFrame &f, ic::PICInfo *pic)
|
|||
} else if (!f.regs.sp[-1].isPrimitive()) {
|
||||
JSObject *obj = &f.regs.sp[-1].toObject();
|
||||
if (obj->isArray() || (obj->isArguments() && !obj->isArgsLengthOverridden())) {
|
||||
GetPropCompiler cc(f, script, obj, *pic, NULL, stubs::Length);
|
||||
GetPropCompiler cc(f, script, obj, *pic, NULL, DisabledLengthIC);
|
||||
if (obj->isArray()) {
|
||||
if (!cc.generateArrayLengthStub()) {
|
||||
cc.disable("error");
|
||||
|
@ -2056,7 +2069,10 @@ ic::GetProp(VMFrame &f, ic::PICInfo *pic)
|
|||
THROW();
|
||||
|
||||
if (pic->shouldGenerate()) {
|
||||
GetPropCompiler cc(f, script, obj, *pic, atom, stubs::GetProp);
|
||||
VoidStubPIC stub = pic->usePropCache
|
||||
? DisabledGetPropIC
|
||||
: DisabledGetPropICNoCache;
|
||||
GetPropCompiler cc(f, script, obj, *pic, atom, stub);
|
||||
if (!cc.update()) {
|
||||
cc.disable("error");
|
||||
THROW();
|
||||
|
@ -2100,16 +2116,16 @@ ic::GetElem(VMFrame &f, ic::PICInfo *pic)
|
|||
|
||||
template <JSBool strict>
|
||||
static void JS_FASTCALL
|
||||
SetPropDumb(VMFrame &f, ic::PICInfo *pic)
|
||||
DisabledSetPropIC(VMFrame &f, ic::PICInfo *pic)
|
||||
{
|
||||
stubs::SetPropNoCache<strict>(f, pic->atom);
|
||||
stubs::SetName<strict>(f, pic->atom);
|
||||
}
|
||||
|
||||
template <JSBool strict>
|
||||
static void JS_FASTCALL
|
||||
SetPropSlow(VMFrame &f, ic::PICInfo *pic)
|
||||
DisabledSetPropICNoCache(VMFrame &f, ic::PICInfo *pic)
|
||||
{
|
||||
stubs::SetName<strict>(f, pic->atom);
|
||||
stubs::SetPropNoCache<strict>(f, pic->atom);
|
||||
}
|
||||
|
||||
void JS_FASTCALL
|
||||
|
@ -2131,22 +2147,9 @@ ic::SetProp(VMFrame &f, ic::PICInfo *pic)
|
|||
// cache can't handle a GET and SET from the same scripted PC.
|
||||
//
|
||||
|
||||
VoidStubPIC stub;
|
||||
switch (JSOp(*f.regs.pc)) {
|
||||
case JSOP_PROPINC:
|
||||
case JSOP_PROPDEC:
|
||||
case JSOP_INCPROP:
|
||||
case JSOP_DECPROP:
|
||||
case JSOP_NAMEINC:
|
||||
case JSOP_NAMEDEC:
|
||||
case JSOP_INCNAME:
|
||||
case JSOP_DECNAME:
|
||||
stub = STRICT_VARIANT(SetPropDumb);
|
||||
break;
|
||||
default:
|
||||
stub = STRICT_VARIANT(SetPropSlow);
|
||||
break;
|
||||
}
|
||||
VoidStubPIC stub = pic->usePropCache
|
||||
? STRICT_VARIANT(DisabledSetPropIC)
|
||||
: STRICT_VARIANT(DisabledSetPropICNoCache);
|
||||
|
||||
SetPropCompiler cc(f, script, obj, *pic, pic->atom, stub);
|
||||
if (!cc.update()) {
|
||||
|
@ -2159,7 +2162,7 @@ ic::SetProp(VMFrame &f, ic::PICInfo *pic)
|
|||
}
|
||||
|
||||
static void JS_FASTCALL
|
||||
CallPropSlow(VMFrame &f, ic::PICInfo *pic)
|
||||
DisabledCallPropIC(VMFrame &f, ic::PICInfo *pic)
|
||||
{
|
||||
stubs::CallProp(f, pic->atom);
|
||||
}
|
||||
|
@ -2254,7 +2257,7 @@ ic::CallProp(VMFrame &f, ic::PICInfo *pic)
|
|||
}
|
||||
}
|
||||
|
||||
GetPropCompiler cc(f, script, &objv.toObject(), *pic, pic->atom, CallPropSlow);
|
||||
GetPropCompiler cc(f, script, &objv.toObject(), *pic, pic->atom, DisabledCallPropIC);
|
||||
if (usePIC) {
|
||||
if (lval.isObject()) {
|
||||
if (!cc.update()) {
|
||||
|
@ -2283,13 +2286,13 @@ ic::CallProp(VMFrame &f, ic::PICInfo *pic)
|
|||
}
|
||||
|
||||
static void JS_FASTCALL
|
||||
SlowName(VMFrame &f, ic::PICInfo *pic)
|
||||
DisabledNameIC(VMFrame &f, ic::PICInfo *pic)
|
||||
{
|
||||
stubs::Name(f);
|
||||
}
|
||||
|
||||
static void JS_FASTCALL
|
||||
SlowXName(VMFrame &f, ic::PICInfo *pic)
|
||||
DisabledXNameIC(VMFrame &f, ic::PICInfo *pic)
|
||||
{
|
||||
stubs::GetProp(f);
|
||||
}
|
||||
|
@ -2302,7 +2305,7 @@ ic::XName(VMFrame &f, ic::PICInfo *pic)
|
|||
/* GETXPROP is guaranteed to have an object. */
|
||||
JSObject *obj = &f.regs.sp[-1].toObject();
|
||||
|
||||
ScopeNameCompiler cc(f, script, obj, *pic, pic->atom, SlowXName);
|
||||
ScopeNameCompiler cc(f, script, obj, *pic, pic->atom, DisabledXNameIC);
|
||||
|
||||
if (!cc.updateForXName()) {
|
||||
cc.disable("error");
|
||||
|
@ -2320,7 +2323,7 @@ ic::Name(VMFrame &f, ic::PICInfo *pic)
|
|||
{
|
||||
JSScript *script = f.fp()->script();
|
||||
|
||||
ScopeNameCompiler cc(f, script, &f.fp()->scopeChain(), *pic, pic->atom, SlowName);
|
||||
ScopeNameCompiler cc(f, script, &f.fp()->scopeChain(), *pic, pic->atom, DisabledNameIC);
|
||||
|
||||
if (!cc.updateForName()) {
|
||||
cc.disable("error");
|
||||
|
@ -2334,17 +2337,26 @@ ic::Name(VMFrame &f, ic::PICInfo *pic)
|
|||
}
|
||||
|
||||
static void JS_FASTCALL
|
||||
SlowBindName(VMFrame &f, ic::PICInfo *pic)
|
||||
DisabledBindNameIC(VMFrame &f, ic::PICInfo *pic)
|
||||
{
|
||||
stubs::BindName(f);
|
||||
}
|
||||
|
||||
static void JS_FASTCALL
|
||||
DisabledBindNameICNoCache(VMFrame &f, ic::PICInfo *pic)
|
||||
{
|
||||
stubs::BindNameNoCache(f, pic->atom);
|
||||
}
|
||||
|
||||
void JS_FASTCALL
|
||||
ic::BindName(VMFrame &f, ic::PICInfo *pic)
|
||||
{
|
||||
JSScript *script = f.fp()->script();
|
||||
|
||||
BindNameCompiler cc(f, script, &f.fp()->scopeChain(), *pic, pic->atom, SlowBindName);
|
||||
VoidStubPIC stub = pic->usePropCache
|
||||
? DisabledBindNameIC
|
||||
: DisabledBindNameICNoCache;
|
||||
BindNameCompiler cc(f, script, &f.fp()->scopeChain(), *pic, pic->atom, stub);
|
||||
|
||||
JSObject *obj = cc.update();
|
||||
if (!obj) {
|
||||
|
|
|
@ -236,6 +236,9 @@ struct PICInfo {
|
|||
// last stub.
|
||||
bool shapeRegHasBaseShape : 1;
|
||||
|
||||
// True if can use the property cache.
|
||||
bool usePropCache : 1;
|
||||
|
||||
// State flags.
|
||||
bool hit : 1; // this PIC has been executed
|
||||
bool inlinePathPatched : 1; // inline path has been patched
|
||||
|
|
|
@ -99,6 +99,15 @@ stubs::BindName(VMFrame &f)
|
|||
f.regs.sp[-1].setObject(*obj);
|
||||
}
|
||||
|
||||
void JS_FASTCALL
|
||||
stubs::BindNameNoCache(VMFrame &f, JSAtom *atom)
|
||||
{
|
||||
JSObject *obj = js_FindIdentifierBase(f.cx, &f.fp()->scopeChain(), ATOM_TO_JSID(atom));
|
||||
if (!obj)
|
||||
THROW();
|
||||
f.regs.sp[0].setObject(*obj);
|
||||
}
|
||||
|
||||
JSObject * JS_FASTCALL
|
||||
stubs::BindGlobalName(VMFrame &f)
|
||||
{
|
||||
|
@ -2068,6 +2077,20 @@ stubs::GetProp(VMFrame &f)
|
|||
THROW();
|
||||
}
|
||||
|
||||
void JS_FASTCALL
|
||||
stubs::GetPropNoCache(VMFrame &f, JSAtom *atom)
|
||||
{
|
||||
JSContext *cx = f.cx;
|
||||
|
||||
Value *vp = &f.regs.sp[-1];
|
||||
JSObject *obj = ValueToObject(cx, vp);
|
||||
if (!obj)
|
||||
THROW();
|
||||
|
||||
if (!obj->getProperty(cx, ATOM_TO_JSID(atom), vp))
|
||||
THROW();
|
||||
}
|
||||
|
||||
void JS_FASTCALL
|
||||
stubs::CallProp(VMFrame &f, JSAtom *origAtom)
|
||||
{
|
||||
|
|
|
@ -65,6 +65,8 @@ void JS_FASTCALL SlowNew(VMFrame &f, uint32 argc);
|
|||
void JS_FASTCALL SlowCall(VMFrame &f, uint32 argc);
|
||||
void * JS_FASTCALL UncachedNew(VMFrame &f, uint32 argc);
|
||||
void * JS_FASTCALL UncachedCall(VMFrame &f, uint32 argc);
|
||||
void JS_FASTCALL EnterScript(VMFrame &f);
|
||||
void JS_FASTCALL LeaveScript(VMFrame &f);
|
||||
|
||||
/*
|
||||
* Result struct for UncachedXHelper.
|
||||
|
@ -112,6 +114,7 @@ void * JS_FASTCALL LookupSwitch(VMFrame &f, jsbytecode *pc);
|
|||
void * JS_FASTCALL TableSwitch(VMFrame &f, jsbytecode *origPc);
|
||||
|
||||
void JS_FASTCALL BindName(VMFrame &f);
|
||||
void JS_FASTCALL BindNameNoCache(VMFrame &f, JSAtom *atom);
|
||||
JSObject * JS_FASTCALL BindGlobalName(VMFrame &f);
|
||||
template<JSBool strict> void JS_FASTCALL SetName(VMFrame &f, JSAtom *atom);
|
||||
template<JSBool strict> void JS_FASTCALL SetPropNoCache(VMFrame &f, JSAtom *atom);
|
||||
|
@ -119,6 +122,7 @@ template<JSBool strict> void JS_FASTCALL SetGlobalName(VMFrame &f, JSAtom *atom)
|
|||
template<JSBool strict> void JS_FASTCALL SetGlobalNameDumb(VMFrame &f, JSAtom *atom);
|
||||
void JS_FASTCALL Name(VMFrame &f);
|
||||
void JS_FASTCALL GetProp(VMFrame &f);
|
||||
void JS_FASTCALL GetPropNoCache(VMFrame &f, JSAtom *atom);
|
||||
void JS_FASTCALL GetElem(VMFrame &f);
|
||||
void JS_FASTCALL CallElem(VMFrame &f);
|
||||
template<JSBool strict> void JS_FASTCALL SetElem(VMFrame &f);
|
||||
|
|
|
@ -3,5 +3,5 @@ function main() {
|
|||
return "failure";
|
||||
}
|
||||
/* JSOP_RETURN in main. */
|
||||
trap(main, 4, "'success'");
|
||||
trap(main, 3, "'success'");
|
||||
assertEq(main(), "success");
|
||||
|
|
|
@ -10,8 +10,8 @@ function child() {
|
|||
function parent() {
|
||||
x = "failure2";
|
||||
}
|
||||
/* First op in parent: because of JSOP_BEGIN, it is op 1. */
|
||||
trap(parent, 1, "child()");
|
||||
/* First op in parent. */
|
||||
trap(parent, 0, "child()");
|
||||
|
||||
function success() {
|
||||
x = "success";
|
||||
|
|
|
@ -6,14 +6,14 @@ function doNothing() { }
|
|||
function myparent(nested) {
|
||||
if (nested) {
|
||||
/* JSOP_CALL to doNothing in myparent with nested = true. */
|
||||
trap(myparent, 25, "success()");
|
||||
trap(myparent, 24, "success()");
|
||||
doNothing();
|
||||
} else {
|
||||
doNothing();
|
||||
}
|
||||
}
|
||||
/* JSOP_CALL to doNothing in myparent with nested = false. */
|
||||
trap(myparent, 36, "myparent(true)");
|
||||
trap(myparent, 35, "myparent(true)");
|
||||
|
||||
function success() {
|
||||
x = "success";
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
var o = { };
|
||||
for (var i = 0; i <= 50; i++)
|
||||
o[i] = i;
|
||||
|
||||
Object.defineProperty(o, "51", { get: assertEq });
|
||||
|
||||
var threw = 0;
|
||||
function g(o, i) {
|
||||
try {
|
||||
assertEq(o[i], i);
|
||||
} catch (e) {
|
||||
threw++;
|
||||
}
|
||||
}
|
||||
|
||||
function f() {
|
||||
for (var i = 0; i <= 51; i++)
|
||||
g(o, i);
|
||||
}
|
||||
|
||||
f();
|
||||
f();
|
||||
f();
|
||||
assertEq(threw, 3);
|
||||
|
Загрузка…
Ссылка в новой задаче