/* -*- 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 Mozilla Communicator client code, released
 * March 31, 1998.
 *
 * The Initial Developer of the Original Code is
 * Netscape Communications Corporation.
 * Portions created by the Initial Developer are Copyright (C) 1998
 * the Initial Developer. All Rights Reserved.
 *
 * Contributor(s):
 *
 * 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 jsfun_h___
#define jsfun_h___
/*
 * JS function definitions.
 */
#include "jsprvtd.h"
#include "jspubtd.h"
#include "jsobj.h"

JS_BEGIN_EXTERN_C

typedef struct JSLocalNameMap JSLocalNameMap;

/*
 * When the sfunOrClass field of the JSFunction instance has the lowest bit
 * set, the function object is JSNativeFunction and the field stores
 * constructor's class for the native function. When the bit is unset, the
 * field stores a pointer to JSScriptedFunction.
 */
struct JSFunction {
    JSObject        object;
    jsuword         sfunOrClass; /* scripted function or constructor class */
};

struct JSNativeFunction {
    JSFunction      base;
    uint16          flags;      /* bound method and other flags, see jsapi.h */
    uint16          nargs;      /* maximum number of specified arguments,
                                   reflected as f.length/f.arity */
    uint16          extra;      /* number of arg slots for local GC roots */
    uint16          minargs;    /* minimum number of specified arguments, used
                                   only when calling fast native */
    JSNative        native;     /* native method pointer */
    JSAtom          *atom;      /* name for diagnostics and decompiling */
};

/*
 * Depending on the number of arguments and variables in the function their
 * names and attributes are stored either as a single atom or as an array of
 * tagged atoms (when there are few locals) or as a hash-based map (when there
 * are many locals). In the first 2 cases the lowest bit of the atom is used
 * as a tag to distinguish const from var. See jsfun.c for details.
 */
typedef union JSLocalNames {
    jsuword         taggedAtom;
    jsuword         *array;
    JSLocalNameMap  *map;
} JSLocalNames;

struct JSScriptedFunction {
    uint16          flags;      /* bound method and other flags, see jsapi.h */
    uint16          nargs;      /* number of arguments */
    uint16          nvars;      /* number of local variables */
    uint16          spare;      /* reserved for future use */
    JSScript        *script;    /* interpreted bytecode descriptor or null */
    JSLocalNames    names;      /* argument and variable names */
    JSAtom          *atom;      /* function name */
};

#define JSFUN_EXPR_CLOSURE   0x4000 /* expression closure: function(x)x*x */

#define FUN_OBJECT(funobj)   (&(funobj)->object)

#define OBJ_TO_FUNCTION(obj)                                                  \
    (JS_ASSERT(HAS_FUNCTION_CLASS(obj)), (JSFunction *) (obj))

#define FUN_IS_SCRIPTED(funobj)                                               \
    (((funobj)->sfunOrClass & (jsuword) 1) == 0)

#define FUN_TO_SCRIPTED(funobj)                                               \
    (JS_ASSERT(FUN_IS_SCRIPTED(funobj)),                                      \
     (JSScriptedFunction *) (funobj)->sfunOrClass)

#define FUN_TO_NATIVE(funobj)                                                 \
    (JS_ASSERT(!FUN_IS_SCRIPTED(funobj)), (JSNativeFunction *) (funobj))

#define FUN_FLAGS(funobj)    (FUN_IS_SCRIPTED(funobj)                         \
                              ? FUN_TO_SCRIPTED(funobj)->flags                \
                              : FUN_TO_NATIVE(funobj)->flags)

#define FUN_NARGS(funobj)    (FUN_IS_SCRIPTED(funobj)                         \
                              ? FUN_TO_SCRIPTED(funobj)->nargs                \
                              : FUN_TO_NATIVE(funobj)->nargs)

#define FUN_ATOM(funobj)     (FUN_IS_SCRIPTED(funobj)                         \
                              ? FUN_TO_SCRIPTED(funobj)->atom                 \
                              : FUN_TO_NATIVE(funobj)->atom)

#define NATIVE_FUN_MINARGS(nfun)                                              \
    (((nfun)->flags & JSFUN_FAST_NATIVE) ? (nfun)->minargs : (nfun)->nargs)

#define NATIVE_FUN_GET_CLASS(nfun)                                            \
    ((JSClass *)((nfun)->base.sfunOrClass & ~(jsuword) 1))

#define NATIVE_FUN_SET_CLASS(nfun, clasp)                                     \
    (JS_ASSERT(((jsuword) (clasp) & (jsuword) 1) == 0),                       \
     (nfun)->base.sfunOrClass = (jsuword) (clasp) | (jsuword) 1)

extern JSClass js_ArgumentsClass;
extern JS_FRIEND_DATA(JSClass) js_CallClass;

/* JS_FRIEND_DATA so that VALUE_IS_FUNCTION is callable from the shell. */
extern JS_FRIEND_DATA(JSClass) js_FunctionClass;

#define HAS_FUNCTION_CLASS(obj) (STOBJ_GET_CLASS(obj) == &js_FunctionClass)

/*
 * NB: jsapi.h and jsobj.h must be included before any call to this macro.
 */
