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"
72 #include "jsstaticcheck.h"
80 # include "jsxdrapi.h"
83 #include "jsatominlines.h"
84 #include "jscntxtinlines.h"
85 #include "jsobjinlines.h"
90 SetArgsPrivateNative(JSObject
*argsobj
, ArgsPrivateNative
*apn
)
92 JS_ASSERT(argsobj
->isArguments());
93 uintptr_t p
= (uintptr_t) apn
;
94 argsobj
->setPrivate((void*) (p
| 2));
98 js_GetArgsValue(JSContext
*cx
, JSStackFrame
*fp
, jsval
*vp
)
102 if (fp
->flags
& JSFRAME_OVERRIDE_ARGS
) {
103 JS_ASSERT(fp
->callobj
);
104 jsid id
= ATOM_TO_JSID(cx
->runtime
->atomState
.argumentsAtom
);
105 return fp
->callobj
->getProperty(cx
, id
, vp
);
107 argsobj
= js_GetArgsObject(cx
, fp
);
110 *vp
= OBJECT_TO_JSVAL(argsobj
);
115 js_GetArgsProperty(JSContext
*cx
, JSStackFrame
*fp
, jsid id
, jsval
*vp
)
117 if (fp
->flags
& JSFRAME_OVERRIDE_ARGS
) {
118 JS_ASSERT(fp
->callobj
);
120 jsid argumentsid
= ATOM_TO_JSID(cx
->runtime
->atomState
.argumentsAtom
);
122 if (!fp
->callobj
->getProperty(cx
, argumentsid
, &v
))
126 if (JSVAL_IS_PRIMITIVE(v
)) {
127 obj
= js_ValueToNonNullObject(cx
, v
);
131 obj
= JSVAL_TO_OBJECT(v
);
133 return obj
->getProperty(cx
, id
, vp
);
137 if (JSID_IS_INT(id
)) {
138 uint32 arg
= uint32(JSID_TO_INT(id
));
139 JSObject
*argsobj
= JSVAL_TO_OBJECT(fp
->argsobj
);
140 if (arg
< fp
->argc
) {
142 jsval v
= argsobj
->getArgsElement(arg
);
144 return argsobj
->getProperty(cx
, id
, vp
);
149 * Per ECMA-262 Ed. 3, 10.1.8, last bulleted item, do not share
150 * storage between the formal parameter and arguments[k] for all
151 * fp->argc <= k && k < fp->fun->nargs. For example, in
153 * function f(x) { x = 42; return arguments[0]; }
156 * the call to f should return undefined, not 42. If fp->argsobj
157 * is null at this point, as it would be in the example, return
161 return argsobj
->getProperty(cx
, id
, vp
);
163 } else if (id
== ATOM_TO_JSID(cx
->runtime
->atomState
.lengthAtom
)) {
164 JSObject
*argsobj
= JSVAL_TO_OBJECT(fp
->argsobj
);
165 if (argsobj
&& argsobj
->isArgsLengthOverridden())
166 return argsobj
->getProperty(cx
, id
, vp
);
167 *vp
= INT_TO_JSVAL(jsint(fp
->argc
));
173 NewArguments(JSContext
*cx
, JSObject
*parent
, uint32 argc
, JSObject
*callee
)
176 if (!js_GetClassPrototype(cx
, parent
, JSProto_Object
, &proto
))
179 JSObject
*argsobj
= js_NewGCObject(cx
);
183 /* Init immediately to avoid GC seeing a half-init'ed object. */
184 argsobj
->init(&js_ArgumentsClass
, proto
, parent
, JSVAL_NULL
);
185 argsobj
->setArgsCallee(OBJECT_TO_JSVAL(callee
));
186 argsobj
->setArgsLength(argc
);
188 argsobj
->map
= cx
->runtime
->emptyArgumentsScope
->hold();
190 /* This must come after argsobj->map has been set. */
191 if (!js_EnsureReservedSlots(cx
, argsobj
, argc
))
197 PutArguments(JSContext
*cx
, JSObject
*argsobj
, jsval
*args
)
199 uint32 argc
= argsobj
->getArgsLength();
200 for (uint32 i
= 0; i
!= argc
; ++i
) {
201 jsval v
= argsobj
->getArgsElement(i
);
203 argsobj
->setArgsElement(i
, args
[i
]);
208 js_GetArgsObject(JSContext
*cx
, JSStackFrame
*fp
)
211 * We must be in a function activation; the function must be lightweight
212 * or else fp must have a variable object.
215 JS_ASSERT_IF(fp
->fun
->flags
& JSFUN_HEAVYWEIGHT
,
216 fp
->varobj(cx
->containingCallStack(fp
)));
218 /* Skip eval and debugger frames. */
219 while (fp
->flags
& JSFRAME_SPECIAL
)
222 /* Create an arguments object for fp only if it lacks one. */
223 JSObject
*argsobj
= JSVAL_TO_OBJECT(fp
->argsobj
);
227 /* Compute the arguments object's parent slot from fp's scope chain. */
228 JSObject
*global
= fp
->scopeChain
->getGlobal();
229 argsobj
= NewArguments(cx
, global
, fp
->argc
, JSVAL_TO_OBJECT(fp
->argv
[-2]));
233 /* Link the new object to fp so it can get actual argument values. */
234 argsobj
->setPrivate(fp
);
235 fp
->argsobj
= OBJECT_TO_JSVAL(argsobj
);
240 js_PutArgsObject(JSContext
*cx
, JSStackFrame
*fp
)
242 JSObject
*argsobj
= JSVAL_TO_OBJECT(fp
->argsobj
);
243 JS_ASSERT(argsobj
->getPrivate() == fp
);
244 PutArguments(cx
, argsobj
, fp
->argv
);
245 argsobj
->setPrivate(NULL
);
246 fp
->argsobj
= JSVAL_NULL
;
250 * Traced versions of js_GetArgsObject and js_PutArgsObject.
254 JSObject
* JS_FASTCALL
255 js_Arguments(JSContext
*cx
, JSObject
*parent
, uint32 argc
, JSObject
*callee
,
256 double *argv
, ArgsPrivateNative
*apn
)
258 JSObject
*argsobj
= NewArguments(cx
, parent
, argc
, callee
);
262 SetArgsPrivateNative(argsobj
, apn
);
267 JS_DEFINE_CALLINFO_6(extern, OBJECT
, js_Arguments
, CONTEXT
, OBJECT
, UINT32
, OBJECT
,
268 DOUBLEPTR
, APNPTR
, 0, nanojit::ACC_STORE_ANY
)
270 /* FIXME change the return type to void. */
272 js_PutArguments(JSContext
*cx
, JSObject
*argsobj
, jsval
*args
)
274 JS_ASSERT(GetArgsPrivateNative(argsobj
));
275 PutArguments(cx
, argsobj
, args
);
276 argsobj
->setPrivate(NULL
);
280 JS_DEFINE_CALLINFO_3(extern, BOOL
, js_PutArguments
, CONTEXT
, OBJECT
, JSVALPTR
, 0,
281 nanojit::ACC_STORE_ANY
)
284 args_delProperty(JSContext
*cx
, JSObject
*obj
, jsval idval
, jsval
*vp
)
286 JS_ASSERT(obj
->isArguments());
288 if (JSVAL_IS_INT(idval
)) {
289 uintN arg
= uintN(JSVAL_TO_INT(idval
));
290 if (arg
< obj
->getArgsLength())
291 obj
->setArgsElement(arg
, JSVAL_HOLE
);
292 } else if (idval
== ATOM_KEY(cx
->runtime
->atomState
.lengthAtom
)) {
293 obj
->setArgsLengthOverridden();
294 } else if (idval
== ATOM_KEY(cx
->runtime
->atomState
.calleeAtom
)) {
295 obj
->setArgsCallee(JSVAL_HOLE
);
300 static JS_REQUIRES_STACK JSObject
*
301 WrapEscapingClosure(JSContext
*cx
, JSStackFrame
*fp
, JSObject
*funobj
, JSFunction
*fun
)
303 JS_ASSERT(GET_FUNCTION_PRIVATE(cx
, funobj
) == fun
);
304 JS_ASSERT(fun
->optimizedClosure());
305 JS_ASSERT(!fun
->u
.i
.wrapper
);
308 * We do not attempt to reify Call and Block objects on demand for outer
309 * scopes. This could be done (see the "v8" patch in bug 494235) but it is
310 * fragile in the face of ongoing compile-time optimization. Instead, the
311 * _DBG* opcodes used by wrappers created here must cope with unresolved
312 * upvars and throw them as reference errors. Caveat debuggers!
314 JSObject
*scopeChain
= js_GetScopeChain(cx
, fp
);
319 * We must wrap funobj with a JSFunction, so use NewObjectWithGivenProto.
320 * This helper has a special case for js_FunctionClass, triggered here and
321 * also possibly via the JS_NewObjectForGivenProto and JS_NewObject APIs.
323 JSObject
*wfunobj
= NewObjectWithGivenProto(cx
, &js_FunctionClass
,
327 AutoValueRooter
tvr(cx
, wfunobj
);
329 JSFunction
*wfun
= (JSFunction
*) wfunobj
;
330 wfunobj
->setPrivate(wfun
);
332 wfun
->flags
= fun
->flags
| JSFUN_HEAVYWEIGHT
;
334 wfun
->u
.i
.nupvars
= 0;
335 wfun
->u
.i
.skipmin
= fun
->u
.i
.skipmin
;
336 wfun
->u
.i
.wrapper
= true;
337 wfun
->u
.i
.script
= NULL
;
338 wfun
->u
.i
.names
.taggedAtom
= NULL
;
339 wfun
->atom
= fun
->atom
;
341 if (fun
->hasLocalNames()) {
342 void *mark
= cx
->tempPool
.getMark();
343 jsuword
*names
= js_GetLocalNameArray(cx
, fun
, &cx
->tempPool
);
348 for (uintN i
= 0, n
= fun
->countLocalNames(); i
!= n
; i
++) {
349 jsuword name
= names
[i
];
350 JSAtom
*atom
= JS_LOCAL_NAME_TO_ATOM(name
);
351 JSLocalKind localKind
= (i
< fun
->nargs
)
353 : (i
< fun
->countArgsAndVars())
354 ? (JS_LOCAL_NAME_IS_CONST(name
)
359 ok
= js_AddLocal(cx
, wfun
, atom
, localKind
);
364 cx
->tempPool
.release(mark
);
367 JS_ASSERT(wfun
->nargs
== fun
->nargs
);
368 JS_ASSERT(wfun
->u
.i
.nvars
== fun
->u
.i
.nvars
);
369 JS_ASSERT(wfun
->u
.i
.nupvars
== fun
->u
.i
.nupvars
);
370 js_FreezeLocalNames(cx
, wfun
);
373 JSScript
*script
= fun
->u
.i
.script
;
374 jssrcnote
*snbase
= script
->notes();
375 jssrcnote
*sn
= snbase
;
376 while (!SN_IS_TERMINATOR(sn
))
378 uintN nsrcnotes
= (sn
- snbase
) + 1;
380 /* NB: GC must not occur before wscript is homed in wfun->u.i.script. */
381 JSScript
*wscript
= js_NewScript(cx
, script
->length
, nsrcnotes
,
382 script
->atomMap
.length
,
383 (script
->objectsOffset
!= 0)
384 ? script
->objects()->length
387 (script
->regexpsOffset
!= 0)
388 ? script
->regexps()->length
390 (script
->trynotesOffset
!= 0)
391 ? script
->trynotes()->length
396 memcpy(wscript
->code
, script
->code
, script
->length
);
397 wscript
->main
= wscript
->code
+ (script
->main
- script
->code
);
399 memcpy(wscript
->notes(), snbase
, nsrcnotes
* sizeof(jssrcnote
));
400 memcpy(wscript
->atomMap
.vector
, script
->atomMap
.vector
,
401 wscript
->atomMap
.length
* sizeof(JSAtom
*));
402 if (script
->objectsOffset
!= 0) {
403 memcpy(wscript
->objects()->vector
, script
->objects()->vector
,
404 wscript
->objects()->length
* sizeof(JSObject
*));
406 if (script
->regexpsOffset
!= 0) {
407 memcpy(wscript
->regexps()->vector
, script
->regexps()->vector
,
408 wscript
->regexps()->length
* sizeof(JSObject
*));
410 if (script
->trynotesOffset
!= 0) {
411 memcpy(wscript
->trynotes()->vector
, script
->trynotes()->vector
,
412 wscript
->trynotes()->length
* sizeof(JSTryNote
));
415 if (wfun
->u
.i
.nupvars
!= 0) {
416 JS_ASSERT(wfun
->u
.i
.nupvars
== wscript
->upvars()->length
);
417 memcpy(wscript
->upvars()->vector
, script
->upvars()->vector
,
418 wfun
->u
.i
.nupvars
* sizeof(uint32
));
421 jsbytecode
*pc
= wscript
->code
;
422 while (*pc
!= JSOP_STOP
) {
423 /* XYZZYbe should copy JSOP_TRAP? */
424 JSOp op
= js_GetOpcode(cx
, wscript
, pc
);
425 const JSCodeSpec
*cs
= &js_CodeSpec
[op
];
426 ptrdiff_t oplen
= cs
->length
;
428 oplen
= js_GetVariableBytecodeLength(pc
);
431 * Rewrite JSOP_{GET,CALL}DSLOT as JSOP_{GET,CALL}UPVAR_DBG for the
432 * case where fun is an escaping flat closure. This works because the
433 * UPVAR and DSLOT ops by design have the same format: an upvar index
437 case JSOP_GETUPVAR
: *pc
= JSOP_GETUPVAR_DBG
; break;
438 case JSOP_CALLUPVAR
: *pc
= JSOP_CALLUPVAR_DBG
; break;
439 case JSOP_GETDSLOT
: *pc
= JSOP_GETUPVAR_DBG
; break;
440 case JSOP_CALLDSLOT
: *pc
= JSOP_CALLUPVAR_DBG
; break;
441 case JSOP_DEFFUN_FC
: *pc
= JSOP_DEFFUN_DBGFC
; break;
442 case JSOP_DEFLOCALFUN_FC
: *pc
= JSOP_DEFLOCALFUN_DBGFC
; break;
443 case JSOP_LAMBDA_FC
: *pc
= JSOP_LAMBDA_DBGFC
; break;
450 * Fill in the rest of wscript. This means if you add members to JSScript
451 * you must update this code. FIXME: factor into JSScript::clone method.
453 wscript
->noScriptRval
= script
->noScriptRval
;
454 wscript
->savedCallerFun
= script
->savedCallerFun
;
455 wscript
->hasSharps
= script
->hasSharps
;
456 wscript
->strictModeCode
= script
->strictModeCode
;
457 wscript
->version
= script
->version
;
458 wscript
->nfixed
= script
->nfixed
;
459 wscript
->filename
= script
->filename
;
460 wscript
->lineno
= script
->lineno
;
461 wscript
->nslots
= script
->nslots
;
462 wscript
->staticLevel
= script
->staticLevel
;
463 wscript
->principals
= script
->principals
;
464 if (wscript
->principals
)
465 JSPRINCIPALS_HOLD(cx
, wscript
->principals
);
466 #ifdef CHECK_SCRIPT_OWNER
467 wscript
->owner
= script
->owner
;
470 /* Deoptimize wfun from FUN_{FLAT,NULL}_CLOSURE to FUN_INTERPRETED. */
471 FUN_SET_KIND(wfun
, JSFUN_INTERPRETED
);
472 wfun
->u
.i
.script
= wscript
;
477 ArgGetter(JSContext
*cx
, JSObject
*obj
, jsval idval
, jsval
*vp
)
479 if (!JS_InstanceOf(cx
, obj
, &js_ArgumentsClass
, NULL
))
482 if (JSVAL_IS_INT(idval
)) {
484 * arg can exceed the number of arguments if a script changed the
485 * prototype to point to another Arguments object with a bigger argc.
487 uintN arg
= uintN(JSVAL_TO_INT(idval
));
488 if (arg
< obj
->getArgsLength()) {
490 ArgsPrivateNative
*argp
= GetArgsPrivateNative(obj
);
492 if (NativeToValue(cx
, *vp
, argp
->typemap()[arg
], &argp
->argv
[arg
]))
499 JSStackFrame
*fp
= (JSStackFrame
*) obj
->getPrivate();
503 jsval v
= obj
->getArgsElement(arg
);
508 } else if (idval
== ATOM_KEY(cx
->runtime
->atomState
.lengthAtom
)) {
509 if (!obj
->isArgsLengthOverridden())
510 *vp
= INT_TO_JSVAL(obj
->getArgsLength());
512 JS_ASSERT(idval
== ATOM_KEY(cx
->runtime
->atomState
.calleeAtom
));
513 jsval v
= obj
->getArgsCallee();
514 if (v
!= JSVAL_HOLE
) {
516 * If this function or one in it needs upvars that reach above it
517 * in the scope chain, it must not be a null closure (it could be a
518 * flat closure, or an unoptimized closure -- the latter itself not
519 * necessarily heavyweight). Rather than wrap here, we simply throw
520 * to reduce code size and tell debugger users the truth instead of
521 * passing off a fibbing wrapper.
523 if (GET_FUNCTION_PRIVATE(cx
, JSVAL_TO_OBJECT(v
))->needsWrapper()) {
524 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
525 JSMSG_OPTIMIZED_CLOSURE_LEAK
);
535 ArgSetter(JSContext
*cx
, JSObject
*obj
, jsval idval
, jsval
*vp
)
538 // To be able to set a property here on trace, we would have to make
539 // sure any updates also get written back to the trace native stack.
540 // For simplicity, we just leave trace, since this is presumably not
541 // a common operation.
542 if (JS_ON_TRACE(cx
)) {
548 if (!JS_InstanceOf(cx
, obj
, &js_ArgumentsClass
, NULL
))
551 if (JSVAL_IS_INT(idval
)) {
552 uintN arg
= uintN(JSVAL_TO_INT(idval
));
553 if (arg
< obj
->getArgsLength()) {
554 JSStackFrame
*fp
= (JSStackFrame
*) obj
->getPrivate();
561 JS_ASSERT(idval
== ATOM_KEY(cx
->runtime
->atomState
.lengthAtom
) ||
562 idval
== ATOM_KEY(cx
->runtime
->atomState
.calleeAtom
));
566 * For simplicity we use delete/set to replace the property with one
567 * backed by the default Object getter and setter. Note the we rely on
568 * args_delete to clear the corresponding reserved slot so the GC can
571 JS_ASSERT_IF(JSVAL_IS_STRING(idval
), JSVAL_TO_STRING(idval
)->isAtomized());
572 jsid id
= (jsid
)idval
;
574 AutoValueRooter
tvr(cx
);
575 return js_DeleteProperty(cx
, obj
, id
, tvr
.addr()) &&
576 js_SetProperty(cx
, obj
, id
, vp
);
580 args_resolve(JSContext
*cx
, JSObject
*obj
, jsval idval
, uintN flags
,
583 JS_ASSERT(obj
->isArguments());
587 if (JSVAL_IS_INT(idval
)) {
588 uint32 arg
= uint32(JSVAL_TO_INT(idval
));
589 if (arg
< obj
->getArgsLength() && obj
->getArgsElement(arg
) != JSVAL_HOLE
)
590 id
= INT_JSVAL_TO_JSID(idval
);
591 } else if (idval
== ATOM_KEY(cx
->runtime
->atomState
.lengthAtom
)) {
592 if (!obj
->isArgsLengthOverridden())
593 id
= ATOM_TO_JSID(cx
->runtime
->atomState
.lengthAtom
);
594 } else if (idval
== ATOM_KEY(cx
->runtime
->atomState
.calleeAtom
)) {
595 if (obj
->getArgsCallee() != JSVAL_HOLE
)
596 id
= ATOM_TO_JSID(cx
->runtime
->atomState
.calleeAtom
);
601 * XXX ECMA specs DontEnum even for indexed properties, contrary to
602 * other array-like objects.
604 if (!js_DefineProperty(cx
, obj
, id
, JSVAL_VOID
, ArgGetter
, ArgSetter
, JSPROP_SHARED
))
612 args_enumerate(JSContext
*cx
, JSObject
*obj
)
614 JS_ASSERT(obj
->isArguments());
617 * Trigger reflection in args_resolve using a series of js_LookupProperty
620 int argc
= int(obj
->getArgsLength());
621 for (int i
= -2; i
!= argc
; i
++) {
623 ? ATOM_TO_JSID(cx
->runtime
->atomState
.lengthAtom
)
625 ? ATOM_TO_JSID(cx
->runtime
->atomState
.calleeAtom
)
626 : INT_JSVAL_TO_JSID(INT_TO_JSVAL(i
));
630 if (!js_LookupProperty(cx
, obj
, id
, &pobj
, &prop
))
633 /* prop is null when the property was deleted. */
635 pobj
->dropProperty(cx
, prop
);
640 #if JS_HAS_GENERATORS
642 * If a generator's arguments or call object escapes, and the generator frame
643 * is not executing, the generator object needs to be marked because it is not
644 * otherwise reachable. An executing generator is rooted by its invocation. To
645 * distinguish the two cases (which imply different access paths to the
646 * generator object), we use the JSFRAME_FLOATING_GENERATOR flag, which is only
647 * set on the JSStackFrame kept in the generator object's JSGenerator.
650 args_or_call_trace(JSTracer
*trc
, JSObject
*obj
)
652 if (obj
->isArguments()) {
653 if (GetArgsPrivateNative(obj
))
656 JS_ASSERT(obj
->getClass() == &js_CallClass
);
659 JSStackFrame
*fp
= (JSStackFrame
*) obj
->getPrivate();
660 if (fp
&& fp
->isFloatingGenerator()) {
661 JSObject
*obj
= js_FloatingFrameToGenerator(fp
)->obj
;
662 JS_CALL_OBJECT_TRACER(trc
, obj
, "generator object");
666 # define args_or_call_trace NULL
670 * The Arguments class is not initialized via JS_InitClass, and must not be,
671 * because its name is "Object". Per ECMA, that causes instances of it to
672 * delegate to the object named by Object.prototype. It also ensures that
673 * arguments.toString() returns "[object Object]".
675 * The JSClass functions below collaborate to lazily reflect and synchronize
676 * actual argument values, argument count, and callee function object stored
677 * in a JSStackFrame with their corresponding property values in the frame's
680 JSClass js_ArgumentsClass
= {
682 JSCLASS_HAS_PRIVATE
| JSCLASS_NEW_RESOLVE
|
683 JSCLASS_HAS_RESERVED_SLOTS(JSObject::ARGS_FIXED_RESERVED_SLOTS
) |
684 JSCLASS_MARK_IS_TRACE
| JSCLASS_HAS_CACHED_PROTO(JSProto_Object
),
685 JS_PropertyStub
, args_delProperty
,
686 JS_PropertyStub
, JS_PropertyStub
,
687 args_enumerate
, (JSResolveOp
) args_resolve
,
688 JS_ConvertStub
, NULL
,
692 JS_CLASS_TRACE(args_or_call_trace
), NULL
695 const uint32 JSSLOT_CALLEE
= JSSLOT_PRIVATE
+ 1;
696 const uint32 JSSLOT_CALL_ARGUMENTS
= JSSLOT_PRIVATE
+ 2;
697 const uint32 CALL_CLASS_FIXED_RESERVED_SLOTS
= 2;
700 * A Declarative Environment object stores its active JSStackFrame pointer in
701 * its private slot, just as Call and Arguments objects do.
703 JSClass js_DeclEnvClass
= {
705 JSCLASS_HAS_PRIVATE
| JSCLASS_HAS_CACHED_PROTO(JSProto_Object
),
706 JS_PropertyStub
, JS_PropertyStub
, JS_PropertyStub
, JS_PropertyStub
,
707 JS_EnumerateStub
, JS_ResolveStub
, JS_ConvertStub
, NULL
,
708 JSCLASS_NO_OPTIONAL_MEMBERS
712 CheckForEscapingClosure(JSContext
*cx
, JSObject
*obj
, jsval
*vp
)
714 JS_ASSERT(obj
->getClass() == &js_CallClass
||
715 obj
->getClass() == &js_DeclEnvClass
);
719 if (VALUE_IS_FUNCTION(cx
, v
)) {
720 JSObject
*funobj
= JSVAL_TO_OBJECT(v
);
721 JSFunction
*fun
= GET_FUNCTION_PRIVATE(cx
, funobj
);
724 * Any escaping null or flat closure that reaches above itself or
725 * contains nested functions that reach above it must be wrapped.
726 * We can wrap only when this Call or Declarative Environment obj
727 * still has an active stack frame associated with it.
729 if (fun
->needsWrapper()) {
732 JSStackFrame
*fp
= (JSStackFrame
*) obj
->getPrivate();
734 JSObject
*wrapper
= WrapEscapingClosure(cx
, fp
, funobj
, fun
);
737 *vp
= OBJECT_TO_JSVAL(wrapper
);
741 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
742 JSMSG_OPTIMIZED_CLOSURE_LEAK
);
750 CalleeGetter(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
752 return CheckForEscapingClosure(cx
, obj
, vp
);
756 NewCallObject(JSContext
*cx
, JSFunction
*fun
, JSObject
*scopeChain
)
758 JSObject
*callobj
= js_NewGCObject(cx
);
762 /* Init immediately to avoid GC seeing a half-init'ed object. */
763 callobj
->init(&js_CallClass
, NULL
, scopeChain
, JSVAL_NULL
);
764 callobj
->map
= cx
->runtime
->emptyCallScope
->hold();
766 /* This must come after callobj->map has been set. */
767 if (!js_EnsureReservedSlots(cx
, callobj
, fun
->countArgsAndVars()))
772 static inline JSObject
*
773 NewDeclEnvObject(JSContext
*cx
, JSStackFrame
*fp
)
775 JSObject
*envobj
= js_NewGCObject(cx
);
779 /* Init immediately to avoid GC seeing a half-init'ed object. */
780 envobj
->init(&js_DeclEnvClass
, NULL
, fp
->scopeChain
, reinterpret_cast<jsval
>(fp
));
781 envobj
->map
= cx
->runtime
->emptyDeclEnvScope
->hold();
786 js_GetCallObject(JSContext
*cx
, JSStackFrame
*fp
)
790 /* Create a call object for fp only if it lacks one. */
792 callobj
= fp
->callobj
;
797 /* A call object should be a frame's outermost scope chain element. */
798 JSClass
*classp
= fp
->scopeChain
->getClass();
799 if (classp
== &js_WithClass
|| classp
== &js_BlockClass
)
800 JS_ASSERT(fp
->scopeChain
->getPrivate() != js_FloatingFrameIfGenerator(cx
, fp
));
801 else if (classp
== &js_CallClass
)
802 JS_ASSERT(fp
->scopeChain
->getPrivate() != fp
);
806 * Create the call object, using the frame's enclosing scope as its
807 * parent, and link the call to its stack frame. For a named function
808 * expression Call's parent points to an environment object holding
811 JSAtom
*lambdaName
= (fp
->fun
->flags
& JSFUN_LAMBDA
) ? fp
->fun
->atom
: NULL
;
813 JSObject
*envobj
= NewDeclEnvObject(cx
, fp
);
817 /* Root envobj before js_DefineNativeProperty (-> JSClass.addProperty). */
818 fp
->scopeChain
= envobj
;
820 if (!js_DefineNativeProperty(cx
, fp
->scopeChain
, ATOM_TO_JSID(lambdaName
),
823 JSPROP_PERMANENT
| JSPROP_READONLY
,
829 callobj
= NewCallObject(cx
, fp
->fun
, fp
->scopeChain
);
833 callobj
->setPrivate(fp
);
835 JS_ASSERT(fp
->fun
== GET_FUNCTION_PRIVATE(cx
, fp
->calleeObject()));
836 callobj
->setSlot(JSSLOT_CALLEE
, fp
->calleeValue());
837 fp
->callobj
= callobj
;
840 * Push callobj on the top of the scope chain, and make it the
843 fp
->scopeChain
= callobj
;
847 JSObject
* JS_FASTCALL
848 js_CreateCallObjectOnTrace(JSContext
*cx
, JSFunction
*fun
, JSObject
*callee
, JSObject
*scopeChain
)
850 JS_ASSERT(!js_IsNamedLambda(fun
));
851 JSObject
*callobj
= NewCallObject(cx
, fun
, scopeChain
);
854 callobj
->setSlot(JSSLOT_CALLEE
, OBJECT_TO_JSVAL(callee
));
858 JS_DEFINE_CALLINFO_4(extern, OBJECT
, js_CreateCallObjectOnTrace
, CONTEXT
, FUNCTION
, OBJECT
, OBJECT
,
859 0, nanojit::ACC_STORE_ANY
)
862 js_GetCallObjectFunction(JSObject
*obj
)
866 JS_ASSERT(obj
->getClass() == &js_CallClass
);
867 v
= obj
->getSlot(JSSLOT_CALLEE
);
868 if (JSVAL_IS_VOID(v
)) {
869 /* Newborn or prototype object. */
872 JS_ASSERT(!JSVAL_IS_PRIMITIVE(v
));
873 return GET_FUNCTION_PRIVATE(cx
, JSVAL_TO_OBJECT(v
));
877 CopyValuesToCallObject(JSObject
*callobj
, int nargs
, jsval
*argv
, int nvars
, jsval
*slots
)
879 memcpy(callobj
->dslots
, argv
, nargs
* sizeof(jsval
));
880 memcpy(callobj
->dslots
+ nargs
, slots
, nvars
* sizeof(jsval
));
884 js_PutCallObject(JSContext
*cx
, JSStackFrame
*fp
)
886 JSObject
*callobj
= fp
->callobj
;
889 /* Get the arguments object to snapshot fp's actual argument values. */
891 if (!(fp
->flags
& JSFRAME_OVERRIDE_ARGS
))
892 callobj
->setSlot(JSSLOT_CALL_ARGUMENTS
, fp
->argsobj
);
893 js_PutArgsObject(cx
, fp
);
896 JSFunction
*fun
= fp
->fun
;
897 JS_ASSERT(fun
== js_GetCallObjectFunction(callobj
));
898 uintN n
= fun
->countArgsAndVars();
901 * Since for a call object all fixed slots happen to be taken, we can copy
902 * arguments and variables straight into JSObject.dslots.
904 JS_STATIC_ASSERT(JS_INITIAL_NSLOTS
- JSSLOT_PRIVATE
==
905 1 + CALL_CLASS_FIXED_RESERVED_SLOTS
);
907 JS_ASSERT(callobj
->numSlots() >= JS_INITIAL_NSLOTS
+ n
);
908 n
+= JS_INITIAL_NSLOTS
;
909 CopyValuesToCallObject(callobj
, fun
->nargs
, fp
->argv
, fun
->u
.i
.nvars
, fp
->slots());
912 /* Clear private pointers to fp, which is about to go away (js_Invoke). */
913 if (js_IsNamedLambda(fun
)) {
914 JSObject
*env
= callobj
->getParent();
916 JS_ASSERT(env
->getClass() == &js_DeclEnvClass
);
917 JS_ASSERT(env
->getPrivate() == fp
);
918 env
->setPrivate(NULL
);
921 callobj
->setPrivate(NULL
);
926 js_PutCallObjectOnTrace(JSContext
*cx
, JSObject
*scopeChain
, uint32 nargs
, jsval
*argv
,
927 uint32 nvars
, jsval
*slots
)
929 JS_ASSERT(scopeChain
->hasClass(&js_CallClass
));
930 JS_ASSERT(!scopeChain
->getPrivate());
932 uintN n
= nargs
+ nvars
;
934 CopyValuesToCallObject(scopeChain
, nargs
, argv
, nvars
, slots
);
939 JS_DEFINE_CALLINFO_6(extern, BOOL
, js_PutCallObjectOnTrace
, CONTEXT
, OBJECT
, UINT32
, JSVALPTR
,
940 UINT32
, JSVALPTR
, 0, nanojit::ACC_STORE_ANY
)
943 call_enumerate(JSContext
*cx
, JSObject
*obj
)
954 fun
= js_GetCallObjectFunction(obj
);
955 n
= fun
? fun
->countArgsAndVars() : 0;
959 mark
= cx
->tempPool
.getMark();
961 MUST_FLOW_THROUGH("out");
962 names
= js_GetLocalNameArray(cx
, fun
, &cx
->tempPool
);
968 for (i
= 0; i
!= n
; ++i
) {
969 name
= JS_LOCAL_NAME_TO_ATOM(names
[i
]);
974 * Trigger reflection by looking up the name of the argument or
977 ok
= js_LookupProperty(cx
, obj
, ATOM_TO_JSID(name
), &pobj
, &prop
);
982 * The call object will always have a property corresponding to the
983 * argument or variable name because call_resolve creates the property
984 * using JSPROP_PERMANENT.
987 JS_ASSERT(pobj
== obj
);
988 pobj
->dropProperty(cx
, prop
);
993 cx
->tempPool
.release(mark
);
997 enum JSCallPropertyKind
{
1005 CallPropertyOp(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
,
1006 JSCallPropertyKind kind
, JSBool setter
= false)
1008 JS_ASSERT(obj
->getClass() == &js_CallClass
);
1011 if (kind
!= JSCPK_ARGUMENTS
) {
1012 JS_ASSERT((int16
) JSVAL_TO_INT(id
) == JSVAL_TO_INT(id
));
1013 i
= (uint16
) JSVAL_TO_INT(id
);
1017 if (kind
== JSCPK_UPVAR
) {
1018 JSObject
*callee
= JSVAL_TO_OBJECT(obj
->getSlot(JSSLOT_CALLEE
));
1021 JSFunction
*callee_fun
= (JSFunction
*) callee
->getPrivate();
1022 JS_ASSERT(FUN_FLAT_CLOSURE(callee_fun
));
1023 JS_ASSERT(i
< callee_fun
->u
.i
.nupvars
);
1026 array
= callee
->dslots
;
1028 JSFunction
*fun
= js_GetCallObjectFunction(obj
);
1029 JS_ASSERT_IF(kind
== JSCPK_ARG
, i
< fun
->nargs
);
1030 JS_ASSERT_IF(kind
== JSCPK_VAR
, i
< fun
->u
.i
.nvars
);
1032 JSStackFrame
*fp
= (JSStackFrame
*) obj
->getPrivate();
1034 if (kind
== JSCPK_ARGUMENTS
) {
1037 fp
->flags
|= JSFRAME_OVERRIDE_ARGS
;
1038 obj
->setSlot(JSSLOT_CALL_ARGUMENTS
, *vp
);
1040 if (fp
&& !(fp
->flags
& JSFRAME_OVERRIDE_ARGS
)) {
1043 argsobj
= js_GetArgsObject(cx
, fp
);
1046 *vp
= OBJECT_TO_JSVAL(argsobj
);
1048 *vp
= obj
->getSlot(JSSLOT_CALL_ARGUMENTS
);
1055 i
+= CALL_CLASS_FIXED_RESERVED_SLOTS
;
1056 if (kind
== JSCPK_VAR
)
1059 JS_ASSERT(kind
== JSCPK_ARG
);
1061 ? JS_SetReservedSlot(cx
, obj
, i
, *vp
)
1062 : JS_GetReservedSlot(cx
, obj
, i
, vp
);
1065 if (kind
== JSCPK_ARG
) {
1068 JS_ASSERT(kind
== JSCPK_VAR
);
1069 array
= fp
->slots();
1074 GC_POKE(cx
, array
[i
]);
1083 GetCallArguments(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
1085 return CallPropertyOp(cx
, obj
, id
, vp
, JSCPK_ARGUMENTS
);
1089 SetCallArguments(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
1091 return CallPropertyOp(cx
, obj
, id
, vp
, JSCPK_ARGUMENTS
, true);
1095 js_GetCallArg(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
1097 return CallPropertyOp(cx
, obj
, id
, vp
, JSCPK_ARG
);
1101 SetCallArg(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
1103 return CallPropertyOp(cx
, obj
, id
, vp
, JSCPK_ARG
, true);
1107 GetFlatUpvar(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
1109 return CallPropertyOp(cx
, obj
, id
, vp
, JSCPK_UPVAR
);
1113 SetFlatUpvar(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
1115 return CallPropertyOp(cx
, obj
, id
, vp
, JSCPK_UPVAR
, true);
1119 js_GetCallVar(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
1121 return CallPropertyOp(cx
, obj
, id
, vp
, JSCPK_VAR
);
1125 js_GetCallVarChecked(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
1127 if (!CallPropertyOp(cx
, obj
, id
, vp
, JSCPK_VAR
))
1130 return CheckForEscapingClosure(cx
, obj
, vp
);
1134 SetCallVar(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval
*vp
)
1136 return CallPropertyOp(cx
, obj
, id
, vp
, JSCPK_VAR
, true);
1140 js_SetCallArg(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval v
)
1142 return CallPropertyOp(cx
, obj
, id
, &v
, JSCPK_ARG
, true);
1146 js_SetCallVar(JSContext
*cx
, JSObject
*obj
, jsid id
, jsval v
)
1148 return CallPropertyOp(cx
, obj
, id
, &v
, JSCPK_VAR
, true);
1151 JS_DEFINE_CALLINFO_4(extern, BOOL
, js_SetCallArg
, CONTEXT
, OBJECT
, JSID
, JSVAL
, 0,
1152 nanojit::ACC_STORE_ANY
)
1153 JS_DEFINE_CALLINFO_4(extern, BOOL
, js_SetCallVar
, CONTEXT
, OBJECT
, JSID
, JSVAL
, 0,
1154 nanojit::ACC_STORE_ANY
)
1157 call_resolve(JSContext
*cx
, JSObject
*obj
, jsval idval
, uintN flags
,
1162 JSLocalKind localKind
;
1163 JSPropertyOp getter
, setter
;
1166 JS_ASSERT(obj
->getClass() == &js_CallClass
);
1167 JS_ASSERT(!obj
->getProto());
1169 if (!JSVAL_IS_STRING(idval
))
1172 JS_ASSERT(JSVAL_TO_STRING(idval
)->isAtomized());
1173 jsid id
= (jsval
)idval
;
1175 callee
= obj
->getSlot(JSSLOT_CALLEE
);
1176 if (JSVAL_IS_VOID(callee
))
1178 fun
= GET_FUNCTION_PRIVATE(cx
, JSVAL_TO_OBJECT(callee
));
1181 * Check whether the id refers to a formal parameter, local variable or
1182 * the arguments special name.
1184 * We define all such names using JSDNP_DONT_PURGE to avoid an expensive
1185 * shape invalidation in js_DefineNativeProperty. If such an id happens to
1186 * shadow a global or upvar of the same name, any inner functions can
1187 * never access the outer binding. Thus it cannot invalidate any property
1188 * cache entries or derived trace guards for the outer binding. See also
1189 * comments in js_PurgeScopeChainHelper from jsobj.cpp.
1191 localKind
= js_LookupLocal(cx
, fun
, JSID_TO_ATOM(id
), &slot
);
1192 if (localKind
!= JSLOCAL_NONE
) {
1193 JS_ASSERT((uint16
) slot
== slot
);
1196 * We follow 10.2.3 of ECMA 262 v3 and make argument and variable
1197 * properties of the Call objects enumerable.
1199 attrs
= JSPROP_ENUMERATE
| JSPROP_PERMANENT
| JSPROP_SHARED
;
1200 if (localKind
== JSLOCAL_ARG
) {
1201 JS_ASSERT(slot
< fun
->nargs
);
1202 getter
= js_GetCallArg
;
1203 setter
= SetCallArg
;
1205 JSCallPropertyKind cpkind
;
1206 if (localKind
== JSLOCAL_UPVAR
) {
1207 if (!FUN_FLAT_CLOSURE(fun
))
1209 getter
= GetFlatUpvar
;
1210 setter
= SetFlatUpvar
;
1211 cpkind
= JSCPK_UPVAR
;
1213 JS_ASSERT(localKind
== JSLOCAL_VAR
|| localKind
== JSLOCAL_CONST
);
1214 JS_ASSERT(slot
< fun
->u
.i
.nvars
);
1215 getter
= js_GetCallVar
;
1216 setter
= SetCallVar
;
1218 if (localKind
== JSLOCAL_CONST
)
1219 attrs
|= JSPROP_READONLY
;
1223 * Use js_GetCallVarChecked if the local's value is a null closure.
1224 * This way we penalize performance only slightly on first use of a
1225 * null closure, not on every use.
1228 if (!CallPropertyOp(cx
, obj
, INT_TO_JSID((int16
)slot
), &v
, cpkind
))
1230 if (VALUE_IS_FUNCTION(cx
, v
) &&
1231 GET_FUNCTION_PRIVATE(cx
, JSVAL_TO_OBJECT(v
))->needsWrapper()) {
1232 getter
= js_GetCallVarChecked
;
1235 if (!js_DefineNativeProperty(cx
, obj
, id
, JSVAL_VOID
, getter
, setter
,
1236 attrs
, JSScopeProperty::HAS_SHORTID
, (int16
) slot
,
1237 NULL
, JSDNP_DONT_PURGE
)) {
1245 * Resolve arguments so that we never store a particular Call object's
1246 * arguments object reference in a Call prototype's |arguments| slot.
1248 if (id
== ATOM_TO_JSID(cx
->runtime
->atomState
.argumentsAtom
)) {
1249 if (!js_DefineNativeProperty(cx
, obj
, id
, JSVAL_VOID
,
1250 GetCallArguments
, SetCallArguments
,
1251 JSPROP_PERMANENT
| JSPROP_SHARED
,
1252 0, 0, NULL
, JSDNP_DONT_PURGE
)) {
1259 /* Control flow reaches here only if id was not resolved. */
1263 JS_FRIEND_DATA(JSClass
) js_CallClass
= {
1265 JSCLASS_HAS_PRIVATE
|
1266 JSCLASS_HAS_RESERVED_SLOTS(CALL_CLASS_FIXED_RESERVED_SLOTS
) |
1267 JSCLASS_NEW_RESOLVE
| JSCLASS_IS_ANONYMOUS
| JSCLASS_MARK_IS_TRACE
,
1268 JS_PropertyStub
, JS_PropertyStub
,
1269 JS_PropertyStub
, JS_PropertyStub
,
1270 call_enumerate
, (JSResolveOp
)call_resolve
,
1275 JS_CLASS_TRACE(args_or_call_trace
), NULL
1278 /* Generic function tinyids. */
1280 FUN_ARGUMENTS
= -1, /* predefined arguments local variable */
1281 FUN_LENGTH
= -2, /* number of actual args, arity if inactive */
1282 FUN_ARITY
= -3, /* number of formal parameters; desired argc */
1283 FUN_NAME
= -4, /* function name, "" if anonymous */
1284 FUN_CALLER
= -5 /* Function.prototype.caller, backward compat */
1288 fun_getProperty(JSContext
*cx
, JSObject
*obj
, jsval id
, jsval
*vp
)
1290 if (!JSVAL_IS_INT(id
))
1293 jsint slot
= JSVAL_TO_INT(id
);
1296 * Loop because getter and setter can be delegated from another class,
1297 * but loop only for FUN_LENGTH because we must pretend that f.length
1298 * is in each function instance f, per ECMA-262, instead of only in the
1299 * Function.prototype object (we use JSPROP_PERMANENT with JSPROP_SHARED
1300 * to make it appear so).
1302 * This code couples tightly to the attributes for lazy_function_props[]
1303 * initializers above, and to js_SetProperty and js_HasOwnProperty.
1305 * It's important to allow delegating objects, even though they inherit
1306 * this getter (fun_getProperty), to override arguments, arity, caller,
1307 * and name. If we didn't return early for slot != FUN_LENGTH, we would
1308 * clobber *vp with the native property value, instead of letting script
1309 * override that value in delegating objects.
1311 * Note how that clobbering is what simulates JSPROP_READONLY for all of
1312 * the non-standard properties when the directly addressed object (obj)
1313 * is a function object (i.e., when this loop does not iterate).
1316 while (!(fun
= (JSFunction
*)
1317 JS_GetInstancePrivate(cx
, obj
, &js_FunctionClass
, NULL
))) {
1318 if (slot
!= FUN_LENGTH
)
1320 obj
= obj
->getProto();
1325 /* Find fun's top-most activation record. */
1327 for (fp
= js_GetTopStackFrame(cx
);
1328 fp
&& (fp
->fun
!= fun
|| (fp
->flags
& JSFRAME_SPECIAL
));
1335 /* Warn if strict about f.arguments or equivalent unqualified uses. */
1336 if (!JS_ReportErrorFlagsAndNumber(cx
,
1337 JSREPORT_WARNING
| JSREPORT_STRICT
,
1338 js_GetErrorMessage
, NULL
,
1339 JSMSG_DEPRECATED_USAGE
,
1340 js_arguments_str
)) {
1344 if (!js_GetArgsValue(cx
, fp
, vp
))
1353 *vp
= INT_TO_JSVAL((jsint
)fun
->nargs
);
1358 ? ATOM_KEY(fun
->atom
)
1359 : STRING_TO_JSVAL(cx
->runtime
->emptyString
);
1363 if (fp
&& fp
->down
&& fp
->down
->fun
) {
1364 JSFunction
*caller
= fp
->down
->fun
;
1366 * See equivalent condition in args_getProperty for ARGS_CALLEE,
1367 * but here we do not want to throw, since this escape can happen
1368 * via foo.caller alone, without any debugger or indirect eval. And
1369 * it seems foo.caller is still used on the Web.
1371 if (caller
->needsWrapper()) {
1372 JSObject
*wrapper
= WrapEscapingClosure(cx
, fp
->down
, FUN_OBJECT(caller
), caller
);
1375 *vp
= OBJECT_TO_JSVAL(wrapper
);
1379 JS_ASSERT(fp
->down
->argv
);
1380 *vp
= fp
->down
->calleeValue();
1385 /* Censor the caller if it is from another compartment. */
1386 if (!JSVAL_IS_PRIMITIVE(*vp
)) {
1387 if (JSVAL_TO_OBJECT(*vp
)->getCompartment(cx
) != cx
->compartment
)
1393 /* XXX fun[0] and fun.arguments[0] are equivalent. */
1394 if (fp
&& fp
->fun
&& (uintN
)slot
< fp
->fun
->nargs
)
1395 *vp
= fp
->argv
[slot
];
1402 struct LazyFunctionProp
{
1408 /* NB: no sentinel at the end -- use JS_ARRAY_LENGTH to bound loops. */
1409 static LazyFunctionProp lazy_function_props
[] = {
1410 {ATOM_OFFSET(arguments
), FUN_ARGUMENTS
, JSPROP_PERMANENT
},
1411 {ATOM_OFFSET(arity
), FUN_ARITY
, JSPROP_PERMANENT
},
1412 {ATOM_OFFSET(caller
), FUN_CALLER
, JSPROP_PERMANENT
},
1413 {ATOM_OFFSET(name
), FUN_NAME
, JSPROP_PERMANENT
},
1417 fun_enumerate(JSContext
*cx
, JSObject
*obj
)
1419 JS_ASSERT(obj
->isFunction());
1422 jsid id
= ATOM_TO_JSID(cx
->runtime
->atomState
.classPrototypeAtom
);
1423 if (!JS_LookupPropertyById(cx
, obj
, id
, &v
))
1425 id
= ATOM_TO_JSID(cx
->runtime
->atomState
.lengthAtom
);
1426 if (!JS_LookupPropertyById(cx
, obj
, id
, &v
))
1429 for (uintN i
= 0; i
< JS_ARRAY_LENGTH(lazy_function_props
); i
++) {
1430 LazyFunctionProp
&lfp
= lazy_function_props
[i
];
1431 id
= ATOM_TO_JSID(OFFSET_TO_ATOM(cx
->runtime
, lfp
.atomOffset
));
1432 if (!JS_LookupPropertyById(cx
, obj
, id
, &v
))
1440 fun_resolve(JSContext
*cx
, JSObject
*obj
, jsval id
, uintN flags
,
1443 if (!JSVAL_IS_STRING(id
))
1446 JSFunction
*fun
= GET_FUNCTION_PRIVATE(cx
, obj
);
1449 * No need to reflect fun.prototype in 'fun.prototype = ... '. Assert that
1450 * fun is not a compiler-created function object, which must never leak to
1451 * script or embedding code and then be mutated.
1453 if ((flags
& JSRESOLVE_ASSIGNING
) && id
!= ATOM_TO_JSID(cx
->runtime
->atomState
.lengthAtom
)) {
1454 JS_ASSERT(!IsInternalFunctionObject(obj
));
1459 * Ok, check whether id is 'prototype' and bootstrap the function object's
1460 * prototype property.
1462 JSAtom
*atom
= cx
->runtime
->atomState
.classPrototypeAtom
;
1463 if (id
== ATOM_TO_JSID(atom
)) {
1464 JS_ASSERT(!IsInternalFunctionObject(obj
));
1467 * Beware of the wacky case of a user function named Object -- trying
1468 * to find a prototype for that will recur back here _ad perniciem_.
1470 if (fun
->atom
== CLASS_ATOM(cx
, Object
))
1474 * Make the prototype object an instance of Object with the same parent
1475 * as the function object itself.
1477 JSObject
*parent
= obj
->getParent();
1479 if (!js_GetClassPrototype(cx
, parent
, JSProto_Object
, &proto
))
1481 proto
= NewNativeClassInstance(cx
, &js_ObjectClass
, proto
, parent
);
1486 * ECMA (15.3.5.2) says that constructor.prototype is DontDelete for
1487 * user-defined functions, but DontEnum | ReadOnly | DontDelete for
1488 * native "system" constructors such as Object or Function. So lazily
1489 * set the former here in fun_resolve, but eagerly define the latter
1490 * in JS_InitClass, with the right attributes.
1492 if (!js_SetClassPrototype(cx
, obj
, proto
, JSPROP_PERMANENT
))
1499 atom
= cx
->runtime
->atomState
.lengthAtom
;
1500 if (id
== ATOM_TO_JSID(atom
)) {
1501 JS_ASSERT(!IsInternalFunctionObject(obj
));
1502 if (!js_DefineNativeProperty(cx
, obj
, ATOM_TO_JSID(atom
), INT_TO_JSVAL(jsint(fun
->nargs
)),
1503 JS_PropertyStub
, JS_PropertyStub
,
1504 JSPROP_PERMANENT
| JSPROP_READONLY
, 0, 0, NULL
)) {
1511 for (uintN i
= 0; i
< JS_ARRAY_LENGTH(lazy_function_props
); i
++) {
1512 LazyFunctionProp
*lfp
= &lazy_function_props
[i
];
1514 atom
= OFFSET_TO_ATOM(cx
->runtime
, lfp
->atomOffset
);
1515 if (id
== ATOM_KEY(atom
)) {
1516 JS_ASSERT(!IsInternalFunctionObject(obj
));
1518 if (!js_DefineNativeProperty(cx
, obj
,
1519 ATOM_TO_JSID(atom
), JSVAL_VOID
,
1520 fun_getProperty
, JS_PropertyStub
,
1521 lfp
->attrs
, JSScopeProperty::HAS_SHORTID
,
1522 lfp
->tinyid
, NULL
)) {
1535 /* XXX store parent and proto, if defined */
1537 js_XDRFunctionObject(JSXDRState
*xdr
, JSObject
**objp
)
1541 uint32 firstword
; /* flag telling whether fun->atom is non-null,
1542 plus for fun->u.i.skipmin, fun->u.i.wrapper,
1543 and 14 bits reserved for future use */
1544 uintN nargs
, nvars
, nupvars
, n
;
1545 uint32 localsword
; /* word for argument and variable counts */
1546 uint32 flagsword
; /* word for fun->u.i.nupvars and fun->flags */
1549 if (xdr
->mode
== JSXDR_ENCODE
) {
1550 fun
= GET_FUNCTION_PRIVATE(cx
, *objp
);
1551 if (!FUN_INTERPRETED(fun
)) {
1552 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1553 JSMSG_NOT_SCRIPTED_FUNCTION
,
1554 JS_GetFunctionName(fun
));
1557 if (fun
->u
.i
.wrapper
) {
1558 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1559 JSMSG_XDR_CLOSURE_WRAPPER
,
1560 JS_GetFunctionName(fun
));
1563 JS_ASSERT((fun
->u
.i
.wrapper
& ~1U) == 0);
1564 firstword
= (fun
->u
.i
.skipmin
<< 2) | (fun
->u
.i
.wrapper
<< 1) | !!fun
->atom
;
1566 nvars
= fun
->u
.i
.nvars
;
1567 nupvars
= fun
->u
.i
.nupvars
;
1568 localsword
= (nargs
<< 16) | nvars
;
1569 flagsword
= (nupvars
<< 16) | fun
->flags
;
1571 fun
= js_NewFunction(cx
, NULL
, NULL
, 0, JSFUN_INTERPRETED
, NULL
, NULL
);
1574 FUN_OBJECT(fun
)->clearParent();
1575 FUN_OBJECT(fun
)->clearProto();
1577 nvars
= nargs
= nupvars
= 0; /* quell GCC uninitialized warning */
1581 AutoValueRooter
tvr(cx
, FUN_OBJECT(fun
));
1583 if (!JS_XDRUint32(xdr
, &firstword
))
1585 if ((firstword
& 1U) && !js_XDRStringAtom(xdr
, &fun
->atom
))
1587 if (!JS_XDRUint32(xdr
, &localsword
) ||
1588 !JS_XDRUint32(xdr
, &flagsword
)) {
1592 if (xdr
->mode
== JSXDR_DECODE
) {
1593 nargs
= localsword
>> 16;
1594 nvars
= uint16(localsword
);
1595 JS_ASSERT((flagsword
& JSFUN_KINDMASK
) >= JSFUN_INTERPRETED
);
1596 nupvars
= flagsword
>> 16;
1597 fun
->flags
= uint16(flagsword
);
1598 fun
->u
.i
.skipmin
= uint16(firstword
>> 2);
1599 fun
->u
.i
.wrapper
= JSPackedBool((firstword
>> 1) & 1);
1602 /* do arguments and local vars */
1603 n
= nargs
+ nvars
+ nupvars
;
1605 JSArenaPool
&tempPool
= xdr
->cx
->tempPool
;
1606 void *mark
= tempPool
.getMark();
1610 JSLocalKind localKind
;
1614 * From this point the control must flow via the label release_mark.
1616 * To xdr the names we prefix the names with a bitmap descriptor and
1617 * then xdr the names as strings. For argument names (indexes below
1618 * nargs) the corresponding bit in the bitmap is unset when the name
1619 * is null. Such null names are not encoded or decoded. For variable
1620 * names (indexes starting from nargs) bitmap's bit is set when the
1621 * name is declared as const, not as ordinary var.
1623 MUST_FLOW_THROUGH("release_mark");
1624 uintN bitmapLength
= JS_HOWMANY(n
, JS_BITS_PER_UINT32
);
1626 tempPool
.allocateCast
<uint32
*>(bitmap
, bitmapLength
* sizeof *bitmap
);
1628 js_ReportOutOfScriptQuota(xdr
->cx
);
1632 if (xdr
->mode
== JSXDR_ENCODE
) {
1633 names
= js_GetLocalNameArray(xdr
->cx
, fun
, &tempPool
);
1638 PodZero(bitmap
, bitmapLength
);
1639 for (i
= 0; i
!= n
; ++i
) {
1641 ? JS_LOCAL_NAME_TO_ATOM(names
[i
]) != NULL
1642 : JS_LOCAL_NAME_IS_CONST(names
[i
])) {
1643 bitmap
[i
>> JS_BITS_PER_UINT32_LOG2
] |=
1644 JS_BIT(i
& (JS_BITS_PER_UINT32
- 1));
1650 names
= NULL
; /* quell GCC uninitialized warning */
1653 for (i
= 0; i
!= bitmapLength
; ++i
) {
1654 ok
= !!JS_XDRUint32(xdr
, &bitmap
[i
]);
1658 for (i
= 0; i
!= n
; ++i
) {
1660 !(bitmap
[i
>> JS_BITS_PER_UINT32_LOG2
] &
1661 JS_BIT(i
& (JS_BITS_PER_UINT32
- 1)))) {
1662 if (xdr
->mode
== JSXDR_DECODE
) {
1663 ok
= !!js_AddLocal(xdr
->cx
, fun
, NULL
, JSLOCAL_ARG
);
1667 JS_ASSERT(!JS_LOCAL_NAME_TO_ATOM(names
[i
]));
1671 if (xdr
->mode
== JSXDR_ENCODE
)
1672 name
= JS_LOCAL_NAME_TO_ATOM(names
[i
]);
1673 ok
= !!js_XDRStringAtom(xdr
, &name
);
1676 if (xdr
->mode
== JSXDR_DECODE
) {
1677 localKind
= (i
< nargs
)
1679 : (i
< nargs
+ nvars
)
1680 ? (bitmap
[i
>> JS_BITS_PER_UINT32_LOG2
] &
1681 JS_BIT(i
& (JS_BITS_PER_UINT32
- 1))
1685 ok
= !!js_AddLocal(xdr
->cx
, fun
, name
, localKind
);
1692 tempPool
.release(mark
);
1696 if (xdr
->mode
== JSXDR_DECODE
)
1697 js_FreezeLocalNames(cx
, fun
);
1700 if (!js_XDRScript(xdr
, &fun
->u
.i
.script
, false, NULL
))
1703 if (xdr
->mode
== JSXDR_DECODE
) {
1704 *objp
= FUN_OBJECT(fun
);
1705 if (fun
->u
.i
.script
!= JSScript::emptyScript()) {
1706 #ifdef CHECK_SCRIPT_OWNER
1707 fun
->u
.i
.script
->owner
= NULL
;
1709 js_CallNewScriptHook(cx
, fun
->u
.i
.script
, fun
);
1716 #else /* !JS_HAS_XDR */
1718 #define js_XDRFunctionObject NULL
1720 #endif /* !JS_HAS_XDR */
1723 * [[HasInstance]] internal method for Function objects: fetch the .prototype
1724 * property of its 'this' parameter, and walks the prototype chain of v (only
1725 * if v is an object) returning true if .prototype is found.
1728 fun_hasInstance(JSContext
*cx
, JSObject
*obj
, jsval v
, JSBool
*bp
)
1731 jsid id
= ATOM_TO_JSID(cx
->runtime
->atomState
.classPrototypeAtom
);
1732 if (!obj
->getProperty(cx
, id
, &pval
))
1735 if (JSVAL_IS_PRIMITIVE(pval
)) {
1737 * Throw a runtime error if instanceof is called on a function that
1738 * has a non-object as its .prototype value.
1740 js_ReportValueError(cx
, JSMSG_BAD_PROTOTYPE
,
1741 -1, OBJECT_TO_JSVAL(obj
), NULL
);
1745 return js_IsDelegate(cx
, JSVAL_TO_OBJECT(pval
), v
, bp
);
1749 TraceLocalNames(JSTracer
*trc
, JSFunction
*fun
);
1752 DestroyLocalNames(JSContext
*cx
, JSFunction
*fun
);
1755 fun_trace(JSTracer
*trc
, JSObject
*obj
)
1757 /* A newborn function object may have a not yet initialized private slot. */
1758 JSFunction
*fun
= (JSFunction
*) obj
->getPrivate();
1762 if (FUN_OBJECT(fun
) != obj
) {
1763 /* obj is cloned function object, trace the original. */
1764 JS_CALL_TRACER(trc
, FUN_OBJECT(fun
), JSTRACE_OBJECT
, "private");
1768 JS_CALL_STRING_TRACER(trc
, ATOM_TO_STRING(fun
->atom
), "atom");
1769 if (FUN_INTERPRETED(fun
)) {
1770 if (fun
->u
.i
.script
)
1771 js_TraceScript(trc
, fun
->u
.i
.script
);
1772 TraceLocalNames(trc
, fun
);
1777 fun_finalize(JSContext
*cx
, JSObject
*obj
)
1779 /* Ignore newborn and cloned function objects. */
1780 JSFunction
*fun
= (JSFunction
*) obj
->getPrivate();
1781 if (!fun
|| FUN_OBJECT(fun
) != obj
)
1785 * Null-check of u.i.script is required since the parser sets interpreted
1788 if (FUN_INTERPRETED(fun
)) {
1789 if (fun
->u
.i
.script
)
1790 js_DestroyScript(cx
, fun
->u
.i
.script
);
1791 DestroyLocalNames(cx
, fun
);
1796 JSFunction::sharpSlotBase(JSContext
*cx
)
1798 #if JS_HAS_SHARP_VARS
1799 JSAtom
*name
= js_Atomize(cx
, "#array", 6, 0);
1801 uintN index
= uintN(-1);
1805 js_LookupLocal(cx
, this, name
, &index
);
1806 JS_ASSERT(kind
== JSLOCAL_VAR
);
1814 JSFunction::countInterpretedReservedSlots() const
1816 JS_ASSERT(FUN_INTERPRETED(this));
1818 return (u
.i
.nupvars
== 0) ? 0 : u
.i
.script
->upvars()->length
;
1822 * Reserve two slots in all function objects for XPConnect. Note that this
1823 * does not bloat every instance, only those on which reserved slots are set,
1824 * and those on which ad-hoc properties are defined.
1826 JS_FRIEND_DATA(JSClass
) js_FunctionClass
= {
1828 JSCLASS_HAS_PRIVATE
| JSCLASS_NEW_RESOLVE
| JSCLASS_HAS_RESERVED_SLOTS(2) |
1829 JSCLASS_MARK_IS_TRACE
| JSCLASS_HAS_CACHED_PROTO(JSProto_Function
),
1830 JS_PropertyStub
, JS_PropertyStub
,
1831 JS_PropertyStub
, JS_PropertyStub
,
1832 fun_enumerate
, (JSResolveOp
)fun_resolve
,
1833 JS_ConvertStub
, fun_finalize
,
1836 js_XDRFunctionObject
, fun_hasInstance
,
1837 JS_CLASS_TRACE(fun_trace
), NULL
1843 fun_toStringHelper(JSContext
*cx
, JSObject
*obj
, uintN indent
)
1845 if (!obj
->isFunction()) {
1846 if (obj
->isFunctionProxy())
1847 return JSProxy::fun_toString(cx
, obj
, indent
);
1848 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1849 JSMSG_INCOMPATIBLE_PROTO
,
1850 js_Function_str
, js_toString_str
,
1855 JSFunction
*fun
= GET_FUNCTION_PRIVATE(cx
, obj
);
1858 return JS_DecompileFunction(cx
, fun
, indent
);
1864 fun_toString(JSContext
*cx
, uintN argc
, jsval
*vp
)
1866 JS_ASSERT(VALUE_IS_FUNCTION(cx
, vp
[0]));
1867 uint32_t indent
= 0;
1869 if (argc
!= 0 && !ValueToECMAUint32(cx
, vp
[2], &indent
))
1872 JSObject
*obj
= JS_THIS_OBJECT(cx
, vp
);
1876 JSString
*str
= fun_toStringHelper(cx
, obj
, indent
);
1880 *vp
= STRING_TO_JSVAL(str
);
1886 fun_toSource(JSContext
*cx
, uintN argc
, jsval
*vp
)
1888 JS_ASSERT(VALUE_IS_FUNCTION(cx
, vp
[0]));
1890 JSObject
*obj
= JS_THIS_OBJECT(cx
, vp
);
1894 JSString
*str
= fun_toStringHelper(cx
, obj
, JS_DONT_PRETTY_PRINT
);
1898 *vp
= STRING_TO_JSVAL(str
);
1904 js_fun_call(JSContext
*cx
, uintN argc
, jsval
*vp
)
1913 obj
= JS_THIS_OBJECT(cx
, vp
);
1918 if (!js_IsCallable(fval
)) {
1919 str
= JS_ValueToString(cx
, fval
);
1921 const char *bytes
= js_GetStringBytes(cx
, str
);
1924 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1925 JSMSG_INCOMPATIBLE_PROTO
,
1926 js_Function_str
, js_call_str
,
1935 /* Call fun with its global object as the 'this' param if no args. */
1938 /* Otherwise convert the first arg to 'this' and skip over it. */
1939 if (!JSVAL_IS_PRIMITIVE(argv
[0]))
1940 obj
= JSVAL_TO_OBJECT(argv
[0]);
1941 else if (!js_ValueToObject(cx
, argv
[0], &obj
))
1947 /* Allocate stack space for fval, obj, and the args. */
1948 InvokeArgsGuard args
;
1949 if (!cx
->stack().pushInvokeArgs(cx
, argc
, args
))
1952 /* Push fval, obj, and the args. */
1953 args
.getvp()[0] = fval
;
1954 args
.getvp()[1] = OBJECT_TO_JSVAL(obj
);
1955 memcpy(args
.getvp() + 2, argv
, argc
* sizeof *argv
);
1957 ok
= js_Invoke(cx
, args
, 0);
1958 *vp
= *args
.getvp();
1963 js_fun_apply(JSContext
*cx
, uintN argc
, jsval
*vp
)
1965 JSObject
*obj
, *aobj
;
1973 /* Will get globalObject as 'this' and no other arguments. */
1974 return js_fun_call(cx
, argc
, vp
);
1979 obj
= JS_THIS_OBJECT(cx
, vp
);
1984 if (!js_IsCallable(fval
)) {
1985 str
= JS_ValueToString(cx
, fval
);
1987 const char *bytes
= js_GetStringBytes(cx
, str
);
1990 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
1991 JSMSG_INCOMPATIBLE_PROTO
,
1992 js_Function_str
, js_apply_str
,
1999 /* Quell GCC overwarnings. */
2004 /* If the 2nd arg is null or void, call the function with 0 args. */
2005 if (JSVAL_IS_NULL(vp
[3]) || JSVAL_IS_VOID(vp
[3])) {
2008 /* The second arg must be an array (or arguments object). */
2009 arraylike
= JS_FALSE
;
2010 if (!JSVAL_IS_PRIMITIVE(vp
[3])) {
2011 aobj
= JSVAL_TO_OBJECT(vp
[3]);
2012 if (!js_IsArrayLike(cx
, aobj
, &arraylike
, &length
))
2016 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
2017 JSMSG_BAD_APPLY_ARGS
, js_apply_str
);
2023 /* Convert the first arg to 'this' and skip over it. */
2024 if (!JSVAL_IS_PRIMITIVE(vp
[2]))
2025 obj
= JSVAL_TO_OBJECT(vp
[2]);
2026 else if (!js_ValueToObject(cx
, vp
[2], &obj
))
2029 /* Allocate stack space for fval, obj, and the args. */
2030 argc
= (uintN
)JS_MIN(length
, JS_ARGS_LENGTH_MAX
);
2032 InvokeArgsGuard args
;
2033 if (!cx
->stack().pushInvokeArgs(cx
, argc
, args
))
2036 /* Push fval, obj, and aobj's elements as args. */
2039 *sp
++ = OBJECT_TO_JSVAL(obj
);
2040 if (aobj
&& aobj
->isArguments() && !aobj
->isArgsLengthOverridden()) {
2042 * Two cases, two loops: note how in the case of an active stack frame
2043 * backing aobj, even though we copy from fp->argv, we still must check
2044 * aobj->getArgsElement(i) for a hole, to handle a delete on the
2045 * corresponding arguments element. See args_delProperty.
2047 JSStackFrame
*fp
= (JSStackFrame
*) aobj
->getPrivate();
2049 memcpy(sp
, fp
->argv
, argc
* sizeof(jsval
));
2050 for (i
= 0; i
< argc
; i
++) {
2051 if (aobj
->getArgsElement(i
) == JSVAL_HOLE
) // suppress deleted element
2055 for (i
= 0; i
< argc
; i
++) {
2056 sp
[i
] = aobj
->getArgsElement(i
);
2057 if (sp
[i
] == JSVAL_HOLE
)
2062 for (i
= 0; i
< argc
; i
++) {
2063 if (!aobj
->getProperty(cx
, INT_TO_JSID(jsint(i
)), sp
))
2069 JSBool ok
= js_Invoke(cx
, args
, 0);
2070 *vp
= *args
.getvp();
2075 static JS_REQUIRES_STACK JSBool
2076 fun_applyConstructor(JSContext
*cx
, uintN argc
, jsval
*vp
)
2081 if (JSVAL_IS_PRIMITIVE(vp
[2]) ||
2082 (aobj
= JSVAL_TO_OBJECT(vp
[2]),
2084 !aobj
->isArguments())) {
2085 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
2086 JSMSG_BAD_APPLY_ARGS
, "__applyConstruct__");
2090 if (!js_GetLengthProperty(cx
, aobj
, &length
))
2093 if (length
> JS_ARGS_LENGTH_MAX
)
2094 length
= JS_ARGS_LENGTH_MAX
;
2096 InvokeArgsGuard args
;
2097 if (!cx
->stack().pushInvokeArgs(cx
, length
, args
))
2100 jsval
*sp
= args
.getvp();
2102 *sp
++ = JSVAL_NULL
; /* this is filled automagically */
2103 for (i
= 0; i
< length
; i
++) {
2104 if (!aobj
->getProperty(cx
, INT_TO_JSID(jsint(i
)), sp
))
2109 JSBool ok
= js_InvokeConstructor(cx
, args
);
2110 *vp
= *args
.getvp();
2115 static JSFunctionSpec function_methods
[] = {
2117 JS_FN(js_toSource_str
, fun_toSource
, 0,0),
2119 JS_FN(js_toString_str
, fun_toString
, 0,0),
2120 JS_FN(js_apply_str
, js_fun_apply
, 2,0),
2121 JS_FN(js_call_str
, js_fun_call
, 1,0),
2123 JS_FN("__applyConstructor__", fun_applyConstructor
, 1,0),
2129 Function(JSContext
*cx
, JSObject
*obj
, uintN argc
, jsval
*argv
, jsval
*rval
)
2133 JSStackFrame
*fp
, *caller
;
2136 const char *filename
;
2138 JSString
*str
, *arg
;
2140 JSPrincipals
*principals
;
2141 jschar
*collected_args
, *cp
;
2143 size_t arg_length
, args_length
, old_args_length
;
2146 if (!JS_IsConstructing(cx
)) {
2147 obj
= NewObject(cx
, &js_FunctionClass
, NULL
, NULL
);
2150 *rval
= OBJECT_TO_JSVAL(obj
);
2153 * The constructor is called before the private slot is initialized so
2154 * we must use getPrivate, not GET_FUNCTION_PRIVATE here.
2156 if (obj
->getPrivate())
2161 * NB: (new Function) is not lexically closed by its caller, it's just an
2162 * anonymous function in the top-level scope that its constructor inhabits.
2163 * Thus 'var x = 42; f = new Function("return x"); print(f())' prints 42,
2164 * and so would a call to f from another top-level's script or function.
2166 * In older versions, before call objects, a new Function was adopted by
2167 * its running context's globalObject, which might be different from the
2168 * top-level reachable from scopeChain (in HTML frames, e.g.).
2170 parent
= JSVAL_TO_OBJECT(argv
[-2])->getParent();
2172 fun
= js_NewFunction(cx
, obj
, NULL
, 0, JSFUN_LAMBDA
| JSFUN_INTERPRETED
,
2173 parent
, cx
->runtime
->atomState
.anonymousAtom
);
2179 * Function is static and not called directly by other functions in this
2180 * file, therefore it is callable only as a native function by js_Invoke.
2181 * Find the scripted caller, possibly skipping other native frames such as
2182 * are built for Function.prototype.call or .apply activations that invoke
2183 * Function indirectly from a script.
2185 fp
= js_GetTopStackFrame(cx
);
2186 JS_ASSERT(!fp
->script
&& fp
->fun
&& fp
->fun
->u
.n
.native
== Function
);
2187 caller
= js_GetScriptedCaller(cx
, fp
);
2189 principals
= JS_EvalFramePrincipals(cx
, fp
, caller
);
2190 filename
= js_ComputeFilename(cx
, caller
, principals
, &lineno
);
2197 /* Belt-and-braces: check that the caller has access to parent. */
2198 if (!js_CheckPrincipalsAccess(cx
, parent
, principals
,
2199 CLASS_ATOM(cx
, Function
))) {
2204 * CSP check: whether new Function() is allowed at all.
2205 * Report errors via CSP is done in the script security manager.
2206 * js_CheckContentSecurityPolicy is defined in jsobj.cpp
2208 if (!js_CheckContentSecurityPolicy(cx
)) {
2209 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
2210 JSMSG_CSP_BLOCKED_FUNCTION
);
2214 n
= argc
? argc
- 1 : 0;
2216 enum { OK
, BAD
, BAD_FORMAL
} state
;
2219 * Collect the function-argument arguments into one string, separated
2220 * by commas, then make a tokenstream from that string, and scan it to
2221 * get the arguments. We need to throw the full scanner at the
2222 * problem, because the argument string can legitimately contain
2223 * comments and linefeeds. XXX It might be better to concatenate
2224 * everything up into a function definition and pass it to the
2225 * compiler, but doing it this way is less of a delta from the old
2226 * code. See ECMA 15.3.2.1.
2230 for (i
= 0; i
< n
; i
++) {
2231 /* Collect the lengths for all the function-argument arguments. */
2232 arg
= js_ValueToString(cx
, argv
[i
]);
2235 argv
[i
] = STRING_TO_JSVAL(arg
);
2238 * Check for overflow. The < test works because the maximum
2239 * JSString length fits in 2 fewer bits than size_t has.
2241 old_args_length
= args_length
;
2242 args_length
= old_args_length
+ arg
->length();
2243 if (args_length
< old_args_length
) {
2244 js_ReportAllocationOverflow(cx
);
2249 /* Add 1 for each joining comma and check for overflow (two ways). */
2250 old_args_length
= args_length
;
2251 args_length
= old_args_length
+ n
- 1;
2252 if (args_length
< old_args_length
||
2253 args_length
>= ~(size_t)0 / sizeof(jschar
)) {
2254 js_ReportAllocationOverflow(cx
);
2259 * Allocate a string to hold the concatenated arguments, including room
2260 * for a terminating 0. Mark cx->tempPool for later release, to free
2261 * collected_args and its tokenstream in one swoop.
2263 mark
= cx
->tempPool
.getMark();
2264 cx
->tempPool
.allocateCast
<jschar
*>(cp
, (args_length
+ 1) * sizeof(jschar
));
2266 js_ReportOutOfScriptQuota(cx
);
2269 collected_args
= cp
;
2272 * Concatenate the arguments into the new string, separated by commas.
2274 for (i
= 0; i
< n
; i
++) {
2275 arg
= JSVAL_TO_STRING(argv
[i
]);
2276 arg_length
= arg
->length();
2277 (void) js_strncpy(cp
, arg
->chars(), arg_length
);
2280 /* Add separating comma or terminating 0. */
2281 *cp
++ = (i
+ 1 < n
) ? ',' : 0;
2284 /* Initialize a tokenstream that reads from the given string. */
2285 if (!ts
.init(collected_args
, args_length
, NULL
, filename
, lineno
)) {
2286 cx
->tempPool
.release(mark
);
2290 /* The argument string may be empty or contain no tokens. */
2292 if (tt
!= TOK_EOF
) {
2295 * Check that it's a name. This also implicitly guards against
2296 * TOK_ERROR, which was already reported.
2302 * Get the atom corresponding to the name from the token
2303 * stream; we're assured at this point that it's a valid
2306 atom
= ts
.currentToken().t_atom
;
2308 /* Check for a duplicate parameter name. */
2309 if (js_LookupLocal(cx
, fun
, atom
, NULL
) != JSLOCAL_NONE
) {
2312 name
= js_AtomToPrintableString(cx
, atom
);
2313 ok
= name
&& ReportCompileErrorNumber(cx
, &ts
, NULL
,
2314 JSREPORT_WARNING
| JSREPORT_STRICT
,
2315 JSMSG_DUPLICATE_FORMAL
, name
);
2319 if (!js_AddLocal(cx
, fun
, atom
, JSLOCAL_ARG
))
2323 * Get the next token. Stop on end of stream. Otherwise
2324 * insist on a comma, get another name, and iterate.
2329 if (tt
!= TOK_COMMA
)
2337 if (state
== BAD_FORMAL
&& !ts
.isError()) {
2339 * Report "malformed formal parameter" iff no illegal char or
2340 * similar scanner error was already reported.
2342 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
2346 cx
->tempPool
.release(mark
);
2352 str
= js_ValueToString(cx
, argv
[argc
-1]);
2355 argv
[argc
-1] = STRING_TO_JSVAL(str
);
2357 str
= cx
->runtime
->emptyString
;
2360 return Compiler::compileFunctionBody(cx
, fun
, principals
,
2361 str
->chars(), str
->length(),
2366 js_InitFunctionClass(JSContext
*cx
, JSObject
*obj
)
2371 proto
= JS_InitClass(cx
, obj
, NULL
, &js_FunctionClass
, Function
, 1,
2372 NULL
, function_methods
, NULL
, NULL
);
2375 fun
= js_NewFunction(cx
, proto
, NULL
, 0, JSFUN_INTERPRETED
, obj
, NULL
);
2378 fun
->u
.i
.script
= JSScript::emptyScript();
2383 js_NewFunction(JSContext
*cx
, JSObject
*funobj
, JSNative native
, uintN nargs
,
2384 uintN flags
, JSObject
*parent
, JSAtom
*atom
)
2389 JS_ASSERT(funobj
->isFunction());
2390 funobj
->setParent(parent
);
2392 funobj
= NewObject(cx
, &js_FunctionClass
, NULL
, parent
);
2396 JS_ASSERT(!funobj
->getPrivate());
2397 fun
= (JSFunction
*) funobj
;
2399 /* Initialize all function members. */
2400 fun
->nargs
= uint16(nargs
);
2401 fun
->flags
= flags
& (JSFUN_FLAGS_MASK
| JSFUN_KINDMASK
| JSFUN_TRCINFO
);
2402 if ((flags
& JSFUN_KINDMASK
) >= JSFUN_INTERPRETED
) {
2404 JS_ASSERT(nargs
== 0);
2406 fun
->u
.i
.nupvars
= 0;
2407 fun
->u
.i
.skipmin
= 0;
2408 fun
->u
.i
.wrapper
= false;
2409 fun
->u
.i
.script
= NULL
;
2411 fun
->u
.i
.names
.taggedAtom
= 0;
2416 fun
->u
.n
.clasp
= NULL
;
2417 if (flags
& JSFUN_TRCINFO
) {
2419 JSNativeTraceInfo
*trcinfo
=
2420 JS_FUNC_TO_DATA_PTR(JSNativeTraceInfo
*, native
);
2421 fun
->u
.n
.native
= (JSNative
) trcinfo
->native
;
2422 fun
->u
.n
.trcinfo
= trcinfo
;
2424 fun
->u
.n
.trcinfo
= NULL
;
2427 fun
->u
.n
.native
= native
;
2428 fun
->u
.n
.trcinfo
= NULL
;
2430 JS_ASSERT(fun
->u
.n
.native
);
2434 /* Set private to self to indicate non-cloned fully initialized function. */
2435 FUN_OBJECT(fun
)->setPrivate(fun
);
2439 JSObject
* JS_FASTCALL
2440 js_CloneFunctionObject(JSContext
*cx
, JSFunction
*fun
, JSObject
*parent
,
2447 * The cloned function object does not need the extra JSFunction members
2448 * beyond JSObject as it points to fun via the private slot.
2450 JSObject
*clone
= NewNativeClassInstance(cx
, &js_FunctionClass
, proto
, parent
);
2453 clone
->setPrivate(fun
);
2458 JS_DEFINE_CALLINFO_4(extern, OBJECT
, js_CloneFunctionObject
, CONTEXT
, FUNCTION
, OBJECT
, OBJECT
, 0,
2459 nanojit::ACC_STORE_ANY
)
2463 * Create a new flat closure, but don't initialize the imported upvar
2464 * values. The tracer calls this function and then initializes the upvar
2467 JSObject
* JS_FASTCALL
2468 js_AllocFlatClosure(JSContext
*cx
, JSFunction
*fun
, JSObject
*scopeChain
)
2470 JS_ASSERT(FUN_FLAT_CLOSURE(fun
));
2471 JS_ASSERT((fun
->u
.i
.script
->upvarsOffset
2472 ? fun
->u
.i
.script
->upvars()->length
2473 : 0) == fun
->u
.i
.nupvars
);
2475 JSObject
*closure
= CloneFunctionObject(cx
, fun
, scopeChain
);
2479 uint32 nslots
= fun
->countInterpretedReservedSlots();
2482 if (!js_EnsureReservedSlots(cx
, closure
, nslots
))
2488 JS_DEFINE_CALLINFO_3(extern, OBJECT
, js_AllocFlatClosure
,
2489 CONTEXT
, FUNCTION
, OBJECT
, 0, nanojit::ACC_STORE_ANY
)
2491 JS_REQUIRES_STACK JSObject
*
2492 js_NewFlatClosure(JSContext
*cx
, JSFunction
*fun
)
2495 * Flat closures can be partial, they may need to search enclosing scope
2496 * objects via JSOP_NAME, etc.
2498 JSObject
*scopeChain
= js_GetScopeChain(cx
, cx
->fp
);
2502 JSObject
*closure
= js_AllocFlatClosure(cx
, fun
, scopeChain
);
2503 if (!closure
|| fun
->u
.i
.nupvars
== 0)
2506 JSUpvarArray
*uva
= fun
->u
.i
.script
->upvars();
2507 JS_ASSERT(uva
->length
<= size_t(closure
->dslots
[-1]));
2509 uintN level
= fun
->u
.i
.script
->staticLevel
;
2510 for (uint32 i
= 0, n
= uva
->length
; i
< n
; i
++)
2511 closure
->dslots
[i
] = js_GetUpvar(cx
, level
, uva
->vector
[i
]);
2517 js_NewDebuggableFlatClosure(JSContext
*cx
, JSFunction
*fun
)
2519 JS_ASSERT(cx
->fp
->fun
->flags
& JSFUN_HEAVYWEIGHT
);
2520 JS_ASSERT(!cx
->fp
->fun
->optimizedClosure());
2521 JS_ASSERT(FUN_FLAT_CLOSURE(fun
));
2523 return WrapEscapingClosure(cx
, cx
->fp
, FUN_OBJECT(fun
), fun
);
2527 js_DefineFunction(JSContext
*cx
, JSObject
*obj
, JSAtom
*atom
, JSNative native
,
2528 uintN nargs
, uintN attrs
)
2533 if (attrs
& JSFUN_STUB_GSOPS
) {
2535 * JSFUN_STUB_GSOPS is a request flag only, not stored in fun->flags or
2536 * the defined property's attributes. This allows us to encode another,
2537 * internal flag using the same bit, JSFUN_EXPR_CLOSURE -- see jsfun.h
2540 attrs
&= ~JSFUN_STUB_GSOPS
;
2541 gsop
= JS_PropertyStub
;
2545 fun
= js_NewFunction(cx
, NULL
, native
, nargs
, attrs
, obj
, atom
);
2548 if (!obj
->defineProperty(cx
, ATOM_TO_JSID(atom
), OBJECT_TO_JSVAL(FUN_OBJECT(fun
)),
2549 gsop
, gsop
, attrs
& ~JSFUN_FLAGS_MASK
)) {
2555 #if (JSV2F_CONSTRUCT & JSV2F_SEARCH_STACK)
2556 # error "JSINVOKE_CONSTRUCT and JSV2F_SEARCH_STACK are not disjoint!"
2560 js_ValueToFunction(JSContext
*cx
, jsval
*vp
, uintN flags
)
2565 if (!VALUE_IS_FUNCTION(cx
, v
)) {
2566 js_ReportIsNotFunction(cx
, vp
, flags
);
2569 return GET_FUNCTION_PRIVATE(cx
, JSVAL_TO_OBJECT(v
));
2573 js_ValueToFunctionObject(JSContext
*cx
, jsval
*vp
, uintN flags
)
2576 JSStackFrame
*caller
;
2577 JSPrincipals
*principals
;
2579 if (VALUE_IS_FUNCTION(cx
, *vp
))
2580 return JSVAL_TO_OBJECT(*vp
);
2582 fun
= js_ValueToFunction(cx
, vp
, flags
);
2585 *vp
= OBJECT_TO_JSVAL(FUN_OBJECT(fun
));
2587 caller
= js_GetScriptedCaller(cx
, NULL
);
2589 principals
= JS_StackFramePrincipals(cx
, caller
);
2591 /* No scripted caller, don't allow access. */
2595 if (!js_CheckPrincipalsAccess(cx
, FUN_OBJECT(fun
), principals
,
2598 : cx
->runtime
->atomState
.anonymousAtom
)) {
2601 return FUN_OBJECT(fun
);
2605 js_ValueToCallableObject(JSContext
*cx
, jsval
*vp
, uintN flags
)
2607 JSObject
*callable
= JSVAL_IS_OBJECT(*vp
) ? JSVAL_TO_OBJECT(*vp
) : NULL
;
2609 if (callable
&& callable
->isCallable()) {
2610 *vp
= OBJECT_TO_JSVAL(callable
);
2613 return js_ValueToFunctionObject(cx
, vp
, flags
);
2617 js_ReportIsNotFunction(JSContext
*cx
, jsval
*vp
, uintN flags
)
2619 const char *name
= NULL
, *source
= NULL
;
2620 AutoValueRooter
tvr(cx
);
2621 uintN error
= (flags
& JSV2F_CONSTRUCT
) ? JSMSG_NOT_CONSTRUCTOR
: JSMSG_NOT_FUNCTION
;
2623 FrameRegsIter
i(cx
);
2624 while (!i
.done() && !i
.pc())
2628 (!i
.done() && StackBase(i
.fp()) <= vp
&& vp
< i
.sp())
2630 : ((flags
& JSV2F_SEARCH_STACK
) ? JSDVG_SEARCH_STACK
: JSDVG_IGNORE_STACK
);
2632 js_ReportValueError3(cx
, error
, spindex
, *vp
, NULL
, name
, source
);
2636 * When a function has between 2 and MAX_ARRAY_LOCALS arguments and variables,
2637 * their name are stored as the JSLocalNames.array.
2639 #define MAX_ARRAY_LOCALS 8
2641 JS_STATIC_ASSERT(2 <= MAX_ARRAY_LOCALS
);
2642 JS_STATIC_ASSERT(MAX_ARRAY_LOCALS
< JS_BITMASK(16));
2645 * We use the lowest bit of the string atom to distinguish const from var
2646 * name when there is only single name or when names are stored as an array.
2648 JS_STATIC_ASSERT((JSVAL_STRING
& 1) == 0);
2651 * When we use a hash table to store the local names, we use a singly linked
2652 * list to record the indexes of duplicated parameter names to preserve the
2653 * duplicates for the decompiler.
2655 typedef struct JSNameIndexPair JSNameIndexPair
;
2657 struct JSNameIndexPair
{
2660 JSNameIndexPair
*link
;
2663 struct JSLocalNameMap
{
2665 JSNameIndexPair
*lastdup
;
2668 typedef struct JSLocalNameHashEntry
{
2669 JSDHashEntryHdr hdr
;
2673 } JSLocalNameHashEntry
;
2676 FreeLocalNameHash(JSContext
*cx
, JSLocalNameMap
*map
)
2678 JSNameIndexPair
*dup
, *next
;
2680 for (dup
= map
->lastdup
; dup
; dup
= next
) {
2684 JS_DHashTableFinish(&map
->names
);
2689 HashLocalName(JSContext
*cx
, JSLocalNameMap
*map
, JSAtom
*name
,
2690 JSLocalKind localKind
, uintN index
)
2692 JSLocalNameHashEntry
*entry
;
2693 JSNameIndexPair
*dup
;
2695 JS_ASSERT(index
<= JS_BITMASK(16));
2696 #if JS_HAS_DESTRUCTURING
2698 /* A destructuring pattern does not need a hash entry. */
2699 JS_ASSERT(localKind
== JSLOCAL_ARG
);
2703 JS_ASSERT(ATOM_IS_STRING(name
));
2704 entry
= (JSLocalNameHashEntry
*)
2705 JS_DHashTableOperate(&map
->names
, name
, JS_DHASH_ADD
);
2707 JS_ReportOutOfMemory(cx
);
2711 JS_ASSERT(entry
->name
== name
);
2712 JS_ASSERT(entry
->localKind
== JSLOCAL_ARG
&& localKind
== JSLOCAL_ARG
);
2713 dup
= (JSNameIndexPair
*) cx
->malloc(sizeof *dup
);
2716 dup
->name
= entry
->name
;
2717 dup
->index
= entry
->index
;
2718 dup
->link
= map
->lastdup
;
2722 entry
->index
= (uint16
) index
;
2723 entry
->localKind
= (uint8
) localKind
;
2728 js_AddLocal(JSContext
*cx
, JSFunction
*fun
, JSAtom
*atom
, JSLocalKind kind
)
2734 JSLocalNameMap
*map
;
2736 JS_ASSERT(FUN_INTERPRETED(fun
));
2737 JS_ASSERT(!fun
->u
.i
.script
);
2738 JS_ASSERT(((jsuword
) atom
& 1) == 0);
2739 taggedAtom
= (jsuword
) atom
;
2740 if (kind
== JSLOCAL_ARG
) {
2741 indexp
= &fun
->nargs
;
2742 } else if (kind
== JSLOCAL_UPVAR
) {
2743 indexp
= &fun
->u
.i
.nupvars
;
2745 indexp
= &fun
->u
.i
.nvars
;
2746 if (kind
== JSLOCAL_CONST
)
2749 JS_ASSERT(kind
== JSLOCAL_VAR
);
2751 n
= fun
->countLocalNames();
2753 JS_ASSERT(fun
->u
.i
.names
.taggedAtom
== 0);
2754 fun
->u
.i
.names
.taggedAtom
= taggedAtom
;
2755 } else if (n
< MAX_ARRAY_LOCALS
) {
2757 array
= fun
->u
.i
.names
.array
;
2759 array
= (jsuword
*) cx
->malloc(MAX_ARRAY_LOCALS
* sizeof *array
);
2762 array
[0] = fun
->u
.i
.names
.taggedAtom
;
2763 fun
->u
.i
.names
.array
= array
;
2765 if (kind
== JSLOCAL_ARG
) {
2767 * A destructuring argument pattern adds variables, not arguments,
2768 * so for the following arguments nvars != 0.
2770 #if JS_HAS_DESTRUCTURING
2771 if (fun
->u
.i
.nvars
!= 0) {
2772 memmove(array
+ fun
->nargs
+ 1, array
+ fun
->nargs
,
2773 fun
->u
.i
.nvars
* sizeof *array
);
2776 JS_ASSERT(fun
->u
.i
.nvars
== 0);
2778 array
[fun
->nargs
] = taggedAtom
;
2780 array
[n
] = taggedAtom
;
2782 } else if (n
== MAX_ARRAY_LOCALS
) {
2783 array
= fun
->u
.i
.names
.array
;
2784 map
= (JSLocalNameMap
*) cx
->malloc(sizeof *map
);
2787 if (!JS_DHashTableInit(&map
->names
, JS_DHashGetStubOps(),
2788 NULL
, sizeof(JSLocalNameHashEntry
),
2789 JS_DHASH_DEFAULT_CAPACITY(MAX_ARRAY_LOCALS
2791 JS_ReportOutOfMemory(cx
);
2796 map
->lastdup
= NULL
;
2797 for (i
= 0; i
!= MAX_ARRAY_LOCALS
; ++i
) {
2798 taggedAtom
= array
[i
];
2800 JSLocalKind k
= JSLOCAL_ARG
;
2801 if (j
>= fun
->nargs
) {
2803 if (j
< fun
->u
.i
.nvars
) {
2804 k
= (taggedAtom
& 1) ? JSLOCAL_CONST
: JSLOCAL_VAR
;
2806 j
-= fun
->u
.i
.nvars
;
2810 if (!HashLocalName(cx
, map
, (JSAtom
*) (taggedAtom
& ~1), k
, j
)) {
2811 FreeLocalNameHash(cx
, map
);
2815 if (!HashLocalName(cx
, map
, atom
, kind
, *indexp
)) {
2816 FreeLocalNameHash(cx
, map
);
2821 * At this point the entry is added and we cannot fail. It is time
2822 * to replace fun->u.i.names with the built map.
2824 fun
->u
.i
.names
.map
= map
;
2827 if (*indexp
== JS_BITMASK(16)) {
2828 JS_ReportErrorNumber(cx
, js_GetErrorMessage
, NULL
,
2829 (kind
== JSLOCAL_ARG
)
2830 ? JSMSG_TOO_MANY_FUN_ARGS
2831 : JSMSG_TOO_MANY_LOCALS
);
2834 if (!HashLocalName(cx
, fun
->u
.i
.names
.map
, atom
, kind
, *indexp
))
2838 /* Update the argument or variable counter. */
2844 js_LookupLocal(JSContext
*cx
, JSFunction
*fun
, JSAtom
*atom
, uintN
*indexp
)
2846 uintN n
, i
, upvar_base
;
2848 JSLocalNameHashEntry
*entry
;
2850 JS_ASSERT(FUN_INTERPRETED(fun
));
2851 n
= fun
->countLocalNames();
2853 return JSLOCAL_NONE
;
2854 if (n
<= MAX_ARRAY_LOCALS
) {
2855 array
= (n
== 1) ? &fun
->u
.i
.names
.taggedAtom
: fun
->u
.i
.names
.array
;
2857 /* Search from the tail to pick up the last duplicated name. */
2859 upvar_base
= fun
->countArgsAndVars();
2862 if (atom
== JS_LOCAL_NAME_TO_ATOM(array
[i
])) {
2863 if (i
< fun
->nargs
) {
2868 if (i
>= upvar_base
) {
2870 *indexp
= i
- upvar_base
;
2871 return JSLOCAL_UPVAR
;
2874 *indexp
= i
- fun
->nargs
;
2875 return JS_LOCAL_NAME_IS_CONST(array
[i
])
2881 entry
= (JSLocalNameHashEntry
*)
2882 JS_DHashTableOperate(&fun
->u
.i
.names
.map
->names
, atom
,
2884 if (JS_DHASH_ENTRY_IS_BUSY(&entry
->hdr
)) {
2885 JS_ASSERT(entry
->localKind
!= JSLOCAL_NONE
);
2887 *indexp
= entry
->index
;
2888 return (JSLocalKind
) entry
->localKind
;
2891 return JSLOCAL_NONE
;
2894 typedef struct JSLocalNameEnumeratorArgs
{
2901 } JSLocalNameEnumeratorArgs
;
2903 static JSDHashOperator
2904 get_local_names_enumerator(JSDHashTable
*table
, JSDHashEntryHdr
*hdr
,
2905 uint32 number
, void *arg
)
2907 JSLocalNameHashEntry
*entry
;
2908 JSLocalNameEnumeratorArgs
*args
;
2912 entry
= (JSLocalNameHashEntry
*) hdr
;
2913 args
= (JSLocalNameEnumeratorArgs
*) arg
;
2914 JS_ASSERT(entry
->name
);
2915 if (entry
->localKind
== JSLOCAL_ARG
) {
2916 JS_ASSERT(entry
->index
< args
->fun
->nargs
);
2917 JS_ASSERT(args
->nCopiedArgs
++ < args
->fun
->nargs
);
2921 JS_ASSERT(entry
->localKind
== JSLOCAL_VAR
||
2922 entry
->localKind
== JSLOCAL_CONST
||
2923 entry
->localKind
== JSLOCAL_UPVAR
);
2924 JS_ASSERT(entry
->index
< args
->fun
->u
.i
.nvars
+ args
->fun
->u
.i
.nupvars
);
2925 JS_ASSERT(args
->nCopiedVars
++ < unsigned(args
->fun
->u
.i
.nvars
+ args
->fun
->u
.i
.nupvars
));
2926 i
= args
->fun
->nargs
;
2927 if (entry
->localKind
== JSLOCAL_UPVAR
)
2928 i
+= args
->fun
->u
.i
.nvars
;
2930 constFlag
= (entry
->localKind
== JSLOCAL_CONST
);
2932 args
->names
[i
] = (jsuword
) entry
->name
| constFlag
;
2933 return JS_DHASH_NEXT
;
2936 JS_FRIEND_API(jsuword
*)
2937 js_GetLocalNameArray(JSContext
*cx
, JSFunction
*fun
, JSArenaPool
*pool
)
2941 JSLocalNameMap
*map
;
2942 JSLocalNameEnumeratorArgs args
;
2943 JSNameIndexPair
*dup
;
2945 JS_ASSERT(fun
->hasLocalNames());
2946 n
= fun
->countLocalNames();
2948 if (n
<= MAX_ARRAY_LOCALS
)
2949 return (n
== 1) ? &fun
->u
.i
.names
.taggedAtom
: fun
->u
.i
.names
.array
;
2952 * No need to check for overflow of the allocation size as we are making a
2953 * copy of already allocated data. As such it must fit size_t.
2955 pool
->allocateCast
<jsuword
*>(names
, (size_t) n
* sizeof *names
);
2957 js_ReportOutOfScriptQuota(cx
);
2961 #if JS_HAS_DESTRUCTURING
2962 /* Some parameter names can be NULL due to destructuring patterns. */
2963 PodZero(names
, fun
->nargs
);
2965 map
= fun
->u
.i
.names
.map
;
2969 args
.nCopiedArgs
= 0;
2970 args
.nCopiedVars
= 0;
2972 JS_DHashTableEnumerate(&map
->names
, get_local_names_enumerator
, &args
);
2973 for (dup
= map
->lastdup
; dup
; dup
= dup
->link
) {
2974 JS_ASSERT(dup
->index
< fun
->nargs
);
2975 JS_ASSERT(args
.nCopiedArgs
++ < fun
->nargs
);
2976 names
[dup
->index
] = (jsuword
) dup
->name
;
2978 #if !JS_HAS_DESTRUCTURING
2979 JS_ASSERT(args
.nCopiedArgs
== fun
->nargs
);
2981 JS_ASSERT(args
.nCopiedVars
== fun
->u
.i
.nvars
+ fun
->u
.i
.nupvars
);
2986 static JSDHashOperator
2987 trace_local_names_enumerator(JSDHashTable
*table
, JSDHashEntryHdr
*hdr
,
2988 uint32 number
, void *arg
)
2990 JSLocalNameHashEntry
*entry
;
2993 entry
= (JSLocalNameHashEntry
*) hdr
;
2994 JS_ASSERT(entry
->name
);
2995 trc
= (JSTracer
*) arg
;
2996 JS_SET_TRACING_INDEX(trc
,
2997 entry
->localKind
== JSLOCAL_ARG
? "arg" : "var",
2999 js_CallGCMarker(trc
, ATOM_TO_STRING(entry
->name
), JSTRACE_STRING
);
3000 return JS_DHASH_NEXT
;
3004 TraceLocalNames(JSTracer
*trc
, JSFunction
*fun
)
3010 JS_ASSERT(FUN_INTERPRETED(fun
));
3011 n
= fun
->countLocalNames();
3014 if (n
<= MAX_ARRAY_LOCALS
) {
3015 array
= (n
== 1) ? &fun
->u
.i
.names
.taggedAtom
: fun
->u
.i
.names
.array
;
3019 atom
= (JSAtom
*) (array
[i
] & ~1);
3021 JS_SET_TRACING_INDEX(trc
,
3022 i
< fun
->nargs
? "arg" : "var",
3023 i
< fun
->nargs
? i
: i
- fun
->nargs
);
3024 js_CallGCMarker(trc
, ATOM_TO_STRING(atom
), JSTRACE_STRING
);
3028 JS_DHashTableEnumerate(&fun
->u
.i
.names
.map
->names
,
3029 trace_local_names_enumerator
, trc
);
3032 * No need to trace the list of duplicates in map->lastdup as the
3033 * names there are traced when enumerating the hash table.
3039 DestroyLocalNames(JSContext
*cx
, JSFunction
*fun
)
3043 n
= fun
->countLocalNames();
3046 if (n
<= MAX_ARRAY_LOCALS
)
3047 cx
->free(fun
->u
.i
.names
.array
);
3049 FreeLocalNameHash(cx
, fun
->u
.i
.names
.map
);
3053 js_FreezeLocalNames(JSContext
*cx
, JSFunction
*fun
)
3058 JS_ASSERT(FUN_INTERPRETED(fun
));
3059 JS_ASSERT(!fun
->u
.i
.script
);
3060 n
= fun
->nargs
+ fun
->u
.i
.nvars
+ fun
->u
.i
.nupvars
;
3061 if (2 <= n
&& n
< MAX_ARRAY_LOCALS
) {
3062 /* Shrink over-allocated array ignoring realloc failures. */
3063 array
= (jsuword
*) cx
->realloc(fun
->u
.i
.names
.array
,
3066 fun
->u
.i
.names
.array
= array
;
3069 if (n
> MAX_ARRAY_LOCALS
)
3070 JS_DHashMarkTableImmutable(&fun
->u
.i
.names
.map
->names
);
3075 JSFunction::findDuplicateFormal() const
3080 /* Function with two to MAX_ARRAY_LOCALS parameters use an aray. */
3081 unsigned n
= nargs
+ u
.i
.nvars
+ u
.i
.nupvars
;
3082 if (n
<= MAX_ARRAY_LOCALS
) {
3083 jsuword
*array
= u
.i
.names
.array
;
3085 /* Quadratic, but MAX_ARRAY_LOCALS is 8, so at most 28 comparisons. */
3086 for (unsigned i
= 0; i
< nargs
; i
++) {
3087 for (unsigned j
= i
+ 1; j
< nargs
; j
++) {
3088 if (array
[i
] == array
[j
])
3089 return JS_LOCAL_NAME_TO_ATOM(array
[i
]);
3096 * Functions with more than MAX_ARRAY_LOCALS parameters use a hash
3097 * table. Hashed local name maps have already made a list of any
3098 * duplicate argument names for us.
3100 JSNameIndexPair
*dup
= u
.i
.names
.map
->lastdup
;
3101 return dup
? dup
->name
: NULL
;