aboutsummaryrefslogtreecommitdiffstats
path: root/tools/node_modules/expresso/deps/jscoverage/js/jsfun.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tools/node_modules/expresso/deps/jscoverage/js/jsfun.cpp')
-rw-r--r--tools/node_modules/expresso/deps/jscoverage/js/jsfun.cpp2706
1 files changed, 2706 insertions, 0 deletions
diff --git a/tools/node_modules/expresso/deps/jscoverage/js/jsfun.cpp b/tools/node_modules/expresso/deps/jscoverage/js/jsfun.cpp
new file mode 100644
index 0000000..a492bcc
--- /dev/null
+++ b/tools/node_modules/expresso/deps/jscoverage/js/jsfun.cpp
@@ -0,0 +1,2706 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sw=4 et tw=99:
+ *
+ * ***** 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 ***** */
+
+/*
+ * JS function support.
+ */
+#include "jsstddef.h"
+#include <string.h>
+#include "jstypes.h"
+#include "jsbit.h"
+#include "jsutil.h" /* Added by JSIFY */
+#include "jsapi.h"
+#include "jsarray.h"
+#include "jsatom.h"
+#include "jsbuiltins.h"
+#include "jscntxt.h"
+#include "jsversion.h"
+#include "jsdbgapi.h"
+#include "jsemit.h"
+#include "jsfun.h"
+#include "jsgc.h"
+#include "jsinterp.h"
+#include "jslock.h"
+#include "jsnum.h"
+#include "jsobj.h"
+#include "jsopcode.h"
+#include "jsparse.h"
+#include "jsscan.h"
+#include "jsscope.h"
+#include "jsscript.h"
+#include "jsstr.h"
+#include "jsexn.h"
+#include "jsstaticcheck.h"
+
+#if JS_HAS_GENERATORS
+# include "jsiter.h"
+#endif
+
+#if JS_HAS_XDR
+# include "jsxdrapi.h"
+#endif
+
+/* Generic function/call/arguments tinyids -- also reflected bit numbers. */
+enum {
+ CALL_ARGUMENTS = -1, /* predefined arguments local variable */
+ ARGS_LENGTH = -2, /* number of actual args, arity if inactive */
+ ARGS_CALLEE = -3, /* reference from arguments to active funobj */
+ FUN_ARITY = -4, /* number of formal parameters; desired argc */
+ FUN_NAME = -5, /* function name, "" if anonymous */
+ FUN_CALLER = -6 /* Function.prototype.caller, backward compat */
+};
+
+#if JSFRAME_OVERRIDE_BITS < 8
+# error "not enough override bits in JSStackFrame.flags!"
+#endif
+
+#define TEST_OVERRIDE_BIT(fp, tinyid) \
+ ((fp)->flags & JS_BIT(JSFRAME_OVERRIDE_SHIFT - ((tinyid) + 1)))
+
+#define SET_OVERRIDE_BIT(fp, tinyid) \
+ ((fp)->flags |= JS_BIT(JSFRAME_OVERRIDE_SHIFT - ((tinyid) + 1)))
+
+JSBool
+js_GetArgsValue(JSContext *cx, JSStackFrame *fp, jsval *vp)
+{
+ JSObject *argsobj;
+
+ if (TEST_OVERRIDE_BIT(fp, CALL_ARGUMENTS)) {
+ JS_ASSERT(fp->callobj);
+ return OBJ_GET_PROPERTY(cx, fp->callobj,
+ ATOM_TO_JSID(cx->runtime->atomState
+ .argumentsAtom),
+ vp);
+ }
+ argsobj = js_GetArgsObject(cx, fp);
+ if (!argsobj)
+ return JS_FALSE;
+ *vp = OBJECT_TO_JSVAL(argsobj);
+ return JS_TRUE;
+}
+
+static JSBool
+MarkArgDeleted(JSContext *cx, JSStackFrame *fp, uintN slot)
+{
+ JSObject *argsobj;
+ jsval bmapval, bmapint;
+ size_t nbits, nbytes;
+ jsbitmap *bitmap;
+
+ argsobj = fp->argsobj;
+ (void) JS_GetReservedSlot(cx, argsobj, 0, &bmapval);
+ nbits = fp->argc;
+ JS_ASSERT(slot < nbits);
+ if (JSVAL_IS_VOID(bmapval)) {
+ if (nbits <= JSVAL_INT_BITS) {
+ bmapint = 0;
+ bitmap = (jsbitmap *) &bmapint;
+ } else {
+ nbytes = JS_HOWMANY(nbits, JS_BITS_PER_WORD) * sizeof(jsbitmap);
+ bitmap = (jsbitmap *) JS_malloc(cx, nbytes);
+ if (!bitmap)
+ return JS_FALSE;
+ memset(bitmap, 0, nbytes);
+ bmapval = PRIVATE_TO_JSVAL(bitmap);
+ JS_SetReservedSlot(cx, argsobj, 0, bmapval);
+ }
+ } else {
+ if (nbits <= JSVAL_INT_BITS) {
+ bmapint = JSVAL_TO_INT(bmapval);
+ bitmap = (jsbitmap *) &bmapint;
+ } else {
+ bitmap = (jsbitmap *) JSVAL_TO_PRIVATE(bmapval);
+ }
+ }
+ JS_SET_BIT(bitmap, slot);
+ if (bitmap == (jsbitmap *) &bmapint) {
+ bmapval = INT_TO_JSVAL(bmapint);
+ JS_SetReservedSlot(cx, argsobj, 0, bmapval);
+ }
+ return JS_TRUE;
+}
+
+/* NB: Infallible predicate, false does not mean error/exception. */
+static JSBool
+ArgWasDeleted(JSContext *cx, JSStackFrame *fp, uintN slot)
+{
+ JSObject *argsobj;
+ jsval bmapval, bmapint;
+ jsbitmap *bitmap;
+
+ argsobj = fp->argsobj;
+ (void) JS_GetReservedSlot(cx, argsobj, 0, &bmapval);
+ if (JSVAL_IS_VOID(bmapval))
+ return JS_FALSE;
+ if (fp->argc <= JSVAL_INT_BITS) {
+ bmapint = JSVAL_TO_INT(bmapval);
+ bitmap = (jsbitmap *) &bmapint;
+ } else {
+ bitmap = (jsbitmap *) JSVAL_TO_PRIVATE(bmapval);
+ }
+ return JS_TEST_BIT(bitmap, slot) != 0;
+}
+
+JSBool
+js_GetArgsProperty(JSContext *cx, JSStackFrame *fp, jsid id, jsval *vp)
+{
+ jsval val;
+ JSObject *obj;
+ uintN slot;
+
+ if (TEST_OVERRIDE_BIT(fp, CALL_ARGUMENTS)) {
+ JS_ASSERT(fp->callobj);
+ if (!OBJ_GET_PROPERTY(cx, fp->callobj,
+ ATOM_TO_JSID(cx->runtime->atomState
+ .argumentsAtom),
+ &val)) {
+ return JS_FALSE;
+ }
+ if (JSVAL_IS_PRIMITIVE(val)) {
+ obj = js_ValueToNonNullObject(cx, val);
+ if (!obj)
+ return JS_FALSE;
+ } else {
+ obj = JSVAL_TO_OBJECT(val);
+ }
+ return OBJ_GET_PROPERTY(cx, obj, id, vp);
+ }
+
+ *vp = JSVAL_VOID;
+ if (JSID_IS_INT(id)) {
+ slot = (uintN) JSID_TO_INT(id);
+ if (slot < fp->argc) {
+ if (fp->argsobj && ArgWasDeleted(cx, fp, slot))
+ return OBJ_GET_PROPERTY(cx, fp->argsobj, id, vp);
+ *vp = fp->argv[slot];
+ } else {
+ /*
+ * Per ECMA-262 Ed. 3, 10.1.8, last bulleted item, do not share
+ * storage between the formal parameter and arguments[k] for all
+ * fp->argc <= k && k < fp->fun->nargs. For example, in
+ *
+ * function f(x) { x = 42; return arguments[0]; }
+ * f();
+ *
+ * the call to f should return undefined, not 42. If fp->argsobj
+ * is null at this point, as it would be in the example, return
+ * undefined in *vp.
+ */
+ if (fp->argsobj)
+ return OBJ_GET_PROPERTY(cx, fp->argsobj, id, vp);
+ }
+ } else {
+ if (id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)) {
+ if (fp->argsobj && TEST_OVERRIDE_BIT(fp, ARGS_LENGTH))
+ return OBJ_GET_PROPERTY(cx, fp->argsobj, id, vp);
+ *vp = INT_TO_JSVAL((jsint) fp->argc);
+ }
+ }
+ return JS_TRUE;
+}
+
+JSObject *
+js_GetArgsObject(JSContext *cx, JSStackFrame *fp)
+{
+ JSObject *argsobj, *global, *parent;
+
+ /*
+ * We must be in a function activation; the function must be lightweight
+ * or else fp must have a variable object.
+ */
+ JS_ASSERT(fp->fun && (!(fp->fun->flags & JSFUN_HEAVYWEIGHT) || fp->varobj));
+
+ /* Skip eval and debugger frames. */
+ while (fp->flags & JSFRAME_SPECIAL)
+ fp = fp->down;
+
+ /* Create an arguments object for fp only if it lacks one. */
+ argsobj = fp->argsobj;
+ if (argsobj)
+ return argsobj;
+
+ /* Link the new object to fp so it can get actual argument values. */
+ argsobj = js_NewObject(cx, &js_ArgumentsClass, NULL, NULL, 0);
+ if (!argsobj || !JS_SetPrivate(cx, argsobj, fp)) {
+ cx->weakRoots.newborn[GCX_OBJECT] = NULL;
+ return NULL;
+ }
+
+ /*
+ * Give arguments an intrinsic scope chain link to fp's global object.
+ * Since the arguments object lacks a prototype because js_ArgumentsClass
+ * is not initialized, js_NewObject won't assign a default parent to it.
+ *
+ * Therefore if arguments is used as the head of an eval scope chain (via
+ * a direct or indirect call to eval(program, arguments)), any reference
+ * to a standard class object in the program will fail to resolve due to
+ * js_GetClassPrototype not being able to find a global object containing
+ * the standard prototype by starting from arguments and following parent.
+ */
+ global = fp->scopeChain;
+ while ((parent = OBJ_GET_PARENT(cx, global)) != NULL)
+ global = parent;
+ STOBJ_SET_PARENT(argsobj, global);
+ fp->argsobj = argsobj;
+ return argsobj;
+}
+
+static JSBool
+args_enumerate(JSContext *cx, JSObject *obj);
+
+JS_FRIEND_API(JSBool)
+js_PutArgsObject(JSContext *cx, JSStackFrame *fp)
+{
+ JSObject *argsobj;
+ jsval bmapval, rval;
+ JSBool ok;
+ JSRuntime *rt;
+
+ /*
+ * Reuse args_enumerate here to reflect fp's actual arguments as indexed
+ * elements of argsobj. Do this first, before clearing and freeing the
+ * deleted argument slot bitmap, because args_enumerate depends on that.
+ */
+ argsobj = fp->argsobj;
+ ok = args_enumerate(cx, argsobj);
+
+ /*
+ * Now clear the deleted argument number bitmap slot and free the bitmap,
+ * if one was actually created due to 'delete arguments[0]' or similar.
+ */
+ (void) JS_GetReservedSlot(cx, argsobj, 0, &bmapval);
+ if (!JSVAL_IS_VOID(bmapval)) {
+ JS_SetReservedSlot(cx, argsobj, 0, JSVAL_VOID);
+ if (fp->argc > JSVAL_INT_BITS)
+ JS_free(cx, JSVAL_TO_PRIVATE(bmapval));
+ }
+
+ /*
+ * Now get the prototype properties so we snapshot fp->fun and fp->argc
+ * before fp goes away.
+ */
+ rt = cx->runtime;
+ ok &= js_GetProperty(cx, argsobj, ATOM_TO_JSID(rt->atomState.calleeAtom),
+ &rval);
+ ok &= js_SetProperty(cx, argsobj, ATOM_TO_JSID(rt->atomState.calleeAtom),
+ &rval);
+ ok &= js_GetProperty(cx, argsobj, ATOM_TO_JSID(rt->atomState.lengthAtom),
+ &rval);
+ ok &= js_SetProperty(cx, argsobj, ATOM_TO_JSID(rt->atomState.lengthAtom),
+ &rval);
+
+ /*
+ * Clear the private pointer to fp, which is about to go away (js_Invoke).
+ * Do this last because the args_enumerate and js_GetProperty calls above
+ * need to follow the private slot to find fp.
+ */
+ ok &= JS_SetPrivate(cx, argsobj, NULL);
+ fp->argsobj = NULL;
+ return ok;
+}
+
+static JSBool
+args_delProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
+{
+ jsint slot;
+ JSStackFrame *fp;
+
+ if (!JSVAL_IS_INT(id))
+ return JS_TRUE;
+ fp = (JSStackFrame *)
+ JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL);
+ if (!fp)
+ return JS_TRUE;
+ JS_ASSERT(fp->argsobj);
+
+ slot = JSVAL_TO_INT(id);
+ switch (slot) {
+ case ARGS_CALLEE:
+ case ARGS_LENGTH:
+ SET_OVERRIDE_BIT(fp, slot);
+ break;
+
+ default:
+ if ((uintN)slot < fp->argc && !MarkArgDeleted(cx, fp, slot))
+ return JS_FALSE;
+ break;
+ }
+ return JS_TRUE;
+}
+
+static JSBool
+args_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
+{
+ jsint slot;
+ JSStackFrame *fp;
+
+ if (!JSVAL_IS_INT(id))
+ return JS_TRUE;
+ fp = (JSStackFrame *)
+ JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL);
+ if (!fp)
+ return JS_TRUE;
+ JS_ASSERT(fp->argsobj);
+
+ slot = JSVAL_TO_INT(id);
+ switch (slot) {
+ case ARGS_CALLEE:
+ if (!TEST_OVERRIDE_BIT(fp, slot))
+ *vp = OBJECT_TO_JSVAL(fp->callee);
+ break;
+
+ case ARGS_LENGTH:
+ if (!TEST_OVERRIDE_BIT(fp, slot))
+ *vp = INT_TO_JSVAL((jsint)fp->argc);
+ break;
+
+ default:
+ if ((uintN)slot < fp->argc && !ArgWasDeleted(cx, fp, slot))
+ *vp = fp->argv[slot];
+ break;
+ }
+ return JS_TRUE;
+}
+
+static JSBool
+args_setProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
+{
+ JSStackFrame *fp;
+ jsint slot;
+
+ if (!JSVAL_IS_INT(id))
+ return JS_TRUE;
+ fp = (JSStackFrame *)
+ JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL);
+ if (!fp)
+ return JS_TRUE;
+ JS_ASSERT(fp->argsobj);
+
+ slot = JSVAL_TO_INT(id);
+ switch (slot) {
+ case ARGS_CALLEE:
+ case ARGS_LENGTH:
+ SET_OVERRIDE_BIT(fp, slot);
+ break;
+
+ default:
+ if (FUN_INTERPRETED(fp->fun) &&
+ (uintN)slot < fp->argc &&
+ !ArgWasDeleted(cx, fp, slot)) {
+ fp->argv[slot] = *vp;
+ }
+ break;
+ }
+ return JS_TRUE;
+}
+
+static JSBool
+args_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
+ JSObject **objp)
+{
+ JSStackFrame *fp;
+ uintN slot;
+ JSString *str;
+ JSAtom *atom;
+ intN tinyid;
+ jsval value;
+
+ *objp = NULL;
+ fp = (JSStackFrame *)
+ JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL);
+ if (!fp)
+ return JS_TRUE;
+ JS_ASSERT(fp->argsobj);
+
+ if (JSVAL_IS_INT(id)) {
+ slot = JSVAL_TO_INT(id);
+ if (slot < fp->argc && !ArgWasDeleted(cx, fp, slot)) {
+ /* XXX ECMA specs DontEnum, contrary to other array-like objects */
+ if (!js_DefineProperty(cx, obj, INT_JSVAL_TO_JSID(id),
+ fp->argv[slot],
+ args_getProperty, args_setProperty,
+ 0, NULL)) {
+ return JS_FALSE;
+ }
+ *objp = obj;
+ }
+ } else {
+ str = JSVAL_TO_STRING(id);
+ atom = cx->runtime->atomState.lengthAtom;
+ if (str == ATOM_TO_STRING(atom)) {
+ tinyid = ARGS_LENGTH;
+ value = INT_TO_JSVAL(fp->argc);
+ } else {
+ atom = cx->runtime->atomState.calleeAtom;
+ if (str == ATOM_TO_STRING(atom)) {
+ tinyid = ARGS_CALLEE;
+ value = OBJECT_TO_JSVAL(fp->callee);
+ } else {
+ atom = NULL;
+
+ /* Quell GCC overwarnings. */
+ tinyid = 0;
+ value = JSVAL_NULL;
+ }
+ }
+
+ if (atom && !TEST_OVERRIDE_BIT(fp, tinyid)) {
+ if (!js_DefineNativeProperty(cx, obj, ATOM_TO_JSID(atom), value,
+ args_getProperty, args_setProperty, 0,
+ SPROP_HAS_SHORTID, tinyid, NULL)) {
+ return JS_FALSE;
+ }
+ *objp = obj;
+ }
+ }
+
+ return JS_TRUE;
+}
+
+static JSBool
+args_enumerate(JSContext *cx, JSObject *obj)
+{
+ JSStackFrame *fp;
+ JSObject *pobj;
+ JSProperty *prop;
+ uintN slot, argc;
+
+ fp = (JSStackFrame *)
+ JS_GetInstancePrivate(cx, obj, &js_ArgumentsClass, NULL);
+ if (!fp)
+ return JS_TRUE;
+ JS_ASSERT(fp->argsobj);
+
+ /*
+ * Trigger reflection with value snapshot in args_resolve using a series
+ * of js_LookupProperty calls. We handle length, callee, and the indexed
+ * argument properties. We know that args_resolve covers all these cases
+ * and creates direct properties of obj, but that it may fail to resolve
+ * length or callee if overridden.
+ */
+ if (!js_LookupProperty(cx, obj,
+ ATOM_TO_JSID(cx->runtime->atomState.lengthAtom),
+ &pobj, &prop)) {
+ return JS_FALSE;
+ }
+ if (prop)
+ OBJ_DROP_PROPERTY(cx, pobj, prop);
+
+ if (!js_LookupProperty(cx, obj,
+ ATOM_TO_JSID(cx->runtime->atomState.calleeAtom),
+ &pobj, &prop)) {
+ return JS_FALSE;
+ }
+ if (prop)
+ OBJ_DROP_PROPERTY(cx, pobj, prop);
+
+ argc = fp->argc;
+ for (slot = 0; slot < argc; slot++) {
+ if (!js_LookupProperty(cx, obj, INT_TO_JSID((jsint)slot), &pobj, &prop))
+ return JS_FALSE;
+ if (prop)
+ OBJ_DROP_PROPERTY(cx, pobj, prop);
+ }
+ return JS_TRUE;
+}
+
+#if JS_HAS_GENERATORS
+/*
+ * If a generator-iterator's arguments or call object escapes, it needs to
+ * mark its generator object.
+ */
+static void
+args_or_call_trace(JSTracer *trc, JSObject *obj)
+{
+ JSStackFrame *fp;
+
+ fp = (JSStackFrame *) JS_GetPrivate(trc->context, obj);
+ if (fp && (fp->flags & JSFRAME_GENERATOR)) {
+ JS_CALL_OBJECT_TRACER(trc, FRAME_TO_GENERATOR(fp)->obj,
+ "FRAME_TO_GENERATOR(fp)->obj");
+ }
+}
+#else
+# define args_or_call_trace NULL
+#endif
+
+/*
+ * The Arguments class is not initialized via JS_InitClass, and must not be,
+ * because its name is "Object". Per ECMA, that causes instances of it to
+ * delegate to the object named by Object.prototype. It also ensures that
+ * arguments.toString() returns "[object Object]".
+ *
+ * The JSClass functions below collaborate to lazily reflect and synchronize
+ * actual argument values, argument count, and callee function object stored
+ * in a JSStackFrame with their corresponding property values in the frame's
+ * arguments object.
+ */
+JSClass js_ArgumentsClass = {
+ js_Object_str,
+ JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(1) |
+ JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
+ JS_PropertyStub, args_delProperty,
+ args_getProperty, args_setProperty,
+ args_enumerate, (JSResolveOp) args_resolve,
+ JS_ConvertStub, JS_FinalizeStub,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ JS_CLASS_TRACE(args_or_call_trace), NULL
+};
+
+#define JSSLOT_SCRIPTED_FUNCTION (JSSLOT_PRIVATE + 1)
+#define JSSLOT_CALL_ARGUMENTS (JSSLOT_PRIVATE + 2)
+#define CALL_CLASS_FIXED_RESERVED_SLOTS 2
+
+JSObject *
+js_GetCallObject(JSContext *cx, JSStackFrame *fp, JSObject *parent)
+{
+ JSObject *callobj, *funobj;
+
+ /* Create a call object for fp only if it lacks one. */
+ JS_ASSERT(fp->fun);
+ callobj = fp->callobj;
+ if (callobj)
+ return callobj;
+
+ /* The default call parent is its function's parent (static link). */
+ if (!parent) {
+ funobj = fp->callee;
+ if (funobj)
+ parent = OBJ_GET_PARENT(cx, funobj);
+ }
+
+ /* Create the call object and link it to its stack frame. */
+ callobj = js_NewObject(cx, &js_CallClass, NULL, parent, 0);
+ if (!callobj)
+ return NULL;
+
+ JS_SetPrivate(cx, callobj, fp);
+ STOBJ_SET_SLOT(callobj, JSSLOT_SCRIPTED_FUNCTION,
+ OBJECT_TO_JSVAL(FUN_OBJECT(fp->fun)));
+ fp->callobj = callobj;
+
+ /* Make callobj be the scope chain and the variables object. */
+ JS_ASSERT(fp->scopeChain == parent);
+ fp->scopeChain = callobj;
+ fp->varobj = callobj;
+ return callobj;
+}
+
+JSFunction *
+js_GetCallObjectFunction(JSObject *obj)
+{
+ jsval v;
+
+ JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_CallClass);
+ v = STOBJ_GET_SLOT(obj, JSSLOT_SCRIPTED_FUNCTION);
+ if (JSVAL_IS_VOID(v)) {
+ /* Newborn or prototype object. */
+ return NULL;
+ }
+ JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));
+ return (JSFunction *) JSVAL_TO_OBJECT(v);
+}
+
+JS_FRIEND_API(JSBool)
+js_PutCallObject(JSContext *cx, JSStackFrame *fp)
+{
+ JSObject *callobj;
+ JSBool ok;
+ JSFunction *fun;
+ uintN n;
+ JSScope *scope;
+
+ /*
+ * Since for a call object all fixed slots happen to be taken, we can copy
+ * arguments and variables straight into JSObject.dslots.
+ */
+ JS_STATIC_ASSERT(JS_INITIAL_NSLOTS - JSSLOT_PRIVATE ==
+ 1 + CALL_CLASS_FIXED_RESERVED_SLOTS);
+
+ callobj = fp->callobj;
+ if (!callobj)
+ return JS_TRUE;
+
+ /*
+ * Get the arguments object to snapshot fp's actual argument values.
+ */
+ ok = JS_TRUE;
+ if (fp->argsobj) {
+ if (!TEST_OVERRIDE_BIT(fp, CALL_ARGUMENTS)) {
+ STOBJ_SET_SLOT(callobj, JSSLOT_CALL_ARGUMENTS,
+ OBJECT_TO_JSVAL(fp->argsobj));
+ }
+ ok &= js_PutArgsObject(cx, fp);
+ }
+
+ fun = fp->fun;
+ JS_ASSERT(fun == js_GetCallObjectFunction(callobj));
+ n = JS_GET_LOCAL_NAME_COUNT(fun);
+ if (n != 0) {
+ JS_LOCK_OBJ(cx, callobj);
+ n += JS_INITIAL_NSLOTS;
+ if (n > STOBJ_NSLOTS(callobj))
+ ok &= js_ReallocSlots(cx, callobj, n, JS_TRUE);
+ scope = OBJ_SCOPE(callobj);
+ if (ok) {
+ memcpy(callobj->dslots, fp->argv, fun->nargs * sizeof(jsval));
+ memcpy(callobj->dslots + fun->nargs, fp->slots,
+ fun->u.i.nvars * sizeof(jsval));
+ if (scope->object == callobj && n > scope->map.freeslot)
+ scope->map.freeslot = n;
+ }
+ JS_UNLOCK_SCOPE(cx, scope);
+ }
+
+ /*
+ * Clear the private pointer to fp, which is about to go away (js_Invoke).
+ * Do this last because js_GetProperty calls above need to follow the
+ * private slot to find fp.
+ */
+ JS_SetPrivate(cx, callobj, NULL);
+ fp->callobj = NULL;
+ return ok;
+}
+
+static JSBool
+call_enumerate(JSContext *cx, JSObject *obj)
+{
+ JSFunction *fun;
+ uintN n, i;
+ void *mark;
+ jsuword *names;
+ JSBool ok;
+ JSAtom *name;
+ JSObject *pobj;
+ JSProperty *prop;
+
+ fun = js_GetCallObjectFunction(obj);
+ n = JS_GET_LOCAL_NAME_COUNT(fun);
+ if (n == 0)
+ return JS_TRUE;
+
+ mark = JS_ARENA_MARK(&cx->tempPool);
+
+ MUST_FLOW_THROUGH("out");
+ names = js_GetLocalNameArray(cx, fun, &cx->tempPool);
+ if (!names) {
+ ok = JS_FALSE;
+ goto out;
+ }
+
+ for (i = 0; i != n; ++i) {
+ name = JS_LOCAL_NAME_TO_ATOM(names[i]);
+ if (!name)
+ continue;
+
+ /*
+ * Trigger reflection by looking up the name of the argument or
+ * variable.
+ */
+ ok = js_LookupProperty(cx, obj, ATOM_TO_JSID(name), &pobj, &prop);
+ if (!ok)
+ goto out;
+
+ /*
+ * At this point the call object always has a property corresponding
+ * to the local name because call_resolve creates the property using
+ * JSPROP_PERMANENT.
+ */
+ JS_ASSERT(prop && pobj == obj);
+ OBJ_DROP_PROPERTY(cx, pobj, prop);
+ }
+ ok = JS_TRUE;
+
+ out:
+ JS_ARENA_RELEASE(&cx->tempPool, mark);
+ return ok;
+}
+
+typedef enum JSCallPropertyKind {
+ JSCPK_ARGUMENTS,
+ JSCPK_ARG,
+ JSCPK_VAR
+} JSCallPropertyKind;
+
+static JSBool
+CallPropertyOp(JSContext *cx, JSObject *obj, jsid id, jsval *vp,
+ JSCallPropertyKind kind, JSBool setter)
+{
+ JSFunction *fun;
+ JSStackFrame *fp;
+ uintN i;
+ jsval *array;
+
+ if (STOBJ_GET_CLASS(obj) != &js_CallClass)
+ return JS_TRUE;
+
+ fun = js_GetCallObjectFunction(obj);
+ fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
+
+ if (kind == JSCPK_ARGUMENTS) {
+ if (setter) {
+ if (fp)
+ SET_OVERRIDE_BIT(fp, CALL_ARGUMENTS);
+ STOBJ_SET_SLOT(obj, JSSLOT_CALL_ARGUMENTS, *vp);
+ } else {
+ if (fp && !TEST_OVERRIDE_BIT(fp, CALL_ARGUMENTS)) {
+ JSObject *argsobj;
+
+ argsobj = js_GetArgsObject(cx, fp);
+ if (!argsobj)
+ return JS_FALSE;
+ *vp = OBJECT_TO_JSVAL(argsobj);
+ } else {
+ *vp = STOBJ_GET_SLOT(obj, JSSLOT_CALL_ARGUMENTS);
+ }
+ }
+ return JS_TRUE;
+ }
+
+ JS_ASSERT((int16) JSVAL_TO_INT(id) == JSVAL_TO_INT(id));
+ i = (uint16) JSVAL_TO_INT(id);
+ JS_ASSERT_IF(kind == JSCPK_ARG, i < fun->nargs);
+ JS_ASSERT_IF(kind == JSCPK_VAR, i < fun->u.i.nvars);
+
+ if (!fp) {
+ i += CALL_CLASS_FIXED_RESERVED_SLOTS;
+ if (kind == JSCPK_VAR)
+ i += fun->nargs;
+ else
+ JS_ASSERT(kind == JSCPK_ARG);
+ return setter
+ ? JS_SetReservedSlot(cx, obj, i, *vp)
+ : JS_GetReservedSlot(cx, obj, i, vp);
+ }
+
+ if (kind == JSCPK_ARG) {
+ array = fp->argv;
+ } else {
+ JS_ASSERT(kind == JSCPK_VAR);
+ array = fp->slots;
+ }
+ if (setter)
+ array[i] = *vp;
+ else
+ *vp = array[i];
+ return JS_TRUE;
+}
+
+static JSBool
+GetCallArguments(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
+{
+ return CallPropertyOp(cx, obj, id, vp, JSCPK_ARGUMENTS, JS_FALSE);
+}
+
+static JSBool
+SetCallArguments(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
+{
+ return CallPropertyOp(cx, obj, id, vp, JSCPK_ARGUMENTS, JS_TRUE);
+}
+
+JSBool
+js_GetCallArg(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
+{
+ return CallPropertyOp(cx, obj, id, vp, JSCPK_ARG, JS_FALSE);
+}
+
+static JSBool
+SetCallArg(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
+{
+ return CallPropertyOp(cx, obj, id, vp, JSCPK_ARG, JS_TRUE);
+}
+
+JSBool
+js_GetCallVar(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
+{
+ return CallPropertyOp(cx, obj, id, vp, JSCPK_VAR, JS_FALSE);
+}
+
+static JSBool
+SetCallVar(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
+{
+ return CallPropertyOp(cx, obj, id, vp, JSCPK_VAR, JS_TRUE);
+}
+
+static JSBool
+call_resolve(JSContext *cx, JSObject *obj, jsval idval, uintN flags,
+ JSObject **objp)
+{
+ JSFunction *fun;
+ jsid id;
+ JSLocalKind localKind;
+ JSPropertyOp getter, setter;
+ uintN slot, attrs;
+
+ if (!JSVAL_IS_STRING(idval))
+ return JS_TRUE;
+
+ fun = js_GetCallObjectFunction(obj);
+ if (!fun)
+ return JS_TRUE;
+
+ if (!js_ValueToStringId(cx, idval, &id))
+ return JS_FALSE;
+
+ localKind = js_LookupLocal(cx, fun, JSID_TO_ATOM(id), &slot);
+ if (localKind != JSLOCAL_NONE) {
+ JS_ASSERT((uint16) slot == slot);
+ attrs = JSPROP_PERMANENT | JSPROP_SHARED;
+ if (localKind == JSLOCAL_ARG) {
+ JS_ASSERT(slot < fun->nargs);
+ getter = js_GetCallArg;
+ setter = SetCallArg;
+ } else {
+ JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST);
+ JS_ASSERT(slot < fun->u.i.nvars);
+ getter = js_GetCallVar;
+ setter = SetCallVar;
+ if (localKind == JSLOCAL_CONST)
+ attrs |= JSPROP_READONLY;
+ }
+ if (!js_DefineNativeProperty(cx, obj, id, JSVAL_VOID, getter, setter,
+ attrs, SPROP_HAS_SHORTID, (int16) slot,
+ NULL)) {
+ return JS_FALSE;
+ }
+ *objp = obj;
+ return JS_TRUE;
+ }
+
+ /*
+ * Resolve arguments so that we never store a particular Call object's
+ * arguments object reference in a Call prototype's |arguments| slot.
+ */
+ if (id == ATOM_TO_JSID(cx->runtime->atomState.argumentsAtom)) {
+ if (!js_DefineNativeProperty(cx, obj, id, JSVAL_VOID,
+ GetCallArguments, SetCallArguments,
+ JSPROP_PERMANENT | JSPROP_SHARED,
+ 0, 0, NULL)) {
+ return JS_FALSE;
+ }
+ *objp = obj;
+ return JS_TRUE;
+ }
+ return JS_TRUE;
+}
+
+static JSBool
+call_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
+{
+ JSStackFrame *fp;
+
+ if (type == JSTYPE_FUNCTION) {
+ fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
+ if (fp) {
+ JS_ASSERT(fp->fun);
+ *vp = OBJECT_TO_JSVAL(fp->callee);
+ }
+ }
+ return JS_TRUE;
+}
+
+static uint32
+call_reserveSlots(JSContext *cx, JSObject *obj)
+{
+ JSFunction *fun;
+
+ fun = js_GetCallObjectFunction(obj);
+ return JS_GET_LOCAL_NAME_COUNT(fun);
+}
+
+JS_FRIEND_DATA(JSClass) js_CallClass = {
+ js_Call_str,
+ JSCLASS_HAS_PRIVATE |
+ JSCLASS_HAS_RESERVED_SLOTS(CALL_CLASS_FIXED_RESERVED_SLOTS) |
+ JSCLASS_NEW_RESOLVE | JSCLASS_IS_ANONYMOUS |
+ JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Call),
+ JS_PropertyStub, JS_PropertyStub,
+ JS_PropertyStub, JS_PropertyStub,
+ call_enumerate, (JSResolveOp)call_resolve,
+ call_convert, JS_FinalizeStub,
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL,
+ JS_CLASS_TRACE(args_or_call_trace), call_reserveSlots
+};
+
+static JSBool
+fun_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
+{
+ jsint slot;
+ JSFunction *fun;
+ JSStackFrame *fp;
+ JSSecurityCallbacks *callbacks;
+
+ if (!JSVAL_IS_INT(id))
+ return JS_TRUE;
+ slot = JSVAL_TO_INT(id);
+
+ /*
+ * Loop because getter and setter can be delegated from another class,
+ * but loop only for ARGS_LENGTH because we must pretend that f.length
+ * is in each function instance f, per ECMA-262, instead of only in the
+ * Function.prototype object (we use JSPROP_PERMANENT with JSPROP_SHARED
+ * to make it appear so).
+ *
+ * This code couples tightly to the attributes for the function_props[]
+ * initializers above, and to js_SetProperty and js_HasOwnProperty.
+ *
+ * It's important to allow delegating objects, even though they inherit
+ * this getter (fun_getProperty), to override arguments, arity, caller,
+ * and name. If we didn't return early for slot != ARGS_LENGTH, we would
+ * clobber *vp with the native property value, instead of letting script
+ * override that value in delegating objects.
+ *
+ * Note how that clobbering is what simulates JSPROP_READONLY for all of
+ * the non-standard properties when the directly addressed object (obj)
+ * is a function object (i.e., when this loop does not iterate).
+ */
+ while (!(fun = (JSFunction *)
+ JS_GetInstancePrivate(cx, obj, &js_FunctionClass, NULL))) {
+ if (slot != ARGS_LENGTH)
+ return JS_TRUE;
+ obj = OBJ_GET_PROTO(cx, obj);
+ if (!obj)
+ return JS_TRUE;
+ }
+
+ /* Find fun's top-most activation record. */
+ for (fp = cx->fp; fp && (fp->fun != fun || (fp->flags & JSFRAME_SPECIAL));
+ fp = fp->down) {
+ continue;
+ }
+
+ switch (slot) {
+ case CALL_ARGUMENTS:
+ /* Warn if strict about f.arguments or equivalent unqualified uses. */
+ if (!JS_ReportErrorFlagsAndNumber(cx,
+ JSREPORT_WARNING | JSREPORT_STRICT,
+ js_GetErrorMessage, NULL,
+ JSMSG_DEPRECATED_USAGE,
+ js_arguments_str)) {
+ return JS_FALSE;
+ }
+ if (fp) {
+ if (!js_GetArgsValue(cx, fp, vp))
+ return JS_FALSE;
+ } else {
+ *vp = JSVAL_NULL;
+ }
+ break;
+
+ case ARGS_LENGTH:
+ case FUN_ARITY:
+ *vp = INT_TO_JSVAL((jsint)fun->nargs);
+ break;
+
+ case FUN_NAME:
+ *vp = fun->atom
+ ? ATOM_KEY(fun->atom)
+ : STRING_TO_JSVAL(cx->runtime->emptyString);
+ break;
+
+ case FUN_CALLER:
+ if (fp && fp->down && fp->down->fun)
+ *vp = OBJECT_TO_JSVAL(fp->down->callee);
+ else
+ *vp = JSVAL_NULL;
+ if (!JSVAL_IS_PRIMITIVE(*vp)) {
+ callbacks = JS_GetSecurityCallbacks(cx);
+ if (callbacks && callbacks->checkObjectAccess) {
+ id = ATOM_KEY(cx->runtime->atomState.callerAtom);
+ if (!callbacks->checkObjectAccess(cx, obj, id, JSACC_READ, vp))
+ return JS_FALSE;
+ }
+ }
+ break;
+
+ default:
+ /* XXX fun[0] and fun.arguments[0] are equivalent. */
+ if (fp && fp->fun && (uintN)slot < fp->fun->nargs)
+ *vp = fp->argv[slot];
+ break;
+ }
+
+ return JS_TRUE;
+}
+
+/*
+ * ECMA-262 specifies that length is a property of function object instances,
+ * but we can avoid that space cost by delegating to a prototype property that
+ * is JSPROP_PERMANENT and JSPROP_SHARED. Each fun_getProperty call computes
+ * a fresh length value based on the arity of the individual function object's
+ * private data.
+ *
+ * The extensions below other than length, i.e., the ones not in ECMA-262,
+ * are neither JSPROP_READONLY nor JSPROP_SHARED, because for compatibility
+ * with ECMA we must allow a delegating object to override them. Therefore to
+ * avoid entraining garbage in Function.prototype slots, they must be resolved
+ * in non-prototype function objects, wherefore the lazy_function_props table
+ * and fun_resolve's use of it.
+ */
+#define LENGTH_PROP_ATTRS (JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED)
+
+static JSPropertySpec function_props[] = {
+ {js_length_str, ARGS_LENGTH, LENGTH_PROP_ATTRS, fun_getProperty, JS_PropertyStub},
+ {0,0,0,0,0}
+};
+
+typedef struct LazyFunctionProp {
+ uint16 atomOffset;
+ int8 tinyid;
+ uint8 attrs;
+} LazyFunctionProp;
+
+/* NB: no sentinel at the end -- use JS_ARRAY_LENGTH to bound loops. */
+static LazyFunctionProp lazy_function_props[] = {
+ {ATOM_OFFSET(arguments), CALL_ARGUMENTS, JSPROP_PERMANENT},
+ {ATOM_OFFSET(arity), FUN_ARITY, JSPROP_PERMANENT},
+ {ATOM_OFFSET(caller), FUN_CALLER, JSPROP_PERMANENT},
+ {ATOM_OFFSET(name), FUN_NAME, JSPROP_PERMANENT},
+};
+
+static JSBool
+fun_enumerate(JSContext *cx, JSObject *obj)
+{
+ jsid prototypeId;
+ JSObject *pobj;
+ JSProperty *prop;
+
+ prototypeId = ATOM_TO_JSID(cx->runtime->atomState.classPrototypeAtom);
+ if (!OBJ_LOOKUP_PROPERTY(cx, obj, prototypeId, &pobj, &prop))
+ return JS_FALSE;
+ if (prop)
+ OBJ_DROP_PROPERTY(cx, pobj, prop);
+ return JS_TRUE;
+}
+
+static JSBool
+fun_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
+ JSObject **objp)
+{
+ JSFunction *fun;
+ JSAtom *atom;
+ uintN i;
+
+ if (!JSVAL_IS_STRING(id))
+ return JS_TRUE;
+
+ fun = GET_FUNCTION_PRIVATE(cx, obj);
+
+ /*
+ * No need to reflect fun.prototype in 'fun.prototype = ... '.
+ *
+ * This is not just an optimization, because we must not resolve when
+ * defining hidden properties during compilation. The setup code for the
+ * prototype and the lazy properties below eventually calls the property
+ * hooks for the function object. That in turn calls fun_reserveSlots to
+ * get the number of the reserved slots which is just the number of
+ * regular expressions literals in the function. When compiling, that
+ * number is not yet ready so we must make sure that fun_resolve does
+ * nothing until the code for the function is generated.
+ */
+ if (flags & JSRESOLVE_ASSIGNING)
+ return JS_TRUE;
+
+ /*
+ * Ok, check whether id is 'prototype' and bootstrap the function object's
+ * prototype property.
+ */
+ atom = cx->runtime->atomState.classPrototypeAtom;
+ if (id == ATOM_KEY(atom)) {
+ JSObject *proto;
+
+ /*
+ * Beware of the wacky case of a user function named Object -- trying
+ * to find a prototype for that will recur back here _ad perniciem_.
+ */
+ if (fun->atom == CLASS_ATOM(cx, Object))
+ return JS_TRUE;
+
+ /*
+ * Make the prototype object to have the same parent as the function
+ * object itself.
+ */
+ proto = js_NewObject(cx, &js_ObjectClass, NULL, OBJ_GET_PARENT(cx, obj),
+ 0);
+ if (!proto)
+ return JS_FALSE;
+
+ /*
+ * ECMA (15.3.5.2) says that constructor.prototype is DontDelete for
+ * user-defined functions, but DontEnum | ReadOnly | DontDelete for
+ * native "system" constructors such as Object or Function. So lazily
+ * set the former here in fun_resolve, but eagerly define the latter
+ * in JS_InitClass, with the right attributes.
+ */
+ if (!js_SetClassPrototype(cx, obj, proto,
+ JSPROP_ENUMERATE | JSPROP_PERMANENT)) {
+ cx->weakRoots.newborn[GCX_OBJECT] = NULL;
+ return JS_FALSE;
+ }
+ *objp = obj;
+ return JS_TRUE;
+ }
+
+ for (i = 0; i < JS_ARRAY_LENGTH(lazy_function_props); i++) {
+ LazyFunctionProp *lfp = &lazy_function_props[i];
+
+ atom = OFFSET_TO_ATOM(cx->runtime, lfp->atomOffset);
+ if (id == ATOM_KEY(atom)) {
+ if (!js_DefineNativeProperty(cx, obj,
+ ATOM_TO_JSID(atom), JSVAL_VOID,
+ fun_getProperty, JS_PropertyStub,
+ lfp->attrs, SPROP_HAS_SHORTID,
+ lfp->tinyid, NULL)) {
+ return JS_FALSE;
+ }
+ *objp = obj;
+ return JS_TRUE;
+ }
+ }
+
+ return JS_TRUE;
+}
+
+static JSBool
+fun_convert(JSContext *cx, JSObject *obj, JSType type, jsval *vp)
+{
+ switch (type) {
+ case JSTYPE_FUNCTION:
+ *vp = OBJECT_TO_JSVAL(obj);
+ return JS_TRUE;
+ default:
+ return js_TryValueOf(cx, obj, type, vp);
+ }
+}
+
+#if JS_HAS_XDR
+
+/* XXX store parent and proto, if defined */
+static JSBool
+fun_xdrObject(JSXDRState *xdr, JSObject **objp)
+{
+ JSContext *cx;
+ JSFunction *fun;
+ uint32 nullAtom; /* flag to indicate if fun->atom is NULL */
+ uintN nargs, nvars, n;
+ uint32 localsword; /* word to xdr argument and variable counts */
+ uint32 flagsword; /* originally only flags was JS_XDRUint8'd */
+ JSTempValueRooter tvr;
+ JSBool ok;
+
+ cx = xdr->cx;
+ if (xdr->mode == JSXDR_ENCODE) {
+ fun = GET_FUNCTION_PRIVATE(cx, *objp);
+ if (!FUN_INTERPRETED(fun)) {
+ JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
+ JSMSG_NOT_SCRIPTED_FUNCTION,
+ JS_GetFunctionName(fun));
+ return JS_FALSE;
+ }
+ nullAtom = !fun->atom;
+ nargs = fun->nargs;
+ nvars = fun->u.i.nvars;
+ localsword = (nargs << 16) | nvars;
+ flagsword = fun->flags;
+ } else {
+ fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, NULL, NULL);
+ if (!fun)
+ return JS_FALSE;
+ STOBJ_CLEAR_PARENT(FUN_OBJECT(fun));
+ STOBJ_CLEAR_PROTO(FUN_OBJECT(fun));
+#ifdef __GNUC__
+ nvars = nargs = 0; /* quell GCC uninitialized warning */
+#endif
+ }
+
+ /* From here on, control flow must flow through label out. */
+ JS_PUSH_TEMP_ROOT_OBJECT(cx, FUN_OBJECT(fun), &tvr);
+ ok = JS_TRUE;
+
+ if (!JS_XDRUint32(xdr, &nullAtom))
+ goto bad;
+ if (!nullAtom && !js_XDRStringAtom(xdr, &fun->atom))
+ goto bad;
+ if (!JS_XDRUint32(xdr, &localsword) ||
+ !JS_XDRUint32(xdr, &flagsword)) {
+ goto bad;
+ }
+
+ if (xdr->mode == JSXDR_DECODE) {
+ nargs = localsword >> 16;
+ nvars = localsword & JS_BITMASK(16);
+ JS_ASSERT(flagsword | JSFUN_INTERPRETED);
+ fun->flags = (uint16) flagsword;
+ }
+
+ /* do arguments and local vars */
+ n = nargs + nvars;
+ if (n != 0) {
+ void *mark;
+ uintN i;
+ uintN bitmapLength;
+ uint32 *bitmap;
+ jsuword *names;
+ JSAtom *name;
+ JSLocalKind localKind;
+
+ mark = JS_ARENA_MARK(&xdr->cx->tempPool);
+
+ /*
+ * From this point the control must flow via the label release_mark.
+ *
+ * To xdr the names we prefix the names with a bitmap descriptor and
+ * then xdr the names as strings. For argument names (indexes below
+ * nargs) the corresponding bit in the bitmap is unset when the name
+ * is null. Such null names are not encoded or decoded. For variable
+ * names (indexes starting from nargs) bitmap's bit is set when the
+ * name is declared as const, not as ordinary var.
+ * */
+ bitmapLength = JS_HOWMANY(n, JS_BITS_PER_UINT32);
+ JS_ARENA_ALLOCATE_CAST(bitmap, uint32 *, &xdr->cx->tempPool,
+ bitmapLength * sizeof *bitmap);
+ if (!bitmap) {
+ js_ReportOutOfScriptQuota(xdr->cx);
+ ok = JS_FALSE;
+ goto release_mark;
+ }
+ if (xdr->mode == JSXDR_ENCODE) {
+ names = js_GetLocalNameArray(xdr->cx, fun, &xdr->cx->tempPool);
+ if (!names) {
+ ok = JS_FALSE;
+ goto release_mark;
+ }
+ memset(bitmap, 0, bitmapLength * sizeof *bitmap);
+ for (i = 0; i != n; ++i) {
+ if (i < fun->nargs
+ ? JS_LOCAL_NAME_TO_ATOM(names[i]) != NULL
+ : JS_LOCAL_NAME_IS_CONST(names[i])) {
+ bitmap[i >> JS_BITS_PER_UINT32_LOG2] |=
+ JS_BIT(i & (JS_BITS_PER_UINT32 - 1));
+ }
+ }
+ }
+#ifdef __GNUC__
+ else {
+ names = NULL; /* quell GCC uninitialized warning */
+ }
+#endif
+ for (i = 0; i != bitmapLength; ++i) {
+ ok = JS_XDRUint32(xdr, &bitmap[i]);
+ if (!ok)
+ goto release_mark;
+ }
+ for (i = 0; i != n; ++i) {
+ if (i < nargs &&
+ !(bitmap[i >> JS_BITS_PER_UINT32_LOG2] &
+ JS_BIT(i & (JS_BITS_PER_UINT32 - 1)))) {
+ if (xdr->mode == JSXDR_DECODE) {
+ ok = js_AddLocal(xdr->cx, fun, NULL, JSLOCAL_ARG);
+ if (!ok)
+ goto release_mark;
+ } else {
+ JS_ASSERT(!JS_LOCAL_NAME_TO_ATOM(names[i]));
+ }
+ continue;
+ }
+ if (xdr->mode == JSXDR_ENCODE)
+ name = JS_LOCAL_NAME_TO_ATOM(names[i]);
+ ok = js_XDRStringAtom(xdr, &name);
+ if (!ok)
+ goto release_mark;
+ if (xdr->mode == JSXDR_DECODE) {
+ localKind = (i < nargs)
+ ? JSLOCAL_ARG
+ : bitmap[i >> JS_BITS_PER_UINT32_LOG2] &
+ JS_BIT(i & (JS_BITS_PER_UINT32 - 1))
+ ? JSLOCAL_CONST
+ : JSLOCAL_VAR;
+ ok = js_AddLocal(xdr->cx, fun, name, localKind);
+ if (!ok)
+ goto release_mark;
+ }
+ }
+ ok = JS_TRUE;
+
+ release_mark:
+ JS_ARENA_RELEASE(&xdr->cx->tempPool, mark);
+ if (!ok)
+ goto out;
+
+ if (xdr->mode == JSXDR_DECODE)
+ js_FreezeLocalNames(cx, fun);
+ }
+
+ if (!js_XDRScript(xdr, &fun->u.i.script, NULL))
+ goto bad;
+
+ if (xdr->mode == JSXDR_DECODE) {
+ *objp = FUN_OBJECT(fun);
+#ifdef CHECK_SCRIPT_OWNER
+ fun->u.i.script->owner = NULL;
+#endif
+ js_CallNewScriptHook(cx, fun->u.i.script, fun);
+ }
+
+out:
+ JS_POP_TEMP_ROOT(cx, &tvr);
+ return ok;
+
+bad:
+ ok = JS_FALSE;
+ goto out;
+}
+
+#else /* !JS_HAS_XDR */
+
+#define fun_xdrObject NULL
+
+#endif /* !JS_HAS_XDR */
+
+/*
+ * [[HasInstance]] internal method for Function objects: fetch the .prototype
+ * property of its 'this' parameter, and walks the prototype chain of v (only
+ * if v is an object) returning true if .prototype is found.
+ */
+static JSBool
+fun_hasInstance(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
+{
+ jsval pval;
+
+ if (!OBJ_GET_PROPERTY(cx, obj,
+ ATOM_TO_JSID(cx->runtime->atomState
+ .classPrototypeAtom),
+ &pval)) {
+ return JS_FALSE;
+ }
+
+ if (JSVAL_IS_PRIMITIVE(pval)) {
+ /*
+ * Throw a runtime error if instanceof is called on a function that
+ * has a non-object as its .prototype value.
+ */
+ js_ReportValueError(cx, JSMSG_BAD_PROTOTYPE,
+ -1, OBJECT_TO_JSVAL(obj), NULL);
+ return JS_FALSE;
+ }
+
+ return js_IsDelegate(cx, JSVAL_TO_OBJECT(pval), v, bp);
+}
+
+static void
+TraceLocalNames(JSTracer *trc, JSFunction *fun);
+
+static void
+DestroyLocalNames(JSContext *cx, JSFunction *fun);
+
+static void
+fun_trace(JSTracer *trc, JSObject *obj)
+{
+ JSFunction *fun;
+
+ /* A newborn function object may have a not yet initialized private slot. */
+ fun = (JSFunction *) JS_GetPrivate(trc->context, obj);
+ if (!fun)
+ return;
+
+ if (FUN_OBJECT(fun) != obj) {
+ /* obj is cloned function object, trace the original. */
+ JS_CALL_TRACER(trc, FUN_OBJECT(fun), JSTRACE_OBJECT, "private");
+ return;
+ }
+ if (fun->atom)
+ JS_CALL_STRING_TRACER(trc, ATOM_TO_STRING(fun->atom), "atom");
+ if (FUN_INTERPRETED(fun)) {
+ if (fun->u.i.script)
+ js_TraceScript(trc, fun->u.i.script);
+ TraceLocalNames(trc, fun);
+ }
+}
+
+static void
+fun_finalize(JSContext *cx, JSObject *obj)
+{
+ JSFunction *fun;
+
+ /* Ignore newborn and cloned function objects. */
+ fun = (JSFunction *) JS_GetPrivate(cx, obj);
+ if (!fun || FUN_OBJECT(fun) != obj)
+ return;
+
+ /*
+ * Null-check of u.i.script is required since the parser sets interpreted
+ * very early.
+ */
+ if (FUN_INTERPRETED(fun)) {
+ if (fun->u.i.script)
+ js_DestroyScript(cx, fun->u.i.script);
+ DestroyLocalNames(cx, fun);
+ }
+}
+
+static uint32
+fun_reserveSlots(JSContext *cx, JSObject *obj)
+{
+ JSFunction *fun;
+ uint32 nslots;
+
+ /*
+ * We use JS_GetPrivate and not GET_FUNCTION_PRIVATE because during
+ * js_InitFunctionClass invocation the function is called before the
+ * private slot of the function object is set.
+ */
+ fun = (JSFunction *) JS_GetPrivate(cx, obj);
+ nslots = 0;
+ if (fun && FUN_INTERPRETED(fun) && fun->u.i.script) {
+ if (fun->u.i.script->upvarsOffset != 0)
+ nslots = JS_SCRIPT_UPVARS(fun->u.i.script)->length;
+ if (fun->u.i.script->regexpsOffset != 0)
+ nslots += JS_SCRIPT_REGEXPS(fun->u.i.script)->length;
+ }
+ return nslots;
+}
+
+/*
+ * Reserve two slots in all function objects for XPConnect. Note that this
+ * does not bloat every instance, only those on which reserved slots are set,
+ * and those on which ad-hoc properties are defined.
+ */
+JS_FRIEND_DATA(JSClass) js_FunctionClass = {
+ js_Function_str,
+ JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE | JSCLASS_HAS_RESERVED_SLOTS(2) |
+ JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Function),
+ JS_PropertyStub, JS_PropertyStub,
+ JS_PropertyStub, JS_PropertyStub,
+ fun_enumerate, (JSResolveOp)fun_resolve,
+ fun_convert, fun_finalize,
+ NULL, NULL,
+ NULL, NULL,
+ fun_xdrObject, fun_hasInstance,
+ JS_CLASS_TRACE(fun_trace), fun_reserveSlots
+};
+
+static JSBool
+fun_toStringHelper(JSContext *cx, uint32 indent, uintN argc, jsval *vp)
+{
+ jsval fval;
+ JSObject *obj;
+ JSFunction *fun;
+ JSString *str;
+
+ fval = JS_THIS(cx, vp);
+ if (JSVAL_IS_NULL(fval))
+ return JS_FALSE;
+
+ if (!VALUE_IS_FUNCTION(cx, fval)) {
+ /*
+ * If we don't have a function to start off with, try converting the
+ * object to a function. If that doesn't work, complain.
+ */
+ if (!JSVAL_IS_PRIMITIVE(fval)) {
+ obj = JSVAL_TO_OBJECT(fval);
+ if (!OBJ_GET_CLASS(cx, obj)->convert(cx, obj, JSTYPE_FUNCTION,
+ &fval)) {
+ return JS_FALSE;
+ }
+ vp[1] = fval;
+ }
+ if (!VALUE_IS_FUNCTION(cx, fval)) {
+ JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
+ JSMSG_INCOMPATIBLE_PROTO,
+ js_Function_str, js_toString_str,
+ JS_GetTypeName(cx, JS_TypeOfValue(cx, fval)));
+ return JS_FALSE;
+ }
+ }
+
+ obj = JSVAL_TO_OBJECT(fval);
+ if (argc != 0) {
+ indent = js_ValueToECMAUint32(cx, &vp[2]);
+ if (JSVAL_IS_NULL(vp[2]))
+ return JS_FALSE;
+ }
+
+ JS_ASSERT(JS_ObjectIsFunction(cx, obj));
+ fun = GET_FUNCTION_PRIVATE(cx, obj);
+ if (!fun)
+ return JS_TRUE;
+ str = JS_DecompileFunction(cx, fun, (uintN)indent);
+ if (!str)
+ return JS_FALSE;
+ *vp = STRING_TO_JSVAL(str);
+ return JS_TRUE;
+}
+
+static JSBool
+fun_toString(JSContext *cx, uintN argc, jsval *vp)
+{
+ return fun_toStringHelper(cx, 0, argc, vp);
+}
+
+#if JS_HAS_TOSOURCE
+static JSBool
+fun_toSource(JSContext *cx, uintN argc, jsval *vp)
+{
+ return fun_toStringHelper(cx, JS_DONT_PRETTY_PRINT, argc, vp);
+}
+#endif
+
+JSBool
+js_fun_call(JSContext *cx, uintN argc, jsval *vp)
+{
+ JSObject *obj;
+ jsval fval, *argv, *invokevp;
+ JSString *str;
+ void *mark;
+ JSBool ok;
+
+ obj = JS_THIS_OBJECT(cx, vp);
+ if (!obj || !OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_FUNCTION, &vp[1]))
+ return JS_FALSE;
+ fval = vp[1];
+
+ if (!VALUE_IS_FUNCTION(cx, fval)) {
+ str = JS_ValueToString(cx, fval);
+ if (str) {
+ const char *bytes = js_GetStringBytes(cx, str);
+
+ if (bytes) {
+ JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
+ JSMSG_INCOMPATIBLE_PROTO,
+ js_Function_str, js_call_str,
+ bytes);
+ }
+ }
+ return JS_FALSE;
+ }
+
+ argv = vp + 2;
+ if (argc == 0) {
+ /* Call fun with its global object as the 'this' param if no args. */
+ obj = NULL;
+ } else {
+ /* Otherwise convert the first arg to 'this' and skip over it. */
+ if (!JSVAL_IS_PRIMITIVE(argv[0]))
+ obj = JSVAL_TO_OBJECT(argv[0]);
+ else if (!js_ValueToObject(cx, argv[0], &obj))
+ return JS_FALSE;
+ argc--;
+ argv++;
+ }
+
+ /* Allocate stack space for fval, obj, and the args. */
+ invokevp = js_AllocStack(cx, 2 + argc, &mark);
+ if (!invokevp)
+ return JS_FALSE;
+
+ /* Push fval, obj, and the args. */
+ invokevp[0] = fval;
+ invokevp[1] = OBJECT_TO_JSVAL(obj);
+ memcpy(invokevp + 2, argv, argc * sizeof *argv);
+
+ ok = js_Invoke(cx, argc, invokevp, 0);
+ *vp = *invokevp;
+ js_FreeStack(cx, mark);
+ return ok;
+}
+
+JSBool
+js_fun_apply(JSContext *cx, uintN argc, jsval *vp)
+{
+ JSObject *obj, *aobj;
+ jsval fval, *invokevp, *sp;
+ JSString *str;
+ jsuint length;
+ JSBool arraylike, ok;
+ void *mark;
+ uintN i;
+
+ if (argc == 0) {
+ /* Will get globalObject as 'this' and no other arguments. */
+ return js_fun_call(cx, argc, vp);
+ }
+
+ obj = JS_THIS_OBJECT(cx, vp);
+ if (!obj || !OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_FUNCTION, &vp[1]))
+ return JS_FALSE;
+ fval = vp[1];
+
+ if (!VALUE_IS_FUNCTION(cx, fval)) {
+ str = JS_ValueToString(cx, fval);
+ if (str) {
+ const char *bytes = js_GetStringBytes(cx, str);
+
+ if (bytes) {
+ JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
+ JSMSG_INCOMPATIBLE_PROTO,
+ js_Function_str, js_apply_str,
+ bytes);
+ }
+ }
+ return JS_FALSE;
+ }
+
+ /* Quell GCC overwarnings. */
+ aobj = NULL;
+ length = 0;
+
+ if (argc >= 2) {
+ /* If the 2nd arg is null or void, call the function with 0 args. */
+ if (JSVAL_IS_NULL(vp[3]) || JSVAL_IS_VOID(vp[3])) {
+ argc = 0;
+ } else {
+ /* The second arg must be an array (or arguments object). */
+ arraylike = JS_FALSE;
+ if (!JSVAL_IS_PRIMITIVE(vp[3])) {
+ aobj = JSVAL_TO_OBJECT(vp[3]);
+ if (!js_IsArrayLike(cx, aobj, &arraylike, &length))
+ return JS_FALSE;
+ }
+ if (!arraylike) {
+ JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
+ JSMSG_BAD_APPLY_ARGS, js_apply_str);
+ return JS_FALSE;
+ }
+ }
+ }
+
+ /* Convert the first arg to 'this' and skip over it. */
+ if (!JSVAL_IS_PRIMITIVE(vp[2]))
+ obj = JSVAL_TO_OBJECT(vp[2]);
+ else if (!js_ValueToObject(cx, vp[2], &obj))
+ return JS_FALSE;
+
+ /* Allocate stack space for fval, obj, and the args. */
+ argc = (uintN)JS_MIN(length, ARRAY_INIT_LIMIT - 1);
+ invokevp = js_AllocStack(cx, 2 + argc, &mark);
+ if (!invokevp)
+ return JS_FALSE;
+
+ /* Push fval, obj, and aobj's elements as args. */
+ sp = invokevp;
+ *sp++ = fval;
+ *sp++ = OBJECT_TO_JSVAL(obj);
+ for (i = 0; i < argc; i++) {
+ ok = JS_GetElement(cx, aobj, (jsint)i, sp);
+ if (!ok)
+ goto out;
+ sp++;
+ }
+
+ ok = js_Invoke(cx, argc, invokevp, 0);
+ *vp = *invokevp;
+out:
+ js_FreeStack(cx, mark);
+ return ok;
+}
+
+#ifdef NARCISSUS
+static JSBool
+fun_applyConstructor(JSContext *cx, uintN argc, jsval *vp)
+{
+ JSObject *aobj;
+ uintN length, i;
+ void *mark;
+ jsval *invokevp, *sp;
+ JSBool ok;
+
+ if (JSVAL_IS_PRIMITIVE(vp[2]) ||
+ (aobj = JSVAL_TO_OBJECT(vp[2]),
+ OBJ_GET_CLASS(cx, aobj) != &js_ArrayClass &&
+ OBJ_GET_CLASS(cx, aobj) != &js_ArgumentsClass)) {
+ JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
+ JSMSG_BAD_APPLY_ARGS, "__applyConstruct__");
+ return JS_FALSE;
+ }
+
+ if (!js_GetLengthProperty(cx, aobj, &length))
+ return JS_FALSE;
+
+ if (length >= ARRAY_INIT_LIMIT)
+ length = ARRAY_INIT_LIMIT - 1;
+ invokevp = js_AllocStack(cx, 2 + length, &mark);
+ if (!invokevp)
+ return JS_FALSE;
+
+ sp = invokevp;
+ *sp++ = vp[1];
+ *sp++ = JSVAL_NULL; /* this is filled automagically */
+ for (i = 0; i < length; i++) {
+ ok = JS_GetElement(cx, aobj, (jsint)i, sp);
+ if (!ok)
+ goto out;
+ sp++;
+ }
+
+ ok = js_InvokeConstructor(cx, length, JS_TRUE, invokevp);
+ *vp = *invokevp;
+out:
+ js_FreeStack(cx, mark);
+ return ok;
+}
+#endif
+
+static JSFunctionSpec function_methods[] = {
+#if JS_HAS_TOSOURCE
+ JS_FN(js_toSource_str, fun_toSource, 0,0),
+#endif
+ JS_FN(js_toString_str, fun_toString, 0,0),
+ JS_FN(js_apply_str, js_fun_apply, 2,0),
+ JS_FN(js_call_str, js_fun_call, 1,0),
+#ifdef NARCISSUS
+ JS_FN("__applyConstructor__", fun_applyConstructor, 1,0),
+#endif
+ JS_FS_END
+};
+
+static JSBool
+Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+{
+ JSStackFrame *fp, *caller;
+ JSFunction *fun;
+ JSObject *parent;
+ uintN i, n, lineno;
+ JSAtom *atom;
+ const char *filename;
+ JSBool ok;
+ JSString *str, *arg;
+ JSTokenStream ts;
+ JSPrincipals *principals;
+ jschar *collected_args, *cp;
+ void *mark;
+ size_t arg_length, args_length, old_args_length;
+ JSTokenType tt;
+
+ fp = cx->fp;
+ if (!(fp->flags & JSFRAME_CONSTRUCTING)) {
+ obj = js_NewObject(cx, &js_FunctionClass, NULL, NULL, 0);
+ if (!obj)
+ return JS_FALSE;
+ *rval = OBJECT_TO_JSVAL(obj);
+ } else {
+ /*
+ * The constructor is called before the private slot is initialized so
+ * we must use JS_GetPrivate, not GET_FUNCTION_PRIVATE here.
+ */
+ if (JS_GetPrivate(cx, obj))
+ return JS_TRUE;
+ }
+
+ /*
+ * NB: (new Function) is not lexically closed by its caller, it's just an
+ * anonymous function in the top-level scope that its constructor inhabits.
+ * Thus 'var x = 42; f = new Function("return x"); print(f())' prints 42,
+ * and so would a call to f from another top-level's script or function.
+ *
+ * In older versions, before call objects, a new Function was adopted by
+ * its running context's globalObject, which might be different from the
+ * top-level reachable from scopeChain (in HTML frames, e.g.).
+ */
+ parent = OBJ_GET_PARENT(cx, JSVAL_TO_OBJECT(argv[-2]));
+
+ fun = js_NewFunction(cx, obj, NULL, 0, JSFUN_LAMBDA | JSFUN_INTERPRETED,
+ parent, cx->runtime->atomState.anonymousAtom);
+
+ if (!fun)
+ return JS_FALSE;
+
+ /*
+ * Function is static and not called directly by other functions in this
+ * file, therefore it is callable only as a native function by js_Invoke.
+ * Find the scripted caller, possibly skipping other native frames such as
+ * are built for Function.prototype.call or .apply activations that invoke
+ * Function indirectly from a script.
+ */
+ JS_ASSERT(!fp->script && fp->fun && fp->fun->u.n.native == Function);
+ caller = JS_GetScriptedCaller(cx, fp);
+ if (caller) {
+ principals = JS_EvalFramePrincipals(cx, fp, caller);
+ filename = js_ComputeFilename(cx, caller, principals, &lineno);
+ } else {
+ filename = NULL;
+ lineno = 0;
+ principals = NULL;
+ }
+
+ /* Belt-and-braces: check that the caller has access to parent. */
+ if (!js_CheckPrincipalsAccess(cx, parent, principals,
+ CLASS_ATOM(cx, Function))) {
+ return JS_FALSE;
+ }
+
+ n = argc ? argc - 1 : 0;
+ if (n > 0) {
+ enum { OK, BAD, BAD_FORMAL } state;
+
+ /*
+ * Collect the function-argument arguments into one string, separated
+ * by commas, then make a tokenstream from that string, and scan it to
+ * get the arguments. We need to throw the full scanner at the
+ * problem, because the argument string can legitimately contain
+ * comments and linefeeds. XXX It might be better to concatenate
+ * everything up into a function definition and pass it to the
+ * compiler, but doing it this way is less of a delta from the old
+ * code. See ECMA 15.3.2.1.
+ */
+ state = BAD_FORMAL;
+ args_length = 0;
+ for (i = 0; i < n; i++) {
+ /* Collect the lengths for all the function-argument arguments. */
+ arg = js_ValueToString(cx, argv[i]);
+ if (!arg)
+ return JS_FALSE;
+ argv[i] = STRING_TO_JSVAL(arg);
+
+ /*
+ * Check for overflow. The < test works because the maximum
+ * JSString length fits in 2 fewer bits than size_t has.
+ */
+ old_args_length = args_length;
+ args_length = old_args_length + JSSTRING_LENGTH(arg);
+ if (args_length < old_args_length) {
+ js_ReportAllocationOverflow(cx);
+ return JS_FALSE;
+ }
+ }
+
+ /* Add 1 for each joining comma and check for overflow (two ways). */
+ old_args_length = args_length;
+ args_length = old_args_length + n - 1;
+ if (args_length < old_args_length ||
+ args_length >= ~(size_t)0 / sizeof(jschar)) {
+ js_ReportAllocationOverflow(cx);
+ return JS_FALSE;
+ }
+
+ /*
+ * Allocate a string to hold the concatenated arguments, including room
+ * for a terminating 0. Mark cx->tempPool for later release, to free
+ * collected_args and its tokenstream in one swoop.
+ */
+ mark = JS_ARENA_MARK(&cx->tempPool);
+ JS_ARENA_ALLOCATE_CAST(cp, jschar *, &cx->tempPool,
+ (args_length+1) * sizeof(jschar));
+ if (!cp) {
+ js_ReportOutOfScriptQuota(cx);
+ return JS_FALSE;
+ }
+ collected_args = cp;
+
+ /*
+ * Concatenate the arguments into the new string, separated by commas.
+ */
+ for (i = 0; i < n; i++) {
+ arg = JSVAL_TO_STRING(argv[i]);
+ arg_length = JSSTRING_LENGTH(arg);
+ (void) js_strncpy(cp, JSSTRING_CHARS(arg), arg_length);
+ cp += arg_length;
+
+ /* Add separating comma or terminating 0. */
+ *cp++ = (i + 1 < n) ? ',' : 0;
+ }
+
+ /* Initialize a tokenstream that reads from the given string. */
+ if (!js_InitTokenStream(cx, &ts, collected_args, args_length,
+ NULL, filename, lineno)) {
+ JS_ARENA_RELEASE(&cx->tempPool, mark);
+ return JS_FALSE;
+ }
+
+ /* The argument string may be empty or contain no tokens. */
+ tt = js_GetToken(cx, &ts);
+ if (tt != TOK_EOF) {
+ for (;;) {
+ /*
+ * Check that it's a name. This also implicitly guards against
+ * TOK_ERROR, which was already reported.
+ */
+ if (tt != TOK_NAME)
+ goto after_args;
+
+ /*
+ * Get the atom corresponding to the name from the token
+ * stream; we're assured at this point that it's a valid
+ * identifier.
+ */
+ atom = CURRENT_TOKEN(&ts).t_atom;
+
+ /* Check for a duplicate parameter name. */
+ if (js_LookupLocal(cx, fun, atom, NULL) != JSLOCAL_NONE) {
+ const char *name;
+
+ name = js_AtomToPrintableString(cx, atom);
+ ok = name &&
+ js_ReportCompileErrorNumber(cx, &ts, NULL,
+ JSREPORT_WARNING |
+ JSREPORT_STRICT,
+ JSMSG_DUPLICATE_FORMAL,
+ name);
+ if (!ok)
+ goto after_args;
+ }
+ if (!js_AddLocal(cx, fun, atom, JSLOCAL_ARG))
+ goto after_args;
+
+ /*
+ * Get the next token. Stop on end of stream. Otherwise
+ * insist on a comma, get another name, and iterate.
+ */
+ tt = js_GetToken(cx, &ts);
+ if (tt == TOK_EOF)
+ break;
+ if (tt != TOK_COMMA)
+ goto after_args;
+ tt = js_GetToken(cx, &ts);
+ }
+ }
+
+ state = OK;
+ after_args:
+ if (state == BAD_FORMAL && !(ts.flags & TSF_ERROR)) {
+ /*
+ * Report "malformed formal parameter" iff no illegal char or
+ * similar scanner error was already reported.
+ */
+ JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
+ JSMSG_BAD_FORMAL);
+ }
+ js_CloseTokenStream(cx, &ts);
+ JS_ARENA_RELEASE(&cx->tempPool, mark);
+ if (state != OK)
+ return JS_FALSE;
+ }
+
+ if (argc) {
+ str = js_ValueToString(cx, argv[argc-1]);
+ if (!str)
+ return JS_FALSE;
+ argv[argc-1] = STRING_TO_JSVAL(str);
+ } else {
+ str = cx->runtime->emptyString;
+ }
+
+ return js_CompileFunctionBody(cx, fun, principals,
+ JSSTRING_CHARS(str), JSSTRING_LENGTH(str),
+ filename, lineno);
+}
+
+JSObject *
+js_InitFunctionClass(JSContext *cx, JSObject *obj)
+{
+ JSObject *proto;
+ JSFunction *fun;
+
+ proto = JS_InitClass(cx, obj, NULL, &js_FunctionClass, Function, 1,
+ function_props, function_methods, NULL, NULL);
+ if (!proto)
+ return NULL;
+ fun = js_NewFunction(cx, proto, NULL, 0, JSFUN_INTERPRETED, obj, NULL);
+ if (!fun)
+ goto bad;
+ fun->u.i.script = js_NewScript(cx, 1, 1, 0, 0, 0, 0, 0);
+ if (!fun->u.i.script)
+ goto bad;
+ fun->u.i.script->code[0] = JSOP_STOP;
+ *SCRIPT_NOTES(fun->u.i.script) = SRC_NULL;
+#ifdef CHECK_SCRIPT_OWNER
+ fun->u.i.script->owner = NULL;
+#endif
+ return proto;
+
+bad:
+ cx->weakRoots.newborn[GCX_OBJECT] = NULL;
+ return NULL;
+}
+
+JSObject *
+js_InitCallClass(JSContext *cx, JSObject *obj)
+{
+ JSObject *proto;
+
+ proto = JS_InitClass(cx, obj, NULL, &js_CallClass, NULL, 0,
+ NULL, NULL, NULL, NULL);
+ if (!proto)
+ return NULL;
+
+ /*
+ * Null Call.prototype's proto slot so that Object.prototype.* does not
+ * pollute the scope of heavyweight functions.
+ */
+ OBJ_CLEAR_PROTO(cx, proto);
+ return proto;
+}
+
+JSFunction *
+js_NewFunction(JSContext *cx, JSObject *funobj, JSNative native, uintN nargs,
+ uintN flags, JSObject *parent, JSAtom *atom)
+{
+ JSFunction *fun;
+
+ if (funobj) {
+ JS_ASSERT(HAS_FUNCTION_CLASS(funobj));
+ OBJ_SET_PARENT(cx, funobj, parent);
+ } else {
+ funobj = js_NewObject(cx, &js_FunctionClass, NULL, parent, 0);
+ if (!funobj)
+ return NULL;
+ }
+ JS_ASSERT(JSVAL_IS_VOID(funobj->fslots[JSSLOT_PRIVATE]));
+ fun = (JSFunction *) funobj;
+
+ /* Initialize all function members. */
+ fun->nargs = nargs;
+ fun->flags = flags & (JSFUN_FLAGS_MASK | JSFUN_INTERPRETED | JSFUN_TRACEABLE);
+ if (flags & JSFUN_INTERPRETED) {
+ JS_ASSERT(!native);
+ JS_ASSERT(nargs == 0);
+ fun->u.i.nvars = 0;
+ fun->u.i.nupvars = 0;
+ fun->u.i.script = NULL;
+#ifdef DEBUG
+ fun->u.i.names.taggedAtom = 0;
+#endif
+ } else {
+ fun->u.n.extra = 0;
+ fun->u.n.spare = 0;
+ if (flags & JSFUN_TRACEABLE) {
+#ifdef JS_TRACER
+ JSTraceableNative *trcinfo = (JSTraceableNative *) native;
+ fun->u.n.native = (JSNative) trcinfo->native;
+ FUN_TRCINFO(fun) = trcinfo;
+#else
+ JS_ASSERT(0);
+#endif
+ } else {
+ fun->u.n.native = native;
+ FUN_CLASP(fun) = NULL;
+ }
+ }
+ fun->atom = atom;
+
+ /* Set private to self to indicate non-cloned fully initialized function. */
+ FUN_OBJECT(fun)->fslots[JSSLOT_PRIVATE] = PRIVATE_TO_JSVAL(fun);
+ return fun;
+}
+
+JSObject *
+js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent)
+{
+ JSObject *clone;
+
+ /*
+ * The cloned function object does not need the extra fields beyond
+ * JSObject as it points to fun via the private slot.
+ */
+ clone = js_NewObject(cx, &js_FunctionClass, NULL, parent,
+ sizeof(JSObject));
+ if (!clone)
+ return NULL;
+ clone->fslots[JSSLOT_PRIVATE] = PRIVATE_TO_JSVAL(fun);
+ return clone;
+}
+
+JSFunction *
+js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, JSNative native,
+ uintN nargs, uintN attrs)
+{
+ JSFunction *fun;
+ JSPropertyOp gsop;
+
+ fun = js_NewFunction(cx, NULL, native, nargs, attrs, obj, atom);
+ if (!fun)
+ return NULL;
+ gsop = (attrs & JSFUN_STUB_GSOPS) ? JS_PropertyStub : NULL;
+ if (!OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom),
+ OBJECT_TO_JSVAL(FUN_OBJECT(fun)),
+ gsop, gsop,
+ attrs & ~JSFUN_FLAGS_MASK, NULL)) {
+ return NULL;
+ }
+ return fun;
+}
+
+#if (JSV2F_CONSTRUCT & JSV2F_SEARCH_STACK)
+# error "JSINVOKE_CONSTRUCT and JSV2F_SEARCH_STACK are not disjoint!"
+#endif
+
+JSFunction *
+js_ValueToFunction(JSContext *cx, jsval *vp, uintN flags)
+{
+ jsval v;
+ JSObject *obj;
+
+ v = *vp;
+ obj = NULL;
+ if (JSVAL_IS_OBJECT(v)) {
+ obj = JSVAL_TO_OBJECT(v);
+ if (obj && OBJ_GET_CLASS(cx, obj) != &js_FunctionClass) {
+ if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_FUNCTION, &v))
+ return NULL;
+ obj = VALUE_IS_FUNCTION(cx, v) ? JSVAL_TO_OBJECT(v) : NULL;
+ }
+ }
+ if (!obj) {
+ js_ReportIsNotFunction(cx, vp, flags);
+ return NULL;
+ }
+ return GET_FUNCTION_PRIVATE(cx, obj);
+}
+
+JSObject *
+js_ValueToFunctionObject(JSContext *cx, jsval *vp, uintN flags)
+{
+ JSFunction *fun;
+ JSStackFrame *caller;
+ JSPrincipals *principals;
+
+ if (VALUE_IS_FUNCTION(cx, *vp))
+ return JSVAL_TO_OBJECT(*vp);
+
+ fun = js_ValueToFunction(cx, vp, flags);
+ if (!fun)
+ return NULL;
+ *vp = OBJECT_TO_JSVAL(FUN_OBJECT(fun));
+
+ caller = JS_GetScriptedCaller(cx, cx->fp);
+ if (caller) {
+ principals = JS_StackFramePrincipals(cx, caller);
+ } else {
+ /* No scripted caller, don't allow access. */
+ principals = NULL;
+ }
+
+ if (!js_CheckPrincipalsAccess(cx, FUN_OBJECT(fun), principals,
+ fun->atom
+ ? fun->atom
+ : cx->runtime->atomState.anonymousAtom)) {
+ return NULL;
+ }
+ return FUN_OBJECT(fun);
+}
+
+JSObject *
+js_ValueToCallableObject(JSContext *cx, jsval *vp, uintN flags)
+{
+ JSObject *callable;
+
+ callable = JSVAL_IS_PRIMITIVE(*vp) ? NULL : JSVAL_TO_OBJECT(*vp);
+ if (callable &&
+ ((callable->map->ops == &js_ObjectOps)
+ ? OBJ_GET_CLASS(cx, callable)->call
+ : callable->map->ops->call)) {
+ *vp = OBJECT_TO_JSVAL(callable);
+ } else {
+ callable = js_ValueToFunctionObject(cx, vp, flags);
+ }
+ return callable;
+}
+
+void
+js_ReportIsNotFunction(JSContext *cx, jsval *vp, uintN flags)
+{
+ JSStackFrame *fp;
+ uintN error;
+ const char *name, *source;
+ JSTempValueRooter tvr;
+
+ for (fp = cx->fp; fp && !fp->regs; fp = fp->down)
+ continue;
+ name = source = NULL;
+ JS_PUSH_TEMP_ROOT_STRING(cx, NULL, &tvr);
+ if (flags & JSV2F_ITERATOR) {
+ error = JSMSG_BAD_ITERATOR;
+ name = js_iterator_str;
+ tvr.u.string = js_ValueToSource(cx, *vp);
+ if (!tvr.u.string)
+ goto out;
+ tvr.u.string = js_QuoteString(cx, tvr.u.string, 0);
+ if (!tvr.u.string)
+ goto out;
+ source = js_GetStringBytes(cx, tvr.u.string);
+ if (!source)
+ goto out;
+ } else if (flags & JSV2F_CONSTRUCT) {
+ error = JSMSG_NOT_CONSTRUCTOR;
+ } else {
+ error = JSMSG_NOT_FUNCTION;
+ }
+
+ js_ReportValueError3(cx, error,
+ (fp && fp->regs &&
+ StackBase(fp) <= vp && vp < fp->regs->sp)
+ ? vp - fp->regs->sp
+ : (flags & JSV2F_SEARCH_STACK)
+ ? JSDVG_SEARCH_STACK
+ : JSDVG_IGNORE_STACK,
+ *vp, NULL,
+ name, source);
+
+ out:
+ JS_POP_TEMP_ROOT(cx, &tvr);
+}
+
+/*
+ * When a function has between 2 and MAX_ARRAY_LOCALS arguments and variables,
+ * their name are stored as the JSLocalNames.array.
+ */
+#define MAX_ARRAY_LOCALS 8
+
+JS_STATIC_ASSERT(2 <= MAX_ARRAY_LOCALS);
+JS_STATIC_ASSERT(MAX_ARRAY_LOCALS < JS_BITMASK(16));
+
+/*
+ * We use the lowest bit of the string atom to distinguish const from var
+ * name when there is only single name or when names are stored as an array.
+ */
+JS_STATIC_ASSERT((JSVAL_STRING & 1) == 0);
+
+/*
+ * When we use a hash table to store the local names, we use a singly linked
+ * list to record the indexes of duplicated parameter names to preserve the
+ * duplicates for the decompiler.
+ */
+typedef struct JSNameIndexPair JSNameIndexPair;
+
+struct JSNameIndexPair {
+ JSAtom *name;
+ uint16 index;
+ JSNameIndexPair *link;
+};
+
+struct JSLocalNameMap {
+ JSDHashTable names;
+ JSNameIndexPair *lastdup;
+};
+
+typedef struct JSLocalNameHashEntry {
+ JSDHashEntryHdr hdr;
+ JSAtom *name;
+ uint16 index;
+ uint8 localKind;
+} JSLocalNameHashEntry;
+
+static void
+FreeLocalNameHash(JSContext *cx, JSLocalNameMap *map)
+{
+ JSNameIndexPair *dup, *next;
+
+ for (dup = map->lastdup; dup; dup = next) {
+ next = dup->link;
+ JS_free(cx, dup);
+ }
+ JS_DHashTableFinish(&map->names);
+ JS_free(cx, map);
+}
+
+static JSBool
+HashLocalName(JSContext *cx, JSLocalNameMap *map, JSAtom *name,
+ JSLocalKind localKind, uintN index)
+{
+ JSLocalNameHashEntry *entry;
+ JSNameIndexPair *dup;
+
+ JS_ASSERT(index <= JS_BITMASK(16));
+#if JS_HAS_DESTRUCTURING
+ if (!name) {
+ /* A destructuring pattern does not need a hash entry. */
+ JS_ASSERT(localKind == JSLOCAL_ARG);
+ return JS_TRUE;
+ }
+#endif
+ JS_ASSERT(ATOM_IS_STRING(name));
+ entry = (JSLocalNameHashEntry *)
+ JS_DHashTableOperate(&map->names, name, JS_DHASH_ADD);
+ if (!entry) {
+ JS_ReportOutOfMemory(cx);
+ return JS_FALSE;
+ }
+ if (entry->name) {
+ JS_ASSERT(entry->name == name);
+ JS_ASSERT(entry->localKind == JSLOCAL_ARG);
+ dup = (JSNameIndexPair *) JS_malloc(cx, sizeof *dup);
+ if (!dup)
+ return JS_FALSE;
+ dup->name = entry->name;
+ dup->index = entry->index;
+ dup->link = map->lastdup;
+ map->lastdup = dup;
+ }
+ entry->name = name;
+ entry->index = (uint16) index;
+ entry->localKind = (uint8) localKind;
+ return JS_TRUE;
+}
+
+JSBool
+js_AddLocal(JSContext *cx, JSFunction *fun, JSAtom *atom, JSLocalKind kind)
+{
+ jsuword taggedAtom;
+ uint16 *indexp;
+ uintN n, i;
+ jsuword *array;
+ JSLocalNameMap *map;
+
+ JS_ASSERT(FUN_INTERPRETED(fun));
+ JS_ASSERT(!fun->u.i.script);
+ JS_ASSERT(((jsuword) atom & 1) == 0);
+ taggedAtom = (jsuword) atom;
+ if (kind == JSLOCAL_ARG) {
+ indexp = &fun->nargs;
+ } else if (kind == JSLOCAL_UPVAR) {
+ indexp = &fun->u.i.nupvars;
+ } else {
+ indexp = &fun->u.i.nvars;
+ if (kind == JSLOCAL_CONST)
+ taggedAtom |= 1;
+ else
+ JS_ASSERT(kind == JSLOCAL_VAR);
+ }
+ n = JS_GET_LOCAL_NAME_COUNT(fun);
+ if (n == 0) {
+ JS_ASSERT(fun->u.i.names.taggedAtom == 0);
+ fun->u.i.names.taggedAtom = taggedAtom;
+ } else if (n < MAX_ARRAY_LOCALS) {
+ if (n > 1) {
+ array = fun->u.i.names.array;
+ } else {
+ array = (jsuword *) JS_malloc(cx, MAX_ARRAY_LOCALS * sizeof *array);
+ if (!array)
+ return JS_FALSE;
+ array[0] = fun->u.i.names.taggedAtom;
+ fun->u.i.names.array = array;
+ }
+ if (kind == JSLOCAL_ARG) {
+ /*
+ * A destructuring argument pattern adds variables, not arguments,
+ * so for the following arguments nvars != 0.
+ */
+#if JS_HAS_DESTRUCTURING
+ if (fun->u.i.nvars != 0) {
+ memmove(array + fun->nargs + 1, array + fun->nargs,
+ fun->u.i.nvars * sizeof *array);
+ }
+#else
+ JS_ASSERT(fun->u.i.nvars == 0);
+#endif
+ array[fun->nargs] = taggedAtom;
+ } else {
+ array[n] = taggedAtom;
+ }
+ } else if (n == MAX_ARRAY_LOCALS) {
+ array = fun->u.i.names.array;
+ map = (JSLocalNameMap *) JS_malloc(cx, sizeof *map);
+ if (!map)
+ return JS_FALSE;
+ if (!JS_DHashTableInit(&map->names, JS_DHashGetStubOps(),
+ NULL, sizeof(JSLocalNameHashEntry),
+ JS_DHASH_DEFAULT_CAPACITY(MAX_ARRAY_LOCALS
+ * 2))) {
+ JS_ReportOutOfMemory(cx);
+ JS_free(cx, map);
+ return JS_FALSE;
+ }
+
+ map->lastdup = NULL;
+ for (i = 0; i != MAX_ARRAY_LOCALS; ++i) {
+ taggedAtom = array[i];
+ if (!HashLocalName(cx, map, (JSAtom *) (taggedAtom & ~1),
+ (i < fun->nargs)
+ ? JSLOCAL_ARG
+ : (taggedAtom & 1) ? JSLOCAL_CONST : JSLOCAL_VAR,
+ (i < fun->nargs) ? i : i - fun->nargs)) {
+ FreeLocalNameHash(cx, map);
+ return JS_FALSE;
+ }
+ }
+ if (!HashLocalName(cx, map, atom, kind, *indexp)) {
+ FreeLocalNameHash(cx, map);
+ return JS_FALSE;
+ }
+
+ /*
+ * At this point the entry is added and we cannot fail. It is time
+ * to replace fun->u.i.names with the built map.
+ */
+ fun->u.i.names.map = map;
+ JS_free(cx, array);
+ } else {
+ if (*indexp == JS_BITMASK(16)) {
+ JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
+ (kind == JSLOCAL_ARG)
+ ? JSMSG_TOO_MANY_FUN_ARGS
+ : JSMSG_TOO_MANY_LOCALS);
+ return JS_FALSE;
+ }
+ if (!HashLocalName(cx, fun->u.i.names.map, atom, kind, *indexp))
+ return JS_FALSE;
+ }
+
+ /* Update the argument or variable counter. */
+ ++*indexp;
+ return JS_TRUE;
+}
+
+JSLocalKind
+js_LookupLocal(JSContext *cx, JSFunction *fun, JSAtom *atom, uintN *indexp)
+{
+ uintN n, i, upvar_base;
+ jsuword *array;
+ JSLocalNameHashEntry *entry;
+
+ JS_ASSERT(FUN_INTERPRETED(fun));
+ n = JS_GET_LOCAL_NAME_COUNT(fun);
+ if (n == 0)
+ return JSLOCAL_NONE;
+ if (n <= MAX_ARRAY_LOCALS) {
+ array = (n == 1) ? &fun->u.i.names.taggedAtom : fun->u.i.names.array;
+
+ /* Search from the tail to pick up the last duplicated name. */
+ i = n;
+ upvar_base = JS_UPVAR_LOCAL_NAME_START(fun);
+ do {
+ --i;
+ if (atom == JS_LOCAL_NAME_TO_ATOM(array[i])) {
+ if (i < fun->nargs) {
+ if (indexp)
+ *indexp = i;
+ return JSLOCAL_ARG;
+ }
+ if (i >= upvar_base) {
+ if (indexp)
+ *indexp = i - upvar_base;
+ return JSLOCAL_UPVAR;
+ }
+ if (indexp)
+ *indexp = i - fun->nargs;
+ return JS_LOCAL_NAME_IS_CONST(array[i])
+ ? JSLOCAL_CONST
+ : JSLOCAL_VAR;
+ }
+ } while (i != 0);
+ } else {
+ entry = (JSLocalNameHashEntry *)
+ JS_DHashTableOperate(&fun->u.i.names.map->names, atom,
+ JS_DHASH_LOOKUP);
+ if (JS_DHASH_ENTRY_IS_BUSY(&entry->hdr)) {
+ JS_ASSERT(entry->localKind != JSLOCAL_NONE);
+ if (indexp)
+ *indexp = entry->index;
+ return (JSLocalKind) entry->localKind;
+ }
+ }
+ return JSLOCAL_NONE;
+}
+
+typedef struct JSLocalNameEnumeratorArgs {
+ JSFunction *fun;
+ jsuword *names;
+#ifdef DEBUG
+ uintN nCopiedArgs;
+ uintN nCopiedVars;
+#endif
+} JSLocalNameEnumeratorArgs;
+
+static JSDHashOperator
+get_local_names_enumerator(JSDHashTable *table, JSDHashEntryHdr *hdr,
+ uint32 number, void *arg)
+{
+ JSLocalNameHashEntry *entry;
+ JSLocalNameEnumeratorArgs *args;
+ uint i;
+ jsuword constFlag;
+
+ entry = (JSLocalNameHashEntry *) hdr;
+ args = (JSLocalNameEnumeratorArgs *) arg;
+ JS_ASSERT(entry->name);
+ if (entry->localKind == JSLOCAL_ARG) {
+ JS_ASSERT(entry->index < args->fun->nargs);
+ JS_ASSERT(args->nCopiedArgs++ < args->fun->nargs);
+ i = entry->index;
+ constFlag = 0;
+ } else {
+ JS_ASSERT(entry->localKind == JSLOCAL_VAR ||
+ entry->localKind == JSLOCAL_CONST);
+ JS_ASSERT(entry->index < args->fun->u.i.nvars);
+ JS_ASSERT(args->nCopiedVars++ < args->fun->u.i.nvars);
+ i = args->fun->nargs + entry->index;
+ constFlag = (entry->localKind == JSLOCAL_CONST);
+ }
+ args->names[i] = (jsuword) entry->name | constFlag;
+ return JS_DHASH_NEXT;
+}
+
+jsuword *
+js_GetLocalNameArray(JSContext *cx, JSFunction *fun, JSArenaPool *pool)
+{
+ uintN n;
+ jsuword *names;
+ JSLocalNameMap *map;
+ JSLocalNameEnumeratorArgs args;
+ JSNameIndexPair *dup;
+
+ JS_ASSERT(FUN_INTERPRETED(fun));
+ n = JS_GET_LOCAL_NAME_COUNT(fun);
+ JS_ASSERT(n != 0);
+
+ if (n <= MAX_ARRAY_LOCALS)
+ return (n == 1) ? &fun->u.i.names.taggedAtom : fun->u.i.names.array;
+
+ /*
+ * No need to check for overflow of the allocation size as we are making a
+ * copy of already allocated data. As such it must fit size_t.
+ */
+ JS_ARENA_ALLOCATE_CAST(names, jsuword *, pool, (size_t) n * sizeof *names);
+ if (!names) {
+ js_ReportOutOfScriptQuota(cx);
+ return NULL;
+ }
+
+#if JS_HAS_DESTRUCTURING
+ /* Some parameter names can be NULL due to destructuring patterns. */
+ memset(names, 0, fun->nargs * sizeof *names);
+#endif
+ map = fun->u.i.names.map;
+ args.fun = fun;
+ args.names = names;
+#ifdef DEBUG
+ args.nCopiedArgs = 0;
+ args.nCopiedVars = 0;
+#endif
+ JS_DHashTableEnumerate(&map->names, get_local_names_enumerator, &args);
+ for (dup = map->lastdup; dup; dup = dup->link) {
+ JS_ASSERT(dup->index < fun->nargs);
+ JS_ASSERT(args.nCopiedArgs++ < fun->nargs);
+ names[dup->index] = (jsuword) dup->name;
+ }
+#if !JS_HAS_DESTRUCTURING
+ JS_ASSERT(args.nCopiedArgs == fun->nargs);
+#endif
+ JS_ASSERT(args.nCopiedVars == fun->u.i.nvars);
+
+ return names;
+}
+
+static JSDHashOperator
+trace_local_names_enumerator(JSDHashTable *table, JSDHashEntryHdr *hdr,
+ uint32 number, void *arg)
+{
+ JSLocalNameHashEntry *entry;
+ JSTracer *trc;
+
+ entry = (JSLocalNameHashEntry *) hdr;
+ JS_ASSERT(entry->name);
+ trc = (JSTracer *) arg;
+ JS_SET_TRACING_INDEX(trc,
+ entry->localKind == JSLOCAL_ARG ? "arg" : "var",
+ entry->index);
+ JS_CallTracer(trc, ATOM_TO_STRING(entry->name), JSTRACE_STRING);
+ return JS_DHASH_NEXT;
+}
+
+static void
+TraceLocalNames(JSTracer *trc, JSFunction *fun)
+{
+ uintN n, i;
+ JSAtom *atom;
+ jsuword *array;
+
+ JS_ASSERT(FUN_INTERPRETED(fun));
+ n = JS_GET_LOCAL_NAME_COUNT(fun);
+ if (n == 0)
+ return;
+ if (n <= MAX_ARRAY_LOCALS) {
+ array = (n == 1) ? &fun->u.i.names.taggedAtom : fun->u.i.names.array;
+ i = n;
+ do {
+ --i;
+ atom = (JSAtom *) (array[i] & ~1);
+ if (atom) {
+ JS_SET_TRACING_INDEX(trc,
+ i < fun->nargs ? "arg" : "var",
+ i < fun->nargs ? i : i - fun->nargs);
+ JS_CallTracer(trc, ATOM_TO_STRING(atom), JSTRACE_STRING);
+ }
+ } while (i != 0);
+ } else {
+ JS_DHashTableEnumerate(&fun->u.i.names.map->names,
+ trace_local_names_enumerator, trc);
+
+ /*
+ * No need to trace the list of duplicates in map->lastdup as the
+ * names there are traced when enumerating the hash table.
+ */
+ }
+}
+
+void
+DestroyLocalNames(JSContext *cx, JSFunction *fun)
+{
+ uintN n;
+
+ n = fun->nargs + fun->u.i.nvars;
+ if (n <= 1)
+ return;
+ if (n <= MAX_ARRAY_LOCALS)
+ JS_free(cx, fun->u.i.names.array);
+ else
+ FreeLocalNameHash(cx, fun->u.i.names.map);
+}
+
+void
+js_FreezeLocalNames(JSContext *cx, JSFunction *fun)
+{
+ uintN n;
+ jsuword *array;
+
+ JS_ASSERT(FUN_INTERPRETED(fun));
+ JS_ASSERT(!fun->u.i.script);
+ n = fun->nargs + fun->u.i.nvars + fun->u.i.nupvars;
+ if (2 <= n && n < MAX_ARRAY_LOCALS) {
+ /* Shrink over-allocated array ignoring realloc failures. */
+ array = (jsuword *) JS_realloc(cx, fun->u.i.names.array,
+ n * sizeof *array);
+ if (array)
+ fun->u.i.names.array = array;
+ }
+}