--- a/js/jsd/idl/jsdIDebuggerService.idl
+++ b/js/jsd/idl/jsdIDebuggerService.idl
@@ -850,17 +850,17 @@ interface jsdIStackFrame : jsdIEphemeral
in unsigned long line, out jsdIValue result);
};
/**
* 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. */
[noscript] readonly attribute JSDContext JSDContext;
/** Internal use only. */
[noscript] readonly attribute JSDScript JSDScript;
/**
@@ -997,27 +997,16 @@ interface jsdIScript : jsdIEphemeral
unsigned long lineToPc (in unsigned long line, in unsigned long pcmap);
/**
* Determine is a particular line is executable, like checking that
* lineToPc == pcToLine, except in one call.
* 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.
*/
void setBreakpoint (in unsigned long pc);
/**
* Clear a breakpoint at a PC in this script.
*/
void clearBreakpoint (in unsigned long pc);
/**
--- a/js/jsd/jsd.h
+++ b/js/jsd/jsd.h
@@ -473,22 +473,16 @@ extern JSBool
jsd_SetScriptHook(JSDContext* jsdc, JSD_ScriptHookProc hook, void* callerdata);
extern JSBool
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);
extern void
jsd_NewScriptHookProc(
JSContext *cx,
const char *filename, /* URL this script loads from */
uintN lineno, /* line where this script starts */
--- a/js/jsd/jsd_scpt.c
+++ b/js/jsd/jsd_scpt.c
@@ -495,32 +495,30 @@ jsd_GetScriptLineExtent(JSDContext* jsdc
if( NOT_SET_YET == (int)jsdscript->lineExtent )
jsdscript->lineExtent = JS_GetScriptLineExtent(jsdc->dumbContext, jsdscript->script);
return jsdscript->lineExtent;
}
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)
{
uintN first = jsdscript->lineBase;
uintN last = first + jsd_GetScriptLineExtent(jsdc, jsdscript) - 1;
uintN line = pc
? JS_PCToLineNumber(jsdc->dumbContext,
jsdscript->script,
--- a/js/jsd/jsd_xpc.cpp
+++ b/js/jsd/jsd_xpc.cpp
@@ -953,34 +953,30 @@ jsdScript::jsdScript (JSDContext *aCx, J
mTag(0),
mCx(aCx),
mScript(aScript),
mFileName(0),
mFunctionName(0),
mBaseLineNumber(0),
mLineExtent(0),
mPPLineMap(0),
- mFirstValidPC(0),
- mFirstPC(0),
- mEndPC(0)
+ mFirstPC(0)
{
DEBUG_CREATE ("jsdScript", gScriptCount);
if (mScript) {
/* copy the script's information now, so we have it later, when it
* gets destroyed. */
JSD_LockScriptSubsystem(mCx);
mFileName = new nsCString(JSD_GetScriptFilename(mCx, mScript));
mFunctionName =
new nsCString(JSD_GetScriptFunctionName(mCx, mScript));
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;
}
}
jsdScript::~jsdScript ()
{
@@ -1475,32 +1471,16 @@ jsdScript::IsLineExecutable(PRUint32 aLi
} else {
return NS_ERROR_INVALID_ARG;
}
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)
{
ASSERT_VALID_EPHEMERAL;
jsuword pc = mFirstPC + aPC;
JSD_SetExecutionHook (mCx, mScript, pc, jsds_ExecutionHookProc, NULL);
return NS_OK;
}
--- a/js/jsd/jsd_xpc.h
+++ b/js/jsd/jsd_xpc.h
@@ -177,19 +177,17 @@ class jsdScript : public jsdIScript
PRUint32 mTag;
JSDContext *mCx;
JSDScript *mScript;
nsCString *mFileName;
nsCString *mFunctionName;
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;
class jsdContext : public jsdIContext
{
public:
NS_DECL_ISUPPORTS
--- a/js/jsd/jsdebug.c
+++ b/js/jsd/jsdebug.c
@@ -343,32 +343,16 @@ JSD_GetScriptHook(JSDContext* jsdc, JSD_
JSD_PUBLIC_API(jsuword)
JSD_GetClosestPC(JSDContext* jsdc, JSDScript* jsdscript, uintN line)
{
JSD_ASSERT_VALID_CONTEXT(jsdc);
JSD_ASSERT_VALID_SCRIPT(jsdscript);
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)
{
JSD_ASSERT_VALID_CONTEXT(jsdc);
JSD_ASSERT_VALID_SCRIPT(jsdscript);
return jsd_GetClosestLine(jsdc, jsdscript, pc);
}
--- a/js/jsd/jsdebug.h
+++ b/js/jsd/jsdebug.h
@@ -482,29 +482,16 @@ JSD_GetScriptHook(JSDContext* jsdc, JSD_
* If no code is on the given line, then the returned pc represents the first
* code within the script (if any) after the given line.
* This function can be used to set breakpoints -- see JSD_SetExecutionHook
*/
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
* the given pc.
*/
extern JSD_PUBLIC_API(uintN)
JSD_GetClosestLine(JSDContext* jsdc, JSDScript* jsdscript, jsuword pc);
/* these are only used in cases where scripts are created outside of JS*/
--- a/js/jsd/test/Makefile.in
+++ b/js/jsd/test/Makefile.in
@@ -43,13 +43,12 @@ relativesrcdir = js/jsd/test
include $(DEPTH)/config/autoconf.mk
MODULE = jsdebug
include $(topsrcdir)/config/rules.mk
_TEST_FILES = test_bug507448.html \
- test_bug602003.html \
$(NULL)
libs:: $(_TEST_FILES)
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/tests/$(relativesrcdir)
deleted file mode 100644
--- a/js/jsd/test/test_bug602003.html
+++ /dev/null
@@ -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>
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -470,19 +470,17 @@ AllFramesIter::AllFramesIter(JSContext *
{
}
AllFramesIter&
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();
}
return *this;
}
bool
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -2003,17 +2003,17 @@ struct JSContext
bool hasfp() {
JS_ASSERT_IF(regs, regs->fp);
return !!regs;
}
public:
friend class js::StackSpace;
- friend bool js::Interpret(JSContext *, JSStackFrame *, uintN, uintN);
+ friend bool js::Interpret(JSContext *, JSStackFrame *, uintN, JSInterpMode);
void resetCompartment();
/* 'regs' must only be changed by calling this function. */
void setCurrentRegs(JSFrameRegs *regs) {
JS_ASSERT_IF(regs, regs->fp);
this->regs = regs;
if (!regs)
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -230,22 +230,16 @@ JS_SetTrap(JSContext *cx, JSScript *scri
return JS_FALSE;
if (script == JSScript::emptyScript()) {
JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage,
NULL, JSMSG_READ_ONLY, "empty script");
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;
DBG_LOCK(rt);
trap = FindTrap(rt, script, pc);
if (trap) {
JS_ASSERT(trap->script == script && trap->pc == pc);
JS_ASSERT(*pc == JSOP_TRAP);
@@ -1017,23 +1011,16 @@ JS_PCToLineNumber(JSContext *cx, JSScrip
JS_PUBLIC_API(jsbytecode *)
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)
{
return script->code + script->length;
}
JS_PUBLIC_API(uintN)
JS_GetFunctionArgumentCount(JSContext *cx, JSFunction *fun)
{
--- a/js/src/jsdbgapi.h
+++ b/js/src/jsdbgapi.h
@@ -155,19 +155,16 @@ js_WrapWatchedSetter(JSContext *cx, jsid
extern JS_PUBLIC_API(uintN)
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);
extern JS_PUBLIC_API(uintN)
JS_GetFunctionArgumentCount(JSContext *cx, JSFunction *fun);
extern JS_PUBLIC_API(JSBool)
JS_FunctionHasLocalNames(JSContext *cx, JSFunction *fun);
--- a/js/src/jsemit.cpp
+++ b/js/src/jsemit.cpp
@@ -3708,25 +3708,20 @@ out:
bad:
ok = JS_FALSE;
goto out;
}
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);
}
if (cg->needsEagerArguments()) {
CG_SWITCH_TO_PROLOG(cg);
if (js_Emit1(cx, cg, JSOP_ARGUMENTS) < 0 || js_Emit1(cx, cg, JSOP_POP) < 0)
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -77,17 +77,16 @@
#include "jsvector.h"
#include "methodjit/MethodJIT.h"
#include "methodjit/Logging.h"
#include "jsatominlines.h"
#include "jscntxtinlines.h"
#include "jsinterpinlines.h"
#include "jsobjinlines.h"
-#include "jsprobes.h"
#include "jspropertycacheinlines.h"
#include "jsscopeinlines.h"
#include "jsscriptinlines.h"
#include "jsstrinlines.h"
#include "jsopcodeinlines.h"
#if JS_HAS_XML_SUPPORT
#include "jsxml.h"
@@ -729,37 +728,22 @@ Invoke(JSContext *cx, const CallArgs &ar
JSObject *thisp = thisv.toObject().thisObject(cx);
if (!thisp)
return false;
JS_ASSERT(IsSaneThisObject(*thisp));
thisv.setObject(*thisp);
}
}
- 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());
return ok;
}
bool
@@ -2156,21 +2140,38 @@ IteratorNext(JSContext *cx, JSObject *it
*rval = *ni->currentValue();
ni->incValueCursor();
return true;
}
}
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);
#endif
JSAutoResolveFlags rf(cx, JSRESOLVE_INFER);
# ifdef DEBUG
/*
@@ -2391,17 +2392,17 @@ Interpret(JSContext *cx, JSStackFrame *e
#if defined(JS_TRACER) && defined(JS_METHODJIT)
# define LEAVE_ON_SAFE_POINT() \
do { \
JS_ASSERT_IF(leaveOnSafePoint, !TRACE_RECORDER(cx)); \
if (leaveOnSafePoint && !regs.fp->hasImacropc() && \
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
# define LEAVE_ON_SAFE_POINT() /* nop */
#endif
#define BRANCH(n) \
JS_BEGIN_MACRO \
@@ -2427,35 +2428,47 @@ Interpret(JSContext *cx, JSStackFrame *e
JS_BEGIN_MACRO \
if (cx->debugHooks->interruptHook) \
ENABLE_INTERRUPTS(); \
JS_END_MACRO
/* 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;
JSScript *script = regs.fp->script();
Value *argv = regs.fp->maybeFormalArgs();
CHECK_INTERRUPT_HANDLER();
JS_ASSERT(!script->isEmpty());
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)
#endif
if (!entryFrame)
entryFrame = regs.fp;
@@ -2465,17 +2478,17 @@ Interpret(JSContext *cx, JSStackFrame *e
* the atom map to turn frequently executed LOAD_ATOM into simple array
* access. For less frequent object and regexp loads we have to recover
* the segment from atoms pointer first.
*/
JSAtom **atoms = script->atomMap.vector;
#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));
/*
* To support generator_throw and to catch ignored exceptions,
* fail if cx->throwing is set.
*/
if (cx->throwing)
@@ -2484,27 +2497,36 @@ Interpret(JSContext *cx, JSStackFrame *e
#endif
#ifdef JS_TRACER
/*
* The method JIT may have already initiated a recording, in which case
* 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)) {
AbortRecording(cx, "attempt to reenter interpreter while recording");
}
if (regs.fp->hasImacropc())
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;
/*
* It is important that "op" be initialized before calling DO_OP because
* it is possible for "op" to be specially assigned during the normal
* processing of an opcode while looping. We rely on DO_NEXT_OP to manage
@@ -2580,17 +2602,18 @@ Interpret(JSContext *cx, JSStackFrame *e
moreInterrupts = true;
}
#ifdef JS_TRACER
if (TraceRecorder* tr = TRACE_RECORDER(cx)) {
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:
case ARECORD_COMPLETED:
case ARECORD_STOP:
#ifdef JS_METHODJIT
leaveOnSafePoint = true;
LEAVE_ON_SAFE_POINT();
@@ -2755,37 +2778,21 @@ BEGIN_CASE(JSOP_STOP)
}
#endif
interpReturnOK = true;
if (entryFrame != regs.fp)
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);
/* Sync interpreter registers. */
script = regs.fp->script();
argv = regs.fp->maybeFormalArgs();
atoms = FrameAtomBase(cx, regs.fp);
@@ -2798,28 +2805,16 @@ BEGIN_CASE(JSOP_STOP)
== JSOP_CALL_LENGTH);
TRACE_0(LeaveFrame);
len = JSOP_CALL_LENGTH;
DO_NEXT_OP(len);
}
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;
}
BEGIN_CASE(JSOP_DEFAULT)
regs.sp--;
/* FALL THROUGH */
@@ -4564,51 +4559,16 @@ BEGIN_CASE(JSOP_ENUMELEM)
FETCH_ELEMENT_ID(obj, -1, id);
Value rval = regs.sp[-3];
if (!obj->setProperty(cx, id, &rval, script->strictModeCode))
goto error;
regs.sp -= 3;
}
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;
uint32 flags;
uintN argc;
Value *vp;
BEGIN_CASE(JSOP_NEW)
@@ -4698,22 +4658,41 @@ BEGIN_CASE(JSOP_APPLY)
argv = regs.fp->formalArgsEnd() - newfun->nargs;
atoms = script->atomMap.vector;
/* Now that the new frame is rooted, maybe create a call object. */
if (newfun->isHeavyweight() && !js_GetCallObject(cx, regs.fp))
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();
}
Probes::enterJSFun(cx, newfun);
JSBool ok = CallJSNative(cx, newfun->u.n.native, argc, vp);
Probes::exitJSFun(cx, newfun);
regs.sp = vp + 1;
if (!ok)
@@ -6928,58 +6907,57 @@ END_CASE(JSOP_ARRAYPUSH)
#ifdef DEBUG
cx->tracePrevPc = NULL;
#endif
if (entryFrame != regs.fp)
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:
* (a) an "out of line" call made through js_Invoke;
* (b) a js_Execute activation;
* (c) a generator (SendToGenerator, jsiter.c).
*
* We must not be in an inline frame. The check above ensures that for the
* error case and for a normal return, the code jumps directly to parent's
* 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:
{
const char *printable;
printable = js_AtomToPrintableString(cx, atomNotDefined);
if (printable)
js_ReportIsNotDefined(cx, printable);
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;
}
} /* namespace js */
#endif /* !defined jsinvoke_cpp___ */
--- a/js/src/jsinterp.h
+++ b/js/src/jsinterp.h
@@ -54,20 +54,21 @@ struct JSFrameRegs
{
STATIC_SKIP_INFERENCE
js::Value *sp; /* stack pointer */
jsbytecode *pc; /* program counter */
JSStackFrame *fp; /* active frame */
};
/* 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_ */
enum JSFrameFlags
{
/* Primary frame type */
JSFRAME_GLOBAL = 0x1, /* frame pushed for a global script */
JSFRAME_FUNCTION = 0x2, /* frame pushed for a scripted call */
@@ -78,17 +79,17 @@ enum JSFrameFlags
JSFRAME_DEBUGGER = 0x10, /* frame pushed by JS_EvaluateInStackFrame */
JSFRAME_GENERATOR = 0x20, /* frame is associated with a generator */
JSFRAME_FLOATING_GENERATOR = 0x40, /* frame is is in generator obj, not on stack */
JSFRAME_CONSTRUCTING = 0x80, /* frame is for a constructor invocation */
/* 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 */
JSFRAME_OVERFLOW_ARGS = 0x2000, /* numActualArgs > numFormalArgs */
JSFRAME_UNDERFLOW_ARGS = 0x4000, /* numActualArgs < numFormalArgs */
/* Lazy frame initialization */
JSFRAME_HAS_IMACRO_PC = 0x8000, /* frame has imacpc value available */
@@ -168,16 +169,20 @@ struct JSStackFrame
return !!(flags_ & (JSFRAME_FUNCTION | JSFRAME_GLOBAL));
}
bool isEvalFrame() const {
JS_ASSERT_IF(flags_ & JSFRAME_EVAL, isScriptFrame());
return flags_ & JSFRAME_EVAL;
}
+ bool isExecuteFrame() const {
+ return !!(flags_ & (JSFRAME_GLOBAL | JSFRAME_EVAL));
+ }
+
/*
* Frame initialization
*
* After acquiring a pointer to an uninitialized stack frame on the VM
* stack from js::StackSpace, these members are used to initialize the
* stack frame before officially pushing the frame into the context.
* Collecting frame initialization into a set of inline helpers allows
* simpler reasoning and makes call-optimization easier.
@@ -675,22 +680,22 @@ struct JSStackFrame
void setYielding() {
flags_ |= JSFRAME_YIELDING;
}
void clearYielding() {
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);
}
/*
* Variables object accessors
*
* A stack frame's 'varobj' refers to the 'variables object' (ES3 term)
* associated with the Execution Context's VariableEnvironment (ES5 10.3).
*
@@ -977,17 +982,17 @@ extern JS_FORCES_STACK bool
Execute(JSContext *cx, JSObject *chain, JSScript *script,
JSStackFrame *prev, uintN flags, Value *result);
/*
* Execute the caller-initialized frame for a user-defined script or function
* 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);
#define JSPROP_INITIALIZER 0x100 /* NB: Not a valid property attribute. */
extern bool
CheckRedeclaration(JSContext *cx, JSObject *obj, jsid id, uintN attrs,
--- a/js/src/jsinterpinlines.h
+++ b/js/src/jsinterpinlines.h
@@ -1,9 +1,10 @@
/* -*- 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
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
@@ -106,17 +107,17 @@ JSStackFrame::resetInvokeCallFrame()
JSFRAME_UNDERFLOW_ARGS |
JSFRAME_HAS_CALL_OBJ |
JSFRAME_HAS_ARGS_OBJ |
JSFRAME_OVERRIDE_ARGS |
JSFRAME_HAS_PREVPC |
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 |
JSFRAME_UNDERFLOW_ARGS;
JS_ASSERT_IF(!hasCallObj(), scopeChain_ == calleeValue().toObject().getParent());
JS_ASSERT_IF(hasCallObj(), scopeChain_ == callObj().getParent());
if (hasCallObj())
@@ -257,16 +258,21 @@ JSStackFrame::stealFrameAndSlots(js::Val
* Repoint Call, Arguments, Block and With objects to the new live frame.
* Call and Arguments are done directly because we have pointers to them.
* Block and With objects are done indirectly through 'liveFrame'. See
* js_LiveFrameToFloating comment in jsiter.h.
*/
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);
otherfp->flags_ &= ~JSFRAME_HAS_ARGS_OBJ;
}
}
inline js::Value &
@@ -668,11 +674,40 @@ ValuePropertyBearer(JSContext *cx, const
}
JSObject *pobj;
if (!js_GetClassPrototype(cx, NULL, protoKey, &pobj))
return NULL;
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__ */
--- a/js/src/jsiter.cpp
+++ b/js/src/jsiter.cpp
@@ -1267,17 +1267,17 @@ SendToGenerator(JSContext *cx, JSGenerat
JS_GetFunctionId(gen->floatingFrame()->fun()));
return JS_FALSE;
}
/* Check for OOM errors here, where we can fail easily. */
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:
if (gen->state == JSGEN_OPEN) {
/*
* Store the argument to send as the result of the yield
* expression.
*/
--- a/js/src/jsopcode.tbl
+++ b/js/src/jsopcode.tbl
@@ -618,11 +618,9 @@ OPDEF(JSOP_FORGLOBAL, 246,"forglobal
* They are emitted directly after instructions, such as DEFFUN, that need fast access to
* the blockChain. The special NULLBLOCKCHAIN is needed because the JOF_OBJECT
* does not permit NULL object references, since it stores an index into a table of
* objects.
*/
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. */
--- a/js/src/jspropertycache.cpp
+++ b/js/src/jspropertycache.cpp
@@ -303,17 +303,17 @@ PropertyCache::fill(JSContext *cx, JSObj
static inline JSAtom *
GetAtomFromBytecode(JSContext *cx, jsbytecode *pc, JSOp op, const JSCodeSpec &cs)
{
if (op == JSOP_LENGTH)
return cx->runtime->atomState.lengthAtom;
// 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;
JSAtom *atom;
GET_ATOM_FROM_BYTECODE(cx->fp()->script(), pc, pcoff, atom);
return atom;
}
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -1092,17 +1092,18 @@ JSScript::NewScriptFromCG(JSContext *cx,
/* The counts of indexed things must be checked during code generation. */
JS_ASSERT(cg->atomList.count <= INDEX_LIMIT);
JS_ASSERT(cg->objectList.length <= INDEX_LIMIT);
JS_ASSERT(cg->regexpList.length <= INDEX_LIMIT);
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.
*/
jsbytecode *pc = prologLength ? CG_PROLOG_BASE(cg) : CG_BASE(cg);
if ((cg->flags & TCF_NO_SCRIPT_RVAL) && JSOp(*pc) == JSOP_FALSE)
++pc;
--- a/js/src/jsscriptinlines.h
+++ b/js/src/jsscriptinlines.h
@@ -65,23 +65,28 @@ JSScript::getRegExp(size_t index)
JSObject *obj = arr->vector[index];
JS_ASSERT(obj->getClass() == &js_RegExpClass);
return obj;
}
inline bool
JSScript::isEmpty() const
{
+ return (this == emptyScript());
+
+ // See bug 603044 comment #21.
+#if 0
if (this == emptyScript())
return true;
if (length <= 3) {
jsbytecode *pc = code;
if (noScriptRval && JSOp(*pc) == JSOP_FALSE)
++pc;
if (JSOp(*pc) == JSOP_STOP)
return true;
}
return false;
+#endif
}
#endif /* jsscriptinlines_h___ */
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -10492,16 +10492,23 @@ TraceRecorder::record_EnterFrame()
}
/* Try inlining one level in case this recursion doesn't go too deep. */
if (fp->script() == fp->prev()->script() &&
fp->prev()->prev() && fp->prev()->prev()->script() == fp->script()) {
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;
}
JS_REQUIRES_STACK AbortableRecordingStatus
TraceRecorder::record_LeaveFrame()
{
debug_only_stmt(JSStackFrame *fp = cx->fp();)
@@ -11194,30 +11201,16 @@ TraceRecorder::emitNativePropertyOp(cons
// If the native op succeeds but we deep-bail here, the result value is
// lost! Therefore this can only be used for setters of shared properties.
// In that case we ignore the result value anyway.
LIns* status_ins = loadFromState(LIR_ldi, builtinStatus);
propagateFailureToBuiltinStatus(ok_ins, status_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)
{
if (JSTN_ERRTYPE(sn) == FAIL_STATUS) {
// This needs to capture the pre-call state of the stack. So do not set
// pendingSpecializedNative before taking this snapshot.
JS_ASSERT(!pendingSpecializedNative);
--- a/js/src/jsxdrapi.h
+++ b/js/src/jsxdrapi.h
@@ -200,17 +200,17 @@ JS_XDRFindClassById(JSXDRState *xdr, uin
* Bytecode version number. Increment the subtrahend whenever JS bytecode
* changes incompatibly.
*
* This version number should be XDR'ed once near the front of any file or
* larger storage unit containing XDR'ed bytecode and other data, and checked
* 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.
*/
extern JSBool
js_XDRAtom(JSXDRState *xdr, JSAtom **atomp);
JS_END_EXTERN_C
--- a/js/src/methodjit/Compiler.cpp
+++ b/js/src/methodjit/Compiler.cpp
@@ -213,16 +213,20 @@ mjit::Compiler::~Compiler()
cx->free(jumpMap);
}
CompileStatus JS_NEVER_INLINE
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();
}
CompileStatus
mjit::Compiler::generatePrologue()
{
@@ -311,16 +315,22 @@ mjit::Compiler::generatePrologue()
FrameFlagsAddress(), Imm32(JSFRAME_HAS_SCOPECHAIN));
masm.loadPayload(Address(JSFrameReg, JSStackFrame::offsetOfCallee(fun)), t0);
masm.loadPtr(Address(t0, offsetof(JSObject, parent)), t0);
masm.storePtr(t0, Address(JSFrameReg, JSStackFrame::offsetOfScopeChain()));
hasScope.linkTo(masm.label(), &masm);
}
}
+ if (isConstructing)
+ constructThis();
+
+ if (debugMode)
+ stubCall(stubs::EnterScript);
+
return Compile_Okay;
}
CompileStatus
mjit::Compiler::generateEpilogue()
{
return Compile_Okay;
}
@@ -1389,27 +1399,27 @@ mjit::Compiler::generateMethod()
PC += JSOP_LOCALINC_LENGTH;
if (popped)
PC += JSOP_POP_LENGTH;
break;
}
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)
BEGIN_CASE(JSOP_THROW)
prepareStubCall(Uses(1));
stubCall(stubs::Throw);
frame.pop();
END_CASE(JSOP_THROW)
@@ -1752,23 +1762,16 @@ mjit::Compiler::generateMethod()
BEGIN_CASE(JSOP_DECGLOBAL)
BEGIN_CASE(JSOP_GLOBALINC)
BEGIN_CASE(JSOP_GLOBALDEC)
/* Advances PC automatically. */
jsop_globalinc(op, GET_SLOTNO(PC));
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
JaegerSpew(JSpew_Abort, "opcode %s not handled yet (%s line %d)\n", OpcodeNames[op],
script->filename, js_PCToLineNumber(cx, script, PC));
#endif
return Compile_Abort;
}
@@ -1982,16 +1985,21 @@ mjit::Compiler::emitReturnValue(Assemble
void
mjit::Compiler::emitReturn(FrameEntry *fe)
{
JS_ASSERT_IF(!fun, JSOp(*PC) == JSOP_STOP);
/* 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
* be thrown away. This will happen anyway because the next live opcode
* (if any) must have an incoming edge.
*
* However, it's an optimization to throw it away early - the tracker
* won't be spilled on further exits or join points.
@@ -2414,30 +2422,38 @@ mjit::Compiler::emitStubCmpOp(BoolStub s
: Assembler::NonZero;
Jump j = masm.branchTest32(cond, Registers::ReturnReg,
Registers::ReturnReg);
jumpAndTrace(j, target);
}
}
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();
}
bool
mjit::Compiler::jsop_callprop_slow(JSAtom *atom)
{
prepareStubCall(Uses(1));
@@ -2493,41 +2509,41 @@ mjit::Compiler::passMICAddress(MICGenInf
#if defined JS_POLYIC
void
mjit::Compiler::passPICAddress(PICGenInfo &pic)
{
pic.addrLabel = stubcc.masm.moveWithPatch(ImmPtr(NULL), Registers::ArgReg1);
}
bool
-mjit::Compiler::jsop_getprop(JSAtom *atom, bool doTypeCheck)
+mjit::Compiler::jsop_getprop(JSAtom *atom, bool doTypeCheck, bool usePropCache)
{
FrameEntry *top = frame.peek(-1);
/* If the incoming type will never PIC, take slow path. */
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;
}
/*
* These two must be loaded first. The objReg because the string path
* wants to read it, and the shapeReg because it could cause a spill that
* the string path wouldn't sink back.
*/
RegisterID objReg = Registers::ReturnReg;
RegisterID shapeReg = Registers::ReturnReg;
if (atom == cx->runtime->atomState.lengthAtom) {
objReg = frame.copyDataIntoReg(top);
shapeReg = frame.allocReg();
}
- PICGenInfo pic(ic::PICInfo::GET);
+ PICGenInfo pic(ic::PICInfo::GET, usePropCache);
/* Guard that the type is an object. */
Jump typeCheck;
if (doTypeCheck && !top->isTypeKnown()) {
RegisterID reg = frame.tempRegForType(top);
pic.typeReg = reg;
/* Start the hot path where it's easy to patch it. */
@@ -2626,17 +2642,17 @@ mjit::Compiler::jsop_getprop(JSAtom *ato
return true;
}
#ifdef JS_POLYIC
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);
pic.shapeReg = shapeReg;
pic.hasTypeCheck = false;
pic.fastPathStart = masm.label();
@@ -2741,17 +2757,17 @@ mjit::Compiler::jsop_callprop_generic(JS
/*
* These two must be loaded first. The objReg because the string path
* wants to read it, and the shapeReg because it could cause a spill that
* the string path wouldn't sink back.
*/
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);
/* Start the hot path where it's easy to patch it. */
pic.fastPathStart = masm.label();
/*
@@ -2911,17 +2927,17 @@ mjit::Compiler::jsop_callprop_str(JSAtom
return true;
}
bool
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);
pic.fastPathStart = masm.label();
pic.hasTypeCheck = false;
pic.typeReg = Registers::ReturnReg;
@@ -3029,58 +3045,50 @@ mjit::Compiler::jsop_callprop(JSAtom *at
}
if (top->isTypeKnown())
return jsop_callprop_obj(atom);
return jsop_callprop_generic(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. */
Jump typeCheck;
if (!lhs->isTypeKnown()) {
RegisterID reg = frame.tempRegForType(lhs);
pic.typeReg = reg;
/* Start the hot path where it's easy to patch it. */
pic.fastPathStart = masm.label();
Jump j = masm.testObject(Assembler::NotEqual, reg);
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 {
pic.fastPathStart = masm.label();
pic.hasTypeCheck = false;
pic.typeReg = Registers::ReturnReg;
}
@@ -3175,17 +3183,17 @@ mjit::Compiler::jsop_setprop(JSAtom *ato
pics.append(pic);
return true;
}
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();
pic.typeReg = Registers::ReturnReg;
pic.atom = atom;
pic.hasTypeCheck = false;
pic.fastPathStart = masm.label();
@@ -3207,17 +3215,17 @@ mjit::Compiler::jsop_name(JSAtom *atom)
stubcc.rejoin(Changes(1));
pics.append(pic);
}
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)) {
return jsop_getprop(atom);
}
if (!fe->isTypeKnown()) {
Jump notObject = frame.testObject(Assembler::NotEqual, fe);
@@ -3249,19 +3257,19 @@ mjit::Compiler::jsop_xname(JSAtom *atom)
stubcc.rejoin(Changes(1));
pics.append(pic);
return true;
}
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();
pic.typeReg = Registers::ReturnReg;
pic.atom = script->getAtom(index);
pic.hasTypeCheck = false;
pic.fastPathStart = masm.label();
@@ -3312,49 +3320,54 @@ mjit::Compiler::jsop_name(JSAtom *atom)
bool
mjit::Compiler::jsop_xname(JSAtom *atom)
{
return jsop_getprop(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;
}
bool
mjit::Compiler::jsop_callprop(JSAtom *atom)
{
return jsop_callprop_slow(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());
masm.loadPtr(scopeChain, reg);
Address address(reg, offsetof(JSObject, parent));
Jump j = masm.branchPtr(Assembler::NotEqual, masm.payloadOf(address), ImmPtr(0));
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);
stubcc.rejoin(Changes(1));
}
#endif
void
@@ -3487,29 +3500,29 @@ mjit::Compiler::jsop_nameinc(JSOp op, Vo
frame.push(Int32Value(amt));
// V 1
/* Use sub since it calls ValueToNumber instead of string concat. */
jsop_binary(JSOP_SUB, stubs::Sub);
// N+1
- jsop_bindname(index);
+ jsop_bindname(index, false);
// V+1 OBJ
frame.dup2();
// V+1 OBJ V+1 OBJ
frame.shift(-3);
// OBJ OBJ V+1
frame.shift(-1);
// OBJ V+1
- if (!jsop_setprop(atom))
+ if (!jsop_setprop(atom, false))
return false;
// V+1
if (pop)
frame.pop();
} else {
/* The pre-value is observed, making this more tricky. */
@@ -3523,29 +3536,29 @@ mjit::Compiler::jsop_nameinc(JSOp op, Vo
// N N
frame.push(Int32Value(-amt));
// N N 1
jsop_binary(JSOP_ADD, stubs::Add);
// N N+1
- jsop_bindname(index);
+ jsop_bindname(index, false);
// N N+1 OBJ
frame.dup2();
// N N+1 OBJ N+1 OBJ
frame.shift(-3);
// N OBJ OBJ N+1
frame.shift(-1);
// N OBJ N+1
- if (!jsop_setprop(atom))
+ if (!jsop_setprop(atom, false))
return false;
// N N+1
frame.pop();
// N
}
if (pop)
@@ -3584,17 +3597,17 @@ mjit::Compiler::jsop_propinc(JSOp op, Vo
frame.push(Int32Value(amt));
// OBJ V 1
/* Use sub since it calls ValueToNumber instead of string concat. */
jsop_binary(JSOP_SUB, stubs::Sub);
// OBJ V+1
- if (!jsop_setprop(atom))
+ if (!jsop_setprop(atom, false))
return false;
// V+1
if (pop)
frame.pop();
} else {
/* The pre-value is observed, making this more tricky. */
@@ -3618,17 +3631,17 @@ mjit::Compiler::jsop_propinc(JSOp op, Vo
// OBJ N N+1
frame.dupAt(-3);
// OBJ N N+1 OBJ
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
frame.popn(2);
// OBJ N
frame.shimmy(1);
// N
@@ -4419,17 +4432,17 @@ mjit::Compiler::constructThis()
// Load the callee.
Address callee(JSFrameReg, JSStackFrame::offsetOfCallee(fun));
RegisterID calleeReg = frame.allocReg();
masm.loadPayload(callee, calleeReg);
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.
FrameEntry *protoFe = frame.peek(-1);
RegisterID protoReg = frame.ownRegForData(protoFe);
// Now, get the type. If it's not an object, set protoReg to NULL.
Jump isNotObject = frame.testObject(Assembler::NotEqual, protoFe);
--- a/js/src/methodjit/Compiler.h
+++ b/js/src/methodjit/Compiler.h
@@ -146,44 +146,47 @@ class Compiler : public BaseCompiler
Label joinPoint;
DataLabelPtr fastNcodePatch;
DataLabelPtr slowNcodePatch;
bool hasSlowNcode;
};
#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;
Label storeBack;
Label typeCheck;
Label slowPathStart;
DataLabelPtr addrLabel;
RegisterID shapeReg;
RegisterID objReg;
RegisterID idReg;
RegisterID typeReg;
+ bool usePropCache;
Label shapeGuard;
JSAtom *atom;
StateRemat objRemat;
StateRemat idRemat;
Call callReturn;
bool hasTypeCheck;
ValueRemat vr;
# if defined JS_CPU_X64
ic::PICLabels labels;
# endif
void copySimpleMembersTo(ic::PICInfo &pi) const {
pi.kind = kind;
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) {
pi.u.get.idReg = idReg;
pi.u.get.typeReg = typeReg;
pi.u.get.hasTypeCheck = hasTypeCheck;
pi.u.get.objRemat = objRemat.offset;
}
@@ -310,20 +313,20 @@ class Compiler : public BaseCompiler
void jsop_getgname(uint32 index);
void jsop_getgname_slow(uint32 index);
void jsop_setgname(uint32 index);
void jsop_setgname_slow(uint32 index);
void jsop_bindgname();
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);
bool jsop_callprop_str(JSAtom *atom);
bool jsop_callprop_generic(JSAtom *atom);
bool jsop_instanceof();
void jsop_name(JSAtom *atom);
bool jsop_xname(JSAtom *atom);
--- a/js/src/methodjit/InvokeHelpers.cpp
+++ b/js/src/methodjit/InvokeHelpers.cpp
@@ -68,19 +68,16 @@
#include "StubCalls-inl.h"
#include "jsautooplen.h"
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)
{
JSStackFrame *fp = cx->fp();
JSScript *script = fp->script();
top:
if (cx->throwing && script->trynotesOffset) {
@@ -172,56 +169,29 @@ top:
/*
* Clean up a frame and return. popFrame indicates whether to additionally pop
* the frame and store the return value on the caller's stack. The frame will
* normally be popped by the caller on return from a call into JIT code,
* so must be popped here when that caller code will not execute. This can be
* 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;
JS_ASSERT(f.fp() != f.entryFp);
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
stubs::SlowCall(VMFrame &f, uint32 argc)
{
Value *vp = f.regs.sp - (argc + 2);
if (!Invoke(f.cx, InvokeArgsAlreadyOnTheStack(vp, argc), 0))
@@ -367,29 +337,28 @@ stubs::CompileFunction(VMFrame &f, uint3
THROWV(NULL);
CompileStatus status = CanMethodJIT(cx, script, fp);
if (status == Compile_Okay)
return script->getJIT(callingNew)->invokeEntry;
/* Function did not compile... interpret it. */
JSBool ok = Interpret(cx, fp);
- InlineReturn(f, ok);
+ InlineReturn(f);
if (!ok)
THROWV(NULL);
return NULL;
}
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();
JSScript *newscript = newfun->script();
/* Get pointer to new frame/slots, prepare arguments. */
StackSpace &stack = cx->stack();
JSStackFrame *newfp = stack.getInlineFrameWithinLimit(cx, f.regs.sp, argc,
@@ -407,40 +376,34 @@ UncachedInlineCall(VMFrame &f, uint32 fl
/* Officially push the frame. */
stack.pushInlineFrame(cx, newscript, newfp, &f.regs);
JS_ASSERT(newfp == f.regs.fp);
/* Scope with a call object parented by callee's parent. */
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;
}
}
/* If newscript was successfully compiled, run it. */
if (JITScript *jit = newscript->getJIT(newfp->isConstructing())) {
*pret = jit->invokeEntry;
return true;
}
/* Otherwise, run newscript in the interpreter. */
bool ok = !!Interpret(cx, cx->fp());
- InlineReturn(f, JS_TRUE);
+ InlineReturn(f);
*pret = NULL;
return ok;
}
void * JS_FASTCALL
stubs::UncachedNew(VMFrame &f, uint32 argc)
{
@@ -569,21 +532,28 @@ js_InternalThrow(VMFrame &f)
break;
// If on the 'topmost' frame (where topmost means the first frame
// called into through js_Interpret). In this case, we still unwind,
// but we shouldn't return from a JS function, because we're not in a
// 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);
if (!pc)
return NULL;
JSStackFrame *fp = cx->fp();
@@ -606,46 +576,74 @@ stubs::CreateThis(VMFrame &f, JSObject *
JSStackFrame *fp = f.fp();
JSObject *callee = &fp->callee();
JSObject *obj = js_CreateThisForFunctionWithProto(cx, callee, proto);
if (!obj)
THROW();
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;
/*
* 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();
/* Clear imacros. */
if (fp->hasImacropc()) {
cx->regs->pc = fp->imacropc();
fp->clearImacropc();
}
@@ -662,100 +660,203 @@ HandleErrorInExcessFrames(VMFrame &f, JS
}
/* Don't unwind if this was the entry frame. */
if (fp == stopFp)
break;
/* 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);
JS_ASSERT_IF(!returnOK, cx->fp() == stopFp);
return returnOK;
}
+/* Returns whether the current PC has method JIT'd code. */
static inline void *
AtSafePoint(JSContext *cx)
{
JSStackFrame *fp = cx->fp();
if (fp->hasImacropc())
return false;
JSScript *script = fp->script();
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)
{
JSContext *cx = f.cx;
JSStackFrame *fp = cx->fp();
#ifdef DEBUG
JSScript *script = fp->script();
+ JS_ASSERT(!fp->finishedInInterpreter());
JS_ASSERT(fp->hasImacropc() ||
!script->maybeNativeCodeForPC(fp->isConstructing(), cx->regs->pc));
#endif
JSBool ok = JS_TRUE;
ok = Interpret(cx, fp, 0, JSINTERP_SAFEPOINT);
return ok;
}
JS_STATIC_ASSERT(JSOP_NOP == 0);
+/* Returns whether the current PC would return, popping the frame. */
static inline JSOp
FrameIsFinished(JSContext *cx)
{
JSOp op = JSOp(*cx->regs->pc);
return (op == JSOP_RETURN ||
op == JSOP_RETRVAL ||
op == JSOP_STOP)
? op
: 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);
- }
- }
+ if (!EvaluateExcessFrame(f, entryFrame)) {
+ if (!HandleErrorInExcessFrame(f, entryFrame))
+ return false;
}
}
return true;
}
#if JS_MONOIC
static void
@@ -817,28 +918,28 @@ RunTracer(VMFrame &f)
tpa = MonitorTracePoint(f.cx, inlineCallCount, blacklist);
JS_ASSERT(!TRACE_RECORDER(cx));
#if JS_MONOIC
if (blacklist)
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;
case TPA_RanStuff:
case TPA_Recorded:
break;
}
@@ -866,42 +967,36 @@ RunTracer(VMFrame &f)
*/
restart:
/* Step 1. Finish frames created after the entry frame. */
if (!FinishExcessFrames(f, entryFrame))
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);
}
goto restart;
}
#endif /* JS_TRACER */
--- a/js/src/methodjit/PolyIC.cpp
+++ b/js/src/methodjit/PolyIC.cpp
@@ -685,20 +685,20 @@ class SetPropCompiler : public PICStubCo
}
return generateStub(obj->shape(), shape, false, !obj->hasSlotsArray());
}
};
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
return GETPROP_INLINE_SHAPE_OFFSET;
#elif defined JS_PUNBOX64
return pic.labels.getprop.inlineShapeOffset;
#endif
}
@@ -728,26 +728,21 @@ class GetPropCompiler : public PICStubCo
}
inline int32 dslotsLoad() {
return dslotsLoad(pic);
}
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)
{ }
static void reset(ic::PICInfo &pic)
{
RepatchBuffer repatcher(pic.fastPathStart.executableAddress(), INLINE_PATH_LENGTH);
repatcher.repatchLEAToLoadPtr(pic.storeBack.instructionAtOffset(dslotsLoad(pic)));
repatcher.repatch(pic.fastPathStart.dataLabel32AtOffset(
@@ -2007,36 +2002,54 @@ class BindNameCompiler : public PICStubC
if (!generateStub(obj))
return NULL;
return obj;
}
};
+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)
{
JSScript *script = f.fp()->script();
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();
}
JSString *str = f.regs.sp[-1].toString();
f.regs.sp[-1].setInt32(str->length());
return;
} 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");
THROW();
}
f.regs.sp[-1].setNumber(obj->getArrayLength());
} else if (obj->isArguments()) {
if (!cc.generateArgsLengthStub()) {
@@ -2051,17 +2064,20 @@ ic::GetProp(VMFrame &f, ic::PICInfo *pic
atom = f.cx->runtime->atomState.lengthAtom;
}
JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-1]);
if (!obj)
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();
}
}
Value v;
if (!obj->getProperty(f.cx, ATOM_TO_JSID(atom), &v))
@@ -2095,26 +2111,26 @@ ic::GetElem(VMFrame &f, ic::PICInfo *pic
Value v;
if (!obj->getProperty(f.cx, ATOM_TO_JSID(atom), &v))
THROW();
f.regs.sp[-2] = v;
}
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
ic::SetProp(VMFrame &f, ic::PICInfo *pic)
{
JSObject *obj = ValueToObject(f.cx, &f.regs.sp[-2]);
if (!obj)
THROW();
@@ -2126,45 +2142,32 @@ ic::SetProp(VMFrame &f, ic::PICInfo *pic
// Important: We update the PIC before looking up the property so that the
// PIC is updated only if the property already exists. The PIC doesn't try
// to optimize adding new properties; that is for the slow case.
//
// Also note, we can't use SetName for PROPINC PICs because the property
// 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()) {
cc.disable("error");
THROW();
}
Value rval = f.regs.sp[-1];
stub(f, pic);
}
static void JS_FASTCALL
-CallPropSlow(VMFrame &f, ic::PICInfo *pic)
+DisabledCallPropIC(VMFrame &f, ic::PICInfo *pic)
{
stubs::CallProp(f, pic->atom);
}
void JS_FASTCALL
ic::CallProp(VMFrame &f, ic::PICInfo *pic)
{
JSContext *cx = f.cx;
@@ -2249,17 +2252,17 @@ ic::CallProp(VMFrame &f, ic::PICInfo *pi
&rval)) {
THROW();
}
regs.sp[-1] = lval;
regs.sp[-2] = rval;
}
}
- 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()) {
cc.disable("error");
THROW();
}
} else if (lval.isString()) {
if (!cc.generateStringCallStub()) {
@@ -2278,36 +2281,36 @@ ic::CallProp(VMFrame &f, ic::PICInfo *pi
regs.sp[-2].setString(ATOM_TO_STRING(pic->atom));
if (!js_OnUnknownMethod(cx, regs.sp - 2))
THROW();
}
#endif
}
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);
}
void JS_FASTCALL
ic::XName(VMFrame &f, ic::PICInfo *pic)
{
JSScript *script = f.fp()->script();
/* 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");
THROW();
}
Value rval;
if (!cc.retrieve(&rval))
@@ -2315,41 +2318,50 @@ ic::XName(VMFrame &f, ic::PICInfo *pic)
f.regs.sp[-1] = rval;
}
void JS_FASTCALL
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");
THROW();
}
Value rval;
if (!cc.retrieve(&rval))
THROW();
f.regs.sp[0] = rval;
}
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) {
cc.disable("error");
THROW();
}
f.regs.sp[0].setObject(*obj);
--- a/js/src/methodjit/PolyIC.h
+++ b/js/src/methodjit/PolyIC.h
@@ -231,16 +231,19 @@ struct PICInfo {
int secondShapeGuard : 11;
Kind kind : 3;
// True if register R holds the base object shape along exits from the
// 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
RegisterID shapeReg : 5; // also the out type reg
RegisterID objReg : 5; // also the out data reg
// Number of stubs generated.
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -94,16 +94,25 @@ stubs::BindName(VMFrame &f)
obj = js_FindIdentifierBase(cx, &f.fp()->scopeChain(), id);
if (!obj)
THROW();
}
f.regs.sp++;
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)
{
return f.fp()->scopeChain().getGlobal();
}
template<JSBool strict>
void JS_FASTCALL
@@ -2064,16 +2073,30 @@ InlineGetProp(VMFrame &f)
void JS_FASTCALL
stubs::GetProp(VMFrame &f)
{
if (!InlineGetProp(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)
{
JSContext *cx = f.cx;
JSFrameRegs ®s = f.regs;
Value lval;
lval = regs.sp[-1];
--- a/js/src/methodjit/StubCalls.h
+++ b/js/src/methodjit/StubCalls.h
@@ -60,16 +60,18 @@ void JS_FASTCALL InitMethod(VMFrame &f,
void JS_FASTCALL HitStackQuota(VMFrame &f);
void * JS_FASTCALL FixupArity(VMFrame &f, uint32 argc);
void * JS_FASTCALL CompileFunction(VMFrame &f, uint32 argc);
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.
*
* These functions can have one of two results:
*
* (1) The function was executed in the interpreter. Then all fields
* are NULL.
@@ -107,23 +109,25 @@ void * JS_FASTCALL InvokeTracer(VMFrame
#else
void * JS_FASTCALL InvokeTracer(VMFrame &f);
#endif
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);
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);
void JS_FASTCALL Length(VMFrame &f);
void JS_FASTCALL CallName(VMFrame &f);
void JS_FASTCALL GetUpvar(VMFrame &f, uint32 index);
void JS_FASTCALL GetGlobalName(VMFrame &f);
--- a/js/src/trace-test/tests/jaeger/bug563000/trap-force-return-1.js
+++ b/js/src/trace-test/tests/jaeger/bug563000/trap-force-return-1.js
@@ -1,7 +1,7 @@
setDebug(true);
function main() {
return "failure";
}
/* JSOP_RETURN in main. */
-trap(main, 4, "'success'");
+trap(main, 3, "'success'");
assertEq(main(), "success");
--- a/js/src/trace-test/tests/jaeger/bug563000/trap-parent-from-trap.js
+++ b/js/src/trace-test/tests/jaeger/bug563000/trap-parent-from-trap.js
@@ -5,17 +5,17 @@ function child() {
x = "failure1";
/* JSOP_STOP in parent. */
trap(parent, 10, "success()");
}
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";
}
parent();
assertEq(x, "success");
--- a/js/src/trace-test/tests/jaeger/bug563000/trap-self-from-trap.js
+++ b/js/src/trace-test/tests/jaeger/bug563000/trap-self-from-trap.js
@@ -1,23 +1,23 @@
setDebug(true);
x = "notset";
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";
}
myparent(false);
assertEq(x, "success");
new file mode 100644
--- /dev/null
+++ b/js/src/trace-test/tests/jaeger/deepBailAfterRunTracer.js
@@ -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);
+