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 */
53 #include "jsbuiltins.h"
55 #include "jsversion.h"
71 #include "jsstaticcheck.h"
79 # include "jsxdrapi.h"
82 #include "jsatominlines.h"
85 SetOverriddenArgsLength(JSObject
*obj
)
87 JS_ASSERT(STOBJ_GET_CLASS(obj
) == &js_ArgumentsClass
);
89 jsval v
= obj
->fslots
[JSSLOT_ARGS_LENGTH
];
90 v
= INT_TO_JSVAL(JSVAL_TO_INT(v
) | 1);
91 JS_ASSERT(JSVAL_IS_INT(v
));
92 obj
->fslots
[JSSLOT_ARGS_LENGTH
] = v
;
96 InitArgsLengthSlot(JSObject
*obj
, uint32 argc
)
98 JS_ASSERT(STOBJ_GET_CLASS(obj
) == &js_ArgumentsClass
);
99 JS_ASSERT(argc
<= JS_ARGS_LENGTH_MAX
);
100 JS_ASSERT(obj
->fslots
[JSSLOT_ARGS_LENGTH
] == JSVAL_VOID
);
101 obj
->fslots
[JSSLOT_ARGS_LENGTH
] = INT_TO_JSVAL(argc
<< 1);
102 JS_ASSERT(!js_IsOverriddenArgsLength(obj
));
106 GetArgsLength(JSObject
*obj
)
108 JS_ASSERT(STOBJ_GET_CLASS(obj
) == &js_ArgumentsClass
);
110 uint32 argc
= uint32(JSVAL_TO_INT(obj
->fslots
[JSSLOT_ARGS_LENGTH
])) >> 1;
111 JS_ASSERT(argc
<= JS_ARGS_LENGTH_MAX
);
116 SetArgsPrivateNative(JSObject
*argsobj
, js_ArgsPrivateNative
*apn
)
118 JS_ASSERT(STOBJ_GET_CLASS(argsobj
) == &js_ArgumentsClass
);
119 uintptr_t p
= (uintptr_t) apn
;
120 argsobj
->setPrivate((void*) (p
| 2));
124 js_GetArgsValue(JSContext
*cx
, JSStackFrame
*fp
, jsval
*vp
)
128 if (fp
->flags
& JSFRAME_OVERRIDE_ARGS
) {
129 JS_ASSERT(fp
->callobj
);
130 jsid id
= ATOM_TO_JSID(cx
->runtime
->atomState
.argumentsAtom
);
131 return fp
->callobj
->getProperty(cx
, id
, vp
);
133 argsobj
= js_GetArgsObject(cx
, fp
);
136 *vp
= OBJECT_TO_JSVAL(argsobj
);
141 js_GetArgsProperty(JSContext
*cx
, JSStackFrame
*fp
, jsid id
, jsval
*vp
)
143 if (fp
->flags
& JSFRAME_OVERRIDE_ARGS
) {
144 JS_ASSERT(fp
->callobj
);
146 jsid argumentsid
= ATOM_TO_JSID(cx
->runtime
->atomState
.argumentsAtom
);
148 if (!fp
->callobj
->getProperty(cx
, argumentsid
, &v
))
152 if (JSVAL_IS_PRIMITIVE(v
)) {
153 obj
= js_ValueToNonNullObject(cx
, v
);
157 obj
= JSVAL_TO_OBJECT(v
);
159 return obj
->getProperty(cx
, id
, vp
);
163 if (JSID_IS_INT(id
)) {
164 uint32 arg
= uint32(JSID_TO_INT(id
));
165 JSObject
*argsobj
= JSVAL_TO_OBJECT(fp
->argsobj
);
166 if (arg
< fp
->argc
) {
168 jsval v
= OBJ_GET_SLOT(cx
, argsobj
, JSSLOT_ARGS_COPY_START
+arg
);
170 return argsobj
->getProperty(cx
, id
, vp
);
175 * Per ECMA-262 Ed. 3, 10.1.8, last bulleted item, do not share
176 * storage between the formal parameter and arguments[k] for all
177 * fp->argc <= k && k < fp->fun->nargs. For example, in
179 * function f(x) { x = 42; return arguments[0]; }
182 * the call to f should return undefined, not 42. If fp->argsobj
183 * is null at this point, as it would be in the example, return
187 return argsobj
->getProperty(cx
, id
, vp
);
189 } else if (id
== ATOM_TO_JSID(cx
->runtime
->atomState
.lengthAtom
)) {
190 JSObject
*argsobj
= JSVAL_TO_OBJECT(fp
->argsobj
);
191 if (argsobj
&& js_IsOverriddenArgsLength(argsobj
))
192 return argsobj
->getProperty(cx
, id
, vp
);
193 *vp
= INT_TO_JSVAL(jsint(fp
->argc
));
199 NewArguments(JSContext
*cx
, JSObject
*parent
, uint32 argc
, JSObject
*callee
)
201 JSObject
*argsobj
= js_NewObject(cx
, &js_ArgumentsClass
, NULL
, parent
, 0);
202 if (!argsobj
|| !js_EnsureReservedSlots(cx
, argsobj
, argc
))
205 argsobj
->fslots
[JSSLOT_ARGS_CALLEE
] = OBJECT_TO_JSVAL(callee
);
206 InitArgsLengthSlot(argsobj
, argc
);
211 PutArguments(JSContext
*cx
, JSObject
*argsobj
, jsval
*args
)
213 uint32 argc
= GetArgsLength(argsobj
);
214 JS_LOCK_OBJ(cx
, argsobj
);
215 for (uint32 i
= 0; i
!= argc
; ++i
) {
216 jsval v
= STOBJ_GET_SLOT(argsobj
, JSSLOT_ARGS_COPY_START
+ i
);
218 STOBJ_SET_SLOT(argsobj
, JSSLOT_ARGS_COPY_START
+ i
, args
[i
]);
220 JS_UNLOCK_OBJ(cx
, argsobj
);
224 js_GetArgsObject(JSContext
*cx
, JSStackFrame
*fp
)
227 * We must be in a function activation; the function must be lightweight
228 * or else fp must have a variable object.
230 JS_ASSERT(fp
->fun
&& (!(fp
->fun
->flags
& JSFUN_HEAVYWEIGHT
) || fp
->varobj
));
232 /* Skip eval and debugger frames. */
233 while (fp
->flags
& JSFRAME_SPECIAL
)
236 /* Create an arguments object for fp only if it lacks one. */
237 JSObject
*argsobj
= JSVAL_TO_OBJECT(fp
->argsobj
);
242 * Give arguments an intrinsic scope chain link to fp's global object.
243 * Since the arguments object lacks a prototype because js_ArgumentsClass
244 * is not initialized, js_NewObject won't assign a default parent to it.
246 * Therefore if arguments is used as the head of an eval scope chain (via
247 * a direct or indirect call to eval(program, arguments)), any reference
248 * to a standard class object in the program will fail to resolve due to
249 * js_GetClassPrototype not being able to find a global object containing
250 * the standard prototype by starting from arguments and following parent.
252 JSObject
*parent
, *global
= fp
->scopeChain
;
253 while ((parent
= OBJ_GET_PARENT(cx
, global
)) != NULL
)
257 argsobj
= NewArguments(cx
, global
, fp
->argc
, JSVAL_TO_OBJECT(fp
->argv
[-2]));
261 /* Link the new object to fp so it can get actual argument values. */
262 argsobj
->setPrivate(fp
);
263 fp
->argsobj
= OBJECT_TO_JSVAL(argsobj
);
268 js_PutArgsObject(JSContext
*cx
, JSStackFrame
*fp
)
270 JSObject
*argsobj
= JSVAL_TO_OBJECT(fp
->argsobj
);
271 JS_ASSERT(argsobj
->getPrivate() == fp
);
272 PutArguments(cx
, argsobj
, fp
->argv
);
273 argsobj
->setPrivate(NULL
);
274 fp
->argsobj
= JSVAL_NULL
;
278 * Traced versions of js_GetArgsObject and js_PutArgsObject.
282 JSObject
* JS_FASTCALL
283 js_Arguments(JSContext
*cx
, JSObject
*parent
, uint32 argc
, JSObject
*callee
,
284 double *argv
, js_ArgsPrivateNative
*apn
)
286 JSObject
*argsobj
= NewArguments(cx
, parent
, argc
, callee
);
290 SetArgsPrivateNative(argsobj
, apn
);
295 JS_DEFINE_CALLINFO_6(extern, OBJECT
, js_Arguments
, CONTEXT
, OBJECT
, UINT32
, OBJECT
,
296 DOUBLEPTR
, APNPTR
, 0, 0)
298 /* FIXME change the return type to void. */
300 js_PutArguments(JSContext
*cx
, JSObject
*argsobj
, jsval
*args
)
302 JS_ASSERT(js_GetArgsPrivateNative(argsobj
));
303 PutArguments(cx
, argsobj
, args
);
304 argsobj
->setPrivate(NULL
);
308 JS_DEFINE_CALLINFO_3(extern, BOOL
, js_PutArguments
, CONTEXT
, OBJECT
, JSVALPTR
, 0, 0)
311 args_delProperty(JSContext
*cx
, JSObject
*obj
, jsval idval
, jsval
*vp
)
313 JS_ASSERT(STOBJ_GET_CLASS(obj
) == &js_ArgumentsClass
);
315 if (JSVAL_IS_INT(idval
)) {
316 uintN arg
= uintN(JSVAL_TO_INT(idval
));
317 if (arg
< GetArgsLength(obj
))
318 OBJ_SET_SLOT(cx
, obj
, JSSLOT_ARGS_COPY_START
+ arg
, JSVAL_HOLE
);
319 } else if (idval
== ATOM_KEY(cx
->runtime
->atomState
.lengthAtom
)) {
320 SetOverriddenArgsLength(obj
);
321 } else if (idval
== ATOM_KEY(cx
->runtime
->atomState
.calleeAtom
)) {
322 obj
->fslots
[JSSLOT_ARGS_CALLEE
] = JSVAL_HOLE
;
327 static JS_REQUIRES_STACK JSObject
*
328 WrapEscapingClosure(JSContext
*cx
, JSStackFrame
*fp
, JSObject
*funobj
, JSFunction
*fun
)
330 JS_ASSERT(GET_FUNCTION_PRIVATE(cx
, funobj
) == fun
);
331 JS_ASSERT(fun
->optimizedClosure());
332 JS_ASSERT(!fun
->u
.i
.wrapper
);
335 * We do not attempt to reify Call and Block objects on demand for outer
336 * scopes. This could be done (see the "v8" patch in bug 494235) but it is
337 * fragile in the face of ongoing compile-time optimization. Instead, the
338 * _DBG* opcodes used by wrappers created here must cope with unresolved
339 * upvars and throw them as reference errors. Caveat debuggers!
341 JSObject
*scopeChain
= js_GetScopeChain(cx
, fp
);
345 JSObject
*wfunobj
= js_NewObjectWithGivenProto(cx
, &js_FunctionClass
,
349 JSAutoTempValueRooter
tvr(cx
, wfunobj
);
351 JSFunction
*wfun
= (JSFunction
*) wfunobj
;
352 wfunobj
->setPrivate(wfun
);
354 wfun
->flags
= fun
->flags
| JSFUN_HEAVYWEIGHT
;
356 wfun
->u
.i
.nupvars
= 0;
357 wfun
->u
.i
.skipmin
= fun
->u
.i
.skipmin
;
358 wfun
->u
.i
.wrapper
= true;
359 wfun
->u
.i
.script
= NULL
;
360 wfun
->u
.i
.names
.taggedAtom
= NULL
;
361 wfun
->atom
= fun
->atom
;
363 if (fun
->hasLocalNames()) {
364 void *mark
= JS_ARENA_MARK(&cx
->tempPool
);
365 jsuword
*names
= js_GetLocalNameArray(cx
, fun
, &cx
->tempPool
);
370 for (uintN i
= 0, n
= fun
->countLocalNames(); i
!= n
; i
++) {
371 jsuword name
= names
[i
];
372 JSAtom
*atom
= JS_LOCAL_NAME_TO_ATOM(name
);
373 JSLocalKind localKind
= (i
< fun
->nargs
)
375 : (i
< fun
->countArgsAndVars())
376 ? (JS_LOCAL_NAME_IS_CONST(name
)
381 ok
= js_AddLocal(cx
, wfun
, atom
, localKind
);
386 JS_ARENA_RELEASE(&cx
->tempPool
, mark
);
389 JS_ASSERT(wfun
->nargs
== fun
->nargs
);
390 JS_ASSERT(wfun
->u
.i
.nvars
== fun
->u
.i
.nvars
);
391 JS_ASSERT(wfun
->u
.i
.nupvars
== fun
->u
.i
.nupvars
);
392 js_FreezeLocalNames(cx
, wfun
);
395 JSScript
*script
= fun
->u
.i
.script
;
396 jssrcnote
*snbase
= script
->notes();
397 jssrcnote
*sn
= snbase
;
398 while (!SN_IS_TERMINATOR(sn
))
400 uintN nsrcnotes
= (sn
- snbase
) + 1;
402 /* NB: GC must not occur before wscript is homed in wfun->u.i.script. */
403 JSScript
*wscript
= js_NewScript(cx
, script
->length
, nsrcnotes
,
404 script
->atomMap
.length
,
405 (script
->objectsOffset
!= 0)
406 ? script
->objects()->length
409 (script
->regexpsOffset
!= 0)
410 ? script
->regexps()->length
412 (script
->trynotesOffset
!= 0)
413 ? script
->trynotes()->length
418 memcpy(wscript
->code
, script
->code
, script
->length
);
419 wscript
->main
= wscript
->code
+ (script
->main
- script
->code
);
421 memcpy(wscript
->notes(), snbase
, nsrcnotes
* sizeof(jssrcnote
));
422 memcpy(wscript
->atomMap
.vector
, script
->atomMap
.vector
,
423 wscript
->atomMap
.length
* sizeof(JSAtom
*));
424 if (script
->objectsOffset
!= 0) {
425 memcpy(wscript
->objects()->vector
, script
->objects()->vector
,
426 wscript
->objects()->length
* sizeof(JSObject
*));
428 if (script
->regexpsOffset
!= 0) {
429 memcpy(wscript
->regexps()->vector
, script
->regexps()->vector
,
430 wscript
->regexps()->length
* sizeof(JSObject
*));
432 if (script
->trynotesOffset
!= 0) {
433 memcpy(wscript
->trynotes()->vector
, script
->trynotes()->vector
,
434 wscript
->trynotes()->length
* sizeof(JSTryNote
));
437 if (wfun
->u
.i
.nupvars
!= 0) {
438 JS_ASSERT(wfun
->u
.i
.nupvars
== wscript
->upvars()->length
);
439 memcpy(wscript
->upvars()->vector
, script
->upvars()->vector
,
440 wfun
->u
.i
.nupvars
* sizeof(uint32
));
443 jsbytecode
*pc
= wscript
->code
;
444 while (*pc
!= JSOP_STOP
) {
445 /* XYZZYbe should copy JSOP_TRAP? */
446 JSOp op
= js_GetOpcode(cx
, wscript
, pc
);
447 const JSCodeSpec
*cs
= &js_CodeSpec
[op
];
448 ptrdiff_t oplen
= cs
->length
;
450 oplen
= js_GetVariableBytecodeLength(pc
);
453 * Rewrite JSOP_{GET,CALL}DSLOT as JSOP_{GET,CALL}UPVAR_DBG for the
454 * case where fun is an escaping flat closure. This works because the
455 * UPVAR and DSLOT ops by design have the same format: an upvar index
459 case JSOP_GETUPVAR
: *pc
= JSOP_GETUPVAR_DBG
; break;
460 case JSOP_CALLUPVAR
: *pc
= JSOP_CALLUPVAR_DBG
; break;
461 case JSOP_GETDSLOT
: *pc
= JSOP_GETUPVAR_DBG
; break;
462 case JSOP_CALLDSLOT
: *pc
= JSOP_CALLUPVAR_DBG
; break;
463 case JSOP_DEFFUN_FC
: *pc
= JSOP_DEFFUN_DBGFC
; break;
464 case JSOP_DEFLOCALFUN_FC
: *pc
= JSOP_DEFLOCALFUN_DBGFC
; break;
465 case JSOP_LAMBDA_FC
: *pc
= JSOP_LAMBDA_DBGFC
; break;
472 * Fill in the rest of wscript. This means if you add members to JSScript
473 * you must update this code. FIXME: factor into JSScript::clone method.
475 wscript
->noScriptRval
= script
->noScriptRval
;
476 wscript
->savedCallerFun
= script
->savedCallerFun
;
477 wscript
->hasSharps
= script
->hasSharps
;
478 wscript
->strictModeCode
= script
->strictModeCode
;
479 wscript
->version
= script
->version
;
480 wscript
->nfixed
= script
->nfixed
;
481 wscript
->filename
= script
->filename
;
482 wscript
->lineno
= script
->lineno
;
483 wscript
->nslots
= script
->nslots
;
484 wscript
->staticLevel
= script
->staticLevel
;
485 wscript
->principals
= script
->principals
;
486 if (wscript
->principals
)
487 JSPRINCIPALS_HOLD(cx
, wscript
->principals
);
488 #ifdef CHECK_SCRIPT_OWNER
489 wscript
->owner
= script
->owner
;
492 /* Deoptimize wfun from FUN_{FLAT,NULL}_CLOSURE to FUN_INTERPRETED. */
493 FUN_SET_KIND(wfun
, JSFUN_INTERPRETED
);
494 wfun
->u
.i
.script
= wscript
;
499 ArgGetter(JSContext
*cx
, JSObject
*obj
, jsval idval
, jsval
*vp
)
501 if (!JS_InstanceOf(cx
, obj
, &js_ArgumentsClass
, NULL
))
504 if (JSVAL_IS_INT(idval
)) {
506 * arg can exceed the number of arguments if a script changed the
507 * prototype to point to another Arguments object with a bigger argc.
509 uintN arg
= uintN(JSVAL_TO_INT(idval
));
510 if (arg
< GetArgsLength(obj
)) {
512 js_ArgsPrivateNative
*argp
= js_GetArgsPrivateNative(obj
);
514 if (js_NativeToValue(cx
, *vp
, argp
->typemap()[arg
], &argp
->argv
[arg
]))
521 JSStackFrame
*fp
= (JSStackFrame
*) obj
->getPrivate();
525 jsval v
= OBJ_GET_SLOT(cx
, obj
, JSSLOT_ARGS_COPY_START
+ arg
);
530 } else if (idval
== ATOM_KEY(cx
->runtime
->atomState
.lengthAtom
)) {
531 if (!js_IsOverriddenArgsLength(obj
))
532 *vp
= INT_TO_JSVAL(GetArgsLength(obj
));
534 JS_ASSERT(idval
== ATOM_KEY(cx
->runtime
->atomState
.calleeAtom
));
535 jsval v
= obj
->fslots
[JSSLOT_ARGS_CALLEE
];
536 if (v
!= JSVAL_HOLE
) {
538 * If this function or one in it needs upvars that reach above it
539 * in the scope chain, it must not be a null closure (it could be a
540 * flat closure, or an unoptimized closure -- the latter itself not
541 * necessarily heavyweight). Rather than wrap here, we simply throw
542 * to reduce code size and tell debugger users the truth instead of
543 * passing off a fibbing wrapper.
545 if (GET_FUNCTION_PRIVATE(cx
, JSVAL_TO_OBJECT(v
))->needsWrapper()) {
546 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
547 JSMSG_OPTIMIZED_CLOSURE_LEAK
);
557 ArgSetter(JSContext
*cx
, JSObject
*obj
, jsval idval
, jsval
*vp
)
560 // To be able to set a property here on trace, we would have to make
561 // sure any updates also get written back to the trace native stack.
562 // For simplicity, we just leave trace, since this is presumably not
563 // a common operation.
564 if (JS_ON_TRACE(cx
)) {
570 if (!JS_InstanceOf(cx
, obj
, &js_ArgumentsClass
, NULL
))
573 if (JSVAL_IS_INT(idval
)) {
574 uintN arg
= uintN(JSVAL_TO_INT(idval
));
575 if (arg
< GetArgsLength(obj
)) {
576 JSStackFrame
*fp
= (JSStackFrame
*) obj
->getPrivate();
583 JS_ASSERT(idval
== ATOM_KEY(cx
->runtime
->atomState
.lengthAtom
) ||
584 idval
== ATOM_KEY(cx
->runtime
->atomState
.calleeAtom
));
588 * For simplicity we use delete/set to replace the property with one
589 * backed by the default Object getter and setter. Note the we rely on
590 * args_delete to clear the corresponding reserved slot so the GC can
594 if (!JS_ValueToId(cx
, idval
, &id
))
597 JSAutoTempValueRooter
tvr(cx
);
598 return js_DeleteProperty(cx
, obj
, id
, tvr
.addr()) &&
599 js_SetProperty(cx
, obj
, id
, vp
);
603 args_resolve(JSContext
*cx
, JSObject
*obj
, jsval idval
, uintN flags
,
606 JS_ASSERT(STOBJ_GET_CLASS(obj
) == &js_ArgumentsClass
);
610 if (JSVAL_IS_INT(idval
)) {
611 uint32 arg
= uint32(JSVAL_TO_INT(idval
));
612 if (arg
< GetArgsLength(obj
) &&
613 OBJ_GET_SLOT(cx
, obj
, JSSLOT_ARGS_COPY_START
+ arg
) != JSVAL_HOLE
) {
614 id
= INT_JSVAL_TO_JSID(idval
);
616 } else if (idval
== ATOM_KEY(cx
->runtime
->atomState
.lengthAtom
)) {
617 if (!js_IsOverriddenArgsLength(obj
))
618 id
= ATOM_TO_JSID(cx
->runtime
->atomState
.lengthAtom
);
620 } else if (idval
== ATOM_KEY(cx
->runtime
->atomState
.calleeAtom
)) {
621 if (obj
->fslots
[JSSLOT_ARGS_CALLEE
] != JSVAL_HOLE
)
622 id
= ATOM_TO_JSID(cx
->runtime
->atomState
.calleeAtom
);
627 * XXX ECMA specs DontEnum even for indexed properties, contrary to
628 * other array-like objects.
630 if (!js_DefineProperty(cx
, obj
, id
, JSVAL_VOID
, ArgGetter
, ArgSetter
, JSPROP_SHARED
))
638 args_enumerate(JSContext
*cx
, JSObject
*obj
)
640 JS_ASSERT(STOBJ_GET_CLASS(obj
) == &js_ArgumentsClass
);
643 * Trigger reflection in args_resolve using a series of js_LookupProperty
646 int argc
= int(GetArgsLength(obj
));
647 for (int i
= -2; i
!= argc
; i
++) {
649 ? ATOM_TO_JSID(cx
->runtime
->atomState
.lengthAtom
)
651 ? ATOM_TO_JSID(cx
->runtime
->atomState
.calleeAtom
)
652 : INT_JSVAL_TO_JSID(INT_TO_JSVAL(i
));
656 if (!js_LookupProperty(cx
, obj
, id
, &pobj
, &prop
))
659 /* prop is null when the property was deleted. */
661 pobj
->dropProperty(cx
, prop
);
666 #if JS_HAS_GENERATORS
668 * If a generator-iterator's arguments or call object escapes, it needs to
669 * mark its generator object.
672 args_or_call_trace(JSTracer
*trc
, JSObject
*obj
)
674 JS_ASSERT(STOBJ_GET_CLASS(obj
) == &js_ArgumentsClass
||
675 STOBJ_GET_CLASS(obj
) == &js_CallClass
);
676 if (STOBJ_GET_CLASS(obj
) == &js_ArgumentsClass
&& js_GetArgsPrivateNative(obj
))
679 JSStackFrame
*fp
= (JSStackFrame
*) obj
->getPrivate();
680 if (fp
&& (fp
->flags
& JSFRAME_GENERATOR
)) {
681 JS_CALL_OBJECT_TRACER(trc
, FRAME_TO_GENERATOR(fp
)->obj
,
682 "FRAME_TO_GENERATOR(fp)->obj");
686 # define args_or_call_trace NULL
690 args_reserveSlots(JSContext
*cx
, JSObject
*obj
)
692 JS_ASSERT(STOBJ_GET_CLASS(obj
) == &js_ArgumentsClass
);
693 return GetArgsLength(obj
);
697 * The Arguments class is not initialized via JS_InitClass, and must not be,
698 * because its name is "Object". Per ECMA, that causes instances of it to
699 * delegate to the object named by Object.prototype. It also ensures that
700 * arguments.toString() returns "[object Object]".
702 * The JSClass functions below collaborate to lazily reflect and synchronize
703 * actual argument values, argument count, and callee function object stored
704 * in a JSStackFrame with their corresponding property values in the frame's
707 JSClass js_ArgumentsClass
= {
709 JSCLASS_HAS_PRIVATE
| JSCLASS_NEW_RESOLVE
|
710 JSCLASS_HAS_RESERVED_SLOTS(ARGS_CLASS_FIXED_RESERVED_SLOTS
) |
711 JSCLASS_MARK_IS_TRACE
| JSCLASS_HAS_CACHED_PROTO(JSProto_Object
),
712 JS_PropertyStub
, args_delProperty
,
713 JS_PropertyStub
, JS_PropertyStub
,
714 args_enumerate
, (JSResolveOp
) args_resolve
,
715 JS_ConvertStub
, NULL
,
719 JS_CLASS_TRACE(args_or_call_trace
), args_reserveSlots
722 const uint32 JSSLOT_CALLEE
= JSSLOT_PRIVATE
+ 1;
723 const uint32 JSSLOT_CALL_ARGUMENTS
= JSSLOT_PRIVATE
+ 2;
724 const uint32 CALL_CLASS_FIXED_RESERVED_SLOTS
= 2;
727 * A Declarative Environment object stores its active JSStackFrame pointer in
728 * its private slot, just as Call and Arguments objects do.
730 JSClass js_DeclEnvClass
= {
732 JSCLASS_HAS_PRIVATE
| JSCLASS_HAS_CACHED_PROTO(JSProto_Object
),
733 JS_PropertyStub
, JS_PropertyStub
, JS_PropertyStub
, JS_PropertyStub
,
734 JS_EnumerateStub
, JS_ResolveStub
, JS_ConvertStub
, NULL
,
735 JSCLASS_NO_OPTIONAL_MEMBERS
739 CheckForEscapingClosure(JSContext
*cx
, JSObject
*obj
, jsval
*vp
)
741 JS_ASSERT(STOBJ_GET_CLASS(obj
) == &js_CallClass
||
742 STOBJ_GET_CLASS(obj
) == &js_DeclEnvClass
);
746 if (VALUE_IS_FUNCTION(cx
, v
)) {
747 JSObject
*funobj
= JSVAL_TO_OBJECT(v
);
748 JSFunction
*fun
= GET_FUNCTION_PRIVATE(cx
, funobj
);
751 * Any escaping null or flat closure that reaches above itself or
752 * contains nested functions that reach above it must be wrapped.
753 * We can wrap only when this Call or Declarative Environment obj
754 * still has an active stack frame associated with it.
756 if (fun
->needsWrapper()) {
759 JSStackFrame
*fp
= (JSStackFrame
*) obj
->getPrivate();
761 JSObject
*wrapper
= WrapEscapingClosure(cx
, fp
, funobj
, fun
);
764 *vp
= OBJECT_TO_JSVAL(wrapper
);
768 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
769 JSMSG_OPTIMIZED_CLOSURE_LEAK
);
777 CalleeGetter(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
779 return CheckForEscapingClosure(cx
, obj
, vp
);
783 js_GetCallObject(JSContext
*cx
, JSStackFrame
*fp
)
787 /* Create a call object for fp only if it lacks one. */
789 callobj
= fp
->callobj
;
794 /* A call object should be a frame's outermost scope chain element. */
795 JSClass
*classp
= OBJ_GET_CLASS(cx
, fp
->scopeChain
);
796 if (classp
== &js_WithClass
|| classp
== &js_BlockClass
|| classp
== &js_CallClass
)
797 JS_ASSERT(fp
->scopeChain
->getPrivate() != fp
);
801 * Create the call object, using the frame's enclosing scope as its
802 * parent, and link the call to its stack frame. For a named function
803 * expression Call's parent points to an environment object holding
806 JSAtom
*lambdaName
= (fp
->fun
->flags
& JSFUN_LAMBDA
) ? fp
->fun
->atom
: NULL
;
808 JSObject
*env
= js_NewObjectWithGivenProto(cx
, &js_DeclEnvClass
, NULL
,
814 /* Root env before js_DefineNativeProperty (-> JSClass.addProperty). */
815 fp
->scopeChain
= env
;
817 if (!js_DefineNativeProperty(cx
, fp
->scopeChain
, ATOM_TO_JSID(lambdaName
),
820 JSPROP_PERMANENT
| JSPROP_READONLY
,
826 callobj
= js_NewObjectWithGivenProto(cx
, &js_CallClass
, NULL
, fp
->scopeChain
);
828 !js_EnsureReservedSlots(cx
, callobj
, fp
->fun
->countArgsAndVars())) {
832 callobj
->setPrivate(fp
);
834 JS_ASSERT(fp
->fun
== GET_FUNCTION_PRIVATE(cx
, fp
->calleeObject()));
835 STOBJ_SET_SLOT(callobj
, JSSLOT_CALLEE
, fp
->calleeValue());
836 fp
->callobj
= callobj
;
839 * Push callobj on the top of the scope chain, and make it the
842 fp
->scopeChain
= callobj
;
843 fp
->varobj
= callobj
;
848 js_GetCallObjectFunction(JSObject
*obj
)
852 JS_ASSERT(STOBJ_GET_CLASS(obj
) == &js_CallClass
);
853 v
= STOBJ_GET_SLOT(obj
, JSSLOT_CALLEE
);
854 if (JSVAL_IS_VOID(v
)) {
855 /* Newborn or prototype object. */
858 JS_ASSERT(!JSVAL_IS_PRIMITIVE(v
));
859 return GET_FUNCTION_PRIVATE(cx
, JSVAL_TO_OBJECT(v
));
863 js_PutCallObject(JSContext
*cx
, JSStackFrame
*fp
)
865 JSObject
*callobj
= fp
->callobj
;
868 /* Get the arguments object to snapshot fp's actual argument values. */
870 if (!(fp
->flags
& JSFRAME_OVERRIDE_ARGS
))
871 STOBJ_SET_SLOT(callobj
, JSSLOT_CALL_ARGUMENTS
, fp
->argsobj
);
872 js_PutArgsObject(cx
, fp
);
875 JSFunction
*fun
= fp
->fun
;
876 JS_ASSERT(fun
== js_GetCallObjectFunction(callobj
));
877 uintN n
= fun
->countArgsAndVars();
880 * Since for a call object all fixed slots happen to be taken, we can copy
881 * arguments and variables straight into JSObject.dslots.
883 JS_STATIC_ASSERT(JS_INITIAL_NSLOTS
- JSSLOT_PRIVATE
==
884 1 + CALL_CLASS_FIXED_RESERVED_SLOTS
);
886 JS_ASSERT(STOBJ_NSLOTS(callobj
) >= JS_INITIAL_NSLOTS
+ n
);
887 n
+= JS_INITIAL_NSLOTS
;
888 JS_LOCK_OBJ(cx
, callobj
);
889 memcpy(callobj
->dslots
, fp
->argv
, fun
->nargs
* sizeof(jsval
));
890 memcpy(callobj
->dslots
+ fun
->nargs
, fp
->slots
,
891 fun
->u
.i
.nvars
* sizeof(jsval
));
892 JS_UNLOCK_OBJ(cx
, callobj
);
895 /* Clear private pointers to fp, which is about to go away (js_Invoke). */
896 if ((fun
->flags
& JSFUN_LAMBDA
) && fun
->atom
) {
897 JSObject
*env
= STOBJ_GET_PARENT(callobj
);
899 JS_ASSERT(STOBJ_GET_CLASS(env
) == &js_DeclEnvClass
);
900 JS_ASSERT(env
->getPrivate() == fp
);
901 env
->setPrivate(NULL
);
904 callobj
->setPrivate(NULL
);
909 call_enumerate(JSContext
*cx
, JSObject
*obj
)
920 fun
= js_GetCallObjectFunction(obj
);
921 n
= fun
? fun
->countArgsAndVars() : 0;
925 mark
= JS_ARENA_MARK(&cx
->tempPool
);
927 MUST_FLOW_THROUGH("out");
928 names
= js_GetLocalNameArray(cx
, fun
, &cx
->tempPool
);
934 for (i
= 0; i
!= n
; ++i
) {
935 name
= JS_LOCAL_NAME_TO_ATOM(names
[i
]);
940 * Trigger reflection by looking up the name of the argument or
943 ok
= js_LookupProperty(cx
, obj
, ATOM_TO_JSID(name
), &pobj
, &prop
);
948 * The call object will always have a property corresponding to the
949 * argument or variable name because call_resolve creates the property
950 * using JSPROP_PERMANENT.
953 JS_ASSERT(pobj
== obj
);
954 pobj
->dropProperty(cx
, prop
);
959 JS_ARENA_RELEASE(&cx
->tempPool
, mark
);
963 typedef enum JSCallPropertyKind
{
967 } JSCallPropertyKind
;
970 CallPropertyOp(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
,
971 JSCallPropertyKind kind
, JSBool setter
)
978 if (STOBJ_GET_CLASS(obj
) != &js_CallClass
)
981 fun
= js_GetCallObjectFunction(obj
);
982 fp
= (JSStackFrame
*) obj
->getPrivate();
984 if (kind
== JSCPK_ARGUMENTS
) {
987 fp
->flags
|= JSFRAME_OVERRIDE_ARGS
;
988 STOBJ_SET_SLOT(obj
, JSSLOT_CALL_ARGUMENTS
, *vp
);
990 if (fp
&& !(fp
->flags
& JSFRAME_OVERRIDE_ARGS
)) {
993 argsobj
= js_GetArgsObject(cx
, fp
);
996 *vp
= OBJECT_TO_JSVAL(argsobj
);
998 *vp
= STOBJ_GET_SLOT(obj
, JSSLOT_CALL_ARGUMENTS
);
1004 JS_ASSERT((int16
) JSVAL_TO_INT(id
) == JSVAL_TO_INT(id
));
1005 i
= (uint16
) JSVAL_TO_INT(id
);
1006 JS_ASSERT_IF(kind
== JSCPK_ARG
, i
< fun
->nargs
);
1007 JS_ASSERT_IF(kind
== JSCPK_VAR
, i
< fun
->u
.i
.nvars
);
1010 i
+= CALL_CLASS_FIXED_RESERVED_SLOTS
;
1011 if (kind
== JSCPK_VAR
)
1014 JS_ASSERT(kind
== JSCPK_ARG
);
1016 ? JS_SetReservedSlot(cx
, obj
, i
, *vp
)
1017 : JS_GetReservedSlot(cx
, obj
, i
, vp
);
1020 if (kind
== JSCPK_ARG
) {
1023 JS_ASSERT(kind
== JSCPK_VAR
);
1027 GC_POKE(cx
, array
[i
]);
1036 GetCallArguments(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
1038 return CallPropertyOp(cx
, obj
, id
, vp
, JSCPK_ARGUMENTS
, JS_FALSE
);
1042 SetCallArguments(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
1044 return CallPropertyOp(cx
, obj
, id
, vp
, JSCPK_ARGUMENTS
, JS_TRUE
);
1048 js_GetCallArg(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
1050 return CallPropertyOp(cx
, obj
, id
, vp
, JSCPK_ARG
, JS_FALSE
);
1054 SetCallArg(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
1056 return CallPropertyOp(cx
, obj
, id
, vp
, JSCPK_ARG
, JS_TRUE
);
1060 js_GetCallVar(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
1062 return CallPropertyOp(cx
, obj
, id
, vp
, JSCPK_VAR
, JS_FALSE
);
1066 js_GetCallVarChecked(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
1068 if (!CallPropertyOp(cx
, obj
, id
, vp
, JSCPK_VAR
, JS_FALSE
))
1071 return CheckForEscapingClosure(cx
, obj
, vp
);
1075 SetCallVar(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
1077 return CallPropertyOp(cx
, obj
, id
, vp
, JSCPK_VAR
, JS_TRUE
);
1081 js_SetCallArg(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval v
)
1083 return CallPropertyOp(cx
, obj
, id
, &v
, JSCPK_ARG
, JS_TRUE
);
1087 js_SetCallVar(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval v
)
1089 return CallPropertyOp(cx
, obj
, id
, &v
, JSCPK_VAR
, JS_TRUE
);
1092 JS_DEFINE_CALLINFO_4(extern, BOOL
, js_SetCallArg
, CONTEXT
, OBJECT
, JSID
, JSVAL
, 0, 0)
1093 JS_DEFINE_CALLINFO_4(extern, BOOL
, js_SetCallVar
, CONTEXT
, OBJECT
, JSID
, JSVAL
, 0, 0)
1096 call_resolve(JSContext
*cx
, JSObject
*obj
, jsval idval
, uintN flags
,
1102 JSLocalKind localKind
;
1103 JSPropertyOp getter
, setter
;
1106 JS_ASSERT(STOBJ_GET_CLASS(obj
) == &js_CallClass
);
1107 JS_ASSERT(!STOBJ_GET_PROTO(obj
));
1109 if (!JSVAL_IS_STRING(idval
))
1112 callee
= STOBJ_GET_SLOT(obj
, JSSLOT_CALLEE
);
1113 if (JSVAL_IS_VOID(callee
))
1115 fun
= GET_FUNCTION_PRIVATE(cx
, JSVAL_TO_OBJECT(callee
));
1117 if (!js_ValueToStringId(cx
, idval
, &id
))
1121 * Check whether the id refers to a formal parameter, local variable or
1122 * the arguments special name.
1124 * We define all such names using JSDNP_DONT_PURGE to avoid an expensive
1125 * shape invalidation in js_DefineNativeProperty. If such an id happens to
1126 * shadow a global or upvar of the same name, any inner functions can
1127 * never access the outer binding. Thus it cannot invalidate any property
1128 * cache entries or derived trace guards for the outer binding. See also
1129 * comments in js_PurgeScopeChainHelper from jsobj.cpp.
1131 localKind
= js_LookupLocal(cx
, fun
, JSID_TO_ATOM(id
), &slot
);
1132 if (localKind
!= JSLOCAL_NONE
&& localKind
!= JSLOCAL_UPVAR
) {
1133 JS_ASSERT((uint16
) slot
== slot
);
1136 * We follow 10.2.3 of ECMA 262 v3 and make argument and variable
1137 * properties of the Call objects enumerable.
1139 attrs
= JSPROP_ENUMERATE
| JSPROP_PERMANENT
| JSPROP_SHARED
;
1140 if (localKind
== JSLOCAL_ARG
) {
1141 JS_ASSERT(slot
< fun
->nargs
);
1142 getter
= js_GetCallArg
;
1143 setter
= SetCallArg
;
1145 JS_ASSERT(localKind
== JSLOCAL_VAR
|| localKind
== JSLOCAL_CONST
);
1146 JS_ASSERT(slot
< fun
->u
.i
.nvars
);
1147 getter
= js_GetCallVar
;
1148 setter
= SetCallVar
;
1149 if (localKind
== JSLOCAL_CONST
)
1150 attrs
|= JSPROP_READONLY
;
1153 * Use js_GetCallVarChecked if the local's value is a null closure.
1154 * This way we penalize performance only slightly on first use of a
1155 * null closure, not on every use.
1158 if (!CallPropertyOp(cx
, obj
, INT_TO_JSID((int16
)slot
), &v
, JSCPK_VAR
, JS_FALSE
))
1160 if (VALUE_IS_FUNCTION(cx
, v
) &&
1161 GET_FUNCTION_PRIVATE(cx
, JSVAL_TO_OBJECT(v
))->needsWrapper()) {
1162 getter
= js_GetCallVarChecked
;
1165 if (!js_DefineNativeProperty(cx
, obj
, id
, JSVAL_VOID
, getter
, setter
,
1166 attrs
, SPROP_HAS_SHORTID
, (int16
) slot
,
1167 NULL
, JSDNP_DONT_PURGE
)) {
1175 * Resolve arguments so that we never store a particular Call object's
1176 * arguments object reference in a Call prototype's |arguments| slot.
1178 if (id
== ATOM_TO_JSID(cx
->runtime
->atomState
.argumentsAtom
)) {
1179 if (!js_DefineNativeProperty(cx
, obj
, id
, JSVAL_VOID
,
1180 GetCallArguments
, SetCallArguments
,
1181 JSPROP_PERMANENT
| JSPROP_SHARED
,
1182 0, 0, NULL
, JSDNP_DONT_PURGE
)) {
1189 /* Control flow reaches here only if id was not resolved. */
1194 call_convert(JSContext
*cx
, JSObject
*obj
, JSType type
, jsval
*vp
)
1196 if (type
== JSTYPE_FUNCTION
) {
1197 JSStackFrame
*fp
= (JSStackFrame
*) obj
->getPrivate();
1200 JS_ASSERT(fp
->argv
);
1201 *vp
= fp
->calleeValue();
1208 call_reserveSlots(JSContext
*cx
, JSObject
*obj
)
1212 fun
= js_GetCallObjectFunction(obj
);
1213 return fun
->countArgsAndVars();
1216 JS_FRIEND_DATA(JSClass
) js_CallClass
= {
1218 JSCLASS_HAS_PRIVATE
|
1219 JSCLASS_HAS_RESERVED_SLOTS(CALL_CLASS_FIXED_RESERVED_SLOTS
) |
1220 JSCLASS_NEW_RESOLVE
| JSCLASS_IS_ANONYMOUS
| JSCLASS_MARK_IS_TRACE
,
1221 JS_PropertyStub
, JS_PropertyStub
,
1222 JS_PropertyStub
, JS_PropertyStub
,
1223 call_enumerate
, (JSResolveOp
)call_resolve
,
1228 JS_CLASS_TRACE(args_or_call_trace
), call_reserveSlots
1231 /* Generic function tinyids. */
1233 FUN_ARGUMENTS
= -1, /* predefined arguments local variable */
1234 FUN_LENGTH
= -2, /* number of actual args, arity if inactive */
1235 FUN_ARITY
= -3, /* number of formal parameters; desired argc */
1236 FUN_NAME
= -4, /* function name, "" if anonymous */
1237 FUN_CALLER
= -5 /* Function.prototype.caller, backward compat */
1241 fun_getProperty(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
1246 JSSecurityCallbacks
*callbacks
;
1248 if (!JSVAL_IS_INT(id
))
1250 slot
= JSVAL_TO_INT(id
);
1253 * Loop because getter and setter can be delegated from another class,
1254 * but loop only for FUN_LENGTH because we must pretend that f.length
1255 * is in each function instance f, per ECMA-262, instead of only in the
1256 * Function.prototype object (we use JSPROP_PERMANENT with JSPROP_SHARED
1257 * to make it appear so).
1259 * This code couples tightly to the attributes for the function_props[]
1260 * initializers above, and to js_SetProperty and js_HasOwnProperty.
1262 * It's important to allow delegating objects, even though they inherit
1263 * this getter (fun_getProperty), to override arguments, arity, caller,
1264 * and name. If we didn't return early for slot != FUN_LENGTH, we would
1265 * clobber *vp with the native property value, instead of letting script
1266 * override that value in delegating objects.
1268 * Note how that clobbering is what simulates JSPROP_READONLY for all of
1269 * the non-standard properties when the directly addressed object (obj)
1270 * is a function object (i.e., when this loop does not iterate).
1272 while (!(fun
= (JSFunction
*)
1273 JS_GetInstancePrivate(cx
, obj
, &js_FunctionClass
, NULL
))) {
1274 if (slot
!= FUN_LENGTH
)
1276 obj
= OBJ_GET_PROTO(cx
, obj
);
1281 /* Find fun's top-most activation record. */
1282 for (fp
= js_GetTopStackFrame(cx
);
1283 fp
&& (fp
->fun
!= fun
|| (fp
->flags
& JSFRAME_SPECIAL
));
1290 /* Warn if strict about f.arguments or equivalent unqualified uses. */
1291 if (!JS_ReportErrorFlagsAndNumber(cx
,
1292 JSREPORT_WARNING
| JSREPORT_STRICT
,
1293 js_GetErrorMessage
, NULL
,
1294 JSMSG_DEPRECATED_USAGE
,
1295 js_arguments_str
)) {
1299 if (!js_GetArgsValue(cx
, fp
, vp
))
1308 *vp
= INT_TO_JSVAL((jsint
)fun
->nargs
);
1313 ? ATOM_KEY(fun
->atom
)
1314 : STRING_TO_JSVAL(cx
->runtime
->emptyString
);
1318 if (fp
&& fp
->down
&& fp
->down
->fun
) {
1319 JSFunction
*caller
= fp
->down
->fun
;
1321 * See equivalent condition in args_getProperty for ARGS_CALLEE,
1322 * but here we do not want to throw, since this escape can happen
1323 * via foo.caller alone, without any debugger or indirect eval. And
1324 * it seems foo.caller is still used on the Web.
1326 if (caller
->needsWrapper()) {
1327 JSObject
*wrapper
= WrapEscapingClosure(cx
, fp
->down
, FUN_OBJECT(caller
), caller
);
1330 *vp
= OBJECT_TO_JSVAL(wrapper
);
1334 JS_ASSERT(fp
->down
->argv
);
1335 *vp
= fp
->down
->calleeValue();
1339 if (!JSVAL_IS_PRIMITIVE(*vp
)) {
1340 callbacks
= JS_GetSecurityCallbacks(cx
);
1341 if (callbacks
&& callbacks
->checkObjectAccess
) {
1342 id
= ATOM_KEY(cx
->runtime
->atomState
.callerAtom
);
1343 if (!callbacks
->checkObjectAccess(cx
, obj
, id
, JSACC_READ
, vp
))
1350 /* XXX fun[0] and fun.arguments[0] are equivalent. */
1351 if (fp
&& fp
->fun
&& (uintN
)slot
< fp
->fun
->nargs
)
1352 *vp
= fp
->argv
[slot
];
1360 * ECMA-262 specifies that length is a property of function object instances,
1361 * but we can avoid that space cost by delegating to a prototype property that
1362 * is JSPROP_PERMANENT and JSPROP_SHARED. Each fun_getProperty call computes
1363 * a fresh length value based on the arity of the individual function object's
1366 * The extensions below other than length, i.e., the ones not in ECMA-262,
1367 * are neither JSPROP_READONLY nor JSPROP_SHARED, because for compatibility
1368 * with ECMA we must allow a delegating object to override them. Therefore to
1369 * avoid entraining garbage in Function.prototype slots, they must be resolved
1370 * in non-prototype function objects, wherefore the lazy_function_props table
1371 * and fun_resolve's use of it.
1373 #define LENGTH_PROP_ATTRS (JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED)
1375 static JSPropertySpec function_props
[] = {
1376 {js_length_str
, FUN_LENGTH
, LENGTH_PROP_ATTRS
, fun_getProperty
, JS_PropertyStub
},
1380 typedef struct LazyFunctionProp
{
1386 /* NB: no sentinel at the end -- use JS_ARRAY_LENGTH to bound loops. */
1387 static LazyFunctionProp lazy_function_props
[] = {
1388 {ATOM_OFFSET(arguments
), FUN_ARGUMENTS
, JSPROP_PERMANENT
},
1389 {ATOM_OFFSET(arity
), FUN_ARITY
, JSPROP_PERMANENT
},
1390 {ATOM_OFFSET(caller
), FUN_CALLER
, JSPROP_PERMANENT
},
1391 {ATOM_OFFSET(name
), FUN_NAME
, JSPROP_PERMANENT
},
1395 fun_resolve(JSContext
*cx
, JSObject
*obj
, jsval id
, uintN flags
,
1402 if (!JSVAL_IS_STRING(id
))
1405 fun
= GET_FUNCTION_PRIVATE(cx
, obj
);
1408 * No need to reflect fun.prototype in 'fun.prototype = ... '. Assert that
1409 * fun is not a compiler-created function object, which must never leak to
1410 * script or embedding code and then be mutated.
1412 if (flags
& JSRESOLVE_ASSIGNING
) {
1413 JS_ASSERT(!js_IsInternalFunctionObject(obj
));
1418 * Ok, check whether id is 'prototype' and bootstrap the function object's
1419 * prototype property.
1421 atom
= cx
->runtime
->atomState
.classPrototypeAtom
;
1422 if (id
== ATOM_KEY(atom
)) {
1423 JS_ASSERT(!js_IsInternalFunctionObject(obj
));
1426 * Beware of the wacky case of a user function named Object -- trying
1427 * to find a prototype for that will recur back here _ad perniciem_.
1429 if (fun
->atom
== CLASS_ATOM(cx
, Object
))
1433 * Make the prototype object to have the same parent as the function
1437 js_NewObject(cx
, &js_ObjectClass
, NULL
, OBJ_GET_PARENT(cx
, obj
));
1442 * ECMA (15.3.5.2) says that constructor.prototype is DontDelete for
1443 * user-defined functions, but DontEnum | ReadOnly | DontDelete for
1444 * native "system" constructors such as Object or Function. So lazily
1445 * set the former here in fun_resolve, but eagerly define the latter
1446 * in JS_InitClass, with the right attributes.
1448 if (!js_SetClassPrototype(cx
, obj
, proto
, JSPROP_PERMANENT
))
1455 for (i
= 0; i
< JS_ARRAY_LENGTH(lazy_function_props
); i
++) {
1456 LazyFunctionProp
*lfp
= &lazy_function_props
[i
];
1458 atom
= OFFSET_TO_ATOM(cx
->runtime
, lfp
->atomOffset
);
1459 if (id
== ATOM_KEY(atom
)) {
1460 JS_ASSERT(!js_IsInternalFunctionObject(obj
));
1462 if (!js_DefineNativeProperty(cx
, obj
,
1463 ATOM_TO_JSID(atom
), JSVAL_VOID
,
1464 fun_getProperty
, JS_PropertyStub
,
1465 lfp
->attrs
, SPROP_HAS_SHORTID
,
1466 lfp
->tinyid
, NULL
)) {
1478 fun_convert(JSContext
*cx
, JSObject
*obj
, JSType type
, jsval
*vp
)
1481 case JSTYPE_FUNCTION
:
1482 *vp
= OBJECT_TO_JSVAL(obj
);
1485 return js_TryValueOf(cx
, obj
, type
, vp
);
1491 /* XXX store parent and proto, if defined */
1493 js_XDRFunctionObject(JSXDRState
*xdr
, JSObject
**objp
)
1497 uint32 firstword
; /* flag telling whether fun->atom is non-null,
1498 plus for fun->u.i.skipmin, fun->u.i.wrapper,
1499 and 14 bits reserved for future use */
1500 uintN nargs
, nvars
, nupvars
, n
;
1501 uint32 localsword
; /* word for argument and variable counts */
1502 uint32 flagsword
; /* word for fun->u.i.nupvars and fun->flags */
1503 JSTempValueRooter tvr
;
1507 if (xdr
->mode
== JSXDR_ENCODE
) {
1508 fun
= GET_FUNCTION_PRIVATE(cx
, *objp
);
1509 if (!FUN_INTERPRETED(fun
)) {
1510 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1511 JSMSG_NOT_SCRIPTED_FUNCTION
,
1512 JS_GetFunctionName(fun
));
1515 if (fun
->u
.i
.wrapper
) {
1516 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1517 JSMSG_XDR_CLOSURE_WRAPPER
,
1518 JS_GetFunctionName(fun
));
1521 JS_ASSERT((fun
->u
.i
.wrapper
& ~1U) == 0);
1522 firstword
= (fun
->u
.i
.skipmin
<< 2) | (fun
->u
.i
.wrapper
<< 1) | !!fun
->atom
;
1524 nvars
= fun
->u
.i
.nvars
;
1525 nupvars
= fun
->u
.i
.nupvars
;
1526 localsword
= (nargs
<< 16) | nvars
;
1527 flagsword
= (nupvars
<< 16) | fun
->flags
;
1529 fun
= js_NewFunction(cx
, NULL
, NULL
, 0, JSFUN_INTERPRETED
, NULL
, NULL
);
1532 STOBJ_CLEAR_PARENT(FUN_OBJECT(fun
));
1533 STOBJ_CLEAR_PROTO(FUN_OBJECT(fun
));
1535 nvars
= nargs
= nupvars
= 0; /* quell GCC uninitialized warning */
1539 /* From here on, control flow must flow through label out. */
1540 MUST_FLOW_THROUGH("out");
1541 JS_PUSH_TEMP_ROOT_OBJECT(cx
, FUN_OBJECT(fun
), &tvr
);
1544 if (!JS_XDRUint32(xdr
, &firstword
))
1546 if ((firstword
& 1U) && !js_XDRStringAtom(xdr
, &fun
->atom
))
1548 if (!JS_XDRUint32(xdr
, &localsword
) ||
1549 !JS_XDRUint32(xdr
, &flagsword
)) {
1553 if (xdr
->mode
== JSXDR_DECODE
) {
1554 nargs
= localsword
>> 16;
1555 nvars
= uint16(localsword
);
1556 JS_ASSERT((flagsword
& JSFUN_KINDMASK
) >= JSFUN_INTERPRETED
);
1557 nupvars
= flagsword
>> 16;
1558 fun
->flags
= uint16(flagsword
);
1559 fun
->u
.i
.skipmin
= uint16(firstword
>> 2);
1560 fun
->u
.i
.wrapper
= JSPackedBool((firstword
>> 1) & 1);
1563 /* do arguments and local vars */
1564 n
= nargs
+ nvars
+ nupvars
;
1572 JSLocalKind localKind
;
1574 mark
= JS_ARENA_MARK(&xdr
->cx
->tempPool
);
1577 * From this point the control must flow via the label release_mark.
1579 * To xdr the names we prefix the names with a bitmap descriptor and
1580 * then xdr the names as strings. For argument names (indexes below
1581 * nargs) the corresponding bit in the bitmap is unset when the name
1582 * is null. Such null names are not encoded or decoded. For variable
1583 * names (indexes starting from nargs) bitmap's bit is set when the
1584 * name is declared as const, not as ordinary var.
1586 MUST_FLOW_THROUGH("release_mark");
1587 bitmapLength
= JS_HOWMANY(n
, JS_BITS_PER_UINT32
);
1588 JS_ARENA_ALLOCATE_CAST(bitmap
, uint32
*, &xdr
->cx
->tempPool
,
1589 bitmapLength
* sizeof *bitmap
);
1591 js_ReportOutOfScriptQuota(xdr
->cx
);
1595 if (xdr
->mode
== JSXDR_ENCODE
) {
1596 names
= js_GetLocalNameArray(xdr
->cx
, fun
, &xdr
->cx
->tempPool
);
1601 memset(bitmap
, 0, bitmapLength
* sizeof *bitmap
);
1602 for (i
= 0; i
!= n
; ++i
) {
1604 ? JS_LOCAL_NAME_TO_ATOM(names
[i
]) != NULL
1605 : JS_LOCAL_NAME_IS_CONST(names
[i
])) {
1606 bitmap
[i
>> JS_BITS_PER_UINT32_LOG2
] |=
1607 JS_BIT(i
& (JS_BITS_PER_UINT32
- 1));
1613 names
= NULL
; /* quell GCC uninitialized warning */
1616 for (i
= 0; i
!= bitmapLength
; ++i
) {
1617 ok
= JS_XDRUint32(xdr
, &bitmap
[i
]);
1621 for (i
= 0; i
!= n
; ++i
) {
1623 !(bitmap
[i
>> JS_BITS_PER_UINT32_LOG2
] &
1624 JS_BIT(i
& (JS_BITS_PER_UINT32
- 1)))) {
1625 if (xdr
->mode
== JSXDR_DECODE
) {
1626 ok
= js_AddLocal(xdr
->cx
, fun
, NULL
, JSLOCAL_ARG
);
1630 JS_ASSERT(!JS_LOCAL_NAME_TO_ATOM(names
[i
]));
1634 if (xdr
->mode
== JSXDR_ENCODE
)
1635 name
= JS_LOCAL_NAME_TO_ATOM(names
[i
]);
1636 ok
= js_XDRStringAtom(xdr
, &name
);
1639 if (xdr
->mode
== JSXDR_DECODE
) {
1640 localKind
= (i
< nargs
)
1642 : (i
< nargs
+ nvars
)
1643 ? (bitmap
[i
>> JS_BITS_PER_UINT32_LOG2
] &
1644 JS_BIT(i
& (JS_BITS_PER_UINT32
- 1))
1648 ok
= js_AddLocal(xdr
->cx
, fun
, name
, localKind
);
1656 JS_ARENA_RELEASE(&xdr
->cx
->tempPool
, mark
);
1660 if (xdr
->mode
== JSXDR_DECODE
)
1661 js_FreezeLocalNames(cx
, fun
);
1664 if (!js_XDRScript(xdr
, &fun
->u
.i
.script
, false, NULL
))
1667 if (xdr
->mode
== JSXDR_DECODE
) {
1668 *objp
= FUN_OBJECT(fun
);
1669 if (fun
->u
.i
.script
!= JSScript::emptyScript()) {
1670 #ifdef CHECK_SCRIPT_OWNER
1671 fun
->u
.i
.script
->owner
= NULL
;
1673 js_CallNewScriptHook(cx
, fun
->u
.i
.script
, fun
);
1678 JS_POP_TEMP_ROOT(cx
, &tvr
);
1686 #else /* !JS_HAS_XDR */
1688 #define js_XDRFunctionObject NULL
1690 #endif /* !JS_HAS_XDR */
1693 * [[HasInstance]] internal method for Function objects: fetch the .prototype
1694 * property of its 'this' parameter, and walks the prototype chain of v (only
1695 * if v is an object) returning true if .prototype is found.
1698 fun_hasInstance(JSContext
*cx
, JSObject
*obj
, jsval v
, JSBool
*bp
)
1701 jsid id
= ATOM_TO_JSID(cx
->runtime
->atomState
.classPrototypeAtom
);
1702 if (!obj
->getProperty(cx
, id
, &pval
))
1705 if (JSVAL_IS_PRIMITIVE(pval
)) {
1707 * Throw a runtime error if instanceof is called on a function that
1708 * has a non-object as its .prototype value.
1710 js_ReportValueError(cx
, JSMSG_BAD_PROTOTYPE
,
1711 -1, OBJECT_TO_JSVAL(obj
), NULL
);
1715 return js_IsDelegate(cx
, JSVAL_TO_OBJECT(pval
), v
, bp
);
1719 TraceLocalNames(JSTracer
*trc
, JSFunction
*fun
);
1722 DestroyLocalNames(JSContext
*cx
, JSFunction
*fun
);
1725 fun_trace(JSTracer
*trc
, JSObject
*obj
)
1727 /* A newborn function object may have a not yet initialized private slot. */
1728 JSFunction
*fun
= (JSFunction
*) obj
->getPrivate();
1732 if (FUN_OBJECT(fun
) != obj
) {
1733 /* obj is cloned function object, trace the original. */
1734 JS_CALL_TRACER(trc
, FUN_OBJECT(fun
), JSTRACE_OBJECT
, "private");
1738 JS_CALL_STRING_TRACER(trc
, ATOM_TO_STRING(fun
->atom
), "atom");
1739 if (FUN_INTERPRETED(fun
)) {
1740 if (fun
->u
.i
.script
)
1741 js_TraceScript(trc
, fun
->u
.i
.script
);
1742 TraceLocalNames(trc
, fun
);
1747 fun_finalize(JSContext
*cx
, JSObject
*obj
)
1749 /* Ignore newborn and cloned function objects. */
1750 JSFunction
*fun
= (JSFunction
*) obj
->getPrivate();
1751 if (!fun
|| FUN_OBJECT(fun
) != obj
)
1755 * Null-check of u.i.script is required since the parser sets interpreted
1758 if (FUN_INTERPRETED(fun
)) {
1759 if (fun
->u
.i
.script
)
1760 js_DestroyScript(cx
, fun
->u
.i
.script
);
1761 DestroyLocalNames(cx
, fun
);
1766 JSFunction::sharpSlotBase(JSContext
*cx
)
1768 #if JS_HAS_SHARP_VARS
1769 JSAtom
*name
= js_Atomize(cx
, "#array", 6, 0);
1771 uintN index
= uintN(-1);
1775 js_LookupLocal(cx
, this, name
, &index
);
1776 JS_ASSERT(kind
== JSLOCAL_VAR
);
1784 JSFunction::countInterpretedReservedSlots() const
1786 JS_ASSERT(FUN_INTERPRETED(this));
1788 uint32 nslots
= (u
.i
.nupvars
== 0)
1790 : u
.i
.script
->upvars()->length
;
1791 if (u
.i
.script
->regexpsOffset
!= 0)
1792 nslots
+= u
.i
.script
->regexps()->length
;
1797 fun_reserveSlots(JSContext
*cx
, JSObject
*obj
)
1800 * We use getPrivate and not GET_FUNCTION_PRIVATE because during
1801 * js_InitFunctionClass invocation the function is called before the
1802 * private slot of the function object is set.
1804 JSFunction
*fun
= (JSFunction
*) obj
->getPrivate();
1805 return (fun
&& FUN_INTERPRETED(fun
))
1806 ? fun
->countInterpretedReservedSlots()
1811 * Reserve two slots in all function objects for XPConnect. Note that this
1812 * does not bloat every instance, only those on which reserved slots are set,
1813 * and those on which ad-hoc properties are defined.
1815 JS_FRIEND_DATA(JSClass
) js_FunctionClass
= {
1817 JSCLASS_HAS_PRIVATE
| JSCLASS_NEW_RESOLVE
| JSCLASS_HAS_RESERVED_SLOTS(2) |
1818 JSCLASS_MARK_IS_TRACE
| JSCLASS_HAS_CACHED_PROTO(JSProto_Function
),
1819 JS_PropertyStub
, JS_PropertyStub
,
1820 JS_PropertyStub
, JS_PropertyStub
,
1821 JS_EnumerateStub
, (JSResolveOp
)fun_resolve
,
1822 fun_convert
, fun_finalize
,
1825 js_XDRFunctionObject
, fun_hasInstance
,
1826 JS_CLASS_TRACE(fun_trace
), fun_reserveSlots
1830 fun_toStringHelper(JSContext
*cx
, uint32 indent
, uintN argc
, jsval
*vp
)
1837 fval
= JS_THIS(cx
, vp
);
1838 if (JSVAL_IS_NULL(fval
))
1841 if (!VALUE_IS_FUNCTION(cx
, fval
)) {
1843 * If we don't have a function to start off with, try converting the
1844 * object to a function. If that doesn't work, complain.
1846 if (!JSVAL_IS_PRIMITIVE(fval
)) {
1847 obj
= JSVAL_TO_OBJECT(fval
);
1848 if (!OBJ_GET_CLASS(cx
, obj
)->convert(cx
, obj
, JSTYPE_FUNCTION
,
1854 if (!VALUE_IS_FUNCTION(cx
, fval
)) {
1855 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1856 JSMSG_INCOMPATIBLE_PROTO
,
1857 js_Function_str
, js_toString_str
,
1858 JS_GetTypeName(cx
, JS_TypeOfValue(cx
, fval
)));
1863 obj
= JSVAL_TO_OBJECT(fval
);
1865 indent
= js_ValueToECMAUint32(cx
, &vp
[2]);
1866 if (JSVAL_IS_NULL(vp
[2]))
1870 JS_ASSERT(JS_ObjectIsFunction(cx
, obj
));
1871 fun
= GET_FUNCTION_PRIVATE(cx
, obj
);
1874 str
= JS_DecompileFunction(cx
, fun
, (uintN
)indent
);
1877 *vp
= STRING_TO_JSVAL(str
);
1882 fun_toString(JSContext
*cx
, uintN argc
, jsval
*vp
)
1884 return fun_toStringHelper(cx
, 0, argc
, vp
);
1889 fun_toSource(JSContext
*cx
, uintN argc
, jsval
*vp
)
1891 return fun_toStringHelper(cx
, JS_DONT_PRETTY_PRINT
, argc
, vp
);
1896 js_fun_call(JSContext
*cx
, uintN argc
, jsval
*vp
)
1899 jsval fval
, *argv
, *invokevp
;
1906 obj
= JS_THIS_OBJECT(cx
, vp
);
1907 if (!obj
|| !obj
->defaultValue(cx
, JSTYPE_FUNCTION
, &vp
[1]))
1911 if (!VALUE_IS_FUNCTION(cx
, fval
)) {
1912 str
= JS_ValueToString(cx
, fval
);
1914 const char *bytes
= js_GetStringBytes(cx
, str
);
1917 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1918 JSMSG_INCOMPATIBLE_PROTO
,
1919 js_Function_str
, js_call_str
,
1928 /* Call fun with its global object as the 'this' param if no args. */
1931 /* Otherwise convert the first arg to 'this' and skip over it. */
1932 if (!JSVAL_IS_PRIMITIVE(argv
[0]))
1933 obj
= JSVAL_TO_OBJECT(argv
[0]);
1934 else if (!js_ValueToObject(cx
, argv
[0], &obj
))
1940 /* Allocate stack space for fval, obj, and the args. */
1941 invokevp
= js_AllocStack(cx
, 2 + argc
, &mark
);
1945 /* Push fval, obj, and the args. */
1947 invokevp
[1] = OBJECT_TO_JSVAL(obj
);
1948 memcpy(invokevp
+ 2, argv
, argc
* sizeof *argv
);
1950 ok
= js_Invoke(cx
, argc
, invokevp
, 0);
1952 js_FreeStack(cx
, mark
);
1957 js_fun_apply(JSContext
*cx
, uintN argc
, jsval
*vp
)
1959 JSObject
*obj
, *aobj
;
1960 jsval fval
, *invokevp
, *sp
;
1963 JSBool arraylike
, ok
;
1968 /* Will get globalObject as 'this' and no other arguments. */
1969 return js_fun_call(cx
, argc
, vp
);
1974 obj
= JS_THIS_OBJECT(cx
, vp
);
1975 if (!obj
|| !obj
->defaultValue(cx
, JSTYPE_FUNCTION
, &vp
[1]))
1979 if (!VALUE_IS_FUNCTION(cx
, fval
)) {
1980 str
= JS_ValueToString(cx
, fval
);
1982 const char *bytes
= js_GetStringBytes(cx
, str
);
1985 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1986 JSMSG_INCOMPATIBLE_PROTO
,
1987 js_Function_str
, js_apply_str
,
1994 /* Quell GCC overwarnings. */
1999 /* If the 2nd arg is null or void, call the function with 0 args. */
2000 if (JSVAL_IS_NULL(vp
[3]) || JSVAL_IS_VOID(vp
[3])) {
2003 /* The second arg must be an array (or arguments object). */
2004 arraylike
= JS_FALSE
;
2005 if (!JSVAL_IS_PRIMITIVE(vp
[3])) {
2006 aobj
= JSVAL_TO_OBJECT(vp
[3]);
2007 if (!js_IsArrayLike(cx
, aobj
, &arraylike
, &length
))
2011 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
2012 JSMSG_BAD_APPLY_ARGS
, js_apply_str
);
2018 /* Convert the first arg to 'this' and skip over it. */
2019 if (!JSVAL_IS_PRIMITIVE(vp
[2]))
2020 obj
= JSVAL_TO_OBJECT(vp
[2]);
2021 else if (!js_ValueToObject(cx
, vp
[2], &obj
))
2024 /* Allocate stack space for fval, obj, and the args. */
2025 argc
= (uintN
)JS_MIN(length
, JS_ARGS_LENGTH_MAX
);
2026 invokevp
= js_AllocStack(cx
, 2 + argc
, &mark
);
2030 /* Push fval, obj, and aobj's elements as args. */
2033 *sp
++ = OBJECT_TO_JSVAL(obj
);
2034 for (i
= 0; i
< argc
; i
++) {
2035 ok
= JS_GetElement(cx
, aobj
, (jsint
)i
, sp
);
2041 ok
= js_Invoke(cx
, argc
, invokevp
, 0);
2044 js_FreeStack(cx
, mark
);
2049 static JS_REQUIRES_STACK JSBool
2050 fun_applyConstructor(JSContext
*cx
, uintN argc
, jsval
*vp
)
2055 jsval
*invokevp
, *sp
;
2058 if (JSVAL_IS_PRIMITIVE(vp
[2]) ||
2059 (aobj
= JSVAL_TO_OBJECT(vp
[2]),
2060 OBJ_GET_CLASS(cx
, aobj
) != &js_ArrayClass
&&
2061 OBJ_GET_CLASS(cx
, aobj
) != &js_ArgumentsClass
)) {
2062 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
2063 JSMSG_BAD_APPLY_ARGS
, "__applyConstruct__");
2067 if (!js_GetLengthProperty(cx
, aobj
, &length
))
2070 if (length
> JS_ARGS_LENGTH_MAX
)
2071 length
= JS_ARGS_LENGTH_MAX
;
2072 invokevp
= js_AllocStack(cx
, 2 + length
, &mark
);
2078 *sp
++ = JSVAL_NULL
; /* this is filled automagically */
2079 for (i
= 0; i
< length
; i
++) {
2080 ok
= JS_GetElement(cx
, aobj
, (jsint
)i
, sp
);
2086 ok
= js_InvokeConstructor(cx
, length
, JS_TRUE
, invokevp
);
2089 js_FreeStack(cx
, mark
);
2094 static JSFunctionSpec function_methods
[] = {
2096 JS_FN(js_toSource_str
, fun_toSource
, 0,0),
2098 JS_FN(js_toString_str
, fun_toString
, 0,0),
2099 JS_FN(js_apply_str
, js_fun_apply
, 2,0),
2100 JS_FN(js_call_str
, js_fun_call
, 1,0),
2102 JS_FN("__applyConstructor__", fun_applyConstructor
, 1,0),
2108 Function(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
2112 JSStackFrame
*fp
, *caller
;
2115 const char *filename
;
2117 JSString
*str
, *arg
;
2118 JSTokenStream
ts(cx
);
2119 JSPrincipals
*principals
;
2120 jschar
*collected_args
, *cp
;
2122 size_t arg_length
, args_length
, old_args_length
;
2125 if (!JS_IsConstructing(cx
)) {
2126 obj
= js_NewObject(cx
, &js_FunctionClass
, NULL
, NULL
);
2129 *rval
= OBJECT_TO_JSVAL(obj
);
2132 * The constructor is called before the private slot is initialized so
2133 * we must use getPrivate, not GET_FUNCTION_PRIVATE here.
2135 if (obj
->getPrivate())
2140 * NB: (new Function) is not lexically closed by its caller, it's just an
2141 * anonymous function in the top-level scope that its constructor inhabits.
2142 * Thus 'var x = 42; f = new Function("return x"); print(f())' prints 42,
2143 * and so would a call to f from another top-level's script or function.
2145 * In older versions, before call objects, a new Function was adopted by
2146 * its running context's globalObject, which might be different from the
2147 * top-level reachable from scopeChain (in HTML frames, e.g.).
2149 parent
= OBJ_GET_PARENT(cx
, JSVAL_TO_OBJECT(argv
[-2]));
2151 fun
= js_NewFunction(cx
, obj
, NULL
, 0, JSFUN_LAMBDA
| JSFUN_INTERPRETED
,
2152 parent
, cx
->runtime
->atomState
.anonymousAtom
);
2158 * Function is static and not called directly by other functions in this
2159 * file, therefore it is callable only as a native function by js_Invoke.
2160 * Find the scripted caller, possibly skipping other native frames such as
2161 * are built for Function.prototype.call or .apply activations that invoke
2162 * Function indirectly from a script.
2164 fp
= js_GetTopStackFrame(cx
);
2165 JS_ASSERT(!fp
->script
&& fp
->fun
&& fp
->fun
->u
.n
.native
== Function
);
2166 caller
= js_GetScriptedCaller(cx
, fp
);
2168 principals
= JS_EvalFramePrincipals(cx
, fp
, caller
);
2169 filename
= js_ComputeFilename(cx
, caller
, principals
, &lineno
);
2176 /* Belt-and-braces: check that the caller has access to parent. */
2177 if (!js_CheckPrincipalsAccess(cx
, parent
, principals
,
2178 CLASS_ATOM(cx
, Function
))) {
2182 n
= argc
? argc
- 1 : 0;
2184 enum { OK
, BAD
, BAD_FORMAL
} state
;
2187 * Collect the function-argument arguments into one string, separated
2188 * by commas, then make a tokenstream from that string, and scan it to
2189 * get the arguments. We need to throw the full scanner at the
2190 * problem, because the argument string can legitimately contain
2191 * comments and linefeeds. XXX It might be better to concatenate
2192 * everything up into a function definition and pass it to the
2193 * compiler, but doing it this way is less of a delta from the old
2194 * code. See ECMA 15.3.2.1.
2198 for (i
= 0; i
< n
; i
++) {
2199 /* Collect the lengths for all the function-argument arguments. */
2200 arg
= js_ValueToString(cx
, argv
[i
]);
2203 argv
[i
] = STRING_TO_JSVAL(arg
);
2206 * Check for overflow. The < test works because the maximum
2207 * JSString length fits in 2 fewer bits than size_t has.
2209 old_args_length
= args_length
;
2210 args_length
= old_args_length
+ arg
->length();
2211 if (args_length
< old_args_length
) {
2212 js_ReportAllocationOverflow(cx
);
2217 /* Add 1 for each joining comma and check for overflow (two ways). */
2218 old_args_length
= args_length
;
2219 args_length
= old_args_length
+ n
- 1;
2220 if (args_length
< old_args_length
||
2221 args_length
>= ~(size_t)0 / sizeof(jschar
)) {
2222 js_ReportAllocationOverflow(cx
);
2227 * Allocate a string to hold the concatenated arguments, including room
2228 * for a terminating 0. Mark cx->tempPool for later release, to free
2229 * collected_args and its tokenstream in one swoop.
2231 mark
= JS_ARENA_MARK(&cx
->tempPool
);
2232 JS_ARENA_ALLOCATE_CAST(cp
, jschar
*, &cx
->tempPool
,
2233 (args_length
+1) * sizeof(jschar
));
2235 js_ReportOutOfScriptQuota(cx
);
2238 collected_args
= cp
;
2241 * Concatenate the arguments into the new string, separated by commas.
2243 for (i
= 0; i
< n
; i
++) {
2244 arg
= JSVAL_TO_STRING(argv
[i
]);
2245 arg_length
= arg
->length();
2246 (void) js_strncpy(cp
, arg
->chars(), arg_length
);
2249 /* Add separating comma or terminating 0. */
2250 *cp
++ = (i
+ 1 < n
) ? ',' : 0;
2253 /* Initialize a tokenstream that reads from the given string. */
2254 if (!ts
.init(cx
, collected_args
, args_length
, NULL
, filename
, lineno
)) {
2255 JS_ARENA_RELEASE(&cx
->tempPool
, mark
);
2259 /* The argument string may be empty or contain no tokens. */
2260 tt
= js_GetToken(cx
, &ts
);
2261 if (tt
!= TOK_EOF
) {
2264 * Check that it's a name. This also implicitly guards against
2265 * TOK_ERROR, which was already reported.
2271 * Get the atom corresponding to the name from the token
2272 * stream; we're assured at this point that it's a valid
2275 atom
= CURRENT_TOKEN(&ts
).t_atom
;
2277 /* Check for a duplicate parameter name. */
2278 if (js_LookupLocal(cx
, fun
, atom
, NULL
) != JSLOCAL_NONE
) {
2281 name
= js_AtomToPrintableString(cx
, atom
);
2283 js_ReportCompileErrorNumber(cx
, &ts
, NULL
,
2286 JSMSG_DUPLICATE_FORMAL
,
2291 if (!js_AddLocal(cx
, fun
, atom
, JSLOCAL_ARG
))
2295 * Get the next token. Stop on end of stream. Otherwise
2296 * insist on a comma, get another name, and iterate.
2298 tt
= js_GetToken(cx
, &ts
);
2301 if (tt
!= TOK_COMMA
)
2303 tt
= js_GetToken(cx
, &ts
);
2309 if (state
== BAD_FORMAL
&& !(ts
.flags
& TSF_ERROR
)) {
2311 * Report "malformed formal parameter" iff no illegal char or
2312 * similar scanner error was already reported.
2314 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
2318 JS_ARENA_RELEASE(&cx
->tempPool
, mark
);
2324 str
= js_ValueToString(cx
, argv
[argc
-1]);
2327 argv
[argc
-1] = STRING_TO_JSVAL(str
);
2329 str
= cx
->runtime
->emptyString
;
2332 return JSCompiler::compileFunctionBody(cx
, fun
, principals
,
2333 str
->chars(), str
->length(),
2338 js_InitFunctionClass(JSContext
*cx
, JSObject
*obj
)
2343 proto
= JS_InitClass(cx
, obj
, NULL
, &js_FunctionClass
, Function
, 1,
2344 function_props
, function_methods
, NULL
, NULL
);
2347 fun
= js_NewFunction(cx
, proto
, NULL
, 0, JSFUN_INTERPRETED
, obj
, NULL
);
2350 fun
->u
.i
.script
= JSScript::emptyScript();
2355 js_NewFunction(JSContext
*cx
, JSObject
*funobj
, JSNative native
, uintN nargs
,
2356 uintN flags
, JSObject
*parent
, JSAtom
*atom
)
2361 JS_ASSERT(HAS_FUNCTION_CLASS(funobj
));
2362 OBJ_SET_PARENT(cx
, funobj
, parent
);
2364 funobj
= js_NewObject(cx
, &js_FunctionClass
, NULL
, parent
);
2368 JS_ASSERT(!funobj
->getPrivate());
2369 fun
= (JSFunction
*) funobj
;
2371 /* Initialize all function members. */
2373 fun
->flags
= flags
& (JSFUN_FLAGS_MASK
| JSFUN_KINDMASK
| JSFUN_TRCINFO
);
2374 if ((flags
& JSFUN_KINDMASK
) >= JSFUN_INTERPRETED
) {
2376 JS_ASSERT(nargs
== 0);
2378 fun
->u
.i
.nupvars
= 0;
2379 fun
->u
.i
.skipmin
= 0;
2380 fun
->u
.i
.wrapper
= false;
2381 fun
->u
.i
.script
= NULL
;
2383 fun
->u
.i
.names
.taggedAtom
= 0;
2388 fun
->u
.n
.clasp
= NULL
;
2389 if (flags
& JSFUN_TRCINFO
) {
2391 JSNativeTraceInfo
*trcinfo
=
2392 JS_FUNC_TO_DATA_PTR(JSNativeTraceInfo
*, native
);
2393 fun
->u
.n
.native
= (JSNative
) trcinfo
->native
;
2394 fun
->u
.n
.trcinfo
= trcinfo
;
2396 fun
->u
.n
.trcinfo
= NULL
;
2399 fun
->u
.n
.native
= native
;
2400 fun
->u
.n
.trcinfo
= NULL
;
2402 JS_ASSERT(fun
->u
.n
.native
);
2406 /* Set private to self to indicate non-cloned fully initialized function. */
2407 FUN_OBJECT(fun
)->setPrivate(fun
);
2412 js_CloneFunctionObject(JSContext
*cx
, JSFunction
*fun
, JSObject
*parent
)
2415 * The cloned function object does not need the extra JSFunction members
2416 * beyond JSObject as it points to fun via the private slot.
2418 JSObject
*clone
= js_NewObject(cx
, &js_FunctionClass
, NULL
, parent
, sizeof(JSObject
));
2421 clone
->setPrivate(fun
);
2426 * Create a new flat closure, but don't initialize the imported upvar
2427 * values. The tracer calls this function and then initializes the upvar
2430 JSObject
* JS_FASTCALL
2431 js_AllocFlatClosure(JSContext
*cx
, JSFunction
*fun
, JSObject
*scopeChain
)
2433 JS_ASSERT(FUN_FLAT_CLOSURE(fun
));
2434 JS_ASSERT((fun
->u
.i
.script
->upvarsOffset
2435 ? fun
->u
.i
.script
->upvars()->length
2436 : 0) == fun
->u
.i
.nupvars
);
2438 JSObject
*closure
= js_CloneFunctionObject(cx
, fun
, scopeChain
);
2442 uint32 nslots
= fun
->countInterpretedReservedSlots();
2445 if (!js_EnsureReservedSlots(cx
, closure
, nslots
))
2451 JS_DEFINE_CALLINFO_3(extern, OBJECT
, js_AllocFlatClosure
,
2452 CONTEXT
, FUNCTION
, OBJECT
, 0, 0)
2455 js_NewFlatClosure(JSContext
*cx
, JSFunction
*fun
)
2457 JSObject
*closure
= js_AllocFlatClosure(cx
, fun
, cx
->fp
->scopeChain
);
2458 if (!closure
|| fun
->u
.i
.nupvars
== 0)
2461 JSUpvarArray
*uva
= fun
->u
.i
.script
->upvars();
2462 JS_ASSERT(uva
->length
<= size_t(closure
->dslots
[-1]));
2464 uintN level
= fun
->u
.i
.script
->staticLevel
;
2465 for (uint32 i
= 0, n
= uva
->length
; i
< n
; i
++)
2466 closure
->dslots
[i
] = js_GetUpvar(cx
, level
, uva
->vector
[i
]);
2472 js_NewDebuggableFlatClosure(JSContext
*cx
, JSFunction
*fun
)
2474 JS_ASSERT(cx
->fp
->fun
->flags
& JSFUN_HEAVYWEIGHT
);
2475 JS_ASSERT(!cx
->fp
->fun
->optimizedClosure());
2477 return WrapEscapingClosure(cx
, cx
->fp
, FUN_OBJECT(fun
), fun
);
2481 js_DefineFunction(JSContext
*cx
, JSObject
*obj
, JSAtom
*atom
, JSNative native
,
2482 uintN nargs
, uintN attrs
)
2487 if (attrs
& JSFUN_STUB_GSOPS
) {
2489 * JSFUN_STUB_GSOPS is a request flag only, not stored in fun->flags or
2490 * the defined property's attributes. This allows us to encode another,
2491 * internal flag using the same bit, JSFUN_EXPR_CLOSURE -- see jsfun.h
2494 attrs
&= ~JSFUN_STUB_GSOPS
;
2495 gsop
= JS_PropertyStub
;
2499 fun
= js_NewFunction(cx
, NULL
, native
, nargs
, attrs
, obj
, atom
);
2502 if (!obj
->defineProperty(cx
, ATOM_TO_JSID(atom
), OBJECT_TO_JSVAL(FUN_OBJECT(fun
)),
2503 gsop
, gsop
, attrs
& ~JSFUN_FLAGS_MASK
)) {
2509 #if (JSV2F_CONSTRUCT & JSV2F_SEARCH_STACK)
2510 # error "JSINVOKE_CONSTRUCT and JSV2F_SEARCH_STACK are not disjoint!"
2514 js_ValueToFunction(JSContext
*cx
, jsval
*vp
, uintN flags
)
2521 if (JSVAL_IS_OBJECT(v
)) {
2522 obj
= JSVAL_TO_OBJECT(v
);
2523 if (obj
&& OBJ_GET_CLASS(cx
, obj
) != &js_FunctionClass
) {
2524 if (!obj
->defaultValue(cx
, JSTYPE_FUNCTION
, &v
))
2526 obj
= VALUE_IS_FUNCTION(cx
, v
) ? JSVAL_TO_OBJECT(v
) : NULL
;
2530 js_ReportIsNotFunction(cx
, vp
, flags
);
2533 return GET_FUNCTION_PRIVATE(cx
, obj
);
2537 js_ValueToFunctionObject(JSContext
*cx
, jsval
*vp
, uintN flags
)
2540 JSStackFrame
*caller
;
2541 JSPrincipals
*principals
;
2543 if (VALUE_IS_FUNCTION(cx
, *vp
))
2544 return JSVAL_TO_OBJECT(*vp
);
2546 fun
= js_ValueToFunction(cx
, vp
, flags
);
2549 *vp
= OBJECT_TO_JSVAL(FUN_OBJECT(fun
));
2551 caller
= js_GetScriptedCaller(cx
, NULL
);
2553 principals
= JS_StackFramePrincipals(cx
, caller
);
2555 /* No scripted caller, don't allow access. */
2559 if (!js_CheckPrincipalsAccess(cx
, FUN_OBJECT(fun
), principals
,
2562 : cx
->runtime
->atomState
.anonymousAtom
)) {
2565 return FUN_OBJECT(fun
);
2569 js_ValueToCallableObject(JSContext
*cx
, jsval
*vp
, uintN flags
)
2571 JSObject
*callable
= JSVAL_IS_OBJECT(*vp
) ? JSVAL_TO_OBJECT(*vp
) : NULL
;
2573 if (callable
&& js_IsCallable(callable
, cx
)) {
2574 *vp
= OBJECT_TO_JSVAL(callable
);
2577 return js_ValueToFunctionObject(cx
, vp
, flags
);
2581 js_ReportIsNotFunction(JSContext
*cx
, jsval
*vp
, uintN flags
)
2585 const char *name
, *source
;
2586 JSTempValueRooter tvr
;
2588 for (fp
= js_GetTopStackFrame(cx
); fp
&& !fp
->regs
; fp
= fp
->down
)
2590 name
= source
= NULL
;
2591 JS_PUSH_TEMP_ROOT_STRING(cx
, NULL
, &tvr
);
2592 if (flags
& JSV2F_ITERATOR
) {
2593 error
= JSMSG_BAD_ITERATOR
;
2594 name
= js_iterator_str
;
2595 JSString
*src
= js_ValueToSource(cx
, *vp
);
2598 tvr
.u
.value
= STRING_TO_JSVAL(src
);
2599 JSString
*qsrc
= js_QuoteString(cx
, src
, 0);
2602 tvr
.u
.value
= STRING_TO_JSVAL(qsrc
);
2603 source
= js_GetStringBytes(cx
, qsrc
);
2606 } else if (flags
& JSV2F_CONSTRUCT
) {
2607 error
= JSMSG_NOT_CONSTRUCTOR
;
2609 error
= JSMSG_NOT_FUNCTION
;
2612 js_ReportValueError3(cx
, error
,
2614 StackBase(fp
) <= vp
&& vp
< fp
->regs
->sp
)
2616 : (flags
& JSV2F_SEARCH_STACK
)
2617 ? JSDVG_SEARCH_STACK
2618 : JSDVG_IGNORE_STACK
,
2623 JS_POP_TEMP_ROOT(cx
, &tvr
);
2627 * When a function has between 2 and MAX_ARRAY_LOCALS arguments and variables,
2628 * their name are stored as the JSLocalNames.array.
2630 #define MAX_ARRAY_LOCALS 8
2632 JS_STATIC_ASSERT(2 <= MAX_ARRAY_LOCALS
);
2633 JS_STATIC_ASSERT(MAX_ARRAY_LOCALS
< JS_BITMASK(16));
2636 * We use the lowest bit of the string atom to distinguish const from var
2637 * name when there is only single name or when names are stored as an array.
2639 JS_STATIC_ASSERT((JSVAL_STRING
& 1) == 0);
2642 * When we use a hash table to store the local names, we use a singly linked
2643 * list to record the indexes of duplicated parameter names to preserve the
2644 * duplicates for the decompiler.
2646 typedef struct JSNameIndexPair JSNameIndexPair
;
2648 struct JSNameIndexPair
{
2651 JSNameIndexPair
*link
;
2654 struct JSLocalNameMap
{
2656 JSNameIndexPair
*lastdup
;
2659 typedef struct JSLocalNameHashEntry
{
2660 JSDHashEntryHdr hdr
;
2664 } JSLocalNameHashEntry
;
2667 FreeLocalNameHash(JSContext
*cx
, JSLocalNameMap
*map
)
2669 JSNameIndexPair
*dup
, *next
;
2671 for (dup
= map
->lastdup
; dup
; dup
= next
) {
2675 JS_DHashTableFinish(&map
->names
);
2680 HashLocalName(JSContext
*cx
, JSLocalNameMap
*map
, JSAtom
*name
,
2681 JSLocalKind localKind
, uintN index
)
2683 JSLocalNameHashEntry
*entry
;
2684 JSNameIndexPair
*dup
;
2686 JS_ASSERT(index
<= JS_BITMASK(16));
2687 #if JS_HAS_DESTRUCTURING
2689 /* A destructuring pattern does not need a hash entry. */
2690 JS_ASSERT(localKind
== JSLOCAL_ARG
);
2694 JS_ASSERT(ATOM_IS_STRING(name
));
2695 entry
= (JSLocalNameHashEntry
*)
2696 JS_DHashTableOperate(&map
->names
, name
, JS_DHASH_ADD
);
2698 JS_ReportOutOfMemory(cx
);
2702 JS_ASSERT(entry
->name
== name
);
2703 JS_ASSERT(entry
->localKind
== JSLOCAL_ARG
&& localKind
== JSLOCAL_ARG
);
2704 dup
= (JSNameIndexPair
*) cx
->malloc(sizeof *dup
);
2707 dup
->name
= entry
->name
;
2708 dup
->index
= entry
->index
;
2709 dup
->link
= map
->lastdup
;
2713 entry
->index
= (uint16
) index
;
2714 entry
->localKind
= (uint8
) localKind
;
2719 js_AddLocal(JSContext
*cx
, JSFunction
*fun
, JSAtom
*atom
, JSLocalKind kind
)
2725 JSLocalNameMap
*map
;
2727 JS_ASSERT(FUN_INTERPRETED(fun
));
2728 JS_ASSERT(!fun
->u
.i
.script
);
2729 JS_ASSERT(((jsuword
) atom
& 1) == 0);
2730 taggedAtom
= (jsuword
) atom
;
2731 if (kind
== JSLOCAL_ARG
) {
2732 indexp
= &fun
->nargs
;
2733 } else if (kind
== JSLOCAL_UPVAR
) {
2734 indexp
= &fun
->u
.i
.nupvars
;
2736 indexp
= &fun
->u
.i
.nvars
;
2737 if (kind
== JSLOCAL_CONST
)
2740 JS_ASSERT(kind
== JSLOCAL_VAR
);
2742 n
= fun
->countLocalNames();
2744 JS_ASSERT(fun
->u
.i
.names
.taggedAtom
== 0);
2745 fun
->u
.i
.names
.taggedAtom
= taggedAtom
;
2746 } else if (n
< MAX_ARRAY_LOCALS
) {
2748 array
= fun
->u
.i
.names
.array
;
2750 array
= (jsuword
*) cx
->malloc(MAX_ARRAY_LOCALS
* sizeof *array
);
2753 array
[0] = fun
->u
.i
.names
.taggedAtom
;
2754 fun
->u
.i
.names
.array
= array
;
2756 if (kind
== JSLOCAL_ARG
) {
2758 * A destructuring argument pattern adds variables, not arguments,
2759 * so for the following arguments nvars != 0.
2761 #if JS_HAS_DESTRUCTURING
2762 if (fun
->u
.i
.nvars
!= 0) {
2763 memmove(array
+ fun
->nargs
+ 1, array
+ fun
->nargs
,
2764 fun
->u
.i
.nvars
* sizeof *array
);
2767 JS_ASSERT(fun
->u
.i
.nvars
== 0);
2769 array
[fun
->nargs
] = taggedAtom
;
2771 array
[n
] = taggedAtom
;
2773 } else if (n
== MAX_ARRAY_LOCALS
) {
2774 array
= fun
->u
.i
.names
.array
;
2775 map
= (JSLocalNameMap
*) cx
->malloc(sizeof *map
);
2778 if (!JS_DHashTableInit(&map
->names
, JS_DHashGetStubOps(),
2779 NULL
, sizeof(JSLocalNameHashEntry
),
2780 JS_DHASH_DEFAULT_CAPACITY(MAX_ARRAY_LOCALS
2782 JS_ReportOutOfMemory(cx
);
2787 map
->lastdup
= NULL
;
2788 for (i
= 0; i
!= MAX_ARRAY_LOCALS
; ++i
) {
2789 taggedAtom
= array
[i
];
2791 JSLocalKind k
= JSLOCAL_ARG
;
2792 if (j
>= fun
->nargs
) {
2794 if (j
< fun
->u
.i
.nvars
) {
2795 k
= (taggedAtom
& 1) ? JSLOCAL_CONST
: JSLOCAL_VAR
;
2797 j
-= fun
->u
.i
.nvars
;
2801 if (!HashLocalName(cx
, map
, (JSAtom
*) (taggedAtom
& ~1), k
, j
)) {
2802 FreeLocalNameHash(cx
, map
);
2806 if (!HashLocalName(cx
, map
, atom
, kind
, *indexp
)) {
2807 FreeLocalNameHash(cx
, map
);
2812 * At this point the entry is added and we cannot fail. It is time
2813 * to replace fun->u.i.names with the built map.
2815 fun
->u
.i
.names
.map
= map
;
2818 if (*indexp
== JS_BITMASK(16)) {
2819 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
2820 (kind
== JSLOCAL_ARG
)
2821 ? JSMSG_TOO_MANY_FUN_ARGS
2822 : JSMSG_TOO_MANY_LOCALS
);
2825 if (!HashLocalName(cx
, fun
->u
.i
.names
.map
, atom
, kind
, *indexp
))
2829 /* Update the argument or variable counter. */
2835 js_LookupLocal(JSContext
*cx
, JSFunction
*fun
, JSAtom
*atom
, uintN
*indexp
)
2837 uintN n
, i
, upvar_base
;
2839 JSLocalNameHashEntry
*entry
;
2841 JS_ASSERT(FUN_INTERPRETED(fun
));
2842 n
= fun
->countLocalNames();
2844 return JSLOCAL_NONE
;
2845 if (n
<= MAX_ARRAY_LOCALS
) {
2846 array
= (n
== 1) ? &fun
->u
.i
.names
.taggedAtom
: fun
->u
.i
.names
.array
;
2848 /* Search from the tail to pick up the last duplicated name. */
2850 upvar_base
= fun
->countArgsAndVars();
2853 if (atom
== JS_LOCAL_NAME_TO_ATOM(array
[i
])) {
2854 if (i
< fun
->nargs
) {
2859 if (i
>= upvar_base
) {
2861 *indexp
= i
- upvar_base
;
2862 return JSLOCAL_UPVAR
;
2865 *indexp
= i
- fun
->nargs
;
2866 return JS_LOCAL_NAME_IS_CONST(array
[i
])
2872 entry
= (JSLocalNameHashEntry
*)
2873 JS_DHashTableOperate(&fun
->u
.i
.names
.map
->names
, atom
,
2875 if (JS_DHASH_ENTRY_IS_BUSY(&entry
->hdr
)) {
2876 JS_ASSERT(entry
->localKind
!= JSLOCAL_NONE
);
2878 *indexp
= entry
->index
;
2879 return (JSLocalKind
) entry
->localKind
;
2882 return JSLOCAL_NONE
;
2885 typedef struct JSLocalNameEnumeratorArgs
{
2892 } JSLocalNameEnumeratorArgs
;
2894 static JSDHashOperator
2895 get_local_names_enumerator(JSDHashTable
*table
, JSDHashEntryHdr
*hdr
,
2896 uint32 number
, void *arg
)
2898 JSLocalNameHashEntry
*entry
;
2899 JSLocalNameEnumeratorArgs
*args
;
2903 entry
= (JSLocalNameHashEntry
*) hdr
;
2904 args
= (JSLocalNameEnumeratorArgs
*) arg
;
2905 JS_ASSERT(entry
->name
);
2906 if (entry
->localKind
== JSLOCAL_ARG
) {
2907 JS_ASSERT(entry
->index
< args
->fun
->nargs
);
2908 JS_ASSERT(args
->nCopiedArgs
++ < args
->fun
->nargs
);
2912 JS_ASSERT(entry
->localKind
== JSLOCAL_VAR
||
2913 entry
->localKind
== JSLOCAL_CONST
||
2914 entry
->localKind
== JSLOCAL_UPVAR
);
2915 JS_ASSERT(entry
->index
< args
->fun
->u
.i
.nvars
+ args
->fun
->u
.i
.nupvars
);
2916 JS_ASSERT(args
->nCopiedVars
++ < unsigned(args
->fun
->u
.i
.nvars
+ args
->fun
->u
.i
.nupvars
));
2917 i
= args
->fun
->nargs
;
2918 if (entry
->localKind
== JSLOCAL_UPVAR
)
2919 i
+= args
->fun
->u
.i
.nvars
;
2921 constFlag
= (entry
->localKind
== JSLOCAL_CONST
);
2923 args
->names
[i
] = (jsuword
) entry
->name
| constFlag
;
2924 return JS_DHASH_NEXT
;
2927 JS_FRIEND_API(jsuword
*)
2928 js_GetLocalNameArray(JSContext
*cx
, JSFunction
*fun
, JSArenaPool
*pool
)
2932 JSLocalNameMap
*map
;
2933 JSLocalNameEnumeratorArgs args
;
2934 JSNameIndexPair
*dup
;
2936 JS_ASSERT(fun
->hasLocalNames());
2937 n
= fun
->countLocalNames();
2939 if (n
<= MAX_ARRAY_LOCALS
)
2940 return (n
== 1) ? &fun
->u
.i
.names
.taggedAtom
: fun
->u
.i
.names
.array
;
2943 * No need to check for overflow of the allocation size as we are making a
2944 * copy of already allocated data. As such it must fit size_t.
2946 JS_ARENA_ALLOCATE_CAST(names
, jsuword
*, pool
, (size_t) n
* sizeof *names
);
2948 js_ReportOutOfScriptQuota(cx
);
2952 #if JS_HAS_DESTRUCTURING
2953 /* Some parameter names can be NULL due to destructuring patterns. */
2954 memset(names
, 0, fun
->nargs
* sizeof *names
);
2956 map
= fun
->u
.i
.names
.map
;
2960 args
.nCopiedArgs
= 0;
2961 args
.nCopiedVars
= 0;
2963 JS_DHashTableEnumerate(&map
->names
, get_local_names_enumerator
, &args
);
2964 for (dup
= map
->lastdup
; dup
; dup
= dup
->link
) {
2965 JS_ASSERT(dup
->index
< fun
->nargs
);
2966 JS_ASSERT(args
.nCopiedArgs
++ < fun
->nargs
);
2967 names
[dup
->index
] = (jsuword
) dup
->name
;
2969 #if !JS_HAS_DESTRUCTURING
2970 JS_ASSERT(args
.nCopiedArgs
== fun
->nargs
);
2972 JS_ASSERT(args
.nCopiedVars
== fun
->u
.i
.nvars
+ fun
->u
.i
.nupvars
);
2977 static JSDHashOperator
2978 trace_local_names_enumerator(JSDHashTable
*table
, JSDHashEntryHdr
*hdr
,
2979 uint32 number
, void *arg
)
2981 JSLocalNameHashEntry
*entry
;
2984 entry
= (JSLocalNameHashEntry
*) hdr
;
2985 JS_ASSERT(entry
->name
);
2986 trc
= (JSTracer
*) arg
;
2987 JS_SET_TRACING_INDEX(trc
,
2988 entry
->localKind
== JSLOCAL_ARG
? "arg" : "var",
2990 JS_CallTracer(trc
, ATOM_TO_STRING(entry
->name
), JSTRACE_STRING
);
2991 return JS_DHASH_NEXT
;
2995 TraceLocalNames(JSTracer
*trc
, JSFunction
*fun
)
3001 JS_ASSERT(FUN_INTERPRETED(fun
));
3002 n
= fun
->countLocalNames();
3005 if (n
<= MAX_ARRAY_LOCALS
) {
3006 array
= (n
== 1) ? &fun
->u
.i
.names
.taggedAtom
: fun
->u
.i
.names
.array
;
3010 atom
= (JSAtom
*) (array
[i
] & ~1);
3012 JS_SET_TRACING_INDEX(trc
,
3013 i
< fun
->nargs
? "arg" : "var",
3014 i
< fun
->nargs
? i
: i
- fun
->nargs
);
3015 JS_CallTracer(trc
, ATOM_TO_STRING(atom
), JSTRACE_STRING
);
3019 JS_DHashTableEnumerate(&fun
->u
.i
.names
.map
->names
,
3020 trace_local_names_enumerator
, trc
);
3023 * No need to trace the list of duplicates in map->lastdup as the
3024 * names there are traced when enumerating the hash table.
3030 DestroyLocalNames(JSContext
*cx
, JSFunction
*fun
)
3034 n
= fun
->countLocalNames();
3037 if (n
<= MAX_ARRAY_LOCALS
)
3038 cx
->free(fun
->u
.i
.names
.array
);
3040 FreeLocalNameHash(cx
, fun
->u
.i
.names
.map
);
3044 js_FreezeLocalNames(JSContext
*cx
, JSFunction
*fun
)
3049 JS_ASSERT(FUN_INTERPRETED(fun
));
3050 JS_ASSERT(!fun
->u
.i
.script
);
3051 n
= fun
->nargs
+ fun
->u
.i
.nvars
+ fun
->u
.i
.nupvars
;
3052 if (2 <= n
&& n
< MAX_ARRAY_LOCALS
) {
3053 /* Shrink over-allocated array ignoring realloc failures. */
3054 array
= (jsuword
*) cx
->realloc(fun
->u
.i
.names
.array
,
3057 fun
->u
.i
.names
.array
= array
;
3060 if (n
> MAX_ARRAY_LOCALS
)
3061 JS_DHashMarkTableImmutable(&fun
->u
.i
.names
.map
->names
);
3066 JSFunction::findDuplicateFormal() const
3071 /* Function with two to MAX_ARRAY_LOCALS parameters use an aray. */
3072 unsigned n
= nargs
+ u
.i
.nvars
+ u
.i
.nupvars
;
3073 if (n
<= MAX_ARRAY_LOCALS
) {
3074 jsuword
*array
= u
.i
.names
.array
;
3076 /* Quadratic, but MAX_ARRAY_LOCALS is 8, so at most 28 comparisons. */
3077 for (unsigned i
= 0; i
< nargs
; i
++) {
3078 for (unsigned j
= i
+ 1; j
< nargs
; j
++) {
3079 if (array
[i
] == array
[j
])
3080 return JS_LOCAL_NAME_TO_ATOM(array
[i
]);
3087 * Functions with more than MAX_ARRAY_LOCALS parameters use a hash
3088 * table. Hashed local name maps have already made a list of any
3089 * duplicate argument names for us.
3091 JSNameIndexPair
*dup
= u
.i
.names
.map
->lastdup
;
3092 return dup
? dup
->name
: NULL
;