1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sw=4 et tw=99:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
17 * The Original Code is Mozilla Communicator client code, released
20 * The Initial Developer of the Original Code is
21 * Netscape Communications Corporation.
22 * Portions created by the Initial Developer are Copyright (C) 1998
23 * the Initial Developer. All Rights Reserved.
27 * Alternatively, the contents of this file may be used under the terms of
28 * either of the GNU General Public License Version 2 or later (the "GPL"),
29 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
30 * in which case the provisions of the GPL or the LGPL are applicable instead
31 * of those above. If you wish to allow use of your version of this file only
32 * under the terms of either the GPL or the LGPL, and not to allow others to
33 * use your version of this file under the terms of the MPL, indicate your
34 * decision by deleting the provisions above and replace them with the notice
35 * and other provisions required by the GPL or the LGPL. If you do not delete
36 * the provisions above, a recipient may use your version of this file under
37 * the terms of any one of the MPL, the GPL or the LGPL.
39 * ***** END LICENSE BLOCK ***** */
42 * JS function support.
48 #include "jsutil.h" /* Added by JSIFY */
52 #include "jsbuiltins.h"
54 #include "jsversion.h"
70 #include "jsstaticcheck.h"
77 # include "jsxdrapi.h"
80 /* Generic function/call/arguments tinyids -- also reflected bit numbers. */
82 CALL_ARGUMENTS
= -1, /* predefined arguments local variable */
83 ARGS_LENGTH
= -2, /* number of actual args, arity if inactive */
84 ARGS_CALLEE
= -3, /* reference from arguments to active funobj */
85 FUN_ARITY
= -4, /* number of formal parameters; desired argc */
86 FUN_NAME
= -5, /* function name, "" if anonymous */
87 FUN_CALLER
= -6 /* Function.prototype.caller, backward compat */
90 #if JSFRAME_OVERRIDE_BITS < 8
91 # error "not enough override bits in JSStackFrame.flags!"
94 #define TEST_OVERRIDE_BIT(fp, tinyid) \
95 ((fp)->flags & JS_BIT(JSFRAME_OVERRIDE_SHIFT - ((tinyid) + 1)))
97 #define SET_OVERRIDE_BIT(fp, tinyid) \
98 ((fp)->flags |= JS_BIT(JSFRAME_OVERRIDE_SHIFT - ((tinyid) + 1)))
101 js_GetArgsValue(JSContext
*cx
, JSStackFrame
*fp
, jsval
*vp
)
105 if (TEST_OVERRIDE_BIT(fp
, CALL_ARGUMENTS
)) {
106 JS_ASSERT(fp
->callobj
);
107 return OBJ_GET_PROPERTY(cx
, fp
->callobj
,
108 ATOM_TO_JSID(cx
->runtime
->atomState
112 argsobj
= js_GetArgsObject(cx
, fp
);
115 *vp
= OBJECT_TO_JSVAL(argsobj
);
120 MarkArgDeleted(JSContext
*cx
, JSStackFrame
*fp
, uintN slot
)
123 jsval bmapval
, bmapint
;
124 size_t nbits
, nbytes
;
127 argsobj
= fp
->argsobj
;
128 (void) JS_GetReservedSlot(cx
, argsobj
, 0, &bmapval
);
130 JS_ASSERT(slot
< nbits
);
131 if (JSVAL_IS_VOID(bmapval
)) {
132 if (nbits
<= JSVAL_INT_BITS
) {
134 bitmap
= (jsbitmap
*) &bmapint
;
136 nbytes
= JS_HOWMANY(nbits
, JS_BITS_PER_WORD
) * sizeof(jsbitmap
);
137 bitmap
= (jsbitmap
*) JS_malloc(cx
, nbytes
);
140 memset(bitmap
, 0, nbytes
);
141 bmapval
= PRIVATE_TO_JSVAL(bitmap
);
142 JS_SetReservedSlot(cx
, argsobj
, 0, bmapval
);
145 if (nbits
<= JSVAL_INT_BITS
) {
146 bmapint
= JSVAL_TO_INT(bmapval
);
147 bitmap
= (jsbitmap
*) &bmapint
;
149 bitmap
= (jsbitmap
*) JSVAL_TO_PRIVATE(bmapval
);
152 JS_SET_BIT(bitmap
, slot
);
153 if (bitmap
== (jsbitmap
*) &bmapint
) {
154 bmapval
= INT_TO_JSVAL(bmapint
);
155 JS_SetReservedSlot(cx
, argsobj
, 0, bmapval
);
160 /* NB: Infallible predicate, false does not mean error/exception. */
162 ArgWasDeleted(JSContext
*cx
, JSStackFrame
*fp
, uintN slot
)
165 jsval bmapval
, bmapint
;
168 argsobj
= fp
->argsobj
;
169 (void) JS_GetReservedSlot(cx
, argsobj
, 0, &bmapval
);
170 if (JSVAL_IS_VOID(bmapval
))
172 if (fp
->argc
<= JSVAL_INT_BITS
) {
173 bmapint
= JSVAL_TO_INT(bmapval
);
174 bitmap
= (jsbitmap
*) &bmapint
;
176 bitmap
= (jsbitmap
*) JSVAL_TO_PRIVATE(bmapval
);
178 return JS_TEST_BIT(bitmap
, slot
) != 0;
182 js_GetArgsProperty(JSContext
*cx
, JSStackFrame
*fp
, jsid id
, jsval
*vp
)
188 if (TEST_OVERRIDE_BIT(fp
, CALL_ARGUMENTS
)) {
189 JS_ASSERT(fp
->callobj
);
190 if (!OBJ_GET_PROPERTY(cx
, fp
->callobj
,
191 ATOM_TO_JSID(cx
->runtime
->atomState
196 if (JSVAL_IS_PRIMITIVE(val
)) {
197 obj
= js_ValueToNonNullObject(cx
, val
);
201 obj
= JSVAL_TO_OBJECT(val
);
203 return OBJ_GET_PROPERTY(cx
, obj
, id
, vp
);
207 if (JSID_IS_INT(id
)) {
208 slot
= (uintN
) JSID_TO_INT(id
);
209 if (slot
< fp
->argc
) {
210 if (fp
->argsobj
&& ArgWasDeleted(cx
, fp
, slot
))
211 return OBJ_GET_PROPERTY(cx
, fp
->argsobj
, id
, vp
);
212 *vp
= fp
->argv
[slot
];
215 * Per ECMA-262 Ed. 3, 10.1.8, last bulleted item, do not share
216 * storage between the formal parameter and arguments[k] for all
217 * fp->argc <= k && k < fp->fun->nargs. For example, in
219 * function f(x) { x = 42; return arguments[0]; }
222 * the call to f should return undefined, not 42. If fp->argsobj
223 * is null at this point, as it would be in the example, return
227 return OBJ_GET_PROPERTY(cx
, fp
->argsobj
, id
, vp
);
230 if (id
== ATOM_TO_JSID(cx
->runtime
->atomState
.lengthAtom
)) {
231 if (fp
->argsobj
&& TEST_OVERRIDE_BIT(fp
, ARGS_LENGTH
))
232 return OBJ_GET_PROPERTY(cx
, fp
->argsobj
, id
, vp
);
233 *vp
= INT_TO_JSVAL((jsint
) fp
->argc
);
240 js_GetArgsObject(JSContext
*cx
, JSStackFrame
*fp
)
242 JSObject
*argsobj
, *global
, *parent
;
245 * We must be in a function activation; the function must be lightweight
246 * or else fp must have a variable object.
248 JS_ASSERT(fp
->fun
&& (!(fp
->fun
->flags
& JSFUN_HEAVYWEIGHT
) || fp
->varobj
));
250 /* Skip eval and debugger frames. */
251 while (fp
->flags
& JSFRAME_SPECIAL
)
254 /* Create an arguments object for fp only if it lacks one. */
255 argsobj
= fp
->argsobj
;
259 /* Link the new object to fp so it can get actual argument values. */
260 argsobj
= js_NewObject(cx
, &js_ArgumentsClass
, NULL
, NULL
, 0);
261 if (!argsobj
|| !JS_SetPrivate(cx
, argsobj
, fp
)) {
262 cx
->weakRoots
.newborn
[GCX_OBJECT
] = NULL
;
267 * Give arguments an intrinsic scope chain link to fp's global object.
268 * Since the arguments object lacks a prototype because js_ArgumentsClass
269 * is not initialized, js_NewObject won't assign a default parent to it.
271 * Therefore if arguments is used as the head of an eval scope chain (via
272 * a direct or indirect call to eval(program, arguments)), any reference
273 * to a standard class object in the program will fail to resolve due to
274 * js_GetClassPrototype not being able to find a global object containing
275 * the standard prototype by starting from arguments and following parent.
277 global
= fp
->scopeChain
;
278 while ((parent
= OBJ_GET_PARENT(cx
, global
)) != NULL
)
280 STOBJ_SET_PARENT(argsobj
, global
);
281 fp
->argsobj
= argsobj
;
286 args_enumerate(JSContext
*cx
, JSObject
*obj
);
288 JS_FRIEND_API(JSBool
)
289 js_PutArgsObject(JSContext
*cx
, JSStackFrame
*fp
)
297 * Reuse args_enumerate here to reflect fp's actual arguments as indexed
298 * elements of argsobj. Do this first, before clearing and freeing the
299 * deleted argument slot bitmap, because args_enumerate depends on that.
301 argsobj
= fp
->argsobj
;
302 ok
= args_enumerate(cx
, argsobj
);
305 * Now clear the deleted argument number bitmap slot and free the bitmap,
306 * if one was actually created due to 'delete arguments[0]' or similar.
308 (void) JS_GetReservedSlot(cx
, argsobj
, 0, &bmapval
);
309 if (!JSVAL_IS_VOID(bmapval
)) {
310 JS_SetReservedSlot(cx
, argsobj
, 0, JSVAL_VOID
);
311 if (fp
->argc
> JSVAL_INT_BITS
)
312 JS_free(cx
, JSVAL_TO_PRIVATE(bmapval
));
316 * Now get the prototype properties so we snapshot fp->fun and fp->argc
317 * before fp goes away.
320 ok
&= js_GetProperty(cx
, argsobj
, ATOM_TO_JSID(rt
->atomState
.calleeAtom
),
322 ok
&= js_SetProperty(cx
, argsobj
, ATOM_TO_JSID(rt
->atomState
.calleeAtom
),
324 ok
&= js_GetProperty(cx
, argsobj
, ATOM_TO_JSID(rt
->atomState
.lengthAtom
),
326 ok
&= js_SetProperty(cx
, argsobj
, ATOM_TO_JSID(rt
->atomState
.lengthAtom
),
330 * Clear the private pointer to fp, which is about to go away (js_Invoke).
331 * Do this last because the args_enumerate and js_GetProperty calls above
332 * need to follow the private slot to find fp.
334 ok
&= JS_SetPrivate(cx
, argsobj
, NULL
);
340 args_delProperty(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
345 if (!JSVAL_IS_INT(id
))
347 fp
= (JSStackFrame
*)
348 JS_GetInstancePrivate(cx
, obj
, &js_ArgumentsClass
, NULL
);
351 JS_ASSERT(fp
->argsobj
);
353 slot
= JSVAL_TO_INT(id
);
357 SET_OVERRIDE_BIT(fp
, slot
);
361 if ((uintN
)slot
< fp
->argc
&& !MarkArgDeleted(cx
, fp
, slot
))
369 args_getProperty(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
374 if (!JSVAL_IS_INT(id
))
376 fp
= (JSStackFrame
*)
377 JS_GetInstancePrivate(cx
, obj
, &js_ArgumentsClass
, NULL
);
380 JS_ASSERT(fp
->argsobj
);
382 slot
= JSVAL_TO_INT(id
);
385 if (!TEST_OVERRIDE_BIT(fp
, slot
))
386 *vp
= OBJECT_TO_JSVAL(fp
->callee
);
390 if (!TEST_OVERRIDE_BIT(fp
, slot
))
391 *vp
= INT_TO_JSVAL((jsint
)fp
->argc
);
395 if ((uintN
)slot
< fp
->argc
&& !ArgWasDeleted(cx
, fp
, slot
))
396 *vp
= fp
->argv
[slot
];
403 args_setProperty(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
408 if (!JSVAL_IS_INT(id
))
410 fp
= (JSStackFrame
*)
411 JS_GetInstancePrivate(cx
, obj
, &js_ArgumentsClass
, NULL
);
414 JS_ASSERT(fp
->argsobj
);
416 slot
= JSVAL_TO_INT(id
);
420 SET_OVERRIDE_BIT(fp
, slot
);
424 if (FUN_INTERPRETED(fp
->fun
) &&
425 (uintN
)slot
< fp
->argc
&&
426 !ArgWasDeleted(cx
, fp
, slot
)) {
427 fp
->argv
[slot
] = *vp
;
435 args_resolve(JSContext
*cx
, JSObject
*obj
, jsval id
, uintN flags
,
446 fp
= (JSStackFrame
*)
447 JS_GetInstancePrivate(cx
, obj
, &js_ArgumentsClass
, NULL
);
450 JS_ASSERT(fp
->argsobj
);
452 if (JSVAL_IS_INT(id
)) {
453 slot
= JSVAL_TO_INT(id
);
454 if (slot
< fp
->argc
&& !ArgWasDeleted(cx
, fp
, slot
)) {
455 /* XXX ECMA specs DontEnum, contrary to other array-like objects */
456 if (!js_DefineProperty(cx
, obj
, INT_JSVAL_TO_JSID(id
),
458 args_getProperty
, args_setProperty
,
465 str
= JSVAL_TO_STRING(id
);
466 atom
= cx
->runtime
->atomState
.lengthAtom
;
467 if (str
== ATOM_TO_STRING(atom
)) {
468 tinyid
= ARGS_LENGTH
;
469 value
= INT_TO_JSVAL(fp
->argc
);
471 atom
= cx
->runtime
->atomState
.calleeAtom
;
472 if (str
== ATOM_TO_STRING(atom
)) {
473 tinyid
= ARGS_CALLEE
;
474 value
= OBJECT_TO_JSVAL(fp
->callee
);
478 /* Quell GCC overwarnings. */
484 if (atom
&& !TEST_OVERRIDE_BIT(fp
, tinyid
)) {
485 if (!js_DefineNativeProperty(cx
, obj
, ATOM_TO_JSID(atom
), value
,
486 args_getProperty
, args_setProperty
, 0,
487 SPROP_HAS_SHORTID
, tinyid
, NULL
)) {
498 args_enumerate(JSContext
*cx
, JSObject
*obj
)
505 fp
= (JSStackFrame
*)
506 JS_GetInstancePrivate(cx
, obj
, &js_ArgumentsClass
, NULL
);
509 JS_ASSERT(fp
->argsobj
);
512 * Trigger reflection with value snapshot in args_resolve using a series
513 * of js_LookupProperty calls. We handle length, callee, and the indexed
514 * argument properties. We know that args_resolve covers all these cases
515 * and creates direct properties of obj, but that it may fail to resolve
516 * length or callee if overridden.
518 if (!js_LookupProperty(cx
, obj
,
519 ATOM_TO_JSID(cx
->runtime
->atomState
.lengthAtom
),
524 OBJ_DROP_PROPERTY(cx
, pobj
, prop
);
526 if (!js_LookupProperty(cx
, obj
,
527 ATOM_TO_JSID(cx
->runtime
->atomState
.calleeAtom
),
532 OBJ_DROP_PROPERTY(cx
, pobj
, prop
);
535 for (slot
= 0; slot
< argc
; slot
++) {
536 if (!js_LookupProperty(cx
, obj
, INT_TO_JSID((jsint
)slot
), &pobj
, &prop
))
539 OBJ_DROP_PROPERTY(cx
, pobj
, prop
);
544 #if JS_HAS_GENERATORS
546 * If a generator-iterator's arguments or call object escapes, it needs to
547 * mark its generator object.
550 args_or_call_trace(JSTracer
*trc
, JSObject
*obj
)
554 fp
= (JSStackFrame
*) JS_GetPrivate(trc
->context
, obj
);
555 if (fp
&& (fp
->flags
& JSFRAME_GENERATOR
)) {
556 JS_CALL_OBJECT_TRACER(trc
, FRAME_TO_GENERATOR(fp
)->obj
,
557 "FRAME_TO_GENERATOR(fp)->obj");
561 # define args_or_call_trace NULL
565 * The Arguments class is not initialized via JS_InitClass, and must not be,
566 * because its name is "Object". Per ECMA, that causes instances of it to
567 * delegate to the object named by Object.prototype. It also ensures that
568 * arguments.toString() returns "[object Object]".
570 * The JSClass functions below collaborate to lazily reflect and synchronize
571 * actual argument values, argument count, and callee function object stored
572 * in a JSStackFrame with their corresponding property values in the frame's
575 JSClass js_ArgumentsClass
= {
577 JSCLASS_HAS_PRIVATE
| JSCLASS_NEW_RESOLVE
| JSCLASS_HAS_RESERVED_SLOTS(1) |
578 JSCLASS_MARK_IS_TRACE
| JSCLASS_HAS_CACHED_PROTO(JSProto_Object
),
579 JS_PropertyStub
, args_delProperty
,
580 args_getProperty
, args_setProperty
,
581 args_enumerate
, (JSResolveOp
) args_resolve
,
582 JS_ConvertStub
, JS_FinalizeStub
,
586 JS_CLASS_TRACE(args_or_call_trace
), NULL
589 #define JSSLOT_SCRIPTED_FUNCTION (JSSLOT_PRIVATE + 1)
590 #define JSSLOT_CALL_ARGUMENTS (JSSLOT_PRIVATE + 2)
591 #define CALL_CLASS_FIXED_RESERVED_SLOTS 2
594 js_GetCallObject(JSContext
*cx
, JSStackFrame
*fp
, JSObject
*parent
)
596 JSObject
*callobj
, *funobj
;
598 /* Create a call object for fp only if it lacks one. */
600 callobj
= fp
->callobj
;
604 /* The default call parent is its function's parent (static link). */
608 parent
= OBJ_GET_PARENT(cx
, funobj
);
611 /* Create the call object and link it to its stack frame. */
612 callobj
= js_NewObject(cx
, &js_CallClass
, NULL
, parent
, 0);
616 JS_SetPrivate(cx
, callobj
, fp
);
617 STOBJ_SET_SLOT(callobj
, JSSLOT_SCRIPTED_FUNCTION
,
618 OBJECT_TO_JSVAL(FUN_OBJECT(fp
->fun
)));
619 fp
->callobj
= callobj
;
621 /* Make callobj be the scope chain and the variables object. */
622 JS_ASSERT(fp
->scopeChain
== parent
);
623 fp
->scopeChain
= callobj
;
624 fp
->varobj
= callobj
;
629 js_GetCallObjectFunction(JSObject
*obj
)
633 JS_ASSERT(STOBJ_GET_CLASS(obj
) == &js_CallClass
);
634 v
= STOBJ_GET_SLOT(obj
, JSSLOT_SCRIPTED_FUNCTION
);
635 if (JSVAL_IS_VOID(v
)) {
636 /* Newborn or prototype object. */
639 JS_ASSERT(!JSVAL_IS_PRIMITIVE(v
));
640 return (JSFunction
*) JSVAL_TO_OBJECT(v
);
643 JS_FRIEND_API(JSBool
)
644 js_PutCallObject(JSContext
*cx
, JSStackFrame
*fp
)
653 * Since for a call object all fixed slots happen to be taken, we can copy
654 * arguments and variables straight into JSObject.dslots.
656 JS_STATIC_ASSERT(JS_INITIAL_NSLOTS
- JSSLOT_PRIVATE
==
657 1 + CALL_CLASS_FIXED_RESERVED_SLOTS
);
659 callobj
= fp
->callobj
;
664 * Get the arguments object to snapshot fp's actual argument values.
668 if (!TEST_OVERRIDE_BIT(fp
, CALL_ARGUMENTS
)) {
669 STOBJ_SET_SLOT(callobj
, JSSLOT_CALL_ARGUMENTS
,
670 OBJECT_TO_JSVAL(fp
->argsobj
));
672 ok
&= js_PutArgsObject(cx
, fp
);
676 JS_ASSERT(fun
== js_GetCallObjectFunction(callobj
));
677 n
= JS_GET_LOCAL_NAME_COUNT(fun
);
679 JS_LOCK_OBJ(cx
, callobj
);
680 n
+= JS_INITIAL_NSLOTS
;
681 if (n
> STOBJ_NSLOTS(callobj
))
682 ok
&= js_ReallocSlots(cx
, callobj
, n
, JS_TRUE
);
683 scope
= OBJ_SCOPE(callobj
);
685 memcpy(callobj
->dslots
, fp
->argv
, fun
->nargs
* sizeof(jsval
));
686 memcpy(callobj
->dslots
+ fun
->nargs
, fp
->slots
,
687 fun
->u
.i
.nvars
* sizeof(jsval
));
688 if (scope
->object
== callobj
&& n
> scope
->map
.freeslot
)
689 scope
->map
.freeslot
= n
;
691 JS_UNLOCK_SCOPE(cx
, scope
);
695 * Clear the private pointer to fp, which is about to go away (js_Invoke).
696 * Do this last because js_GetProperty calls above need to follow the
697 * private slot to find fp.
699 JS_SetPrivate(cx
, callobj
, NULL
);
705 call_enumerate(JSContext
*cx
, JSObject
*obj
)
716 fun
= js_GetCallObjectFunction(obj
);
717 n
= JS_GET_LOCAL_NAME_COUNT(fun
);
721 mark
= JS_ARENA_MARK(&cx
->tempPool
);
723 MUST_FLOW_THROUGH("out");
724 names
= js_GetLocalNameArray(cx
, fun
, &cx
->tempPool
);
730 for (i
= 0; i
!= n
; ++i
) {
731 name
= JS_LOCAL_NAME_TO_ATOM(names
[i
]);
736 * Trigger reflection by looking up the name of the argument or
739 ok
= js_LookupProperty(cx
, obj
, ATOM_TO_JSID(name
), &pobj
, &prop
);
744 * At this point the call object always has a property corresponding
745 * to the local name because call_resolve creates the property using
748 JS_ASSERT(prop
&& pobj
== obj
);
749 OBJ_DROP_PROPERTY(cx
, pobj
, prop
);
754 JS_ARENA_RELEASE(&cx
->tempPool
, mark
);
758 typedef enum JSCallPropertyKind
{
762 } JSCallPropertyKind
;
765 CallPropertyOp(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
,
766 JSCallPropertyKind kind
, JSBool setter
)
773 if (STOBJ_GET_CLASS(obj
) != &js_CallClass
)
776 fun
= js_GetCallObjectFunction(obj
);
777 fp
= (JSStackFrame
*) JS_GetPrivate(cx
, obj
);
779 if (kind
== JSCPK_ARGUMENTS
) {
782 SET_OVERRIDE_BIT(fp
, CALL_ARGUMENTS
);
783 STOBJ_SET_SLOT(obj
, JSSLOT_CALL_ARGUMENTS
, *vp
);
785 if (fp
&& !TEST_OVERRIDE_BIT(fp
, CALL_ARGUMENTS
)) {
788 argsobj
= js_GetArgsObject(cx
, fp
);
791 *vp
= OBJECT_TO_JSVAL(argsobj
);
793 *vp
= STOBJ_GET_SLOT(obj
, JSSLOT_CALL_ARGUMENTS
);
799 JS_ASSERT((int16
) JSVAL_TO_INT(id
) == JSVAL_TO_INT(id
));
800 i
= (uint16
) JSVAL_TO_INT(id
);
801 JS_ASSERT_IF(kind
== JSCPK_ARG
, i
< fun
->nargs
);
802 JS_ASSERT_IF(kind
== JSCPK_VAR
, i
< fun
->u
.i
.nvars
);
805 i
+= CALL_CLASS_FIXED_RESERVED_SLOTS
;
806 if (kind
== JSCPK_VAR
)
809 JS_ASSERT(kind
== JSCPK_ARG
);
811 ? JS_SetReservedSlot(cx
, obj
, i
, *vp
)
812 : JS_GetReservedSlot(cx
, obj
, i
, vp
);
815 if (kind
== JSCPK_ARG
) {
818 JS_ASSERT(kind
== JSCPK_VAR
);
829 GetCallArguments(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
831 return CallPropertyOp(cx
, obj
, id
, vp
, JSCPK_ARGUMENTS
, JS_FALSE
);
835 SetCallArguments(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
837 return CallPropertyOp(cx
, obj
, id
, vp
, JSCPK_ARGUMENTS
, JS_TRUE
);
841 js_GetCallArg(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
843 return CallPropertyOp(cx
, obj
, id
, vp
, JSCPK_ARG
, JS_FALSE
);
847 SetCallArg(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
849 return CallPropertyOp(cx
, obj
, id
, vp
, JSCPK_ARG
, JS_TRUE
);
853 js_GetCallVar(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
855 return CallPropertyOp(cx
, obj
, id
, vp
, JSCPK_VAR
, JS_FALSE
);
859 SetCallVar(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
861 return CallPropertyOp(cx
, obj
, id
, vp
, JSCPK_VAR
, JS_TRUE
);
865 call_resolve(JSContext
*cx
, JSObject
*obj
, jsval idval
, uintN flags
,
870 JSLocalKind localKind
;
871 JSPropertyOp getter
, setter
;
874 if (!JSVAL_IS_STRING(idval
))
877 fun
= js_GetCallObjectFunction(obj
);
881 if (!js_ValueToStringId(cx
, idval
, &id
))
884 localKind
= js_LookupLocal(cx
, fun
, JSID_TO_ATOM(id
), &slot
);
885 if (localKind
!= JSLOCAL_NONE
) {
886 JS_ASSERT((uint16
) slot
== slot
);
887 attrs
= JSPROP_PERMANENT
| JSPROP_SHARED
;
888 if (localKind
== JSLOCAL_ARG
) {
889 JS_ASSERT(slot
< fun
->nargs
);
890 getter
= js_GetCallArg
;
893 JS_ASSERT(localKind
== JSLOCAL_VAR
|| localKind
== JSLOCAL_CONST
);
894 JS_ASSERT(slot
< fun
->u
.i
.nvars
);
895 getter
= js_GetCallVar
;
897 if (localKind
== JSLOCAL_CONST
)
898 attrs
|= JSPROP_READONLY
;
900 if (!js_DefineNativeProperty(cx
, obj
, id
, JSVAL_VOID
, getter
, setter
,
901 attrs
, SPROP_HAS_SHORTID
, (int16
) slot
,
910 * Resolve arguments so that we never store a particular Call object's
911 * arguments object reference in a Call prototype's |arguments| slot.
913 if (id
== ATOM_TO_JSID(cx
->runtime
->atomState
.argumentsAtom
)) {
914 if (!js_DefineNativeProperty(cx
, obj
, id
, JSVAL_VOID
,
915 GetCallArguments
, SetCallArguments
,
916 JSPROP_PERMANENT
| JSPROP_SHARED
,
927 call_convert(JSContext
*cx
, JSObject
*obj
, JSType type
, jsval
*vp
)
931 if (type
== JSTYPE_FUNCTION
) {
932 fp
= (JSStackFrame
*) JS_GetPrivate(cx
, obj
);
935 *vp
= OBJECT_TO_JSVAL(fp
->callee
);
942 call_reserveSlots(JSContext
*cx
, JSObject
*obj
)
946 fun
= js_GetCallObjectFunction(obj
);
947 return JS_GET_LOCAL_NAME_COUNT(fun
);
950 JS_FRIEND_DATA(JSClass
) js_CallClass
= {
952 JSCLASS_HAS_PRIVATE
|
953 JSCLASS_HAS_RESERVED_SLOTS(CALL_CLASS_FIXED_RESERVED_SLOTS
) |
954 JSCLASS_NEW_RESOLVE
| JSCLASS_IS_ANONYMOUS
|
955 JSCLASS_MARK_IS_TRACE
| JSCLASS_HAS_CACHED_PROTO(JSProto_Call
),
956 JS_PropertyStub
, JS_PropertyStub
,
957 JS_PropertyStub
, JS_PropertyStub
,
958 call_enumerate
, (JSResolveOp
)call_resolve
,
959 call_convert
, JS_FinalizeStub
,
963 JS_CLASS_TRACE(args_or_call_trace
), call_reserveSlots
967 fun_getProperty(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
972 JSSecurityCallbacks
*callbacks
;
974 if (!JSVAL_IS_INT(id
))
976 slot
= JSVAL_TO_INT(id
);
979 * Loop because getter and setter can be delegated from another class,
980 * but loop only for ARGS_LENGTH because we must pretend that f.length
981 * is in each function instance f, per ECMA-262, instead of only in the
982 * Function.prototype object (we use JSPROP_PERMANENT with JSPROP_SHARED
983 * to make it appear so).
985 * This code couples tightly to the attributes for the function_props[]
986 * initializers above, and to js_SetProperty and js_HasOwnProperty.
988 * It's important to allow delegating objects, even though they inherit
989 * this getter (fun_getProperty), to override arguments, arity, caller,
990 * and name. If we didn't return early for slot != ARGS_LENGTH, we would
991 * clobber *vp with the native property value, instead of letting script
992 * override that value in delegating objects.
994 * Note how that clobbering is what simulates JSPROP_READONLY for all of
995 * the non-standard properties when the directly addressed object (obj)
996 * is a function object (i.e., when this loop does not iterate).
998 while (!(fun
= (JSFunction
*)
999 JS_GetInstancePrivate(cx
, obj
, &js_FunctionClass
, NULL
))) {
1000 if (slot
!= ARGS_LENGTH
)
1002 obj
= OBJ_GET_PROTO(cx
, obj
);
1007 /* Find fun's top-most activation record. */
1008 for (fp
= cx
->fp
; fp
&& (fp
->fun
!= fun
|| (fp
->flags
& JSFRAME_SPECIAL
));
1014 case CALL_ARGUMENTS
:
1015 /* Warn if strict about f.arguments or equivalent unqualified uses. */
1016 if (!JS_ReportErrorFlagsAndNumber(cx
,
1017 JSREPORT_WARNING
| JSREPORT_STRICT
,
1018 js_GetErrorMessage
, NULL
,
1019 JSMSG_DEPRECATED_USAGE
,
1020 js_arguments_str
)) {
1024 if (!js_GetArgsValue(cx
, fp
, vp
))
1033 *vp
= INT_TO_JSVAL((jsint
)fun
->nargs
);
1038 ? ATOM_KEY(fun
->atom
)
1039 : STRING_TO_JSVAL(cx
->runtime
->emptyString
);
1043 if (fp
&& fp
->down
&& fp
->down
->fun
)
1044 *vp
= OBJECT_TO_JSVAL(fp
->down
->callee
);
1047 if (!JSVAL_IS_PRIMITIVE(*vp
)) {
1048 callbacks
= JS_GetSecurityCallbacks(cx
);
1049 if (callbacks
&& callbacks
->checkObjectAccess
) {
1050 id
= ATOM_KEY(cx
->runtime
->atomState
.callerAtom
);
1051 if (!callbacks
->checkObjectAccess(cx
, obj
, id
, JSACC_READ
, vp
))
1058 /* XXX fun[0] and fun.arguments[0] are equivalent. */
1059 if (fp
&& fp
->fun
&& (uintN
)slot
< fp
->fun
->nargs
)
1060 *vp
= fp
->argv
[slot
];
1068 * ECMA-262 specifies that length is a property of function object instances,
1069 * but we can avoid that space cost by delegating to a prototype property that
1070 * is JSPROP_PERMANENT and JSPROP_SHARED. Each fun_getProperty call computes
1071 * a fresh length value based on the arity of the individual function object's
1074 * The extensions below other than length, i.e., the ones not in ECMA-262,
1075 * are neither JSPROP_READONLY nor JSPROP_SHARED, because for compatibility
1076 * with ECMA we must allow a delegating object to override them. Therefore to
1077 * avoid entraining garbage in Function.prototype slots, they must be resolved
1078 * in non-prototype function objects, wherefore the lazy_function_props table
1079 * and fun_resolve's use of it.
1081 #define LENGTH_PROP_ATTRS (JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED)
1083 static JSPropertySpec function_props
[] = {
1084 {js_length_str
, ARGS_LENGTH
, LENGTH_PROP_ATTRS
, fun_getProperty
, JS_PropertyStub
},
1088 typedef struct LazyFunctionProp
{
1094 /* NB: no sentinel at the end -- use JS_ARRAY_LENGTH to bound loops. */
1095 static LazyFunctionProp lazy_function_props
[] = {
1096 {ATOM_OFFSET(arguments
), CALL_ARGUMENTS
, JSPROP_PERMANENT
},
1097 {ATOM_OFFSET(arity
), FUN_ARITY
, JSPROP_PERMANENT
},
1098 {ATOM_OFFSET(caller
), FUN_CALLER
, JSPROP_PERMANENT
},
1099 {ATOM_OFFSET(name
), FUN_NAME
, JSPROP_PERMANENT
},
1103 fun_enumerate(JSContext
*cx
, JSObject
*obj
)
1109 prototypeId
= ATOM_TO_JSID(cx
->runtime
->atomState
.classPrototypeAtom
);
1110 if (!OBJ_LOOKUP_PROPERTY(cx
, obj
, prototypeId
, &pobj
, &prop
))
1113 OBJ_DROP_PROPERTY(cx
, pobj
, prop
);
1118 fun_resolve(JSContext
*cx
, JSObject
*obj
, jsval id
, uintN flags
,
1125 if (!JSVAL_IS_STRING(id
))
1128 fun
= GET_FUNCTION_PRIVATE(cx
, obj
);
1131 * No need to reflect fun.prototype in 'fun.prototype = ... '.
1133 * This is not just an optimization, because we must not resolve when
1134 * defining hidden properties during compilation. The setup code for the
1135 * prototype and the lazy properties below eventually calls the property
1136 * hooks for the function object. That in turn calls fun_reserveSlots to
1137 * get the number of the reserved slots which is just the number of
1138 * regular expressions literals in the function. When compiling, that
1139 * number is not yet ready so we must make sure that fun_resolve does
1140 * nothing until the code for the function is generated.
1142 if (flags
& JSRESOLVE_ASSIGNING
)
1146 * Ok, check whether id is 'prototype' and bootstrap the function object's
1147 * prototype property.
1149 atom
= cx
->runtime
->atomState
.classPrototypeAtom
;
1150 if (id
== ATOM_KEY(atom
)) {
1154 * Beware of the wacky case of a user function named Object -- trying
1155 * to find a prototype for that will recur back here _ad perniciem_.
1157 if (fun
->atom
== CLASS_ATOM(cx
, Object
))
1161 * Make the prototype object to have the same parent as the function
1164 proto
= js_NewObject(cx
, &js_ObjectClass
, NULL
, OBJ_GET_PARENT(cx
, obj
),
1170 * ECMA (15.3.5.2) says that constructor.prototype is DontDelete for
1171 * user-defined functions, but DontEnum | ReadOnly | DontDelete for
1172 * native "system" constructors such as Object or Function. So lazily
1173 * set the former here in fun_resolve, but eagerly define the latter
1174 * in JS_InitClass, with the right attributes.
1176 if (!js_SetClassPrototype(cx
, obj
, proto
,
1177 JSPROP_ENUMERATE
| JSPROP_PERMANENT
)) {
1178 cx
->weakRoots
.newborn
[GCX_OBJECT
] = NULL
;
1185 for (i
= 0; i
< JS_ARRAY_LENGTH(lazy_function_props
); i
++) {
1186 LazyFunctionProp
*lfp
= &lazy_function_props
[i
];
1188 atom
= OFFSET_TO_ATOM(cx
->runtime
, lfp
->atomOffset
);
1189 if (id
== ATOM_KEY(atom
)) {
1190 if (!js_DefineNativeProperty(cx
, obj
,
1191 ATOM_TO_JSID(atom
), JSVAL_VOID
,
1192 fun_getProperty
, JS_PropertyStub
,
1193 lfp
->attrs
, SPROP_HAS_SHORTID
,
1194 lfp
->tinyid
, NULL
)) {
1206 fun_convert(JSContext
*cx
, JSObject
*obj
, JSType type
, jsval
*vp
)
1209 case JSTYPE_FUNCTION
:
1210 *vp
= OBJECT_TO_JSVAL(obj
);
1213 return js_TryValueOf(cx
, obj
, type
, vp
);
1219 /* XXX store parent and proto, if defined */
1221 fun_xdrObject(JSXDRState
*xdr
, JSObject
**objp
)
1225 uint32 nullAtom
; /* flag to indicate if fun->atom is NULL */
1226 uintN nargs
, nvars
, n
;
1227 uint32 localsword
; /* word to xdr argument and variable counts */
1228 uint32 flagsword
; /* originally only flags was JS_XDRUint8'd */
1229 JSTempValueRooter tvr
;
1233 if (xdr
->mode
== JSXDR_ENCODE
) {
1234 fun
= GET_FUNCTION_PRIVATE(cx
, *objp
);
1235 if (!FUN_INTERPRETED(fun
)) {
1236 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1237 JSMSG_NOT_SCRIPTED_FUNCTION
,
1238 JS_GetFunctionName(fun
));
1241 nullAtom
= !fun
->atom
;
1243 nvars
= fun
->u
.i
.nvars
;
1244 localsword
= (nargs
<< 16) | nvars
;
1245 flagsword
= fun
->flags
;
1247 fun
= js_NewFunction(cx
, NULL
, NULL
, 0, JSFUN_INTERPRETED
, NULL
, NULL
);
1250 STOBJ_CLEAR_PARENT(FUN_OBJECT(fun
));
1251 STOBJ_CLEAR_PROTO(FUN_OBJECT(fun
));
1253 nvars
= nargs
= 0; /* quell GCC uninitialized warning */
1257 /* From here on, control flow must flow through label out. */
1258 JS_PUSH_TEMP_ROOT_OBJECT(cx
, FUN_OBJECT(fun
), &tvr
);
1261 if (!JS_XDRUint32(xdr
, &nullAtom
))
1263 if (!nullAtom
&& !js_XDRStringAtom(xdr
, &fun
->atom
))
1265 if (!JS_XDRUint32(xdr
, &localsword
) ||
1266 !JS_XDRUint32(xdr
, &flagsword
)) {
1270 if (xdr
->mode
== JSXDR_DECODE
) {
1271 nargs
= localsword
>> 16;
1272 nvars
= localsword
& JS_BITMASK(16);
1273 JS_ASSERT(flagsword
| JSFUN_INTERPRETED
);
1274 fun
->flags
= (uint16
) flagsword
;
1277 /* do arguments and local vars */
1286 JSLocalKind localKind
;
1288 mark
= JS_ARENA_MARK(&xdr
->cx
->tempPool
);
1291 * From this point the control must flow via the label release_mark.
1293 * To xdr the names we prefix the names with a bitmap descriptor and
1294 * then xdr the names as strings. For argument names (indexes below
1295 * nargs) the corresponding bit in the bitmap is unset when the name
1296 * is null. Such null names are not encoded or decoded. For variable
1297 * names (indexes starting from nargs) bitmap's bit is set when the
1298 * name is declared as const, not as ordinary var.
1300 bitmapLength
= JS_HOWMANY(n
, JS_BITS_PER_UINT32
);
1301 JS_ARENA_ALLOCATE_CAST(bitmap
, uint32
*, &xdr
->cx
->tempPool
,
1302 bitmapLength
* sizeof *bitmap
);
1304 js_ReportOutOfScriptQuota(xdr
->cx
);
1308 if (xdr
->mode
== JSXDR_ENCODE
) {
1309 names
= js_GetLocalNameArray(xdr
->cx
, fun
, &xdr
->cx
->tempPool
);
1314 memset(bitmap
, 0, bitmapLength
* sizeof *bitmap
);
1315 for (i
= 0; i
!= n
; ++i
) {
1317 ? JS_LOCAL_NAME_TO_ATOM(names
[i
]) != NULL
1318 : JS_LOCAL_NAME_IS_CONST(names
[i
])) {
1319 bitmap
[i
>> JS_BITS_PER_UINT32_LOG2
] |=
1320 JS_BIT(i
& (JS_BITS_PER_UINT32
- 1));
1326 names
= NULL
; /* quell GCC uninitialized warning */
1329 for (i
= 0; i
!= bitmapLength
; ++i
) {
1330 ok
= JS_XDRUint32(xdr
, &bitmap
[i
]);
1334 for (i
= 0; i
!= n
; ++i
) {
1336 !(bitmap
[i
>> JS_BITS_PER_UINT32_LOG2
] &
1337 JS_BIT(i
& (JS_BITS_PER_UINT32
- 1)))) {
1338 if (xdr
->mode
== JSXDR_DECODE
) {
1339 ok
= js_AddLocal(xdr
->cx
, fun
, NULL
, JSLOCAL_ARG
);
1343 JS_ASSERT(!JS_LOCAL_NAME_TO_ATOM(names
[i
]));
1347 if (xdr
->mode
== JSXDR_ENCODE
)
1348 name
= JS_LOCAL_NAME_TO_ATOM(names
[i
]);
1349 ok
= js_XDRStringAtom(xdr
, &name
);
1352 if (xdr
->mode
== JSXDR_DECODE
) {
1353 localKind
= (i
< nargs
)
1355 : bitmap
[i
>> JS_BITS_PER_UINT32_LOG2
] &
1356 JS_BIT(i
& (JS_BITS_PER_UINT32
- 1))
1359 ok
= js_AddLocal(xdr
->cx
, fun
, name
, localKind
);
1367 JS_ARENA_RELEASE(&xdr
->cx
->tempPool
, mark
);
1371 if (xdr
->mode
== JSXDR_DECODE
)
1372 js_FreezeLocalNames(cx
, fun
);
1375 if (!js_XDRScript(xdr
, &fun
->u
.i
.script
, NULL
))
1378 if (xdr
->mode
== JSXDR_DECODE
) {
1379 *objp
= FUN_OBJECT(fun
);
1380 #ifdef CHECK_SCRIPT_OWNER
1381 fun
->u
.i
.script
->owner
= NULL
;
1383 js_CallNewScriptHook(cx
, fun
->u
.i
.script
, fun
);
1387 JS_POP_TEMP_ROOT(cx
, &tvr
);
1395 #else /* !JS_HAS_XDR */
1397 #define fun_xdrObject NULL
1399 #endif /* !JS_HAS_XDR */
1402 * [[HasInstance]] internal method for Function objects: fetch the .prototype
1403 * property of its 'this' parameter, and walks the prototype chain of v (only
1404 * if v is an object) returning true if .prototype is found.
1407 fun_hasInstance(JSContext
*cx
, JSObject
*obj
, jsval v
, JSBool
*bp
)
1411 if (!OBJ_GET_PROPERTY(cx
, obj
,
1412 ATOM_TO_JSID(cx
->runtime
->atomState
1413 .classPrototypeAtom
),
1418 if (JSVAL_IS_PRIMITIVE(pval
)) {
1420 * Throw a runtime error if instanceof is called on a function that
1421 * has a non-object as its .prototype value.
1423 js_ReportValueError(cx
, JSMSG_BAD_PROTOTYPE
,
1424 -1, OBJECT_TO_JSVAL(obj
), NULL
);
1428 return js_IsDelegate(cx
, JSVAL_TO_OBJECT(pval
), v
, bp
);
1432 TraceLocalNames(JSTracer
*trc
, JSFunction
*fun
);
1435 DestroyLocalNames(JSContext
*cx
, JSFunction
*fun
);
1438 fun_trace(JSTracer
*trc
, JSObject
*obj
)
1442 /* A newborn function object may have a not yet initialized private slot. */
1443 fun
= (JSFunction
*) JS_GetPrivate(trc
->context
, obj
);
1447 if (FUN_OBJECT(fun
) != obj
) {
1448 /* obj is cloned function object, trace the original. */
1449 JS_CALL_TRACER(trc
, FUN_OBJECT(fun
), JSTRACE_OBJECT
, "private");
1453 JS_CALL_STRING_TRACER(trc
, ATOM_TO_STRING(fun
->atom
), "atom");
1454 if (FUN_INTERPRETED(fun
)) {
1455 if (fun
->u
.i
.script
)
1456 js_TraceScript(trc
, fun
->u
.i
.script
);
1457 TraceLocalNames(trc
, fun
);
1462 fun_finalize(JSContext
*cx
, JSObject
*obj
)
1466 /* Ignore newborn and cloned function objects. */
1467 fun
= (JSFunction
*) JS_GetPrivate(cx
, obj
);
1468 if (!fun
|| FUN_OBJECT(fun
) != obj
)
1472 * Null-check of u.i.script is required since the parser sets interpreted
1475 if (FUN_INTERPRETED(fun
)) {
1476 if (fun
->u
.i
.script
)
1477 js_DestroyScript(cx
, fun
->u
.i
.script
);
1478 DestroyLocalNames(cx
, fun
);
1483 fun_reserveSlots(JSContext
*cx
, JSObject
*obj
)
1489 * We use JS_GetPrivate and not GET_FUNCTION_PRIVATE because during
1490 * js_InitFunctionClass invocation the function is called before the
1491 * private slot of the function object is set.
1493 fun
= (JSFunction
*) JS_GetPrivate(cx
, obj
);
1495 if (fun
&& FUN_INTERPRETED(fun
) && fun
->u
.i
.script
) {
1496 if (fun
->u
.i
.script
->upvarsOffset
!= 0)
1497 nslots
= JS_SCRIPT_UPVARS(fun
->u
.i
.script
)->length
;
1498 if (fun
->u
.i
.script
->regexpsOffset
!= 0)
1499 nslots
+= JS_SCRIPT_REGEXPS(fun
->u
.i
.script
)->length
;
1505 * Reserve two slots in all function objects for XPConnect. Note that this
1506 * does not bloat every instance, only those on which reserved slots are set,
1507 * and those on which ad-hoc properties are defined.
1509 JS_FRIEND_DATA(JSClass
) js_FunctionClass
= {
1511 JSCLASS_HAS_PRIVATE
| JSCLASS_NEW_RESOLVE
| JSCLASS_HAS_RESERVED_SLOTS(2) |
1512 JSCLASS_MARK_IS_TRACE
| JSCLASS_HAS_CACHED_PROTO(JSProto_Function
),
1513 JS_PropertyStub
, JS_PropertyStub
,
1514 JS_PropertyStub
, JS_PropertyStub
,
1515 fun_enumerate
, (JSResolveOp
)fun_resolve
,
1516 fun_convert
, fun_finalize
,
1519 fun_xdrObject
, fun_hasInstance
,
1520 JS_CLASS_TRACE(fun_trace
), fun_reserveSlots
1524 fun_toStringHelper(JSContext
*cx
, uint32 indent
, uintN argc
, jsval
*vp
)
1531 fval
= JS_THIS(cx
, vp
);
1532 if (JSVAL_IS_NULL(fval
))
1535 if (!VALUE_IS_FUNCTION(cx
, fval
)) {
1537 * If we don't have a function to start off with, try converting the
1538 * object to a function. If that doesn't work, complain.
1540 if (!JSVAL_IS_PRIMITIVE(fval
)) {
1541 obj
= JSVAL_TO_OBJECT(fval
);
1542 if (!OBJ_GET_CLASS(cx
, obj
)->convert(cx
, obj
, JSTYPE_FUNCTION
,
1548 if (!VALUE_IS_FUNCTION(cx
, fval
)) {
1549 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1550 JSMSG_INCOMPATIBLE_PROTO
,
1551 js_Function_str
, js_toString_str
,
1552 JS_GetTypeName(cx
, JS_TypeOfValue(cx
, fval
)));
1557 obj
= JSVAL_TO_OBJECT(fval
);
1559 indent
= js_ValueToECMAUint32(cx
, &vp
[2]);
1560 if (JSVAL_IS_NULL(vp
[2]))
1564 JS_ASSERT(JS_ObjectIsFunction(cx
, obj
));
1565 fun
= GET_FUNCTION_PRIVATE(cx
, obj
);
1568 str
= JS_DecompileFunction(cx
, fun
, (uintN
)indent
);
1571 *vp
= STRING_TO_JSVAL(str
);
1576 fun_toString(JSContext
*cx
, uintN argc
, jsval
*vp
)
1578 return fun_toStringHelper(cx
, 0, argc
, vp
);
1583 fun_toSource(JSContext
*cx
, uintN argc
, jsval
*vp
)
1585 return fun_toStringHelper(cx
, JS_DONT_PRETTY_PRINT
, argc
, vp
);
1590 js_fun_call(JSContext
*cx
, uintN argc
, jsval
*vp
)
1593 jsval fval
, *argv
, *invokevp
;
1598 obj
= JS_THIS_OBJECT(cx
, vp
);
1599 if (!obj
|| !OBJ_DEFAULT_VALUE(cx
, obj
, JSTYPE_FUNCTION
, &vp
[1]))
1603 if (!VALUE_IS_FUNCTION(cx
, fval
)) {
1604 str
= JS_ValueToString(cx
, fval
);
1606 const char *bytes
= js_GetStringBytes(cx
, str
);
1609 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1610 JSMSG_INCOMPATIBLE_PROTO
,
1611 js_Function_str
, js_call_str
,
1620 /* Call fun with its global object as the 'this' param if no args. */
1623 /* Otherwise convert the first arg to 'this' and skip over it. */
1624 if (!JSVAL_IS_PRIMITIVE(argv
[0]))
1625 obj
= JSVAL_TO_OBJECT(argv
[0]);
1626 else if (!js_ValueToObject(cx
, argv
[0], &obj
))
1632 /* Allocate stack space for fval, obj, and the args. */
1633 invokevp
= js_AllocStack(cx
, 2 + argc
, &mark
);
1637 /* Push fval, obj, and the args. */
1639 invokevp
[1] = OBJECT_TO_JSVAL(obj
);
1640 memcpy(invokevp
+ 2, argv
, argc
* sizeof *argv
);
1642 ok
= js_Invoke(cx
, argc
, invokevp
, 0);
1644 js_FreeStack(cx
, mark
);
1649 js_fun_apply(JSContext
*cx
, uintN argc
, jsval
*vp
)
1651 JSObject
*obj
, *aobj
;
1652 jsval fval
, *invokevp
, *sp
;
1655 JSBool arraylike
, ok
;
1660 /* Will get globalObject as 'this' and no other arguments. */
1661 return js_fun_call(cx
, argc
, vp
);
1664 obj
= JS_THIS_OBJECT(cx
, vp
);
1665 if (!obj
|| !OBJ_DEFAULT_VALUE(cx
, obj
, JSTYPE_FUNCTION
, &vp
[1]))
1669 if (!VALUE_IS_FUNCTION(cx
, fval
)) {
1670 str
= JS_ValueToString(cx
, fval
);
1672 const char *bytes
= js_GetStringBytes(cx
, str
);
1675 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1676 JSMSG_INCOMPATIBLE_PROTO
,
1677 js_Function_str
, js_apply_str
,
1684 /* Quell GCC overwarnings. */
1689 /* If the 2nd arg is null or void, call the function with 0 args. */
1690 if (JSVAL_IS_NULL(vp
[3]) || JSVAL_IS_VOID(vp
[3])) {
1693 /* The second arg must be an array (or arguments object). */
1694 arraylike
= JS_FALSE
;
1695 if (!JSVAL_IS_PRIMITIVE(vp
[3])) {
1696 aobj
= JSVAL_TO_OBJECT(vp
[3]);
1697 if (!js_IsArrayLike(cx
, aobj
, &arraylike
, &length
))
1701 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1702 JSMSG_BAD_APPLY_ARGS
, js_apply_str
);
1708 /* Convert the first arg to 'this' and skip over it. */
1709 if (!JSVAL_IS_PRIMITIVE(vp
[2]))
1710 obj
= JSVAL_TO_OBJECT(vp
[2]);
1711 else if (!js_ValueToObject(cx
, vp
[2], &obj
))
1714 /* Allocate stack space for fval, obj, and the args. */
1715 argc
= (uintN
)JS_MIN(length
, ARRAY_INIT_LIMIT
- 1);
1716 invokevp
= js_AllocStack(cx
, 2 + argc
, &mark
);
1720 /* Push fval, obj, and aobj's elements as args. */
1723 *sp
++ = OBJECT_TO_JSVAL(obj
);
1724 for (i
= 0; i
< argc
; i
++) {
1725 ok
= JS_GetElement(cx
, aobj
, (jsint
)i
, sp
);
1731 ok
= js_Invoke(cx
, argc
, invokevp
, 0);
1734 js_FreeStack(cx
, mark
);
1740 fun_applyConstructor(JSContext
*cx
, uintN argc
, jsval
*vp
)
1745 jsval
*invokevp
, *sp
;
1748 if (JSVAL_IS_PRIMITIVE(vp
[2]) ||
1749 (aobj
= JSVAL_TO_OBJECT(vp
[2]),
1750 OBJ_GET_CLASS(cx
, aobj
) != &js_ArrayClass
&&
1751 OBJ_GET_CLASS(cx
, aobj
) != &js_ArgumentsClass
)) {
1752 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1753 JSMSG_BAD_APPLY_ARGS
, "__applyConstruct__");
1757 if (!js_GetLengthProperty(cx
, aobj
, &length
))
1760 if (length
>= ARRAY_INIT_LIMIT
)
1761 length
= ARRAY_INIT_LIMIT
- 1;
1762 invokevp
= js_AllocStack(cx
, 2 + length
, &mark
);
1768 *sp
++ = JSVAL_NULL
; /* this is filled automagically */
1769 for (i
= 0; i
< length
; i
++) {
1770 ok
= JS_GetElement(cx
, aobj
, (jsint
)i
, sp
);
1776 ok
= js_InvokeConstructor(cx
, length
, JS_TRUE
, invokevp
);
1779 js_FreeStack(cx
, mark
);
1784 static JSFunctionSpec function_methods
[] = {
1786 JS_FN(js_toSource_str
, fun_toSource
, 0,0),
1788 JS_FN(js_toString_str
, fun_toString
, 0,0),
1789 JS_FN(js_apply_str
, js_fun_apply
, 2,0),
1790 JS_FN(js_call_str
, js_fun_call
, 1,0),
1792 JS_FN("__applyConstructor__", fun_applyConstructor
, 1,0),
1798 Function(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
1800 JSStackFrame
*fp
, *caller
;
1805 const char *filename
;
1807 JSString
*str
, *arg
;
1809 JSPrincipals
*principals
;
1810 jschar
*collected_args
, *cp
;
1812 size_t arg_length
, args_length
, old_args_length
;
1816 if (!(fp
->flags
& JSFRAME_CONSTRUCTING
)) {
1817 obj
= js_NewObject(cx
, &js_FunctionClass
, NULL
, NULL
, 0);
1820 *rval
= OBJECT_TO_JSVAL(obj
);
1823 * The constructor is called before the private slot is initialized so
1824 * we must use JS_GetPrivate, not GET_FUNCTION_PRIVATE here.
1826 if (JS_GetPrivate(cx
, obj
))
1831 * NB: (new Function) is not lexically closed by its caller, it's just an
1832 * anonymous function in the top-level scope that its constructor inhabits.
1833 * Thus 'var x = 42; f = new Function("return x"); print(f())' prints 42,
1834 * and so would a call to f from another top-level's script or function.
1836 * In older versions, before call objects, a new Function was adopted by
1837 * its running context's globalObject, which might be different from the
1838 * top-level reachable from scopeChain (in HTML frames, e.g.).
1840 parent
= OBJ_GET_PARENT(cx
, JSVAL_TO_OBJECT(argv
[-2]));
1842 fun
= js_NewFunction(cx
, obj
, NULL
, 0, JSFUN_LAMBDA
| JSFUN_INTERPRETED
,
1843 parent
, cx
->runtime
->atomState
.anonymousAtom
);
1849 * Function is static and not called directly by other functions in this
1850 * file, therefore it is callable only as a native function by js_Invoke.
1851 * Find the scripted caller, possibly skipping other native frames such as
1852 * are built for Function.prototype.call or .apply activations that invoke
1853 * Function indirectly from a script.
1855 JS_ASSERT(!fp
->script
&& fp
->fun
&& fp
->fun
->u
.n
.native
== Function
);
1856 caller
= JS_GetScriptedCaller(cx
, fp
);
1858 principals
= JS_EvalFramePrincipals(cx
, fp
, caller
);
1859 filename
= js_ComputeFilename(cx
, caller
, principals
, &lineno
);
1866 /* Belt-and-braces: check that the caller has access to parent. */
1867 if (!js_CheckPrincipalsAccess(cx
, parent
, principals
,
1868 CLASS_ATOM(cx
, Function
))) {
1872 n
= argc
? argc
- 1 : 0;
1874 enum { OK
, BAD
, BAD_FORMAL
} state
;
1877 * Collect the function-argument arguments into one string, separated
1878 * by commas, then make a tokenstream from that string, and scan it to
1879 * get the arguments. We need to throw the full scanner at the
1880 * problem, because the argument string can legitimately contain
1881 * comments and linefeeds. XXX It might be better to concatenate
1882 * everything up into a function definition and pass it to the
1883 * compiler, but doing it this way is less of a delta from the old
1884 * code. See ECMA 15.3.2.1.
1888 for (i
= 0; i
< n
; i
++) {
1889 /* Collect the lengths for all the function-argument arguments. */
1890 arg
= js_ValueToString(cx
, argv
[i
]);
1893 argv
[i
] = STRING_TO_JSVAL(arg
);
1896 * Check for overflow. The < test works because the maximum
1897 * JSString length fits in 2 fewer bits than size_t has.
1899 old_args_length
= args_length
;
1900 args_length
= old_args_length
+ JSSTRING_LENGTH(arg
);
1901 if (args_length
< old_args_length
) {
1902 js_ReportAllocationOverflow(cx
);
1907 /* Add 1 for each joining comma and check for overflow (two ways). */
1908 old_args_length
= args_length
;
1909 args_length
= old_args_length
+ n
- 1;
1910 if (args_length
< old_args_length
||
1911 args_length
>= ~(size_t)0 / sizeof(jschar
)) {
1912 js_ReportAllocationOverflow(cx
);
1917 * Allocate a string to hold the concatenated arguments, including room
1918 * for a terminating 0. Mark cx->tempPool for later release, to free
1919 * collected_args and its tokenstream in one swoop.
1921 mark
= JS_ARENA_MARK(&cx
->tempPool
);
1922 JS_ARENA_ALLOCATE_CAST(cp
, jschar
*, &cx
->tempPool
,
1923 (args_length
+1) * sizeof(jschar
));
1925 js_ReportOutOfScriptQuota(cx
);
1928 collected_args
= cp
;
1931 * Concatenate the arguments into the new string, separated by commas.
1933 for (i
= 0; i
< n
; i
++) {
1934 arg
= JSVAL_TO_STRING(argv
[i
]);
1935 arg_length
= JSSTRING_LENGTH(arg
);
1936 (void) js_strncpy(cp
, JSSTRING_CHARS(arg
), arg_length
);
1939 /* Add separating comma or terminating 0. */
1940 *cp
++ = (i
+ 1 < n
) ? ',' : 0;
1943 /* Initialize a tokenstream that reads from the given string. */
1944 if (!js_InitTokenStream(cx
, &ts
, collected_args
, args_length
,
1945 NULL
, filename
, lineno
)) {
1946 JS_ARENA_RELEASE(&cx
->tempPool
, mark
);
1950 /* The argument string may be empty or contain no tokens. */
1951 tt
= js_GetToken(cx
, &ts
);
1952 if (tt
!= TOK_EOF
) {
1955 * Check that it's a name. This also implicitly guards against
1956 * TOK_ERROR, which was already reported.
1962 * Get the atom corresponding to the name from the token
1963 * stream; we're assured at this point that it's a valid
1966 atom
= CURRENT_TOKEN(&ts
).t_atom
;
1968 /* Check for a duplicate parameter name. */
1969 if (js_LookupLocal(cx
, fun
, atom
, NULL
) != JSLOCAL_NONE
) {
1972 name
= js_AtomToPrintableString(cx
, atom
);
1974 js_ReportCompileErrorNumber(cx
, &ts
, NULL
,
1977 JSMSG_DUPLICATE_FORMAL
,
1982 if (!js_AddLocal(cx
, fun
, atom
, JSLOCAL_ARG
))
1986 * Get the next token. Stop on end of stream. Otherwise
1987 * insist on a comma, get another name, and iterate.
1989 tt
= js_GetToken(cx
, &ts
);
1992 if (tt
!= TOK_COMMA
)
1994 tt
= js_GetToken(cx
, &ts
);
2000 if (state
== BAD_FORMAL
&& !(ts
.flags
& TSF_ERROR
)) {
2002 * Report "malformed formal parameter" iff no illegal char or
2003 * similar scanner error was already reported.
2005 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
2008 js_CloseTokenStream(cx
, &ts
);
2009 JS_ARENA_RELEASE(&cx
->tempPool
, mark
);
2015 str
= js_ValueToString(cx
, argv
[argc
-1]);
2018 argv
[argc
-1] = STRING_TO_JSVAL(str
);
2020 str
= cx
->runtime
->emptyString
;
2023 return js_CompileFunctionBody(cx
, fun
, principals
,
2024 JSSTRING_CHARS(str
), JSSTRING_LENGTH(str
),
2029 js_InitFunctionClass(JSContext
*cx
, JSObject
*obj
)
2034 proto
= JS_InitClass(cx
, obj
, NULL
, &js_FunctionClass
, Function
, 1,
2035 function_props
, function_methods
, NULL
, NULL
);
2038 fun
= js_NewFunction(cx
, proto
, NULL
, 0, JSFUN_INTERPRETED
, obj
, NULL
);
2041 fun
->u
.i
.script
= js_NewScript(cx
, 1, 1, 0, 0, 0, 0, 0);
2042 if (!fun
->u
.i
.script
)
2044 fun
->u
.i
.script
->code
[0] = JSOP_STOP
;
2045 *SCRIPT_NOTES(fun
->u
.i
.script
) = SRC_NULL
;
2046 #ifdef CHECK_SCRIPT_OWNER
2047 fun
->u
.i
.script
->owner
= NULL
;
2052 cx
->weakRoots
.newborn
[GCX_OBJECT
] = NULL
;
2057 js_InitCallClass(JSContext
*cx
, JSObject
*obj
)
2061 proto
= JS_InitClass(cx
, obj
, NULL
, &js_CallClass
, NULL
, 0,
2062 NULL
, NULL
, NULL
, NULL
);
2067 * Null Call.prototype's proto slot so that Object.prototype.* does not
2068 * pollute the scope of heavyweight functions.
2070 OBJ_CLEAR_PROTO(cx
, proto
);
2075 js_NewFunction(JSContext
*cx
, JSObject
*funobj
, JSNative native
, uintN nargs
,
2076 uintN flags
, JSObject
*parent
, JSAtom
*atom
)
2081 JS_ASSERT(HAS_FUNCTION_CLASS(funobj
));
2082 OBJ_SET_PARENT(cx
, funobj
, parent
);
2084 funobj
= js_NewObject(cx
, &js_FunctionClass
, NULL
, parent
, 0);
2088 JS_ASSERT(JSVAL_IS_VOID(funobj
->fslots
[JSSLOT_PRIVATE
]));
2089 fun
= (JSFunction
*) funobj
;
2091 /* Initialize all function members. */
2093 fun
->flags
= flags
& (JSFUN_FLAGS_MASK
| JSFUN_INTERPRETED
| JSFUN_TRACEABLE
);
2094 if (flags
& JSFUN_INTERPRETED
) {
2096 JS_ASSERT(nargs
== 0);
2098 fun
->u
.i
.nupvars
= 0;
2099 fun
->u
.i
.script
= NULL
;
2101 fun
->u
.i
.names
.taggedAtom
= 0;
2106 if (flags
& JSFUN_TRACEABLE
) {
2108 JSTraceableNative
*trcinfo
= (JSTraceableNative
*) native
;
2109 fun
->u
.n
.native
= (JSNative
) trcinfo
->native
;
2110 FUN_TRCINFO(fun
) = trcinfo
;
2115 fun
->u
.n
.native
= native
;
2116 FUN_CLASP(fun
) = NULL
;
2121 /* Set private to self to indicate non-cloned fully initialized function. */
2122 FUN_OBJECT(fun
)->fslots
[JSSLOT_PRIVATE
] = PRIVATE_TO_JSVAL(fun
);
2127 js_CloneFunctionObject(JSContext
*cx
, JSFunction
*fun
, JSObject
*parent
)
2132 * The cloned function object does not need the extra fields beyond
2133 * JSObject as it points to fun via the private slot.
2135 clone
= js_NewObject(cx
, &js_FunctionClass
, NULL
, parent
,
2139 clone
->fslots
[JSSLOT_PRIVATE
] = PRIVATE_TO_JSVAL(fun
);
2144 js_DefineFunction(JSContext
*cx
, JSObject
*obj
, JSAtom
*atom
, JSNative native
,
2145 uintN nargs
, uintN attrs
)
2150 fun
= js_NewFunction(cx
, NULL
, native
, nargs
, attrs
, obj
, atom
);
2153 gsop
= (attrs
& JSFUN_STUB_GSOPS
) ? JS_PropertyStub
: NULL
;
2154 if (!OBJ_DEFINE_PROPERTY(cx
, obj
, ATOM_TO_JSID(atom
),
2155 OBJECT_TO_JSVAL(FUN_OBJECT(fun
)),
2157 attrs
& ~JSFUN_FLAGS_MASK
, NULL
)) {
2163 #if (JSV2F_CONSTRUCT & JSV2F_SEARCH_STACK)
2164 # error "JSINVOKE_CONSTRUCT and JSV2F_SEARCH_STACK are not disjoint!"
2168 js_ValueToFunction(JSContext
*cx
, jsval
*vp
, uintN flags
)
2175 if (JSVAL_IS_OBJECT(v
)) {
2176 obj
= JSVAL_TO_OBJECT(v
);
2177 if (obj
&& OBJ_GET_CLASS(cx
, obj
) != &js_FunctionClass
) {
2178 if (!OBJ_DEFAULT_VALUE(cx
, obj
, JSTYPE_FUNCTION
, &v
))
2180 obj
= VALUE_IS_FUNCTION(cx
, v
) ? JSVAL_TO_OBJECT(v
) : NULL
;
2184 js_ReportIsNotFunction(cx
, vp
, flags
);
2187 return GET_FUNCTION_PRIVATE(cx
, obj
);
2191 js_ValueToFunctionObject(JSContext
*cx
, jsval
*vp
, uintN flags
)
2194 JSStackFrame
*caller
;
2195 JSPrincipals
*principals
;
2197 if (VALUE_IS_FUNCTION(cx
, *vp
))
2198 return JSVAL_TO_OBJECT(*vp
);
2200 fun
= js_ValueToFunction(cx
, vp
, flags
);
2203 *vp
= OBJECT_TO_JSVAL(FUN_OBJECT(fun
));
2205 caller
= JS_GetScriptedCaller(cx
, cx
->fp
);
2207 principals
= JS_StackFramePrincipals(cx
, caller
);
2209 /* No scripted caller, don't allow access. */
2213 if (!js_CheckPrincipalsAccess(cx
, FUN_OBJECT(fun
), principals
,
2216 : cx
->runtime
->atomState
.anonymousAtom
)) {
2219 return FUN_OBJECT(fun
);
2223 js_ValueToCallableObject(JSContext
*cx
, jsval
*vp
, uintN flags
)
2227 callable
= JSVAL_IS_PRIMITIVE(*vp
) ? NULL
: JSVAL_TO_OBJECT(*vp
);
2229 ((callable
->map
->ops
== &js_ObjectOps
)
2230 ? OBJ_GET_CLASS(cx
, callable
)->call
2231 : callable
->map
->ops
->call
)) {
2232 *vp
= OBJECT_TO_JSVAL(callable
);
2234 callable
= js_ValueToFunctionObject(cx
, vp
, flags
);
2240 js_ReportIsNotFunction(JSContext
*cx
, jsval
*vp
, uintN flags
)
2244 const char *name
, *source
;
2245 JSTempValueRooter tvr
;
2247 for (fp
= cx
->fp
; fp
&& !fp
->regs
; fp
= fp
->down
)
2249 name
= source
= NULL
;
2250 JS_PUSH_TEMP_ROOT_STRING(cx
, NULL
, &tvr
);
2251 if (flags
& JSV2F_ITERATOR
) {
2252 error
= JSMSG_BAD_ITERATOR
;
2253 name
= js_iterator_str
;
2254 tvr
.u
.string
= js_ValueToSource(cx
, *vp
);
2257 tvr
.u
.string
= js_QuoteString(cx
, tvr
.u
.string
, 0);
2260 source
= js_GetStringBytes(cx
, tvr
.u
.string
);
2263 } else if (flags
& JSV2F_CONSTRUCT
) {
2264 error
= JSMSG_NOT_CONSTRUCTOR
;
2266 error
= JSMSG_NOT_FUNCTION
;
2269 js_ReportValueError3(cx
, error
,
2271 StackBase(fp
) <= vp
&& vp
< fp
->regs
->sp
)
2273 : (flags
& JSV2F_SEARCH_STACK
)
2274 ? JSDVG_SEARCH_STACK
2275 : JSDVG_IGNORE_STACK
,
2280 JS_POP_TEMP_ROOT(cx
, &tvr
);
2284 * When a function has between 2 and MAX_ARRAY_LOCALS arguments and variables,
2285 * their name are stored as the JSLocalNames.array.
2287 #define MAX_ARRAY_LOCALS 8
2289 JS_STATIC_ASSERT(2 <= MAX_ARRAY_LOCALS
);
2290 JS_STATIC_ASSERT(MAX_ARRAY_LOCALS
< JS_BITMASK(16));
2293 * We use the lowest bit of the string atom to distinguish const from var
2294 * name when there is only single name or when names are stored as an array.
2296 JS_STATIC_ASSERT((JSVAL_STRING
& 1) == 0);
2299 * When we use a hash table to store the local names, we use a singly linked
2300 * list to record the indexes of duplicated parameter names to preserve the
2301 * duplicates for the decompiler.
2303 typedef struct JSNameIndexPair JSNameIndexPair
;
2305 struct JSNameIndexPair
{
2308 JSNameIndexPair
*link
;
2311 struct JSLocalNameMap
{
2313 JSNameIndexPair
*lastdup
;
2316 typedef struct JSLocalNameHashEntry
{
2317 JSDHashEntryHdr hdr
;
2321 } JSLocalNameHashEntry
;
2324 FreeLocalNameHash(JSContext
*cx
, JSLocalNameMap
*map
)
2326 JSNameIndexPair
*dup
, *next
;
2328 for (dup
= map
->lastdup
; dup
; dup
= next
) {
2332 JS_DHashTableFinish(&map
->names
);
2337 HashLocalName(JSContext
*cx
, JSLocalNameMap
*map
, JSAtom
*name
,
2338 JSLocalKind localKind
, uintN index
)
2340 JSLocalNameHashEntry
*entry
;
2341 JSNameIndexPair
*dup
;
2343 JS_ASSERT(index
<= JS_BITMASK(16));
2344 #if JS_HAS_DESTRUCTURING
2346 /* A destructuring pattern does not need a hash entry. */
2347 JS_ASSERT(localKind
== JSLOCAL_ARG
);
2351 JS_ASSERT(ATOM_IS_STRING(name
));
2352 entry
= (JSLocalNameHashEntry
*)
2353 JS_DHashTableOperate(&map
->names
, name
, JS_DHASH_ADD
);
2355 JS_ReportOutOfMemory(cx
);
2359 JS_ASSERT(entry
->name
== name
);
2360 JS_ASSERT(entry
->localKind
== JSLOCAL_ARG
);
2361 dup
= (JSNameIndexPair
*) JS_malloc(cx
, sizeof *dup
);
2364 dup
->name
= entry
->name
;
2365 dup
->index
= entry
->index
;
2366 dup
->link
= map
->lastdup
;
2370 entry
->index
= (uint16
) index
;
2371 entry
->localKind
= (uint8
) localKind
;
2376 js_AddLocal(JSContext
*cx
, JSFunction
*fun
, JSAtom
*atom
, JSLocalKind kind
)
2382 JSLocalNameMap
*map
;
2384 JS_ASSERT(FUN_INTERPRETED(fun
));
2385 JS_ASSERT(!fun
->u
.i
.script
);
2386 JS_ASSERT(((jsuword
) atom
& 1) == 0);
2387 taggedAtom
= (jsuword
) atom
;
2388 if (kind
== JSLOCAL_ARG
) {
2389 indexp
= &fun
->nargs
;
2390 } else if (kind
== JSLOCAL_UPVAR
) {
2391 indexp
= &fun
->u
.i
.nupvars
;
2393 indexp
= &fun
->u
.i
.nvars
;
2394 if (kind
== JSLOCAL_CONST
)
2397 JS_ASSERT(kind
== JSLOCAL_VAR
);
2399 n
= JS_GET_LOCAL_NAME_COUNT(fun
);
2401 JS_ASSERT(fun
->u
.i
.names
.taggedAtom
== 0);
2402 fun
->u
.i
.names
.taggedAtom
= taggedAtom
;
2403 } else if (n
< MAX_ARRAY_LOCALS
) {
2405 array
= fun
->u
.i
.names
.array
;
2407 array
= (jsuword
*) JS_malloc(cx
, MAX_ARRAY_LOCALS
* sizeof *array
);
2410 array
[0] = fun
->u
.i
.names
.taggedAtom
;
2411 fun
->u
.i
.names
.array
= array
;
2413 if (kind
== JSLOCAL_ARG
) {
2415 * A destructuring argument pattern adds variables, not arguments,
2416 * so for the following arguments nvars != 0.
2418 #if JS_HAS_DESTRUCTURING
2419 if (fun
->u
.i
.nvars
!= 0) {
2420 memmove(array
+ fun
->nargs
+ 1, array
+ fun
->nargs
,
2421 fun
->u
.i
.nvars
* sizeof *array
);
2424 JS_ASSERT(fun
->u
.i
.nvars
== 0);
2426 array
[fun
->nargs
] = taggedAtom
;
2428 array
[n
] = taggedAtom
;
2430 } else if (n
== MAX_ARRAY_LOCALS
) {
2431 array
= fun
->u
.i
.names
.array
;
2432 map
= (JSLocalNameMap
*) JS_malloc(cx
, sizeof *map
);
2435 if (!JS_DHashTableInit(&map
->names
, JS_DHashGetStubOps(),
2436 NULL
, sizeof(JSLocalNameHashEntry
),
2437 JS_DHASH_DEFAULT_CAPACITY(MAX_ARRAY_LOCALS
2439 JS_ReportOutOfMemory(cx
);
2444 map
->lastdup
= NULL
;
2445 for (i
= 0; i
!= MAX_ARRAY_LOCALS
; ++i
) {
2446 taggedAtom
= array
[i
];
2447 if (!HashLocalName(cx
, map
, (JSAtom
*) (taggedAtom
& ~1),
2450 : (taggedAtom
& 1) ? JSLOCAL_CONST
: JSLOCAL_VAR
,
2451 (i
< fun
->nargs
) ? i
: i
- fun
->nargs
)) {
2452 FreeLocalNameHash(cx
, map
);
2456 if (!HashLocalName(cx
, map
, atom
, kind
, *indexp
)) {
2457 FreeLocalNameHash(cx
, map
);
2462 * At this point the entry is added and we cannot fail. It is time
2463 * to replace fun->u.i.names with the built map.
2465 fun
->u
.i
.names
.map
= map
;
2468 if (*indexp
== JS_BITMASK(16)) {
2469 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
2470 (kind
== JSLOCAL_ARG
)
2471 ? JSMSG_TOO_MANY_FUN_ARGS
2472 : JSMSG_TOO_MANY_LOCALS
);
2475 if (!HashLocalName(cx
, fun
->u
.i
.names
.map
, atom
, kind
, *indexp
))
2479 /* Update the argument or variable counter. */
2485 js_LookupLocal(JSContext
*cx
, JSFunction
*fun
, JSAtom
*atom
, uintN
*indexp
)
2487 uintN n
, i
, upvar_base
;
2489 JSLocalNameHashEntry
*entry
;
2491 JS_ASSERT(FUN_INTERPRETED(fun
));
2492 n
= JS_GET_LOCAL_NAME_COUNT(fun
);
2494 return JSLOCAL_NONE
;
2495 if (n
<= MAX_ARRAY_LOCALS
) {
2496 array
= (n
== 1) ? &fun
->u
.i
.names
.taggedAtom
: fun
->u
.i
.names
.array
;
2498 /* Search from the tail to pick up the last duplicated name. */
2500 upvar_base
= JS_UPVAR_LOCAL_NAME_START(fun
);
2503 if (atom
== JS_LOCAL_NAME_TO_ATOM(array
[i
])) {
2504 if (i
< fun
->nargs
) {
2509 if (i
>= upvar_base
) {
2511 *indexp
= i
- upvar_base
;
2512 return JSLOCAL_UPVAR
;
2515 *indexp
= i
- fun
->nargs
;
2516 return JS_LOCAL_NAME_IS_CONST(array
[i
])
2522 entry
= (JSLocalNameHashEntry
*)
2523 JS_DHashTableOperate(&fun
->u
.i
.names
.map
->names
, atom
,
2525 if (JS_DHASH_ENTRY_IS_BUSY(&entry
->hdr
)) {
2526 JS_ASSERT(entry
->localKind
!= JSLOCAL_NONE
);
2528 *indexp
= entry
->index
;
2529 return (JSLocalKind
) entry
->localKind
;
2532 return JSLOCAL_NONE
;
2535 typedef struct JSLocalNameEnumeratorArgs
{
2542 } JSLocalNameEnumeratorArgs
;
2544 static JSDHashOperator
2545 get_local_names_enumerator(JSDHashTable
*table
, JSDHashEntryHdr
*hdr
,
2546 uint32 number
, void *arg
)
2548 JSLocalNameHashEntry
*entry
;
2549 JSLocalNameEnumeratorArgs
*args
;
2553 entry
= (JSLocalNameHashEntry
*) hdr
;
2554 args
= (JSLocalNameEnumeratorArgs
*) arg
;
2555 JS_ASSERT(entry
->name
);
2556 if (entry
->localKind
== JSLOCAL_ARG
) {
2557 JS_ASSERT(entry
->index
< args
->fun
->nargs
);
2558 JS_ASSERT(args
->nCopiedArgs
++ < args
->fun
->nargs
);
2562 JS_ASSERT(entry
->localKind
== JSLOCAL_VAR
||
2563 entry
->localKind
== JSLOCAL_CONST
);
2564 JS_ASSERT(entry
->index
< args
->fun
->u
.i
.nvars
);
2565 JS_ASSERT(args
->nCopiedVars
++ < args
->fun
->u
.i
.nvars
);
2566 i
= args
->fun
->nargs
+ entry
->index
;
2567 constFlag
= (entry
->localKind
== JSLOCAL_CONST
);
2569 args
->names
[i
] = (jsuword
) entry
->name
| constFlag
;
2570 return JS_DHASH_NEXT
;
2574 js_GetLocalNameArray(JSContext
*cx
, JSFunction
*fun
, JSArenaPool
*pool
)
2578 JSLocalNameMap
*map
;
2579 JSLocalNameEnumeratorArgs args
;
2580 JSNameIndexPair
*dup
;
2582 JS_ASSERT(FUN_INTERPRETED(fun
));
2583 n
= JS_GET_LOCAL_NAME_COUNT(fun
);
2586 if (n
<= MAX_ARRAY_LOCALS
)
2587 return (n
== 1) ? &fun
->u
.i
.names
.taggedAtom
: fun
->u
.i
.names
.array
;
2590 * No need to check for overflow of the allocation size as we are making a
2591 * copy of already allocated data. As such it must fit size_t.
2593 JS_ARENA_ALLOCATE_CAST(names
, jsuword
*, pool
, (size_t) n
* sizeof *names
);
2595 js_ReportOutOfScriptQuota(cx
);
2599 #if JS_HAS_DESTRUCTURING
2600 /* Some parameter names can be NULL due to destructuring patterns. */
2601 memset(names
, 0, fun
->nargs
* sizeof *names
);
2603 map
= fun
->u
.i
.names
.map
;
2607 args
.nCopiedArgs
= 0;
2608 args
.nCopiedVars
= 0;
2610 JS_DHashTableEnumerate(&map
->names
, get_local_names_enumerator
, &args
);
2611 for (dup
= map
->lastdup
; dup
; dup
= dup
->link
) {
2612 JS_ASSERT(dup
->index
< fun
->nargs
);
2613 JS_ASSERT(args
.nCopiedArgs
++ < fun
->nargs
);
2614 names
[dup
->index
] = (jsuword
) dup
->name
;
2616 #if !JS_HAS_DESTRUCTURING
2617 JS_ASSERT(args
.nCopiedArgs
== fun
->nargs
);
2619 JS_ASSERT(args
.nCopiedVars
== fun
->u
.i
.nvars
);
2624 static JSDHashOperator
2625 trace_local_names_enumerator(JSDHashTable
*table
, JSDHashEntryHdr
*hdr
,
2626 uint32 number
, void *arg
)
2628 JSLocalNameHashEntry
*entry
;
2631 entry
= (JSLocalNameHashEntry
*) hdr
;
2632 JS_ASSERT(entry
->name
);
2633 trc
= (JSTracer
*) arg
;
2634 JS_SET_TRACING_INDEX(trc
,
2635 entry
->localKind
== JSLOCAL_ARG
? "arg" : "var",
2637 JS_CallTracer(trc
, ATOM_TO_STRING(entry
->name
), JSTRACE_STRING
);
2638 return JS_DHASH_NEXT
;
2642 TraceLocalNames(JSTracer
*trc
, JSFunction
*fun
)
2648 JS_ASSERT(FUN_INTERPRETED(fun
));
2649 n
= JS_GET_LOCAL_NAME_COUNT(fun
);
2652 if (n
<= MAX_ARRAY_LOCALS
) {
2653 array
= (n
== 1) ? &fun
->u
.i
.names
.taggedAtom
: fun
->u
.i
.names
.array
;
2657 atom
= (JSAtom
*) (array
[i
] & ~1);
2659 JS_SET_TRACING_INDEX(trc
,
2660 i
< fun
->nargs
? "arg" : "var",
2661 i
< fun
->nargs
? i
: i
- fun
->nargs
);
2662 JS_CallTracer(trc
, ATOM_TO_STRING(atom
), JSTRACE_STRING
);
2666 JS_DHashTableEnumerate(&fun
->u
.i
.names
.map
->names
,
2667 trace_local_names_enumerator
, trc
);
2670 * No need to trace the list of duplicates in map->lastdup as the
2671 * names there are traced when enumerating the hash table.
2677 DestroyLocalNames(JSContext
*cx
, JSFunction
*fun
)
2681 n
= fun
->nargs
+ fun
->u
.i
.nvars
;
2684 if (n
<= MAX_ARRAY_LOCALS
)
2685 JS_free(cx
, fun
->u
.i
.names
.array
);
2687 FreeLocalNameHash(cx
, fun
->u
.i
.names
.map
);
2691 js_FreezeLocalNames(JSContext
*cx
, JSFunction
*fun
)
2696 JS_ASSERT(FUN_INTERPRETED(fun
));
2697 JS_ASSERT(!fun
->u
.i
.script
);
2698 n
= fun
->nargs
+ fun
->u
.i
.nvars
+ fun
->u
.i
.nupvars
;
2699 if (2 <= n
&& n
< MAX_ARRAY_LOCALS
) {
2700 /* Shrink over-allocated array ignoring realloc failures. */
2701 array
= (jsuword
*) JS_realloc(cx
, fun
->u
.i
.names
.array
,
2704 fun
->u
.i
.names
.array
= array
;