#define VALUE_IS_FUNCTION(cx, v)                                              \
    (!JSVAL_IS_PRIMITIVE(v) && HAS_FUNCTION_CLASS(JSVAL_TO_OBJECT(v)))

extern JSObject *
js_InitFunctionClass(JSContext *cx, JSObject *obj);

extern JSObject *
js_InitArgumentsClass(JSContext *cx, JSObject *obj);

extern JSObject *
js_InitCallClass(JSContext *cx, JSObject *obj);

extern JSFunction *
js_NewFunction(JSContext *cx, JSObject *funobj, JSNative native, uintN nargs,
               uintN flags, JSObject *parent, JSAtom *atom);

extern JSNativeFunction *
js_NewNativeFunction(JSContext *cx, JSNative native, uintN nargs, uintN flags,
                     JSObject *parent, JSAtom *atom);

extern JSFunction *
js_NewScriptedFunction(JSContext *cx, JSFunction *funobj, uintN flags,
                       JSObject *parent, JSAtom *atom);

extern void
js_TraceScriptedFunction(JSTracer *trc, JSScriptedFunction *sfun);

extern void
js_FinalizeFunction(JSContext *cx, JSScriptedFunction *sfun);

extern JSObject *
js_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent);

extern JSNativeFunction *
js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, JSNative native,
                  uintN nargs, uintN flags);

/*
 * Flags for js_ValueToFunction and js_ReportIsNotFunction.  We depend on the
 * fact that JSINVOKE_CONSTRUCT (aka JSFRAME_CONSTRUCTING) is 1, and test that
 * with #if/#error in jsfun.c.
 */
#define JSV2F_CONSTRUCT         JSINVOKE_CONSTRUCT
#define JSV2F_ITERATOR          JSINVOKE_ITERATOR
#define JSV2F_SEARCH_STACK      0x10000

extern JSFunction *
js_ValueToFunction(JSContext *cx, jsval *vp, uintN flags);

extern JSObject *
js_ValueToFunctionObject(JSContext *cx, jsval *vp, uintN flags);

extern JSObject *
js_ValueToCallableObject(JSContext *cx, jsval *vp, uintN flags);

extern void
js_ReportIsNotFunction(JSContext *cx, jsval *vp, uintN flags);

extern JSObject *
js_GetCallObject(JSContext *cx, JSStackFrame *fp, JSObject *parent);

extern JS_FRIEND_API(JSBool)
js_PutCallObject(JSContext *cx, JSStackFrame *fp);

extern JSBool
js_GetCallVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp);

extern JSBool
js_SetCallVariable(JSContext *cx, JSObject *obj, jsval id, jsval *vp);

extern JSBool
js_GetArgsValue(JSContext *cx, JSStackFrame *fp, jsval *vp);

extern JSBool
js_GetArgsProperty(JSContext *cx, JSStackFrame *fp, jsid id, jsval *vp);

extern JSObject *
js_GetArgsObject(JSContext *cx, JSStackFrame *fp);

extern JS_FRIEND_API(JSBool)
js_PutArgsObject(JSContext *cx, JSStackFrame *fp);

extern JSBool
js_XDRFunction(JSXDRState *xdr, JSObject **objp);

typedef enum JSLocalKind {
    JSLOCAL_NONE,
    JSLOCAL_ARG,
    JSLOCAL_VAR,
    JSLOCAL_CONST
} JSLocalKind;

#define JS_GET_LOCAL_NAME_COUNT(fun)    ((fun)->nargs + (fun)->nvars)

extern JSBool
js_AddLocal(JSContext *cx, JSScriptedFunction *sfun, JSAtom *atom,
            JSLocalKind kind);

/*
 * Look up an argument or variable name returning its kind when found or
 * JSLOCAL_NONE when no such name exists. When indexp is not null and the name
 * exists, *indexp will receive the index of the corresponding argument or
 * variable.
 */
extern JSLocalKind
js_LookupLocal(JSContext *cx, JSScriptedFunction *sfun, JSAtom *atom,
               uintN *indexp);

/*
 * Functions to work with local names as an array of words.
 *
 * js_GetLocalNameArray returns the array or null when it cannot be allocated
 * The function must be called only when JS_GET_LOCAL_NAME_COUNT(fun) is not
 * zero. The function use the supplied pool to allocate the array.
 *
 * The elements of the array with index below fun->nargs correspond to the
 * names of function arguments and of function variables otherwise. Use
 * JS_LOCAL_NAME_TO_ATOM to convert array's element into an atom. It can be
 * null when the element is an argument corresponding to a destructuring
 * pattern. For a variable use JS_LOCAL_NAME_IS_CONST to check if it
 * corresponds to the const declaration.
 */
extern jsuword *
js_GetLocalNameArray(JSContext *cx, JSScriptedFunction *sfun,
                     JSArenaPool *pool);

#define JS_LOCAL_NAME_TO_ATOM(nameWord)                                       \
    ((JSAtom *) ((nameWord) & ~(jsuword) 1))

#define JS_LOCAL_NAME_IS_CONST(nameWord)                                      \
    ((((nameWord) & (jsuword) 1)) != 0)

extern void
js_FreezeLocalNames(JSContext *cx, JSScriptedFunction *sfun);

JS_END_EXTERN_C

#endif /* jsfun_h___ */
