2010-08-09 22:43:33 -07:00
|
|
|
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
2010-10-15 11:36:56 -07:00
|
|
|
* vim: set ts=4 sw=4 et tw=99:
|
2010-08-09 22:43:33 -07:00
|
|
|
*
|
|
|
|
* ***** 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__
|
|
|
|
|
2010-10-12 11:50:02 -07:00
|
|
|
#include "jsapi.h"
|
|
|
|
#include "jsbool.h"
|
2011-03-14 11:30:35 -07:00
|
|
|
#include "jscompartment.h"
|
2010-10-12 11:50:02 -07:00
|
|
|
#include "jsinterp.h"
|
|
|
|
#include "jsnum.h"
|
2010-09-28 15:23:43 -07:00
|
|
|
#include "jsprobes.h"
|
2010-10-12 11:50:02 -07:00
|
|
|
#include "jsstr.h"
|
2010-09-28 15:23:43 -07:00
|
|
|
#include "methodjit/MethodJIT.h"
|
|
|
|
|
2010-12-22 15:05:07 -08:00
|
|
|
#include "jsfuninlines.h"
|
|
|
|
|
2011-04-13 09:27:37 -07:00
|
|
|
#include "vm/Stack-inl.h"
|
2011-03-14 11:30:36 -07:00
|
|
|
|
2010-08-09 22:43:33 -07:00
|
|
|
namespace js {
|
|
|
|
|
2010-09-28 15:23:43 -07:00
|
|
|
class AutoPreserveEnumerators {
|
|
|
|
JSContext *cx;
|
|
|
|
JSObject *enumerators;
|
|
|
|
|
|
|
|
public:
|
|
|
|
AutoPreserveEnumerators(JSContext *cx) : cx(cx), enumerators(cx->enumerators)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
~AutoPreserveEnumerators()
|
|
|
|
{
|
|
|
|
cx->enumerators = enumerators;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class InvokeSessionGuard
|
|
|
|
{
|
|
|
|
InvokeArgsGuard args_;
|
|
|
|
InvokeFrameGuard frame_;
|
2010-10-28 17:26:19 -07:00
|
|
|
Value savedCallee_, savedThis_;
|
2010-09-28 15:23:43 -07:00
|
|
|
Value *formals_, *actuals_;
|
|
|
|
unsigned nformals_;
|
|
|
|
JSScript *script_;
|
|
|
|
Value *stackLimit_;
|
|
|
|
jsbytecode *stop_;
|
|
|
|
|
|
|
|
bool optimized() const { return frame_.pushed(); }
|
|
|
|
|
|
|
|
public:
|
|
|
|
InvokeSessionGuard() : args_(), frame_() {}
|
2011-03-14 11:30:36 -07:00
|
|
|
~InvokeSessionGuard() {}
|
2010-09-28 15:23:43 -07:00
|
|
|
|
|
|
|
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 */
|
|
|
|
|
2010-10-28 17:26:19 -07:00
|
|
|
/* Refer to canonical (callee, this) for optimized() sessions. */
|
|
|
|
formals_[-2] = savedCallee_;
|
|
|
|
formals_[-1] = savedThis_;
|
|
|
|
|
2011-04-12 17:16:12 -07:00
|
|
|
/* Prevent spurious accessing-callee-after-rval assert. */
|
|
|
|
args_.calleeHasBeenReset();
|
|
|
|
|
2011-01-08 22:42:00 -08:00
|
|
|
#ifdef JS_METHODJIT
|
2011-02-11 13:45:56 -08:00
|
|
|
void *code;
|
2011-01-03 09:06:30 -08:00
|
|
|
if (!optimized() || !(code = script_->getJIT(false /* !constructing */)->invokeEntry))
|
2011-01-08 22:42:00 -08:00
|
|
|
#else
|
|
|
|
if (!optimized())
|
|
|
|
#endif
|
2011-04-13 09:27:37 -07:00
|
|
|
return Invoke(cx, args_);
|
2010-09-28 15:23:43 -07:00
|
|
|
|
|
|
|
/* Clear any garbage left from the last Invoke. */
|
2011-04-13 09:27:37 -07:00
|
|
|
StackFrame *fp = frame_.fp();
|
2010-09-28 15:23:43 -07:00
|
|
|
fp->clearMissingArgs();
|
|
|
|
fp->resetInvokeCallFrame();
|
|
|
|
SetValueRangeToUndefined(fp->slots(), script_->nfixed);
|
|
|
|
|
|
|
|
JSBool ok;
|
|
|
|
{
|
|
|
|
AutoPreserveEnumerators preserve(cx);
|
2010-09-20 12:43:51 -07:00
|
|
|
Probes::enterJSFun(cx, fp->fun(), script_);
|
2010-09-28 15:23:43 -07:00
|
|
|
#ifdef JS_METHODJIT
|
2011-01-03 09:06:30 -08:00
|
|
|
ok = mjit::EnterMethodJIT(cx, fp, code, stackLimit_);
|
2011-04-13 09:27:37 -07:00
|
|
|
cx->regs().pc = stop_;
|
2010-09-28 15:23:43 -07:00
|
|
|
#else
|
2011-04-26 13:39:59 -07:00
|
|
|
cx->regs().pc = script_->code;
|
2010-09-28 15:23:43 -07:00
|
|
|
ok = Interpret(cx, cx->fp());
|
|
|
|
#endif
|
2010-09-20 12:43:51 -07:00
|
|
|
Probes::exitJSFun(cx, fp->fun(), script_);
|
2010-09-28 15:23:43 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Don't clobber callee with rval; rval gets read from fp->rval. */
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
2010-10-12 11:50:02 -07:00
|
|
|
namespace detail {
|
|
|
|
|
|
|
|
template<typename T> class PrimitiveBehavior { };
|
|
|
|
|
|
|
|
template<>
|
|
|
|
class PrimitiveBehavior<JSString *> {
|
|
|
|
public:
|
|
|
|
static inline bool isType(const Value &v) { return v.isString(); }
|
|
|
|
static inline JSString *extract(const Value &v) { return v.toString(); }
|
|
|
|
static inline Class *getClass() { return &js_StringClass; }
|
|
|
|
};
|
|
|
|
|
|
|
|
template<>
|
|
|
|
class PrimitiveBehavior<bool> {
|
|
|
|
public:
|
|
|
|
static inline bool isType(const Value &v) { return v.isBoolean(); }
|
|
|
|
static inline bool extract(const Value &v) { return v.toBoolean(); }
|
|
|
|
static inline Class *getClass() { return &js_BooleanClass; }
|
|
|
|
};
|
|
|
|
|
|
|
|
template<>
|
|
|
|
class PrimitiveBehavior<double> {
|
|
|
|
public:
|
|
|
|
static inline bool isType(const Value &v) { return v.isNumber(); }
|
|
|
|
static inline double extract(const Value &v) { return v.toNumber(); }
|
|
|
|
static inline Class *getClass() { return &js_NumberClass; }
|
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace detail
|
|
|
|
|
2011-04-12 17:16:12 -07:00
|
|
|
template <typename T>
|
2011-04-13 09:27:37 -07:00
|
|
|
inline bool
|
2011-04-12 17:16:12 -07:00
|
|
|
GetPrimitiveThis(JSContext *cx, Value *vp, T *v)
|
|
|
|
{
|
|
|
|
typedef detail::PrimitiveBehavior<T> Behavior;
|
|
|
|
|
|
|
|
const Value &thisv = vp[1];
|
|
|
|
if (Behavior::isType(thisv)) {
|
|
|
|
*v = Behavior::extract(thisv);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (thisv.isObject() && thisv.toObject().getClass() == Behavior::getClass()) {
|
|
|
|
*v = Behavior::extract(thisv.toObject().getPrimitiveThis());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
ReportIncompatibleMethod(cx, vp, Behavior::getClass());
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2011-02-23 19:38:27 -08:00
|
|
|
/*
|
|
|
|
* Compute the implicit |this| parameter for a call expression where the callee
|
|
|
|
* is an unqualified name reference.
|
|
|
|
*
|
|
|
|
* We can avoid computing |this| eagerly and push the implicit callee-coerced
|
|
|
|
* |this| value, undefined, according to this decision tree:
|
|
|
|
*
|
|
|
|
* 1. If the called value, funval, is not an object, bind |this| to undefined.
|
|
|
|
*
|
|
|
|
* 2. The nominal |this|, obj, has one of Block, Call, or DeclEnv class (this
|
|
|
|
* is what IsCacheableNonGlobalScope tests). Such objects-as-scopes must be
|
|
|
|
* censored.
|
|
|
|
*
|
|
|
|
* 3. obj is a global. There are several sub-cases:
|
|
|
|
*
|
|
|
|
* a) obj is a proxy: we try unwrapping it (see jswrapper.cpp) in order to find
|
|
|
|
* a function object inside. If the proxy is not a wrapper, or else it wraps
|
|
|
|
* a non-function, then bind |this| to undefined per ES5-strict/Harmony.
|
|
|
|
*
|
|
|
|
* [Else fall through with callee pointing to an unwrapped function object.]
|
|
|
|
*
|
|
|
|
* b) If callee is a function (after unwrapping if necessary), check whether it
|
|
|
|
* is interpreted and in strict mode. If so, then bind |this| to undefined
|
|
|
|
* per ES5 strict.
|
|
|
|
*
|
|
|
|
* c) Now check that callee is scoped by the same global object as the object
|
|
|
|
* in which its unqualified name was bound as a property. ES1-3 bound |this|
|
|
|
|
* to the name's "Reference base object", which in the context of multiple
|
|
|
|
* global objects may not be the callee's global. If globals match, bind
|
|
|
|
* |this| to undefined.
|
|
|
|
*
|
|
|
|
* This is a backward compatibility measure; see bug 634590.
|
|
|
|
*
|
2011-02-24 11:12:03 -08:00
|
|
|
* 4. Finally, obj is neither a declarative scope object to be censored, nor a
|
2011-02-24 11:13:55 -08:00
|
|
|
* global where the callee requires no backward-compatible special handling
|
|
|
|
* or future-proofing based on (explicit or imputed by Harmony status in the
|
|
|
|
* proxy case) strict mode opt-in. Bind |this| to obj->thisObject().
|
2011-02-23 19:38:27 -08:00
|
|
|
*
|
|
|
|
* We set *vp to undefined early to reduce code size and bias this code for the
|
|
|
|
* common and future-friendly cases.
|
|
|
|
*/
|
|
|
|
inline bool
|
|
|
|
ComputeImplicitThis(JSContext *cx, JSObject *obj, const Value &funval, Value *vp)
|
|
|
|
{
|
|
|
|
vp->setUndefined();
|
|
|
|
|
|
|
|
if (!funval.isObject())
|
|
|
|
return true;
|
|
|
|
|
|
|
|
if (!obj->isGlobal()) {
|
|
|
|
if (IsCacheableNonGlobalScope(obj))
|
|
|
|
return true;
|
|
|
|
} else {
|
|
|
|
JSObject *callee = &funval.toObject();
|
|
|
|
|
|
|
|
if (callee->isProxy()) {
|
|
|
|
callee = callee->unwrap();
|
|
|
|
if (!callee->isFunction())
|
|
|
|
return true; // treat any non-wrapped-function proxy as strict
|
|
|
|
}
|
|
|
|
if (callee->isFunction()) {
|
|
|
|
JSFunction *fun = callee->getFunctionPrivate();
|
|
|
|
if (fun->isInterpreted() && fun->inStrictMode())
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
if (callee->getGlobal() == cx->fp()->scopeChain().getGlobal())
|
|
|
|
return true;;
|
|
|
|
}
|
|
|
|
|
|
|
|
obj = obj->thisObject(cx);
|
|
|
|
if (!obj)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
vp->setObject(*obj);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2011-04-12 17:16:12 -07:00
|
|
|
inline bool
|
2011-04-13 09:27:37 -07:00
|
|
|
ComputeThis(JSContext *cx, StackFrame *fp)
|
2010-10-12 11:50:02 -07:00
|
|
|
{
|
2011-04-12 17:16:12 -07:00
|
|
|
Value &thisv = fp->thisValue();
|
|
|
|
if (thisv.isObject())
|
2010-10-12 11:50:02 -07:00
|
|
|
return true;
|
2011-04-12 17:16:12 -07:00
|
|
|
if (fp->isFunctionFrame()) {
|
|
|
|
if (fp->fun()->inStrictMode())
|
|
|
|
return true;
|
|
|
|
/*
|
|
|
|
* Eval function frames have their own |this| slot, which is a copy of the function's
|
|
|
|
* |this| slot. If we lazily wrap a primitive |this| in an eval function frame, the
|
|
|
|
* eval's frame will get the wrapper, but the function's frame will not. To prevent
|
|
|
|
* this, we always wrap a function's |this| before pushing an eval frame, and should
|
|
|
|
* thus never see an unwrapped primitive in a non-strict eval function frame.
|
|
|
|
*/
|
|
|
|
JS_ASSERT(!fp->isEvalFrame());
|
2010-10-12 11:50:02 -07:00
|
|
|
}
|
2011-04-12 17:16:12 -07:00
|
|
|
return BoxNonStrictThis(cx, fp->callReceiver());
|
2010-10-12 11:50:02 -07:00
|
|
|
}
|
|
|
|
|
2010-10-12 11:50:03 -07:00
|
|
|
/*
|
|
|
|
* Return an object on which we should look for the properties of |value|.
|
|
|
|
* This helps us implement the custom [[Get]] method that ES5's GetValue
|
|
|
|
* algorithm uses for primitive values, without actually constructing the
|
|
|
|
* temporary object that the specification does.
|
|
|
|
*
|
|
|
|
* For objects, return the object itself. For string, boolean, and number
|
|
|
|
* primitive values, return the appropriate constructor's prototype. For
|
|
|
|
* undefined and null, throw an error and return NULL, attributing the
|
|
|
|
* problem to the value at |spindex| on the stack.
|
|
|
|
*/
|
|
|
|
JS_ALWAYS_INLINE JSObject *
|
|
|
|
ValuePropertyBearer(JSContext *cx, const Value &v, int spindex)
|
|
|
|
{
|
|
|
|
if (v.isObject())
|
|
|
|
return &v.toObject();
|
|
|
|
|
|
|
|
JSProtoKey protoKey;
|
|
|
|
if (v.isString()) {
|
|
|
|
protoKey = JSProto_String;
|
|
|
|
} else if (v.isNumber()) {
|
|
|
|
protoKey = JSProto_Number;
|
|
|
|
} else if (v.isBoolean()) {
|
|
|
|
protoKey = JSProto_Boolean;
|
|
|
|
} else {
|
|
|
|
JS_ASSERT(v.isNull() || v.isUndefined());
|
|
|
|
js_ReportIsNullOrUndefined(cx, spindex, v, NULL);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
JSObject *pobj;
|
|
|
|
if (!js_GetClassPrototype(cx, NULL, protoKey, &pobj))
|
|
|
|
return NULL;
|
|
|
|
return pobj;
|
|
|
|
}
|
|
|
|
|
2011-03-14 11:30:35 -07:00
|
|
|
inline bool
|
2011-04-13 09:27:37 -07:00
|
|
|
ScriptPrologue(JSContext *cx, StackFrame *fp)
|
2010-10-15 11:36:56 -07:00
|
|
|
{
|
2011-03-14 11:30:36 -07:00
|
|
|
JS_ASSERT_IF(fp->isNonEvalFunctionFrame() && fp->fun()->isHeavyweight(), fp->hasCallObj());
|
2011-03-14 11:30:35 -07:00
|
|
|
|
|
|
|
if (fp->isConstructing()) {
|
|
|
|
JSObject *obj = js_CreateThisForFunction(cx, &fp->callee());
|
|
|
|
if (!obj)
|
|
|
|
return false;
|
|
|
|
fp->functionThis().setObject(*obj);
|
|
|
|
}
|
2010-09-20 12:43:51 -07:00
|
|
|
|
2011-03-14 11:30:35 -07:00
|
|
|
if (cx->compartment->debugMode)
|
|
|
|
ScriptDebugPrologue(cx, fp);
|
|
|
|
return true;
|
|
|
|
}
|
2010-11-16 14:16:49 -08:00
|
|
|
|
2011-03-14 11:30:35 -07:00
|
|
|
inline bool
|
2011-04-13 09:27:37 -07:00
|
|
|
ScriptEpilogue(JSContext *cx, StackFrame *fp, bool ok)
|
2011-03-14 11:30:35 -07:00
|
|
|
{
|
|
|
|
if (cx->compartment->debugMode)
|
|
|
|
ok = ScriptDebugEpilogue(cx, fp, ok);
|
2010-10-15 11:36:56 -07:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If inline-constructing, replace primitive rval with the new object
|
|
|
|
* passed in via |this|, and instrument this constructor invocation.
|
|
|
|
*/
|
2010-12-08 15:01:42 -08:00
|
|
|
if (fp->isConstructing() && ok) {
|
2010-10-15 11:36:56 -07:00
|
|
|
if (fp->returnValue().isPrimitive())
|
|
|
|
fp->setReturnValue(ObjectValue(fp->constructorThis()));
|
|
|
|
JS_RUNTIME_METER(cx->runtime, constructs);
|
|
|
|
}
|
|
|
|
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
2011-03-28 11:57:43 -07:00
|
|
|
inline bool
|
2011-04-13 09:27:37 -07:00
|
|
|
ScriptPrologueOrGeneratorResume(JSContext *cx, StackFrame *fp)
|
2011-03-28 11:57:43 -07:00
|
|
|
{
|
|
|
|
if (!fp->isGeneratorFrame())
|
|
|
|
return ScriptPrologue(cx, fp);
|
|
|
|
if (cx->compartment->debugMode)
|
|
|
|
ScriptDebugPrologue(cx, fp);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline bool
|
2011-04-13 09:27:37 -07:00
|
|
|
ScriptEpilogueOrGeneratorYield(JSContext *cx, StackFrame *fp, bool ok)
|
2011-03-28 11:57:43 -07:00
|
|
|
{
|
|
|
|
if (!fp->isYielding())
|
|
|
|
return ScriptEpilogue(cx, fp, ok);
|
|
|
|
if (cx->compartment->debugMode)
|
|
|
|
return ScriptDebugEpilogue(cx, fp, ok);
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
2011-04-13 09:27:37 -07:00
|
|
|
} /* namespace js */
|
2010-08-09 22:43:33 -07:00
|
|
|
|
|
|
|
#endif /* jsinterpinlines_h__ */
|