mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
407b2242d7
--HG-- extra : rebase_source : bd664d9cd752992faa280d8d3a848058883f57e4
569 lines
16 KiB
C++
569 lines
16 KiB
C++
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
*
|
|
* ***** 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/
|
|
*
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
* for the specific language governing rights and limitations under the
|
|
* License.
|
|
*
|
|
* The Original Code is SpiderMonkey code.
|
|
*
|
|
* The Initial Developer of the Original Code is
|
|
* Mozilla Corporation.
|
|
* Portions created by the Initial Developer are Copyright (C) 2010
|
|
* the Initial Developer. All Rights Reserved.
|
|
*
|
|
* Contributor(s):
|
|
* Luke Wagner <lw@mozilla.com>
|
|
*
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
|
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
* the provisions above, a recipient may use your version of this file under
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
*
|
|
* ***** END LICENSE BLOCK ***** */
|
|
|
|
#ifndef jsinterpinlines_h__
|
|
#define jsinterpinlines_h__
|
|
|
|
#include "jsprobes.h"
|
|
#include "methodjit/MethodJIT.h"
|
|
|
|
inline void
|
|
JSStackFrame::initPrev(JSContext *cx)
|
|
{
|
|
JS_ASSERT(flags_ & JSFRAME_HAS_PREVPC);
|
|
if (JSFrameRegs *regs = cx->regs) {
|
|
prev_ = regs->fp;
|
|
prevpc_ = regs->pc;
|
|
JS_ASSERT_IF(!prev_->isDummyFrame() && !prev_->hasImacropc(),
|
|
uint32(prevpc_ - prev_->script()->code) < prev_->script()->length);
|
|
} else {
|
|
prev_ = NULL;
|
|
#ifdef DEBUG
|
|
prevpc_ = (jsbytecode *)0xbadc;
|
|
#endif
|
|
}
|
|
}
|
|
|
|
inline void
|
|
JSStackFrame::resetGeneratorPrev(JSContext *cx)
|
|
{
|
|
initPrev(cx);
|
|
}
|
|
|
|
inline void
|
|
JSStackFrame::initCallFrame(JSContext *cx, JSObject &callee, JSFunction *fun,
|
|
uint32 nactual, uint32 flagsArg)
|
|
{
|
|
JS_ASSERT((flagsArg & ~(JSFRAME_CONSTRUCTING |
|
|
JSFRAME_OVERFLOW_ARGS |
|
|
JSFRAME_UNDERFLOW_ARGS)) == 0);
|
|
JS_ASSERT(fun == callee.getFunctionPrivate());
|
|
|
|
/* Initialize stack frame members. */
|
|
flags_ = JSFRAME_FUNCTION | JSFRAME_HAS_PREVPC | JSFRAME_HAS_SCOPECHAIN | flagsArg;
|
|
exec.fun = fun;
|
|
args.nactual = nactual; /* only need to write if over/under-flow */
|
|
scopeChain_ = callee.getParent();
|
|
initPrev(cx);
|
|
JS_ASSERT(!hasImacropc());
|
|
JS_ASSERT(!hasHookData());
|
|
JS_ASSERT(annotation() == NULL);
|
|
|
|
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
|
|
JSStackFrame::initCallFrameCallerHalf(JSContext *cx, uint32 nactual, uint32 flagsArg)
|
|
{
|
|
JS_ASSERT((flagsArg & ~(JSFRAME_CONSTRUCTING |
|
|
JSFRAME_FUNCTION |
|
|
JSFRAME_OVERFLOW_ARGS |
|
|
JSFRAME_UNDERFLOW_ARGS)) == 0);
|
|
JSFrameRegs *regs = cx->regs;
|
|
|
|
/* Initialize the caller half of the stack frame members. */
|
|
flags_ = JSFRAME_FUNCTION | flagsArg;
|
|
args.nactual = nactual; /* only need to write if over/under-flow */
|
|
prev_ = regs->fp;
|
|
JS_ASSERT(!hasImacropc());
|
|
JS_ASSERT(!hasHookData());
|
|
JS_ASSERT(annotation() == NULL);
|
|
|
|
JS_ASSERT(!hasCallObj());
|
|
}
|
|
|
|
/*
|
|
* The "early prologue" refers to the members that are stored for the benefit
|
|
* of slow paths before initializing the rest of the members.
|
|
*/
|
|
inline void
|
|
JSStackFrame::initCallFrameEarlyPrologue(JSFunction *fun, void *ncode)
|
|
{
|
|
exec.fun = fun;
|
|
ncode_ = ncode;
|
|
}
|
|
|
|
/*
|
|
* The "late prologue" refers to the members that are stored after having
|
|
* checked for stack overflow and formal/actual arg mismatch.
|
|
*/
|
|
inline void
|
|
JSStackFrame::initCallFrameLatePrologue()
|
|
{
|
|
SetValueRangeToUndefined(slots(), script()->nfixed);
|
|
}
|
|
|
|
inline void
|
|
JSStackFrame::initEvalFrame(JSContext *cx, JSScript *script, JSStackFrame *prev, uint32 flagsArg)
|
|
{
|
|
JS_ASSERT(flagsArg & JSFRAME_EVAL);
|
|
JS_ASSERT((flagsArg & ~(JSFRAME_EVAL | JSFRAME_DEBUGGER)) == 0);
|
|
JS_ASSERT(prev->flags_ & (JSFRAME_FUNCTION | JSFRAME_GLOBAL));
|
|
|
|
/* Copy (callee, thisv). */
|
|
js::Value *dstvp = (js::Value *)this - 2;
|
|
js::Value *srcvp = prev->flags_ & (JSFRAME_GLOBAL | JSFRAME_EVAL)
|
|
? (js::Value *)prev - 2
|
|
: prev->formalArgs() - 2;
|
|
dstvp[0] = srcvp[0];
|
|
dstvp[1] = srcvp[1];
|
|
JS_ASSERT_IF(prev->flags_ & JSFRAME_FUNCTION,
|
|
dstvp[0].toObject().isFunction());
|
|
|
|
/* Initialize stack frame members. */
|
|
flags_ = flagsArg | JSFRAME_HAS_PREVPC | JSFRAME_HAS_SCOPECHAIN |
|
|
(prev->flags_ & (JSFRAME_FUNCTION |
|
|
JSFRAME_GLOBAL |
|
|
JSFRAME_HAS_CALL_OBJ));
|
|
if (isFunctionFrame()) {
|
|
exec = prev->exec;
|
|
args.script = script;
|
|
} else {
|
|
exec.script = script;
|
|
}
|
|
|
|
scopeChain_ = &prev->scopeChain();
|
|
JS_ASSERT_IF(isFunctionFrame(), &callObj() == &prev->callObj());
|
|
|
|
prev_ = prev;
|
|
prevpc_ = prev->pc(cx);
|
|
JS_ASSERT(!hasImacropc());
|
|
JS_ASSERT(!hasHookData());
|
|
setAnnotation(prev->annotation());
|
|
}
|
|
|
|
inline void
|
|
JSStackFrame::initGlobalFrame(JSScript *script, JSObject &chain, uint32 flagsArg)
|
|
{
|
|
JS_ASSERT((flagsArg & ~(JSFRAME_EVAL | JSFRAME_DEBUGGER)) == 0);
|
|
|
|
/* Initialize (callee, thisv). */
|
|
js::Value *vp = (js::Value *)this - 2;
|
|
vp[0].setUndefined();
|
|
vp[1].setUndefined(); /* Set after frame pushed using thisObject */
|
|
|
|
/* Initialize stack frame members. */
|
|
flags_ = flagsArg | JSFRAME_GLOBAL | JSFRAME_HAS_PREVPC | JSFRAME_HAS_SCOPECHAIN;
|
|
exec.script = script;
|
|
args.script = (JSScript *)0xbad;
|
|
scopeChain_ = &chain;
|
|
prev_ = NULL;
|
|
JS_ASSERT(!hasImacropc());
|
|
JS_ASSERT(!hasHookData());
|
|
JS_ASSERT(annotation() == NULL);
|
|
}
|
|
|
|
inline void
|
|
JSStackFrame::initDummyFrame(JSContext *cx, JSObject &chain)
|
|
{
|
|
js::PodZero(this);
|
|
flags_ = JSFRAME_DUMMY | JSFRAME_HAS_PREVPC | JSFRAME_HAS_SCOPECHAIN;
|
|
initPrev(cx);
|
|
chain.isGlobal();
|
|
setScopeChainNoCallObj(chain);
|
|
}
|
|
|
|
inline void
|
|
JSStackFrame::stealFrameAndSlots(js::Value *vp, JSStackFrame *otherfp,
|
|
js::Value *othervp, js::Value *othersp)
|
|
{
|
|
JS_ASSERT(vp == (js::Value *)this - (otherfp->formalArgsEnd() - othervp));
|
|
JS_ASSERT(othervp == otherfp->actualArgs() - 2);
|
|
JS_ASSERT(othersp >= otherfp->slots());
|
|
JS_ASSERT(othersp <= otherfp->base() + otherfp->numSlots());
|
|
|
|
size_t nbytes = (othersp - othervp) * sizeof(js::Value);
|
|
memcpy(vp, othervp, nbytes);
|
|
JS_ASSERT(vp == actualArgs() - 2);
|
|
|
|
/*
|
|
* 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 (hasArgsObj()) {
|
|
argsObj().setPrivate(this);
|
|
otherfp->flags_ &= ~JSFRAME_HAS_ARGS_OBJ;
|
|
}
|
|
}
|
|
|
|
inline js::Value &
|
|
JSStackFrame::canonicalActualArg(uintN i) const
|
|
{
|
|
if (i < numFormalArgs())
|
|
return formalArg(i);
|
|
JS_ASSERT(i < numActualArgs());
|
|
return actualArgs()[i];
|
|
}
|
|
|
|
template <class Op>
|
|
inline void
|
|
JSStackFrame::forEachCanonicalActualArg(Op op)
|
|
{
|
|
uintN nformal = fun()->nargs;
|
|
js::Value *formals = formalArgsEnd() - nformal;
|
|
uintN nactual = numActualArgs();
|
|
if (nactual <= nformal) {
|
|
uintN i = 0;
|
|
js::Value *actualsEnd = formals + nactual;
|
|
for (js::Value *p = formals; p != actualsEnd; ++p, ++i)
|
|
op(i, p);
|
|
} else {
|
|
uintN i = 0;
|
|
js::Value *formalsEnd = formalArgsEnd();
|
|
for (js::Value *p = formals; p != formalsEnd; ++p, ++i)
|
|
op(i, p);
|
|
js::Value *actuals = formalsEnd - (nactual + 2);
|
|
js::Value *actualsEnd = formals - 2;
|
|
for (js::Value *p = actuals; p != actualsEnd; ++p, ++i)
|
|
op(i, p);
|
|
}
|
|
}
|
|
|
|
template <class Op>
|
|
inline void
|
|
JSStackFrame::forEachFormalArg(Op op)
|
|
{
|
|
js::Value *formals = formalArgsEnd() - fun()->nargs;
|
|
js::Value *formalsEnd = formalArgsEnd();
|
|
uintN i = 0;
|
|
for (js::Value *p = formals; p != formalsEnd; ++p, ++i)
|
|
op(i, p);
|
|
}
|
|
|
|
JS_ALWAYS_INLINE void
|
|
JSStackFrame::clearMissingArgs()
|
|
{
|
|
if (flags_ & JSFRAME_UNDERFLOW_ARGS)
|
|
SetValueRangeToUndefined(formalArgs() + numActualArgs(), formalArgsEnd());
|
|
}
|
|
|
|
inline JSObject *
|
|
JSStackFrame::computeThisObject(JSContext *cx)
|
|
{
|
|
js::Value &thisv = thisValue();
|
|
if (JS_LIKELY(!thisv.isPrimitive()))
|
|
return &thisv.toObject();
|
|
if (!js::ComputeThisFromArgv(cx, &thisv + 1))
|
|
return NULL;
|
|
JS_ASSERT(IsSaneThisObject(thisv.toObject()));
|
|
return &thisv.toObject();
|
|
}
|
|
|
|
inline JSObject &
|
|
JSStackFrame::varobj(js::StackSegment *seg) const
|
|
{
|
|
JS_ASSERT(seg->contains(this));
|
|
return isFunctionFrame() ? callObj() : seg->getInitialVarObj();
|
|
}
|
|
|
|
inline JSObject &
|
|
JSStackFrame::varobj(JSContext *cx) const
|
|
{
|
|
JS_ASSERT(cx->activeSegment()->contains(this));
|
|
return isFunctionFrame() ? callObj() : cx->activeSegment()->getInitialVarObj();
|
|
}
|
|
|
|
inline uintN
|
|
JSStackFrame::numActualArgs() const
|
|
{
|
|
JS_ASSERT(hasArgs());
|
|
if (JS_UNLIKELY(flags_ & (JSFRAME_OVERFLOW_ARGS | JSFRAME_UNDERFLOW_ARGS)))
|
|
return hasArgsObj() ? argsObj().getArgsInitialLength() : args.nactual;
|
|
return numFormalArgs();
|
|
}
|
|
|
|
inline js::Value *
|
|
JSStackFrame::actualArgs() const
|
|
{
|
|
JS_ASSERT(hasArgs());
|
|
js::Value *argv = formalArgs();
|
|
if (JS_UNLIKELY(flags_ & JSFRAME_OVERFLOW_ARGS)) {
|
|
uintN nactual = hasArgsObj() ? argsObj().getArgsInitialLength() : args.nactual;
|
|
return argv - (2 + nactual);
|
|
}
|
|
return argv;
|
|
}
|
|
|
|
inline js::Value *
|
|
JSStackFrame::actualArgsEnd() const
|
|
{
|
|
JS_ASSERT(hasArgs());
|
|
if (JS_UNLIKELY(flags_ & JSFRAME_OVERFLOW_ARGS))
|
|
return formalArgs() - 2;
|
|
return formalArgs() + numActualArgs();
|
|
}
|
|
|
|
inline void
|
|
JSStackFrame::setArgsObj(JSObject &obj)
|
|
{
|
|
JS_ASSERT_IF(hasArgsObj(), &obj == args.obj);
|
|
JS_ASSERT_IF(!hasArgsObj(), numActualArgs() == obj.getArgsInitialLength());
|
|
args.obj = &obj;
|
|
flags_ |= JSFRAME_HAS_ARGS_OBJ;
|
|
}
|
|
|
|
inline void
|
|
JSStackFrame::clearArgsObj()
|
|
{
|
|
JS_ASSERT(hasArgsObj());
|
|
args.nactual = args.obj->getArgsInitialLength();
|
|
flags_ ^= JSFRAME_HAS_ARGS_OBJ;
|
|
}
|
|
|
|
inline void
|
|
JSStackFrame::setScopeChainNoCallObj(JSObject &obj)
|
|
{
|
|
#ifdef DEBUG
|
|
JS_ASSERT(&obj != NULL);
|
|
JSObject *callObjBefore = maybeCallObj();
|
|
if (!hasCallObj() && &scopeChain() != sInvalidScopeChain) {
|
|
for (JSObject *pobj = &scopeChain(); pobj; pobj = pobj->getParent())
|
|
JS_ASSERT_IF(pobj->isCall(), pobj->getPrivate() != this);
|
|
}
|
|
#endif
|
|
scopeChain_ = &obj;
|
|
flags_ |= JSFRAME_HAS_SCOPECHAIN;
|
|
JS_ASSERT(callObjBefore == maybeCallObj());
|
|
}
|
|
|
|
inline void
|
|
JSStackFrame::setScopeChainAndCallObj(JSObject &obj)
|
|
{
|
|
JS_ASSERT(&obj != NULL);
|
|
JS_ASSERT(!hasCallObj() && obj.isCall() && obj.getPrivate() == this);
|
|
scopeChain_ = &obj;
|
|
flags_ |= JSFRAME_HAS_SCOPECHAIN | JSFRAME_HAS_CALL_OBJ;
|
|
}
|
|
|
|
inline void
|
|
JSStackFrame::clearCallObj()
|
|
{
|
|
JS_ASSERT(hasCallObj());
|
|
flags_ ^= JSFRAME_HAS_CALL_OBJ;
|
|
}
|
|
|
|
inline JSObject &
|
|
JSStackFrame::callObj() const
|
|
{
|
|
JS_ASSERT(hasCallObj());
|
|
JSObject *pobj = &scopeChain();
|
|
while (JS_UNLIKELY(pobj->getClass() != &js_CallClass)) {
|
|
JS_ASSERT(js_IsCacheableNonGlobalScope(pobj) || pobj->isWith());
|
|
pobj = pobj->getParent();
|
|
}
|
|
return *pobj;
|
|
}
|
|
|
|
inline JSObject *
|
|
JSStackFrame::maybeCallObj() const
|
|
{
|
|
return hasCallObj() ? &callObj() : NULL;
|
|
}
|
|
|
|
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
|
|
PutActivationObjects(JSContext *cx, JSStackFrame *fp)
|
|
{
|
|
JS_ASSERT(fp->isFunctionFrame() && !fp->isEvalFrame());
|
|
|
|
/* The order is important as js_PutCallObject needs to access argsObj. */
|
|
if (fp->hasCallObj()) {
|
|
js_PutCallObject(cx, fp);
|
|
} else if (fp->hasArgsObj()) {
|
|
js_PutArgsObject(cx, 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__ */
|