mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 581893 - build a js::Invoke Gatling gun (r=waldo)
This commit is contained in:
parent
1f3d45495c
commit
cbc9601aa0
@ -1766,12 +1766,11 @@ js_MergeSort(void *src, size_t nel, size_t elsize,
|
|||||||
|
|
||||||
struct CompareArgs
|
struct CompareArgs
|
||||||
{
|
{
|
||||||
JSContext *context;
|
JSContext *context;
|
||||||
Value fval;
|
InvokeSessionGuard session;
|
||||||
InvokeArgsGuard args;
|
|
||||||
|
|
||||||
CompareArgs(JSContext *cx, const Value &fval)
|
CompareArgs(JSContext *cx)
|
||||||
: context(cx), fval(fval)
|
: context(cx)
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1792,17 +1791,15 @@ sort_compare(void *arg, const void *a, const void *b, int *result)
|
|||||||
if (!JS_CHECK_OPERATION_LIMIT(cx))
|
if (!JS_CHECK_OPERATION_LIMIT(cx))
|
||||||
return JS_FALSE;
|
return JS_FALSE;
|
||||||
|
|
||||||
CallArgs &args = ca->args;
|
InvokeSessionGuard &session = ca->session;
|
||||||
args.callee() = ca->fval;
|
session[0] = *av;
|
||||||
args.thisv().setNull();
|
session[1] = *bv;
|
||||||
args[0] = *av;
|
|
||||||
args[1] = *bv;
|
|
||||||
|
|
||||||
if (!Invoke(cx, ca->args, 0))
|
if (!session.invoke(cx))
|
||||||
return JS_FALSE;
|
return JS_FALSE;
|
||||||
|
|
||||||
jsdouble cmp;
|
jsdouble cmp;
|
||||||
if (!ValueToNumber(cx, args.rval(), &cmp))
|
if (!ValueToNumber(cx, session.rval(), &cmp))
|
||||||
return JS_FALSE;
|
return JS_FALSE;
|
||||||
|
|
||||||
/* Clamp cmp to -1, 0, 1. */
|
/* Clamp cmp to -1, 0, 1. */
|
||||||
@ -2040,10 +2037,8 @@ js::array_sort(JSContext *cx, uintN argc, Value *vp)
|
|||||||
} while (++i != newlen);
|
} while (++i != newlen);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
LeaveTrace(cx);
|
CompareArgs ca(cx);
|
||||||
|
if (!ca.session.start(cx, fval, NullValue(), 2))
|
||||||
CompareArgs ca(cx, fval);
|
|
||||||
if (!cx->stack().pushInvokeArgs(cx, 2, &ca.args))
|
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!js_MergeSort(vec, size_t(newlen), sizeof(Value),
|
if (!js_MergeSort(vec, size_t(newlen), sizeof(Value),
|
||||||
@ -2819,34 +2814,31 @@ array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, Value *vp)
|
|||||||
if (length == 0)
|
if (length == 0)
|
||||||
return JS_TRUE;
|
return JS_TRUE;
|
||||||
|
|
||||||
JSObject *thisp;
|
Value thisv;
|
||||||
if (argc > 1 && !REDUCE_MODE(mode)) {
|
if (argc > 1 && !REDUCE_MODE(mode)) {
|
||||||
|
JSObject *thisp;
|
||||||
if (!js_ValueToObjectOrNull(cx, argv[1], &thisp))
|
if (!js_ValueToObjectOrNull(cx, argv[1], &thisp))
|
||||||
return JS_FALSE;
|
return JS_FALSE;
|
||||||
argv[1].setObjectOrNull(thisp);
|
thisv.setObjectOrNull(thisp);
|
||||||
} else {
|
} else {
|
||||||
thisp = NULL;
|
thisv.setNull();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* For all but REDUCE, we call with 3 args (value, index, array). REDUCE
|
* For all but REDUCE, we call with 3 args (value, index, array). REDUCE
|
||||||
* requires 4 args (accum, value, index, array).
|
* requires 4 args (accum, value, index, array).
|
||||||
*/
|
*/
|
||||||
LeaveTrace(cx);
|
|
||||||
argc = 3 + REDUCE_MODE(mode);
|
argc = 3 + REDUCE_MODE(mode);
|
||||||
|
|
||||||
InvokeArgsGuard args;
|
InvokeSessionGuard session;
|
||||||
if (!cx->stack().pushInvokeArgs(cx, argc, &args))
|
if (!session.start(cx, ObjectValue(*callable), thisv, argc))
|
||||||
return JS_FALSE;
|
return JS_FALSE;
|
||||||
|
|
||||||
MUST_FLOW_THROUGH("out");
|
MUST_FLOW_THROUGH("out");
|
||||||
JSBool ok = JS_TRUE;
|
JSBool ok = JS_TRUE;
|
||||||
JSBool cond;
|
JSBool cond;
|
||||||
|
|
||||||
Value calleev, thisv, objv;
|
Value objv = ObjectValue(*obj);
|
||||||
calleev.setObject(*callable);
|
|
||||||
thisv.setObjectOrNull(thisp);
|
|
||||||
objv.setObject(*obj);
|
|
||||||
AutoValueRooter tvr(cx);
|
AutoValueRooter tvr(cx);
|
||||||
for (jsint i = start; i != end; i += step) {
|
for (jsint i = start; i != end; i += step) {
|
||||||
JSBool hole;
|
JSBool hole;
|
||||||
@ -2861,22 +2853,22 @@ array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, Value *vp)
|
|||||||
* Push callable and 'this', then args. We must do this for every
|
* Push callable and 'this', then args. We must do this for every
|
||||||
* iteration around the loop since Invoke clobbers its arguments.
|
* iteration around the loop since Invoke clobbers its arguments.
|
||||||
*/
|
*/
|
||||||
args.callee() = calleev;
|
uintN argi = 0;
|
||||||
args.thisv() = thisv;
|
|
||||||
Value *sp = args.argv();
|
|
||||||
if (REDUCE_MODE(mode))
|
if (REDUCE_MODE(mode))
|
||||||
*sp++ = *vp;
|
session[argi++] = *vp;
|
||||||
sp[0] = tvr.value();
|
session[argi++] = tvr.value();
|
||||||
sp[1].setInt32(i);
|
session[argi++] = Int32Value(i);
|
||||||
sp[2] = objv;
|
session[argi] = objv;
|
||||||
|
|
||||||
/* Do the call. */
|
/* Do the call. */
|
||||||
ok = Invoke(cx, args, 0);
|
ok = session.invoke(cx);
|
||||||
if (!ok)
|
if (!ok)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
const Value &rval = session.rval();
|
||||||
|
|
||||||
if (mode > MAP)
|
if (mode > MAP)
|
||||||
cond = js_ValueToBoolean(args.rval());
|
cond = js_ValueToBoolean(rval);
|
||||||
#ifdef __GNUC__ /* quell GCC overwarning */
|
#ifdef __GNUC__ /* quell GCC overwarning */
|
||||||
else
|
else
|
||||||
cond = JS_FALSE;
|
cond = JS_FALSE;
|
||||||
@ -2887,10 +2879,10 @@ array_extra(JSContext *cx, ArrayExtraMode mode, uintN argc, Value *vp)
|
|||||||
break;
|
break;
|
||||||
case REDUCE:
|
case REDUCE:
|
||||||
case REDUCE_RIGHT:
|
case REDUCE_RIGHT:
|
||||||
*vp = args.rval();
|
*vp = rval;
|
||||||
break;
|
break;
|
||||||
case MAP:
|
case MAP:
|
||||||
ok = SetArrayElement(cx, newarr, i, args.rval());
|
ok = SetArrayElement(cx, newarr, i, rval);
|
||||||
if (!ok)
|
if (!ok)
|
||||||
goto out;
|
goto out;
|
||||||
break;
|
break;
|
||||||
|
@ -541,9 +541,8 @@ class InvokeArgsGuard : public CallArgs
|
|||||||
JSStackFrame *prevInvokeFrame;
|
JSStackFrame *prevInvokeFrame;
|
||||||
#endif
|
#endif
|
||||||
public:
|
public:
|
||||||
inline InvokeArgsGuard() : cx(NULL), seg(NULL) {}
|
InvokeArgsGuard() : cx(NULL), seg(NULL) {}
|
||||||
inline InvokeArgsGuard(JSContext *cx, Value *vp, uintN argc);
|
~InvokeArgsGuard();
|
||||||
inline ~InvokeArgsGuard();
|
|
||||||
bool pushed() const { return cx != NULL; }
|
bool pushed() const { return cx != NULL; }
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -565,8 +564,9 @@ class InvokeFrameGuard
|
|||||||
JSFrameRegs *prevRegs_;
|
JSFrameRegs *prevRegs_;
|
||||||
public:
|
public:
|
||||||
InvokeFrameGuard() : cx_(NULL) {}
|
InvokeFrameGuard() : cx_(NULL) {}
|
||||||
JS_REQUIRES_STACK ~InvokeFrameGuard();
|
~InvokeFrameGuard() { if (pushed()) pop(); }
|
||||||
bool pushed() const { return cx_ != NULL; }
|
bool pushed() const { return cx_ != NULL; }
|
||||||
|
void pop();
|
||||||
JSStackFrame *fp() const { return regs_.fp; }
|
JSStackFrame *fp() const { return regs_.fp; }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -352,12 +352,12 @@ StackSpace::popInvokeFrame(const InvokeFrameGuard &fg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
JS_REQUIRES_STACK JS_ALWAYS_INLINE
|
JS_ALWAYS_INLINE void
|
||||||
InvokeFrameGuard::~InvokeFrameGuard()
|
InvokeFrameGuard::pop()
|
||||||
{
|
{
|
||||||
if (JS_UNLIKELY(!pushed()))
|
JS_ASSERT(pushed());
|
||||||
return;
|
|
||||||
cx_->stack().popInvokeFrame(*this);
|
cx_->stack().popInvokeFrame(*this);
|
||||||
|
cx_ = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
JS_REQUIRES_STACK JS_ALWAYS_INLINE JSStackFrame *
|
JS_REQUIRES_STACK JS_ALWAYS_INLINE JSStackFrame *
|
||||||
|
@ -586,37 +586,6 @@ NoSuchMethod(JSContext *cx, uintN argc, Value *vp, uint32 flags)
|
|||||||
|
|
||||||
namespace js {
|
namespace js {
|
||||||
|
|
||||||
class AutoPreserveEnumerators {
|
|
||||||
JSContext *cx;
|
|
||||||
JSObject *enumerators;
|
|
||||||
|
|
||||||
public:
|
|
||||||
AutoPreserveEnumerators(JSContext *cx) : cx(cx), enumerators(cx->enumerators)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
~AutoPreserveEnumerators()
|
|
||||||
{
|
|
||||||
cx->enumerators = enumerators;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct AutoInterpPreparer {
|
|
||||||
JSContext *cx;
|
|
||||||
JSScript *script;
|
|
||||||
|
|
||||||
AutoInterpPreparer(JSContext *cx, JSScript *script)
|
|
||||||
: cx(cx), script(script)
|
|
||||||
{
|
|
||||||
cx->interpLevel++;
|
|
||||||
}
|
|
||||||
|
|
||||||
~AutoInterpPreparer()
|
|
||||||
{
|
|
||||||
--cx->interpLevel;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
JS_REQUIRES_STACK bool
|
JS_REQUIRES_STACK bool
|
||||||
RunScript(JSContext *cx, JSScript *script, JSStackFrame *fp)
|
RunScript(JSContext *cx, JSScript *script, JSStackFrame *fp)
|
||||||
{
|
{
|
||||||
@ -652,6 +621,8 @@ RunScript(JSContext *cx, JSScript *script, JSStackFrame *fp)
|
|||||||
JS_REQUIRES_STACK bool
|
JS_REQUIRES_STACK bool
|
||||||
Invoke(JSContext *cx, const CallArgs &argsRef, uint32 flags)
|
Invoke(JSContext *cx, const CallArgs &argsRef, uint32 flags)
|
||||||
{
|
{
|
||||||
|
/* N.B. Must be kept in sync with InvokeSessionGuard::start/invoke */
|
||||||
|
|
||||||
CallArgs args = argsRef;
|
CallArgs args = argsRef;
|
||||||
JS_ASSERT(args.argc() <= JS_ARGS_LENGTH_MAX);
|
JS_ASSERT(args.argc() <= JS_ARGS_LENGTH_MAX);
|
||||||
|
|
||||||
@ -685,10 +656,8 @@ Invoke(JSContext *cx, const CallArgs &argsRef, uint32 flags)
|
|||||||
return CallJSNative(cx, fun->u.n.native, args.argc(), args.base());
|
return CallJSNative(cx, fun->u.n.native, args.argc(), args.base());
|
||||||
}
|
}
|
||||||
|
|
||||||
JS_ASSERT(fun->isInterpreted());
|
|
||||||
JSScript *script = fun->u.i.script;
|
|
||||||
|
|
||||||
/* Handle the empty-script special case. */
|
/* Handle the empty-script special case. */
|
||||||
|
JSScript *script = fun->script();
|
||||||
if (JS_UNLIKELY(script->isEmpty())) {
|
if (JS_UNLIKELY(script->isEmpty())) {
|
||||||
if (flags & JSINVOKE_CONSTRUCT) {
|
if (flags & JSINVOKE_CONSTRUCT) {
|
||||||
JSObject *obj = js_CreateThisForFunction(cx, &callee);
|
JSObject *obj = js_CreateThisForFunction(cx, &callee);
|
||||||
@ -719,6 +688,8 @@ Invoke(JSContext *cx, const CallArgs &argsRef, uint32 flags)
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
* FIXME bug 592992: hoist to ExternalInvoke
|
||||||
|
*
|
||||||
* Compute |this|. Currently, this must happen after the frame is pushed
|
* Compute |this|. Currently, this must happen after the frame is pushed
|
||||||
* and fp->scopeChain is correct because the thisObject hook may call
|
* and fp->scopeChain is correct because the thisObject hook may call
|
||||||
* JS_GetScopeChain.
|
* JS_GetScopeChain.
|
||||||
@ -767,6 +738,98 @@ Invoke(JSContext *cx, const CallArgs &argsRef, uint32 flags)
|
|||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
InvokeSessionGuard::start(JSContext *cx, const Value &calleev, const Value &thisv, uintN argc)
|
||||||
|
{
|
||||||
|
#ifdef JS_TRACER
|
||||||
|
if (TRACE_RECORDER(cx))
|
||||||
|
AbortRecording(cx, "attempt to reenter VM while recording");
|
||||||
|
LeaveTrace(cx);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Always push arguments, regardless of optimized/normal invoke. */
|
||||||
|
StackSpace &stack = cx->stack();
|
||||||
|
if (!stack.pushInvokeArgs(cx, argc, &args_))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
do {
|
||||||
|
/* Hoist dynamic checks from scripted Invoke. */
|
||||||
|
if (!calleev.isObject())
|
||||||
|
break;
|
||||||
|
JSObject &callee = calleev.toObject();
|
||||||
|
if (callee.getClass() != &js_FunctionClass)
|
||||||
|
break;
|
||||||
|
JSFunction *fun = callee.getFunctionPrivate();
|
||||||
|
if (fun->isNative())
|
||||||
|
break;
|
||||||
|
script_ = fun->script();
|
||||||
|
if (fun->isHeavyweight() || script_->isEmpty() || cx->compartment->debugMode)
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Set (callee, this) once for the session (before args are duped). */
|
||||||
|
args_.callee().setObject(callee);
|
||||||
|
args_.thisv() = thisv;
|
||||||
|
|
||||||
|
/* Push the stack frame once for the session. */
|
||||||
|
uint32 flags = 0;
|
||||||
|
if (!stack.getInvokeFrame(cx, args_, fun, script_, &flags, &frame_))
|
||||||
|
return false;
|
||||||
|
JSStackFrame *fp = frame_.fp();
|
||||||
|
fp->initCallFrame(cx, calleev.toObject(), fun, argc, flags);
|
||||||
|
stack.pushInvokeFrame(cx, args_, &frame_);
|
||||||
|
|
||||||
|
// FIXME bug 592992: hoist thisObject hook to ExternalInvoke
|
||||||
|
if (thisv.isObject()) {
|
||||||
|
JSObject *thisp = thisv.toObject().thisObject(cx);
|
||||||
|
if (!thisp)
|
||||||
|
return false;
|
||||||
|
JS_ASSERT(IsSaneThisObject(*thisp));
|
||||||
|
fp->functionThis().setObject(*thisp);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef JS_METHODJIT
|
||||||
|
/* Hoist dynamic checks from RunScript. */
|
||||||
|
mjit::CompileStatus status = mjit::CanMethodJIT(cx, script_, fp);
|
||||||
|
if (status == mjit::Compile_Error)
|
||||||
|
return false;
|
||||||
|
if (status != mjit::Compile_Okay)
|
||||||
|
break;
|
||||||
|
code_ = script_->getJIT(fp->isConstructing())->invokeEntry;
|
||||||
|
|
||||||
|
/* Hoist dynamic checks from CheckStackAndEnterMethodJIT. */
|
||||||
|
JS_CHECK_RECURSION(cx, return JS_FALSE);
|
||||||
|
stackLimit_ = stack.getStackLimit(cx);
|
||||||
|
if (!stackLimit_)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
stop_ = script_->code + script_->length - 1;
|
||||||
|
JS_ASSERT(*stop_ == JSOP_STOP);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Cached to avoid canonicalActualArg in InvokeSessionGuard::operator[]. */
|
||||||
|
nformals_ = fp->numFormalArgs();
|
||||||
|
formals_ = fp->formalArgs();
|
||||||
|
actuals_ = args_.argv();
|
||||||
|
JS_ASSERT(actuals_ == fp->actualArgs());
|
||||||
|
return true;
|
||||||
|
} while (0);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Use the normal invoke path.
|
||||||
|
*
|
||||||
|
* The callee slot gets overwritten during an unoptimized Invoke, so we
|
||||||
|
* cache it here and restore it before every Invoke call. The 'this' value
|
||||||
|
* does not get overwritten, so we can fill it here once.
|
||||||
|
*/
|
||||||
|
if (frame_.pushed())
|
||||||
|
frame_.pop();
|
||||||
|
args_.thisv() = thisv;
|
||||||
|
savedCallee_ = calleev;
|
||||||
|
formals_ = actuals_ = args_.argv();
|
||||||
|
nformals_ = (unsigned)-1;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
ExternalInvoke(JSContext *cx, const Value &thisv, const Value &fval,
|
ExternalInvoke(JSContext *cx, const Value &thisv, const Value &fval,
|
||||||
uintN argc, Value *argv, Value *rval)
|
uintN argc, Value *argv, Value *rval)
|
||||||
|
@ -191,6 +191,9 @@ struct JSStackFrame
|
|||||||
inline void initCallFrame(JSContext *cx, JSObject &callee, JSFunction *fun,
|
inline void initCallFrame(JSContext *cx, JSObject &callee, JSFunction *fun,
|
||||||
uint32 nactual, uint32 flags);
|
uint32 nactual, uint32 flags);
|
||||||
|
|
||||||
|
/* Used for SessionInvoke. */
|
||||||
|
inline void resetInvokeCallFrame();
|
||||||
|
|
||||||
/* Called by method-jit stubs and serve as a specification for jit-code. */
|
/* Called by method-jit stubs and serve as a specification for jit-code. */
|
||||||
inline void initCallFrameCallerHalf(JSContext *cx, uint32 nactual, uint32 flags);
|
inline void initCallFrameCallerHalf(JSContext *cx, uint32 nactual, uint32 flags);
|
||||||
inline void initCallFrameEarlyPrologue(JSFunction *fun, void *ncode);
|
inline void initCallFrameEarlyPrologue(JSFunction *fun, void *ncode);
|
||||||
@ -368,7 +371,8 @@ struct JSStackFrame
|
|||||||
template <class Op> inline void forEachCanonicalActualArg(Op op);
|
template <class Op> inline void forEachCanonicalActualArg(Op op);
|
||||||
template <class Op> inline void forEachFormalArg(Op op);
|
template <class Op> inline void forEachFormalArg(Op op);
|
||||||
|
|
||||||
/* True if we have created an arguments object for this frame; implies hasArgs(). */
|
inline void clearMissingArgs();
|
||||||
|
|
||||||
bool hasArgsObj() const {
|
bool hasArgsObj() const {
|
||||||
return !!(flags_ & JSFRAME_HAS_ARGS_OBJ);
|
return !!(flags_ & JSFRAME_HAS_ARGS_OBJ);
|
||||||
}
|
}
|
||||||
@ -558,7 +562,7 @@ struct JSStackFrame
|
|||||||
|
|
||||||
/* Return value */
|
/* Return value */
|
||||||
|
|
||||||
const js::Value& returnValue() {
|
const js::Value &returnValue() {
|
||||||
if (!(flags_ & JSFRAME_HAS_RVAL))
|
if (!(flags_ & JSFRAME_HAS_RVAL))
|
||||||
rval_.setUndefined();
|
rval_.setUndefined();
|
||||||
return rval_;
|
return rval_;
|
||||||
@ -885,6 +889,32 @@ struct CallArgs
|
|||||||
extern JS_REQUIRES_STACK bool
|
extern JS_REQUIRES_STACK bool
|
||||||
Invoke(JSContext *cx, const CallArgs &args, uint32 flags);
|
Invoke(JSContext *cx, const CallArgs &args, uint32 flags);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Natives like sort/forEach/replace call Invoke repeatedly with the same
|
||||||
|
* callee, this, and number of arguments. To optimize this, such natives can
|
||||||
|
* start an "invoke session" to factor out much of the dynamic setup logic
|
||||||
|
* required by a normal Invoke. Usage is:
|
||||||
|
*
|
||||||
|
* InvokeSessionGuard session(cx);
|
||||||
|
* if (!session.start(cx, callee, thisp, argc, &session))
|
||||||
|
* ...
|
||||||
|
*
|
||||||
|
* while (...) {
|
||||||
|
* // write actual args (not callee, this)
|
||||||
|
* session[0] = ...
|
||||||
|
* ...
|
||||||
|
* session[argc - 1] = ...
|
||||||
|
*
|
||||||
|
* if (!session.invoke(cx, session))
|
||||||
|
* ...
|
||||||
|
*
|
||||||
|
* ... = session.rval();
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* // session ended by ~InvokeSessionGuard
|
||||||
|
*/
|
||||||
|
class InvokeSessionGuard;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Consolidated js_Invoke flags simply rename certain JSFRAME_* flags, so that
|
* Consolidated js_Invoke flags simply rename certain JSFRAME_* flags, so that
|
||||||
* we can share bits stored in JSStackFrame.flags and passed to:
|
* we can share bits stored in JSStackFrame.flags and passed to:
|
||||||
|
@ -40,6 +40,9 @@
|
|||||||
#ifndef jsinterpinlines_h__
|
#ifndef jsinterpinlines_h__
|
||||||
#define jsinterpinlines_h__
|
#define jsinterpinlines_h__
|
||||||
|
|
||||||
|
#include "jsprobes.h"
|
||||||
|
#include "methodjit/MethodJIT.h"
|
||||||
|
|
||||||
inline void
|
inline void
|
||||||
JSStackFrame::initPrev(JSContext *cx)
|
JSStackFrame::initPrev(JSContext *cx)
|
||||||
{
|
{
|
||||||
@ -85,6 +88,42 @@ JSStackFrame::initCallFrame(JSContext *cx, JSObject &callee, JSFunction *fun,
|
|||||||
JS_ASSERT(!hasCallObj());
|
JS_ASSERT(!hasCallObj());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void
|
||||||
|
JSStackFrame::resetInvokeCallFrame()
|
||||||
|
{
|
||||||
|
/* Undo changes to frame made during execution; see initCallFrame */
|
||||||
|
|
||||||
|
if (hasArgsObj())
|
||||||
|
args.nactual = argsObj().getArgsInitialLength();
|
||||||
|
|
||||||
|
JS_ASSERT(!(flags_ & ~(JSFRAME_FUNCTION |
|
||||||
|
JSFRAME_OVERFLOW_ARGS |
|
||||||
|
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)));
|
||||||
|
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())
|
||||||
|
scopeChain_ = callObj().getParent();
|
||||||
|
|
||||||
|
JS_ASSERT(exec.fun == calleeValue().toObject().getFunctionPrivate());
|
||||||
|
JS_ASSERT(!hasImacropc());
|
||||||
|
JS_ASSERT(!hasHookData());
|
||||||
|
JS_ASSERT(annotation() == NULL);
|
||||||
|
JS_ASSERT(!hasCallObj());
|
||||||
|
}
|
||||||
|
|
||||||
inline void
|
inline void
|
||||||
JSStackFrame::initCallFrameCallerHalf(JSContext *cx, uint32 nactual, uint32 flagsArg)
|
JSStackFrame::initCallFrameCallerHalf(JSContext *cx, uint32 nactual, uint32 flagsArg)
|
||||||
{
|
{
|
||||||
@ -269,6 +308,13 @@ JSStackFrame::forEachFormalArg(Op op)
|
|||||||
op(i, p);
|
op(i, p);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JS_ALWAYS_INLINE void
|
||||||
|
JSStackFrame::clearMissingArgs()
|
||||||
|
{
|
||||||
|
if (flags_ & JSFRAME_UNDERFLOW_ARGS)
|
||||||
|
SetValueRangeToUndefined(formalArgs() + numActualArgs(), formalArgsEnd());
|
||||||
|
}
|
||||||
|
|
||||||
inline JSObject *
|
inline JSObject *
|
||||||
JSStackFrame::computeThisObject(JSContext *cx)
|
JSStackFrame::computeThisObject(JSContext *cx)
|
||||||
{
|
{
|
||||||
@ -394,6 +440,37 @@ JSStackFrame::maybeCallObj() const
|
|||||||
|
|
||||||
namespace js {
|
namespace js {
|
||||||
|
|
||||||
|
class AutoPreserveEnumerators {
|
||||||
|
JSContext *cx;
|
||||||
|
JSObject *enumerators;
|
||||||
|
|
||||||
|
public:
|
||||||
|
AutoPreserveEnumerators(JSContext *cx) : cx(cx), enumerators(cx->enumerators)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~AutoPreserveEnumerators()
|
||||||
|
{
|
||||||
|
cx->enumerators = enumerators;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct AutoInterpPreparer {
|
||||||
|
JSContext *cx;
|
||||||
|
JSScript *script;
|
||||||
|
|
||||||
|
AutoInterpPreparer(JSContext *cx, JSScript *script)
|
||||||
|
: cx(cx), script(script)
|
||||||
|
{
|
||||||
|
cx->interpLevel++;
|
||||||
|
}
|
||||||
|
|
||||||
|
~AutoInterpPreparer()
|
||||||
|
{
|
||||||
|
--cx->interpLevel;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
inline void
|
inline void
|
||||||
PutActivationObjects(JSContext *cx, JSStackFrame *fp)
|
PutActivationObjects(JSContext *cx, JSStackFrame *fp)
|
||||||
{
|
{
|
||||||
@ -407,6 +484,85 @@ PutActivationObjects(JSContext *cx, JSStackFrame *fp)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class InvokeSessionGuard
|
||||||
|
{
|
||||||
|
InvokeArgsGuard args_;
|
||||||
|
InvokeFrameGuard frame_;
|
||||||
|
Value savedCallee_;
|
||||||
|
Value *formals_, *actuals_;
|
||||||
|
unsigned nformals_;
|
||||||
|
JSScript *script_;
|
||||||
|
void *code_;
|
||||||
|
Value *stackLimit_;
|
||||||
|
jsbytecode *stop_;
|
||||||
|
|
||||||
|
bool optimized() const { return frame_.pushed(); }
|
||||||
|
|
||||||
|
public:
|
||||||
|
InvokeSessionGuard() : args_(), frame_() {}
|
||||||
|
~InvokeSessionGuard() {}
|
||||||
|
|
||||||
|
bool start(JSContext *cx, const Value &callee, const Value &thisv, uintN argc);
|
||||||
|
bool invoke(JSContext *cx) const;
|
||||||
|
|
||||||
|
bool started() const {
|
||||||
|
return args_.pushed();
|
||||||
|
}
|
||||||
|
|
||||||
|
Value &operator[](unsigned i) const {
|
||||||
|
JS_ASSERT(i < argc());
|
||||||
|
Value &arg = i < nformals_ ? formals_[i] : actuals_[i];
|
||||||
|
JS_ASSERT_IF(optimized(), &arg == &frame_.fp()->canonicalActualArg(i));
|
||||||
|
JS_ASSERT_IF(!optimized(), &arg == &args_[i]);
|
||||||
|
return arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
uintN argc() const {
|
||||||
|
return args_.argc();
|
||||||
|
}
|
||||||
|
|
||||||
|
const Value &rval() const {
|
||||||
|
return optimized() ? frame_.fp()->returnValue() : args_.rval();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
inline bool
|
||||||
|
InvokeSessionGuard::invoke(JSContext *cx) const
|
||||||
|
{
|
||||||
|
/* N.B. Must be kept in sync with Invoke */
|
||||||
|
|
||||||
|
if (!optimized()) {
|
||||||
|
args_.callee() = savedCallee_;
|
||||||
|
return Invoke(cx, args_, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Clear any garbage left from the last Invoke. */
|
||||||
|
JSStackFrame *fp = frame_.fp();
|
||||||
|
fp->clearMissingArgs();
|
||||||
|
fp->resetInvokeCallFrame();
|
||||||
|
SetValueRangeToUndefined(fp->slots(), script_->nfixed);
|
||||||
|
|
||||||
|
JSBool ok;
|
||||||
|
{
|
||||||
|
AutoPreserveEnumerators preserve(cx);
|
||||||
|
Probes::enterJSFun(cx, fp->fun());
|
||||||
|
#ifdef JS_METHODJIT
|
||||||
|
AutoInterpPreparer prepareInterp(cx, script_);
|
||||||
|
ok = mjit::EnterMethodJIT(cx, fp, code_, stackLimit_);
|
||||||
|
cx->regs->pc = stop_;
|
||||||
|
#else
|
||||||
|
cx->regs->pc = script_->code;
|
||||||
|
ok = Interpret(cx, cx->fp());
|
||||||
|
#endif
|
||||||
|
Probes::exitJSFun(cx, fp->fun());
|
||||||
|
}
|
||||||
|
|
||||||
|
PutActivationObjects(cx, fp);
|
||||||
|
|
||||||
|
/* Don't clobber callee with rval; rval gets read from fp->rval. */
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* jsinterpinlines_h__ */
|
#endif /* jsinterpinlines_h__ */
|
||||||
|
@ -128,6 +128,7 @@ class AutoStringRooter;
|
|||||||
class ExecuteArgsGuard;
|
class ExecuteArgsGuard;
|
||||||
class InvokeFrameGuard;
|
class InvokeFrameGuard;
|
||||||
class InvokeArgsGuard;
|
class InvokeArgsGuard;
|
||||||
|
class InvokeSessionGuard;
|
||||||
class TraceRecorder;
|
class TraceRecorder;
|
||||||
struct TraceMonitor;
|
struct TraceMonitor;
|
||||||
class StackSpace;
|
class StackSpace;
|
||||||
|
@ -77,6 +77,7 @@
|
|||||||
#include "jsversion.h"
|
#include "jsversion.h"
|
||||||
|
|
||||||
#include "jscntxtinlines.h"
|
#include "jscntxtinlines.h"
|
||||||
|
#include "jsinterpinlines.h"
|
||||||
#include "jsobjinlines.h"
|
#include "jsobjinlines.h"
|
||||||
#include "jsstrinlines.h"
|
#include "jsstrinlines.h"
|
||||||
#include "jsregexpinlines.h"
|
#include "jsregexpinlines.h"
|
||||||
@ -1977,18 +1978,19 @@ struct ReplaceData
|
|||||||
: g(cx), cb(cx)
|
: g(cx), cb(cx)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
JSString *str; /* 'this' parameter object as a string */
|
JSString *str; /* 'this' parameter object as a string */
|
||||||
RegExpGuard g; /* regexp parameter object and private data */
|
RegExpGuard g; /* regexp parameter object and private data */
|
||||||
JSObject *lambda; /* replacement function object or null */
|
JSObject *lambda; /* replacement function object or null */
|
||||||
JSString *repstr; /* replacement string */
|
JSString *repstr; /* replacement string */
|
||||||
jschar *dollar; /* null or pointer to first $ in repstr */
|
jschar *dollar; /* null or pointer to first $ in repstr */
|
||||||
jschar *dollarEnd; /* limit pointer for js_strchr_limit */
|
jschar *dollarEnd; /* limit pointer for js_strchr_limit */
|
||||||
jsint index; /* index in result of next replacement */
|
jsint index; /* index in result of next replacement */
|
||||||
jsint leftIndex; /* left context index in str->chars */
|
jsint leftIndex; /* left context index in str->chars */
|
||||||
JSSubString dollarStr; /* for "$$" InterpretDollar result */
|
JSSubString dollarStr; /* for "$$" InterpretDollar result */
|
||||||
bool calledBack; /* record whether callback has been called */
|
bool calledBack; /* record whether callback has been called */
|
||||||
InvokeArgsGuard args; /* arguments for lambda's js_Invoke call */
|
InvokeSessionGuard session; /* arguments for repeated lambda Invoke call */
|
||||||
JSCharBuffer cb; /* buffer built during DoMatch */
|
InvokeArgsGuard singleShot; /* arguments for single lambda Invoke call */
|
||||||
|
JSCharBuffer cb; /* buffer built during DoMatch */
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
@ -2076,8 +2078,6 @@ FindReplaceLength(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, size_t
|
|||||||
{
|
{
|
||||||
JSObject *lambda = rdata.lambda;
|
JSObject *lambda = rdata.lambda;
|
||||||
if (lambda) {
|
if (lambda) {
|
||||||
LeaveTrace(cx);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* In the lambda case, not only do we find the replacement string's
|
* In the lambda case, not only do we find the replacement string's
|
||||||
* length, we compute repstr and return it via rdata for use within
|
* length, we compute repstr and return it via rdata for use within
|
||||||
@ -2089,32 +2089,33 @@ FindReplaceLength(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, size_t
|
|||||||
uintN p = res->getParenCount();
|
uintN p = res->getParenCount();
|
||||||
uintN argc = 1 + p + 2;
|
uintN argc = 1 + p + 2;
|
||||||
|
|
||||||
if (!rdata.args.pushed() && !cx->stack().pushInvokeArgs(cx, argc, &rdata.args))
|
if (!rdata.session.started()) {
|
||||||
return false;
|
Value lambdav = ObjectValue(*lambda);
|
||||||
|
if (!rdata.session.start(cx, lambdav, NullValue(), argc))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
PreserveRegExpStatics save(res);
|
PreserveRegExpStatics save(res);
|
||||||
|
|
||||||
/* Push lambda and its 'this' parameter. */
|
/* Push lambda and its 'this' parameter. */
|
||||||
CallArgs &args = rdata.args;
|
InvokeSessionGuard &session = rdata.session;
|
||||||
args.callee().setObject(*lambda);
|
|
||||||
args.thisv().setNull();
|
|
||||||
|
|
||||||
Value *sp = args.argv();
|
uintN argi = 0;
|
||||||
|
|
||||||
/* Push $&, $1, $2, ... */
|
/* Push $&, $1, $2, ... */
|
||||||
if (!res->createLastMatch(cx, sp++))
|
if (!res->createLastMatch(cx, &session[argi++]))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
for (size_t i = 0; i < res->getParenCount(); ++i) {
|
for (size_t i = 0; i < res->getParenCount(); ++i) {
|
||||||
if (!res->createParen(cx, i, sp++))
|
if (!res->createParen(cx, i, &session[argi++]))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Push match index and input string. */
|
/* Push match index and input string. */
|
||||||
sp[0].setInt32(res->get(0, 0));
|
session[argi++].setInt32(res->get(0, 0));
|
||||||
sp[1].setString(rdata.str);
|
session[argi].setString(rdata.str);
|
||||||
|
|
||||||
if (!Invoke(cx, rdata.args, 0))
|
if (!session.invoke(cx))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -2122,7 +2123,7 @@ FindReplaceLength(JSContext *cx, RegExpStatics *res, ReplaceData &rdata, size_t
|
|||||||
* created by this js_ValueToString that would otherwise be GC-
|
* created by this js_ValueToString that would otherwise be GC-
|
||||||
* able, until we use rdata.repstr in DoReplace.
|
* able, until we use rdata.repstr in DoReplace.
|
||||||
*/
|
*/
|
||||||
JSString *repstr = js_ValueToString(cx, args.rval());
|
JSString *repstr = js_ValueToString(cx, session.rval());
|
||||||
if (!repstr)
|
if (!repstr)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@ -2402,10 +2403,10 @@ str_replace_flat_lambda(JSContext *cx, uintN argc, Value *vp, ReplaceData &rdata
|
|||||||
|
|
||||||
/* lambda(matchStr, matchStart, textstr) */
|
/* lambda(matchStr, matchStart, textstr) */
|
||||||
static const uint32 lambdaArgc = 3;
|
static const uint32 lambdaArgc = 3;
|
||||||
if (!cx->stack().pushInvokeArgs(cx, lambdaArgc, &rdata.args))
|
if (!cx->stack().pushInvokeArgs(cx, lambdaArgc, &rdata.singleShot))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
CallArgs &args = rdata.args;
|
CallArgs &args = rdata.singleShot;
|
||||||
args.callee().setObject(*rdata.lambda);
|
args.callee().setObject(*rdata.lambda);
|
||||||
args.thisv().setNull();
|
args.thisv().setNull();
|
||||||
|
|
||||||
@ -2414,7 +2415,7 @@ str_replace_flat_lambda(JSContext *cx, uintN argc, Value *vp, ReplaceData &rdata
|
|||||||
sp[1].setInt32(fm.match());
|
sp[1].setInt32(fm.match());
|
||||||
sp[2].setString(rdata.str);
|
sp[2].setString(rdata.str);
|
||||||
|
|
||||||
if (!Invoke(cx, rdata.args, 0))
|
if (!Invoke(cx, rdata.singleShot, 0))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
JSString *repstr = js_ValueToString(cx, args.rval());
|
JSString *repstr = js_ValueToString(cx, args.rval());
|
||||||
|
@ -720,15 +720,12 @@ ThreadData::Finish()
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "C" JSBool JaegerTrampoline(JSContext *cx, JSStackFrame *fp, void *code,
|
extern "C" JSBool
|
||||||
Value *stackLimit);
|
JaegerTrampoline(JSContext *cx, JSStackFrame *fp, void *code, Value *stackLimit);
|
||||||
|
|
||||||
static inline JSBool
|
JSBool
|
||||||
EnterMethodJIT(JSContext *cx, JSStackFrame *fp, void *code)
|
mjit::EnterMethodJIT(JSContext *cx, JSStackFrame *fp, void *code, Value *stackLimit)
|
||||||
{
|
{
|
||||||
JS_ASSERT(cx->regs);
|
|
||||||
JS_CHECK_RECURSION(cx, return JS_FALSE;);
|
|
||||||
|
|
||||||
#ifdef JS_METHODJIT_SPEW
|
#ifdef JS_METHODJIT_SPEW
|
||||||
Profiler prof;
|
Profiler prof;
|
||||||
JSScript *script = fp->script();
|
JSScript *script = fp->script();
|
||||||
@ -738,10 +735,7 @@ EnterMethodJIT(JSContext *cx, JSStackFrame *fp, void *code)
|
|||||||
prof.start();
|
prof.start();
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
Value *stackLimit = cx->stack().getStackLimit(cx);
|
JS_ASSERT(cx->regs->fp == fp);
|
||||||
if (!stackLimit)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
JSFrameRegs *oldRegs = cx->regs;
|
JSFrameRegs *oldRegs = cx->regs;
|
||||||
|
|
||||||
JSAutoResolveFlags rf(cx, JSRESOLVE_INFER);
|
JSAutoResolveFlags rf(cx, JSRESOLVE_INFER);
|
||||||
@ -761,6 +755,18 @@ EnterMethodJIT(JSContext *cx, JSStackFrame *fp, void *code)
|
|||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline JSBool
|
||||||
|
CheckStackAndEnterMethodJIT(JSContext *cx, JSStackFrame *fp, void *code)
|
||||||
|
{
|
||||||
|
JS_CHECK_RECURSION(cx, return JS_FALSE;);
|
||||||
|
|
||||||
|
Value *stackLimit = cx->stack().getStackLimit(cx);
|
||||||
|
if (!stackLimit)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return EnterMethodJIT(cx, fp, code, stackLimit);
|
||||||
|
}
|
||||||
|
|
||||||
JSBool
|
JSBool
|
||||||
mjit::JaegerShot(JSContext *cx)
|
mjit::JaegerShot(JSContext *cx)
|
||||||
{
|
{
|
||||||
@ -775,7 +781,7 @@ mjit::JaegerShot(JSContext *cx)
|
|||||||
|
|
||||||
JS_ASSERT(cx->regs->pc == script->code);
|
JS_ASSERT(cx->regs->pc == script->code);
|
||||||
|
|
||||||
return EnterMethodJIT(cx, cx->fp(), jit->invokeEntry);
|
return CheckStackAndEnterMethodJIT(cx, cx->fp(), jit->invokeEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
JSBool
|
JSBool
|
||||||
@ -785,7 +791,7 @@ js::mjit::JaegerShotAtSafePoint(JSContext *cx, void *safePoint)
|
|||||||
JS_ASSERT(!TRACE_RECORDER(cx));
|
JS_ASSERT(!TRACE_RECORDER(cx));
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
return EnterMethodJIT(cx, cx->fp(), safePoint);
|
return CheckStackAndEnterMethodJIT(cx, cx->fp(), safePoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
|
@ -216,6 +216,12 @@ struct JITScript {
|
|||||||
void release();
|
void release();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Execute the given mjit code. This is a low-level call and callers must
|
||||||
|
* provide the same guarantees as JaegerShot/CheckStackAndEnterMethodJIT.
|
||||||
|
*/
|
||||||
|
JSBool EnterMethodJIT(JSContext *cx, JSStackFrame *fp, void *code, Value *stackLimit);
|
||||||
|
|
||||||
/* Execute a method that has been JIT compiled. */
|
/* Execute a method that has been JIT compiled. */
|
||||||
JSBool JaegerShot(JSContext *cx);
|
JSBool JaegerShot(JSContext *cx);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user