/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef jsfun_h___ #define jsfun_h___ /* * JS function definitions. */ #include "jsprvtd.h" #include "jspubtd.h" #include "jsobj.h" #include "jsatom.h" #include "jsscript.h" #include "jsstr.h" #include "gc/Barrier.h" /* * The high two bits of JSFunction.flags encode whether the function is native * or interpreted, and if interpreted, what kind of optimized closure form (if * any) it might be. * * 00 not interpreted * 01 interpreted, not null closure * 11 interpreted, null closure * * NB: JSFUN_EXPR_CLOSURE reuses JSFUN_STUB_GSOPS, which is an API request flag * bit only, never stored in fun->flags. * * If we need more bits in the future, all flags for interpreted functions can * move to u.i.script->flags. For now we use function flag bits to minimize * pointer-chasing. */ #define JSFUN_PROTOTYPE 0x0800 /* function is Function.prototype for some global object */ #define JSFUN_EXPR_CLOSURE 0x1000 /* expression closure: function(x) x*x */ #define JSFUN_EXTENDED 0x2000 /* structure is FunctionExtended */ /* * NB: JSFUN_INTERPRETED is hardcode duplicated in SET_JITINFO() in * jsfriendapi.h. If it changes, it must also be updated there. */ #define JSFUN_INTERPRETED 0x4000 /* use u.i if kind >= this value else u.native */ #define JSFUN_HAS_GUESSED_ATOM 0x8000 /* function had no explicit name, but a name was guessed for it anyway */ namespace js { class FunctionExtended; } struct JSFunction : public JSObject { uint16_t nargs; /* maximum number of specified arguments, reflected as f.length/f.arity */ uint16_t flags; /* flags, see JSFUN_* below and in jsapi.h */ union U { class Native { friend struct JSFunction; js::Native native; /* native method pointer or null */ const JSJitInfo *jitinfo; /* Information about this function to be used by the JIT; use the accessor! */ } n; struct Scripted { JSScript *script_; /* interpreted bytecode descriptor or null; use the accessor! */ JSObject *env_; /* environment for new activations; use the accessor! */ } i; void *nativeOrScript; } u; private: js::HeapPtrAtom atom_; /* name for diagnostics and decompiling */ public: bool hasDefaults() const { return flags & JSFUN_HAS_DEFAULTS; } bool hasRest() const { return flags & JSFUN_HAS_REST; } bool hasGuessedAtom() const { return flags & JSFUN_HAS_GUESSED_ATOM; } bool isInterpreted() const { return flags & JSFUN_INTERPRETED; } bool isNative() const { return !isInterpreted(); } bool isSelfHostedBuiltin() const { return flags & JSFUN_SELF_HOSTED; } bool isBuiltin() const { return isNative() || isSelfHostedBuiltin(); } bool isSelfHostedConstructor() const { return flags & JSFUN_SELF_HOSTED_CTOR; } bool isNativeConstructor() const { return flags & JSFUN_CONSTRUCTOR; } bool isHeavyweight() const { return JSFUN_HEAVYWEIGHT_TEST(flags); } bool isFunctionPrototype() const { return flags & JSFUN_PROTOTYPE; } bool isInterpretedConstructor() const { return isInterpreted() && !isFunctionPrototype() && (!isSelfHostedBuiltin() || isSelfHostedConstructor()); } bool isNamedLambda() const { return (flags & JSFUN_LAMBDA) && atom_ && !hasGuessedAtom(); } /* Returns the strictness of this function, which must be interpreted. */ inline bool inStrictMode() const; void setArgCount(uint16_t nargs) { JS_ASSERT(this->nargs == 0); this->nargs = nargs; } void setHasRest() { JS_ASSERT(!hasRest()); this->flags |= JSFUN_HAS_REST; } void setHasDefaults() { JS_ASSERT(!hasDefaults()); this->flags |= JSFUN_HAS_DEFAULTS; } JSAtom *atom() const { return hasGuessedAtom() ? NULL : atom_.get(); } inline void initAtom(JSAtom *atom); JSAtom *displayAtom() const { return atom_; } inline void setGuessedAtom(JSAtom *atom); /* uint16_t representation bounds number of call object dynamic slots. */ enum { MAX_ARGS_AND_VARS = 2 * ((1U << 16) - 1) }; /* * For an interpreted function, accessors for the initial scope object of * activations (stack frames) of the function. */ inline JSObject *environment() const; inline void setEnvironment(JSObject *obj); inline void initEnvironment(JSObject *obj); static inline size_t offsetOfEnvironment() { return offsetof(JSFunction, u.i.env_); } static inline size_t offsetOfAtom() { return offsetof(JSFunction, atom_); } JS::HandleScript script() const { JS_ASSERT(isInterpreted()); return JS::HandleScript::fromMarkedLocation(&u.i.script_); } js::HeapPtrScript &mutableScript() { JS_ASSERT(isInterpreted()); return *(js::HeapPtrScript *)&u.i.script_; } inline void setScript(JSScript *script_); inline void initScript(JSScript *script_); JS::HandleScript maybeScript() const { return isInterpreted() ? script() : JS::NullPtr(); } JSNative native() const { JS_ASSERT(isNative()); return u.n.native; } JSNative maybeNative() const { return isInterpreted() ? NULL : native(); } inline void initNative(js::Native native, const JSJitInfo *jitinfo); inline const JSJitInfo *jitInfo() const; inline void setJitInfo(const JSJitInfo *data); static unsigned offsetOfNativeOrScript() { JS_STATIC_ASSERT(offsetof(U, n.native) == offsetof(U, i.script_)); JS_STATIC_ASSERT(offsetof(U, n.native) == offsetof(U, nativeOrScript)); return offsetof(JSFunction, u.nativeOrScript); } #if JS_BITS_PER_WORD == 32 static const js::gc::AllocKind FinalizeKind = js::gc::FINALIZE_OBJECT2_BACKGROUND; static const js::gc::AllocKind ExtendedFinalizeKind = js::gc::FINALIZE_OBJECT4_BACKGROUND; #else static const js::gc::AllocKind FinalizeKind = js::gc::FINALIZE_OBJECT4_BACKGROUND; static const js::gc::AllocKind ExtendedFinalizeKind = js::gc::FINALIZE_OBJECT8_BACKGROUND; #endif inline void trace(JSTracer *trc); /* Bound function accessors. */ inline bool initBoundFunction(JSContext *cx, js::HandleValue thisArg, const js::Value *args, unsigned argslen); inline JSObject *getBoundFunctionTarget() const; inline const js::Value &getBoundFunctionThis() const; inline const js::Value &getBoundFunctionArgument(unsigned which) const; inline size_t getBoundFunctionArgumentCount() const; private: inline js::FunctionExtended *toExtended(); inline const js::FunctionExtended *toExtended() const; inline bool isExtended() const { JS_STATIC_ASSERT(FinalizeKind != ExtendedFinalizeKind); JS_ASSERT(!!(flags & JSFUN_EXTENDED) == (getAllocKind() == ExtendedFinalizeKind)); return !!(flags & JSFUN_EXTENDED); } public: /* Accessors for data stored in extended functions. */ inline void initializeExtended(); inline void setExtendedSlot(size_t which, const js::Value &val); inline const js::Value &getExtendedSlot(size_t which) const; /* Constructs a new type for the function if necessary. */ static bool setTypeForScriptedFunction(JSContext *cx, js::HandleFunction fun, bool singleton = false); private: static void staticAsserts() { MOZ_STATIC_ASSERT(sizeof(JSFunction) == sizeof(js::shadow::Function), "shadow interface must match actual interface"); } /* * These member functions are inherited from JSObject, but should never be applied to * a value statically known to be a JSFunction. */ inline JSFunction *toFunction() MOZ_DELETE; inline const JSFunction *toFunction() const MOZ_DELETE; }; inline JSFunction * JSObject::toFunction() { JS_ASSERT(JS_ObjectIsFunction(NULL, this)); return static_cast(this); } inline const JSFunction * JSObject::toFunction() const { JS_ASSERT(JS_ObjectIsFunction(NULL, const_cast(this))); return static_cast(this); } extern JSString * fun_toStringHelper(JSContext *cx, js::HandleObject obj, unsigned indent); extern JSFunction * js_NewFunction(JSContext *cx, js::HandleObject funobj, JSNative native, unsigned nargs, unsigned flags, js::HandleObject parent, js::HandleAtom atom, js::gc::AllocKind kind = JSFunction::FinalizeKind); extern JSFunction * JS_FASTCALL js_CloneFunctionObject(JSContext *cx, js::HandleFunction fun, js::HandleObject parent, js::HandleObject proto, js::gc::AllocKind kind = JSFunction::FinalizeKind); extern JSFunction * js_DefineFunction(JSContext *cx, js::HandleObject obj, js::HandleId id, JSNative native, unsigned nargs, unsigned flags, const char *selfHostedName = NULL, js::gc::AllocKind kind = JSFunction::FinalizeKind); namespace js { /* * Function extended with reserved slots for use by various kinds of functions. * Most functions do not have these extensions, but enough are that efficient * storage is required (no malloc'ed reserved slots). */ class FunctionExtended : public JSFunction { friend struct JSFunction; /* Reserved slots available for storage by particular native functions. */ HeapValue extendedSlots[2]; }; } // namespace js inline js::FunctionExtended * JSFunction::toExtended() { JS_ASSERT(isExtended()); return static_cast(this); } inline const js::FunctionExtended * JSFunction::toExtended() const { JS_ASSERT(isExtended()); return static_cast(this); } namespace js { JSString *FunctionToString(JSContext *cx, HandleFunction fun, bool bodyOnly, bool lambdaParen); template bool XDRInterpretedFunction(XDRState *xdr, HandleObject enclosingScope, HandleScript enclosingScript, MutableHandleObject objp); extern JSObject * CloneInterpretedFunction(JSContext *cx, HandleObject enclosingScope, HandleFunction fun); /* * Report an error that call.thisv is not compatible with the specified class, * assuming that the method (clasp->name).prototype. * is what was called. */ extern void ReportIncompatibleMethod(JSContext *cx, CallReceiver call, Class *clasp); /* * Report an error that call.thisv is not an acceptable this for the callee * function. */ extern void ReportIncompatible(JSContext *cx, CallReceiver call); } /* namespace js */ extern JSBool js_fun_apply(JSContext *cx, unsigned argc, js::Value *vp); extern JSBool js_fun_call(JSContext *cx, unsigned argc, js::Value *vp); extern JSObject* js_fun_bind(JSContext *cx, js::HandleObject target, js::HandleValue thisArg, js::Value *boundArgs, unsigned argslen); #endif /* jsfun_h___ */